use std::collections::BTreeSet;
use futures_core::FusedFuture;
use pin_project::pin_project;
use crate::le::TxPower;
use super::*;
#[derive(Debug)]
pub struct Advertiser {
host: Host,
handles: BTreeSet<AdvHandle>,
max_data_len: usize,
local_addr: le::Addr,
}
impl Advertiser {
pub async fn new(host: &Host) -> Result<Self> {
host.le_clear_advertising_sets().await?;
Ok(Self {
host: host.clone(),
handles: BTreeSet::new(),
max_data_len: host.le_read_maximum_advertising_data_length().await?,
local_addr: host.read_bd_addr().await?,
})
}
#[inline]
#[must_use]
pub const fn max_data_len(&self) -> usize {
self.max_data_len
}
pub async fn create(&mut self, p: AdvParams) -> Result<(AdvHandle, TxPower)> {
let h = self.alloc_handle()?;
(self.host.le_set_extended_advertising_parameters(h, p).await).map(|p| {
self.handles.insert(h);
(h, p)
})
}
pub async fn set_data<V>(&mut self, h: AdvHandle, d: V) -> Result<()>
where
V: AsRef<[u8]> + Send + Sync,
{
for (op, chunk) in Self::op_chunks(d.as_ref(), 251) {
self.host
.le_set_extended_advertising_data(h, op, true, chunk)
.await?;
}
Ok(())
}
pub async fn set_scan_response<V>(&mut self, h: AdvHandle, d: V) -> Result<()>
where
V: AsRef<[u8]> + Send + Sync,
{
for (op, chunk) in Self::op_chunks(d.as_ref(), 31) {
self.host
.le_set_extended_scan_response_data(h, op, true, chunk)
.await?;
}
Ok(())
}
pub async fn enable(&mut self, p: impl Into<AdvEnableParams> + Send) -> Result<AdvFuture> {
let p = p.into();
let ctl = self.host.events();
(self.host.le_set_extended_advertising_enable(true, &[p])).await?;
Ok(AdvFuture::new(p.handle, ctl, self.local_addr))
}
pub async fn disable(&mut self, h: AdvHandle) -> Result<()> {
self.host
.le_set_extended_advertising_enable(false, &[h.into()])
.await
}
pub async fn disable_all(&mut self) -> Result<()> {
(self.host.le_set_extended_advertising_enable(false, &[])).await
}
pub async fn remove(&mut self, h: AdvHandle) -> Result<()> {
self.host.le_remove_advertising_set(h).await
}
pub async fn remove_all(&mut self) -> Result<()> {
self.host.le_clear_advertising_sets().await
}
fn alloc_handle(&self) -> Result<AdvHandle> {
for i in AdvHandle::MIN..=AdvHandle::MAX {
let h = unsafe { AdvHandle::new(i).unwrap_unchecked() };
if !self.handles.contains(&h) {
return Ok(h);
}
}
Err(Status::LimitReached.into())
}
fn op_chunks(d: &[u8], chunk_size: usize) -> impl ExactSizeIterator<Item = (AdvDataOp, &[u8])> {
let mut chunks = d.chunks(chunk_size);
if chunks.len() == 0 {
chunks = [0].chunks(1);
}
let last = chunks.len() - 1;
chunks.enumerate().map(move |(i, c)| match i {
0 if last == 0 => (AdvDataOp::Complete, d),
0 => (AdvDataOp::First, c),
i if last == i => (AdvDataOp::Last, c),
_ => (AdvDataOp::Cont, c),
})
}
}
#[allow(variant_size_differences)]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum AdvEvent {
Conn {
conn: LeConnectionComplete,
term: LeAdvertisingSetTerminated,
},
Term(LeAdvertisingSetTerminated),
}
#[pin_project(project = AdvFutureProj)]
#[derive(Debug)]
pub struct AdvFuture {
hdl: Option<AdvHandle>,
ctl: EventStream,
local_addr: le::Addr,
conn: BTreeMap<ConnHandle, LeConnectionComplete>,
term: Option<LeAdvertisingSetTerminated>,
#[pin]
timeout: Option<tokio::time::Sleep>,
}
impl AdvFuture {
#[inline]
#[must_use]
fn new(hdl: AdvHandle, ctl: EventStream, local_addr: le::Addr) -> Self {
Self {
hdl: Some(hdl),
ctl,
local_addr,
conn: BTreeMap::new(),
term: None,
timeout: None,
}
}
}
impl AdvFutureProj<'_> {
#[inline]
fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Poll<<AdvFuture as Future>::Output> {
match self.timeout.as_mut().as_pin_mut().map(|s| s.poll(cx)) {
Some(Poll::Ready(_)) => {
let term = self.term.take().unwrap();
self.ready(Ok(AdvEvent::Term(term)))
}
_ => Poll::Pending,
}
}
#[inline(always)]
fn ready(&mut self, r: <AdvFuture as Future>::Output) -> Poll<<AdvFuture as Future>::Output> {
*self.hdl = None;
Poll::Ready(r)
}
#[inline]
fn ready_conn(
&mut self,
conn: LeConnectionComplete,
term: LeAdvertisingSetTerminated,
) -> Poll<<AdvFuture as Future>::Output> {
if conn.status.is_ok() {
(self.ctl).update_conn(conn.handle, |cn| cn.local_addr = Some(*self.local_addr));
}
self.ready(Ok(AdvEvent::Conn { conn, term }))
}
}
impl Future for AdvFuture {
type Output = Result<AdvEvent>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
use EventCode::*;
let mut this = self.project();
let hdl = this.hdl.expect("poll of a completed future");
let evt = match this.ctl.poll(Some(cx)) {
Poll::Ready(r) => match r {
Ok(evt) => {
let p = this.ctl.poll(Some(cx));
debug_assert!(p.is_pending());
evt
}
Err(e) => return this.ready(Err(e)),
},
Poll::Pending => return this.poll_timeout(cx),
};
let term = match evt.code() {
LeConnectionComplete | LeEnhancedConnectionComplete => {
let conn: super::LeConnectionComplete = evt.get();
if let Some(term) = this.term.as_ref() {
if conn.handle == term.conn_handle.unwrap() {
return this.ready_conn(conn, term.clone());
}
}
this.conn.insert(conn.handle, conn);
return Poll::Pending;
}
LeAdvertisingSetTerminated => {
let term: super::LeAdvertisingSetTerminated = evt.get();
if term.adv_handle != hdl || this.term.is_some() {
return Poll::Pending;
}
term
}
_ => return Poll::Pending,
};
if !term.status.is_ok() || term.conn_handle.is_none() {
return this.ready(Ok(AdvEvent::Term(term)));
}
if let Some(conn) = this.conn.remove(&term.conn_handle.unwrap()) {
return this.ready_conn(conn, term);
}
*this.term = Some(term);
(this.timeout).set(Some(tokio::time::sleep(Duration::from_millis(100))));
this.poll_timeout(cx)
}
}
impl FusedFuture for AdvFuture {
#[inline(always)]
fn is_terminated(&self) -> bool {
self.hdl.is_none()
}
}