use std::fmt::{Debug, Display, Formatter};
use std::time::Duration;
use structbuf::{Pack, Packer, Unpacker};
use tracing::{debug, error, trace, warn};
pub use {consts::*, handle::*, perm::*};
use crate::gap::Uuid;
use crate::l2cap::{BasicChan, Cid, LeCid, Payload};
use crate::{hci, l2cap};
mod consts;
mod handle;
mod pdu;
mod perm;
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error(transparent)]
L2Cap(#[from] l2cap::Error),
#[error(transparent)]
Att(#[from] ErrorRsp),
#[error("timeout while waiting for {0}")]
Timeout(Opcode),
#[error("notification/indication session closed")]
NotifyClosed,
}
pub type Result<T> = std::result::Result<T, Error>;
pub type RspResult<T> = std::result::Result<T, ErrorRsp>;
#[derive(Debug)]
#[repr(transparent)]
pub struct Pdu(Payload);
#[derive(Debug)]
#[repr(transparent)]
pub struct Rsp(Payload);
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub struct ErrorRsp {
req: u8,
hdl: Option<Handle>,
err: ErrorCode,
}
impl ErrorRsp {
#[inline(always)]
#[must_use]
pub(crate) const fn new(req: u8, hdl: Option<Handle>, err: ErrorCode) -> Self {
Self { req, hdl, err }
}
}
impl Display for ErrorRsp {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use num_enum::TryFromPrimitive;
f.write_str("ATT ")?;
match Opcode::try_from_primitive(self.req) {
Ok(op) => write!(f, "{op} "),
Err(_) => write!(f, "request {:#04X} ", self.req),
}?;
if let Some(hdl) = self.hdl {
write!(f, "for handle {:#06X} ", u16::from(hdl))?;
}
write!(f, "failed with {}", self.err)
}
}
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct Bearer(BasicChan);
impl Bearer {
#[inline]
#[must_use]
pub(crate) const fn new(ch: BasicChan) -> Self {
Self(ch)
}
#[inline(always)]
#[must_use]
pub fn cid(&self) -> LeCid {
self.0.cid()
}
#[inline(always)]
pub(crate) fn conn(&self) -> &hci::ConnWatch {
self.0.conn()
}
#[inline(always)]
#[must_use]
pub const fn mtu(&self) -> u16 {
self.0.mtu()
}
pub async fn recv(&mut self) -> Result<Pdu> {
let pdu = self.0.recv().await?;
let Some(&op) = pdu.as_ref().first() else {
warn!("Empty PDU");
return Err(ErrorRsp::new(0, None, ErrorCode::InvalidPdu).into());
};
let Some(op) = Opcode::try_from(op).ok() else {
warn!("Unknown opcode: {op}");
return Err(ErrorRsp::new(op, None, ErrorCode::RequestNotSupported).into());
};
if matches!(op.typ(), PduType::Rsp | PduType::Cfm) {
warn!("Unexpected PDU: {op}");
return Err(ErrorRsp::new(op as _, None, ErrorCode::UnlikelyError).into());
}
Ok(Pdu(pdu))
}
#[inline]
pub(crate) fn access_req(&self, pdu: &Pdu) -> Request {
let sec = self.0.conn().borrow().sec;
pdu.opcode().request(sec)
}
pub async fn send_rsp(&mut self, r: RspResult<Rsp>) -> Result<()> {
let rsp = match r {
Ok(r) => r.0,
Err(e) => {
warn!("{e}");
if Opcode::is_cmd(e.req) {
return Ok(());
}
self.pack(Opcode::ErrorRsp, |p| {
p.u8(e.req).u16(e.hdl.map_or(0, u16::from)).u8(e.err);
})
}
};
self.send(rsp).await
}
pub(crate) async fn exchange_mtu(&mut self) -> Result<()> {
if self.0.cid().chan != Cid::ATT {
return Ok(());
}
let local = self.0.preferred_mtu();
let req = self.pack(Opcode::ExchangeMtuReq, |p| {
p.u16(local);
});
self.send(req).await?;
loop {
let pdu = match self.recv_rsp(Opcode::ExchangeMtuRsp).await {
Ok(p) => Pdu(p),
Err(Error::Att(ErrorRsp {
err: ErrorCode::RequestNotSupported,
..
})) => {
debug!("Remote host does not support ATT_EXCHANGE_MTU_REQ");
return Ok(());
}
Err(e) => return Err(e),
};
match pdu.opcode() {
Opcode::ExchangeMtuReq => {
self.handle_exchange_mtu_req(&pdu).await?;
}
Opcode::ExchangeMtuRsp => {
let remote = pdu.unpack(Opcode::ExchangeMtuRsp, |p| Ok(p.u16()))?;
debug!("{} remote preferred MTU: {}", self.cid(), remote);
self.0.set_mtu(local.min(remote));
return Ok(());
}
_ => unreachable!(),
}
}
}
pub(crate) async fn handle_exchange_mtu_req(&mut self, pdu: &Pdu) -> Result<()> {
let r = (pdu.unpack(Opcode::ExchangeMtuReq, |p| Ok(p.u16()))).and_then(|remote| {
debug!("{} remote preferred MTU: {}", self.cid(), remote);
let local = self.0.preferred_mtu();
self.0.set_mtu(local.min(remote));
self.rsp(Opcode::ExchangeMtuRsp, |p| {
p.u16(local);
Ok(())
})
});
self.send_rsp(r).await
}
async fn recv_rsp(&mut self, rsp: Opcode) -> Result<Payload> {
let want = u8::from(rsp);
let err = matches!(rsp.typ(), PduType::Rsp).then_some(Opcode::ErrorRsp as u8);
let r = tokio::time::timeout(
Duration::from_secs(30),
self.0.recv_filter(|mut pdu| {
let have = pdu.u8();
have == want
|| (Some(have) == err && pdu.u8() == want)
|| (want == Opcode::ExchangeMtuRsp as u8
&& have == Opcode::ExchangeMtuReq as u8)
}),
);
let pdu = match r.await {
Ok(Ok(pdu)) => pdu,
Ok(Err(e)) => return Err(e.into()),
Err(_) => {
self.0.set_error();
return Err(Error::Timeout(rsp));
}
};
let mut p = Unpacker::new(pdu.as_ref());
if Some(p.u8()) == err {
Err(Error::Att(ErrorRsp {
req: p.u8(),
hdl: Handle::new(p.u16()),
err: ErrorCode::try_from(p.u8()).unwrap_or(ErrorCode::UnlikelyError),
}))
} else {
Ok(pdu)
}
}
#[allow(clippy::unnecessary_wraps)]
#[inline(always)]
fn rsp(&self, op: Opcode, f: impl FnOnce(&mut Packer) -> RspResult<()>) -> RspResult<Rsp> {
let mut pdu = self.0.new_payload();
f(pdu.append().u8(op))?;
trace!("{op}: {:02X?}", pdu.as_ref());
Ok(Rsp(pdu))
}
#[inline(always)]
fn pack(&self, op: Opcode, f: impl FnOnce(&mut Packer)) -> Payload {
let mut pdu = self.0.new_payload();
f(pdu.append().u8(op));
trace!("{op}: {:02X?}", pdu.as_ref());
pdu
}
#[inline(always)]
async fn send(&mut self, pdu: Payload) -> Result<()> {
Ok(self.0.send(pdu).await?)
}
}
pub trait ValueIter<H> {
fn more(&mut self) -> RspResult<bool>;
#[must_use]
fn handle(&self) -> H {
unimplemented!()
}
#[must_use]
fn value(&self) -> &[u8];
}
pub(crate) struct ValueFn<'a, H, F> {
f: F,
v: Option<(H, &'a [u8])>,
}
impl<'a, H, F: FnMut() -> Option<(H, &'a [u8])>> ValueFn<'a, H, F> {
#[inline(always)]
#[must_use]
pub const fn new(f: F) -> Self {
Self { f, v: None }
}
}
impl<'a, H: Copy, F: FnMut() -> Option<(H, &'a [u8])>> ValueIter<H> for ValueFn<'a, H, F> {
fn more(&mut self) -> RspResult<bool> {
let Some(val) = (self.f)() else { return Ok(false) };
self.v = Some(val);
Ok(true)
}
#[inline(always)]
fn handle(&self) -> H {
self.v.unwrap().0
}
#[inline(always)]
fn value(&self) -> &[u8] {
self.v.unwrap().1
}
}