use bt_hci::controller::{blocking, Controller};
use bt_hci::param::ConnHandle;
pub use crate::channel_manager::CreditFlowPolicy;
#[cfg(feature = "channel-metrics")]
pub use crate::channel_manager::Metrics as ChannelMetrics;
use crate::channel_manager::{ChannelIndex, ChannelManager};
use crate::connection::Connection;
use crate::host::BleHost;
use crate::pdu::Sdu;
pub use crate::types::l2cap::LeCreditConnResultCode;
use crate::{BleHostError, Error, PacketPool, Stack};
pub(crate) mod sar;
pub struct L2capChannel<'d, P: PacketPool> {
index: ChannelIndex,
manager: &'d ChannelManager<'d, P>,
}
pub struct L2capChannelWriter<'d, P: PacketPool> {
index: ChannelIndex,
manager: &'d ChannelManager<'d, P>,
}
pub struct L2capChannelReader<'d, P: PacketPool> {
index: ChannelIndex,
manager: &'d ChannelManager<'d, P>,
}
pub struct L2capChannelRef<'d, P: PacketPool> {
index: ChannelIndex,
manager: &'d ChannelManager<'d, P>,
}
#[cfg(feature = "defmt")]
impl<P: PacketPool> defmt::Format for L2capChannel<'_, P> {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "{}, ", self.index);
self.manager.print(self.index, f);
}
}
impl<P: PacketPool> Drop for L2capChannel<'_, P> {
fn drop(&mut self) {
self.manager.dec_ref(self.index);
}
}
impl<P: PacketPool> Drop for L2capChannelRef<'_, P> {
fn drop(&mut self) {
self.manager.dec_ref(self.index);
}
}
#[cfg(feature = "defmt")]
impl<P: PacketPool> defmt::Format for L2capChannelWriter<'_, P> {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "{}, ", self.index);
self.manager.print(self.index, f);
}
}
impl<P: PacketPool> Drop for L2capChannelWriter<'_, P> {
fn drop(&mut self) {
self.manager.dec_ref(self.index);
}
}
#[cfg(feature = "defmt")]
impl<P: PacketPool> defmt::Format for L2capChannelReader<'_, P> {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "{}, ", self.index);
self.manager.print(self.index, f);
}
}
impl<P: PacketPool> Drop for L2capChannelReader<'_, P> {
fn drop(&mut self) {
self.manager.dec_ref(self.index);
}
}
pub struct L2capPendingConnection<'d, P: PacketPool> {
index: Option<ChannelIndex>,
manager: &'d ChannelManager<'d, P>,
conn: ConnHandle,
}
impl<'d, P: PacketPool> L2capPendingConnection<'d, P> {
pub(crate) fn new(index: ChannelIndex, manager: &'d ChannelManager<'d, P>, conn: ConnHandle) -> Self {
Self {
index: Some(index),
manager,
conn,
}
}
pub(crate) fn into_index(mut self) -> ChannelIndex {
self.index.take().unwrap()
}
pub fn spsm(&self) -> u16 {
self.manager.spsm(self.index.unwrap())
}
pub fn mtu(&self) -> u16 {
self.manager.mtu(self.index.unwrap())
}
pub async fn accept<T: Controller>(
self,
stack: &'d Stack<'_, T, P>,
config: &L2capChannelConfig,
) -> Result<L2capChannel<'d, P>, BleHostError<T::Error>> {
let index = self.into_index();
stack.host.channels.accept_pending(index, config, stack.host).await
}
pub async fn reject<T: Controller>(
mut self,
stack: &Stack<'_, T, P>,
result: LeCreditConnResultCode,
) -> Result<(), BleHostError<T::Error>> {
let index = self.index.take().unwrap();
stack.host.channels.reject_pending(index, result, stack.host).await
}
}
impl<P: PacketPool> Drop for L2capPendingConnection<'_, P> {
fn drop(&mut self) {
if let Some(index) = self.index.take() {
error!("L2capPendingConnection dropped without being accepted or rejected");
self.manager.drop_pending(index);
}
}
}
#[derive(Default)]
pub struct L2capChannelConfig {
pub mtu: Option<u16>,
pub mps: Option<u16>,
pub flow_policy: CreditFlowPolicy,
pub initial_credits: Option<u16>,
}
impl<'d, P: PacketPool> L2capChannel<'d, P> {
pub(crate) fn new(index: ChannelIndex, manager: &'d ChannelManager<'d, P>) -> Self {
Self { index, manager }
}
pub fn disconnect(&mut self) {
self.manager.disconnect(self.index);
}
pub fn spsm(&self) -> u16 {
self.manager.spsm(self.index)
}
pub fn mtu(&self) -> u16 {
self.manager.mtu(self.index)
}
pub fn mps(&self) -> u16 {
self.manager.mps(self.index)
}
pub fn peer_mtu(&self) -> u16 {
self.manager.peer_mtu(self.index)
}
pub fn peer_mps(&self) -> u16 {
self.manager.peer_mps(self.index)
}
pub async fn send<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &[u8],
) -> Result<(), BleHostError<T::Error>> {
let mut p_buf = P::allocate().ok_or(Error::OutOfMemory)?;
stack
.host
.channels
.send(self.index, buf, p_buf.as_mut(), stack.host)
.await
}
pub fn try_send<T: Controller + blocking::Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &[u8],
) -> Result<(), BleHostError<T::Error>> {
let mut p_buf = P::allocate().ok_or(Error::OutOfMemory)?;
stack
.host
.channels
.try_send(self.index, buf, p_buf.as_mut(), stack.host)
}
pub async fn receive<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &mut [u8],
) -> Result<usize, BleHostError<T::Error>> {
stack.host.channels.receive(self.index, buf, stack.host).await
}
pub async fn receive_sdu<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
) -> Result<Sdu<P::Packet>, BleHostError<T::Error>> {
stack.host.channels.receive_sdu(self.index, stack.host).await
}
#[cfg(feature = "channel-metrics")]
pub fn metrics<F: FnOnce(&ChannelMetrics) -> R, R>(&self, f: F) -> R {
self.manager.metrics(self.index, f)
}
pub fn listen<T: Controller>(
stack: &'d Stack<'_, T, P>,
connection: &Connection<'d, P>,
) -> L2capChannelListener<'d, T, P> {
connection.set_l2cap_listening(true);
L2capChannelListener {
conn: connection.clone(),
host: stack.host,
}
}
pub async fn create<T: Controller>(
stack: &'d Stack<'_, T, P>,
connection: &Connection<'_, P>,
spsm: u16,
config: &L2capChannelConfig,
) -> Result<Self, BleHostError<T::Error>> {
stack
.host
.channels
.create(connection.handle(), spsm, config, stack.host)
.await
}
pub fn split(self) -> (L2capChannelWriter<'d, P>, L2capChannelReader<'d, P>) {
self.manager.inc_ref(self.index);
self.manager.inc_ref(self.index);
(
L2capChannelWriter {
index: self.index,
manager: self.manager,
},
L2capChannelReader {
index: self.index,
manager: self.manager,
},
)
}
pub fn merge(writer: L2capChannelWriter<'d, P>, reader: L2capChannelReader<'d, P>) -> Self {
assert_eq!(writer.index, reader.index);
let manager = writer.manager;
let index = writer.index;
manager.inc_ref(index);
Self { index, manager }
}
}
impl<'d, P: PacketPool> L2capChannelReader<'d, P> {
pub fn disconnect(&mut self) {
self.manager.disconnect(self.index);
}
pub fn mtu(&self) -> u16 {
self.manager.mtu(self.index)
}
pub fn mps(&self) -> u16 {
self.manager.mps(self.index)
}
pub async fn receive<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &mut [u8],
) -> Result<usize, BleHostError<T::Error>> {
stack.host.channels.receive(self.index, buf, stack.host).await
}
pub async fn receive_sdu<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
) -> Result<Sdu<P::Packet>, BleHostError<T::Error>> {
stack.host.channels.receive_sdu(self.index, stack.host).await
}
#[cfg(feature = "channel-metrics")]
pub fn metrics<F: FnOnce(&ChannelMetrics) -> R, R>(&self, f: F) -> R {
self.manager.metrics(self.index, f)
}
pub fn channel_ref(&mut self) -> L2capChannelRef<'d, P> {
self.manager.inc_ref(self.index);
L2capChannelRef {
index: self.index,
manager: self.manager,
}
}
}
impl<'d, P: PacketPool> L2capChannelRef<'d, P> {
#[cfg(feature = "channel-metrics")]
pub fn metrics<F: FnOnce(&ChannelMetrics) -> R, R>(&self, f: F) -> R {
self.manager.metrics(self.index, f)
}
}
impl<'d, P: PacketPool> L2capChannelWriter<'d, P> {
pub fn disconnect(&mut self) {
self.manager.disconnect(self.index);
}
pub fn peer_mtu(&self) -> u16 {
self.manager.peer_mtu(self.index)
}
pub fn peer_mps(&self) -> u16 {
self.manager.peer_mps(self.index)
}
pub async fn send<T: Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &[u8],
) -> Result<(), BleHostError<T::Error>> {
let mut p_buf = P::allocate().ok_or(Error::OutOfMemory)?;
stack
.host
.channels
.send(self.index, buf, p_buf.as_mut(), stack.host)
.await
}
pub fn try_send<T: Controller + blocking::Controller>(
&mut self,
stack: &Stack<'_, T, P>,
buf: &[u8],
) -> Result<(), BleHostError<T::Error>> {
let mut p_buf = P::allocate().ok_or(Error::OutOfMemory)?;
stack
.host
.channels
.try_send(self.index, buf, p_buf.as_mut(), stack.host)
}
#[cfg(feature = "channel-metrics")]
pub fn metrics<F: FnOnce(&ChannelMetrics) -> R, R>(&self, f: F) -> R {
self.manager.metrics(self.index, f)
}
pub fn channel_ref(&mut self) -> L2capChannelRef<'d, P> {
self.manager.inc_ref(self.index);
L2capChannelRef {
index: self.index,
manager: self.manager,
}
}
}
pub struct L2capChannelListener<'d, T: Controller, P: PacketPool> {
conn: Connection<'d, P>,
host: &'d BleHost<'d, T, P>,
}
impl<'d, T: Controller, P: PacketPool> L2capChannelListener<'d, T, P> {
pub async fn next(&self) -> Result<L2capPendingConnection<'d, P>, Error> {
self.host
.channels
.next_pending(self.conn.handle(), &self.host.connections)
.await
}
pub async fn accept(&self, config: &L2capChannelConfig) -> Result<L2capChannel<'d, P>, BleHostError<T::Error>> {
let pending = self.next().await?;
let index = pending.into_index();
self.host.channels.accept_pending(index, config, self.host).await
}
pub fn connection(&self) -> &Connection<'d, P> {
&self.conn
}
}
impl<T: Controller, P: PacketPool> Drop for L2capChannelListener<'_, T, P> {
fn drop(&mut self) {
self.conn.set_l2cap_listening(false);
self.host
.channels
.reject_all_pending(self.conn.handle(), &self.host.connections);
}
}