use core::ffi::{c_int, c_void};
use crate::sys::*;
use super::mbuf::Mbuf;
use super::{BleDriver, BleError, ConnHandle};
#[cfg(all(esp_idf_soc_esp_nimble_controller, esp_idf_bt_controller_enabled))]
use crate::sys::r_os_mbuf_free_chain as os_mbuf_free_chain;
#[cfg(all(esp_idf_soc_esp_nimble_controller, esp_idf_bt_controller_enabled))]
use crate::sys::r_os_msys_get_pkthdr as os_msys_get_pkthdr;
#[derive(Clone, Copy)]
pub struct L2capChan(*mut ble_l2cap_chan);
unsafe impl Send for L2capChan {}
unsafe impl Sync for L2capChan {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SendOutcome {
Sent,
Stalled,
}
pub enum L2capEvent<'a> {
Connected {
conn_handle: ConnHandle,
status: i32,
chan: L2capChan,
},
Disconnected {
conn_handle: ConnHandle,
chan: L2capChan,
},
Accept {
conn_handle: ConnHandle,
peer_sdu_size: u16,
chan: L2capChan,
},
Received {
conn_handle: ConnHandle,
chan: L2capChan,
data: Mbuf<'a>,
},
TxUnstalled {
conn_handle: ConnHandle,
status: i32,
chan: L2capChan,
},
Reconfigured {
conn_handle: ConnHandle,
status: i32,
chan: L2capChan,
by_peer: bool,
},
}
impl<'a> L2capEvent<'a> {
pub(crate) fn from_raw(event: &'a ble_l2cap_event) -> Option<Self> {
let anon = &event.__bindgen_anon_1;
Some(match event.type_ as u32 {
BLE_L2CAP_EVENT_COC_CONNECTED => {
let e = unsafe { &anon.connect };
Self::Connected {
conn_handle: e.conn_handle,
status: e.status,
chan: L2capChan(e.chan),
}
}
BLE_L2CAP_EVENT_COC_DISCONNECTED => {
let e = unsafe { &anon.disconnect };
Self::Disconnected {
conn_handle: e.conn_handle,
chan: L2capChan(e.chan),
}
}
BLE_L2CAP_EVENT_COC_ACCEPT => {
let e = unsafe { &anon.accept };
Self::Accept {
conn_handle: e.conn_handle,
peer_sdu_size: e.peer_sdu_size,
chan: L2capChan(e.chan),
}
}
BLE_L2CAP_EVENT_COC_DATA_RECEIVED => {
let e = unsafe { &anon.receive };
Self::Received {
conn_handle: e.conn_handle,
chan: L2capChan(e.chan),
data: Mbuf::from_raw(e.sdu_rx),
}
}
BLE_L2CAP_EVENT_COC_TX_UNSTALLED => {
let e = unsafe { &anon.tx_unstalled };
Self::TxUnstalled {
conn_handle: e.conn_handle,
status: e.status,
chan: L2capChan(e.chan),
}
}
BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED | BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED => {
let e = unsafe { &anon.reconfigured };
Self::Reconfigured {
conn_handle: e.conn_handle,
status: e.status,
chan: L2capChan(e.chan),
by_peer: event.type_ as u32 == BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED,
}
}
_ => return None,
})
}
}
pub(crate) fn free_mbuf(om: *mut os_mbuf) {
if !om.is_null() {
unsafe { os_mbuf_free_chain(om) };
}
}
fn alloc_sdu(size: u16) -> Result<*mut os_mbuf, BleError> {
let om = unsafe { os_msys_get_pkthdr(size, 0) };
if om.is_null() {
Err(BleError::new(BLE_HS_ENOMEM as c_int))
} else {
Ok(om)
}
}
impl<'d, S> BleDriver<'d, S> {
pub fn l2cap_subscribe<F>(&self, callback: F)
where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'static,
{
unsafe { self.l2cap_subscribe_nonstatic(callback) }
}
pub unsafe fn l2cap_subscribe_nonstatic<F>(&self, callback: F)
where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'd,
{
unsafe { super::SINGLETON.l2cap.subscribe_nonstatic(callback) };
}
pub fn l2cap_unsubscribe(&self) {
super::SINGLETON.l2cap.unsubscribe();
}
pub fn l2cap_create_server(&self, psm: u16, mtu: u16) -> Result<(), BleError> {
BleError::from_raw(unsafe {
ble_l2cap_create_server(
psm,
mtu,
Some(super::BleSingleton::l2cap_event_cb),
core::ptr::null_mut(),
)
})
}
pub fn l2cap_connect(
&self,
conn_handle: ConnHandle,
psm: u16,
mtu: u16,
) -> Result<(), BleError> {
let sdu_rx = alloc_sdu(mtu)?;
let rc = unsafe {
ble_l2cap_connect(
conn_handle,
psm,
mtu,
sdu_rx,
Some(super::BleSingleton::l2cap_event_cb),
core::ptr::null_mut(),
)
};
if rc == BLE_HS_ENOTCONN as c_int || rc == BLE_HS_EINVAL as c_int {
free_mbuf(sdu_rx);
}
BleError::from_raw(rc)
}
pub fn l2cap_send(&self, chan: L2capChan, data: &[u8]) -> Result<SendOutcome, BleError> {
let sdu =
unsafe { ble_hs_mbuf_from_flat(data.as_ptr() as *const c_void, data.len() as u16) };
if sdu.is_null() {
return Err(BleError::new(BLE_HS_ENOMEM as c_int));
}
let rc = unsafe { ble_l2cap_send(chan.0, sdu) };
match rc as u32 {
0 => Ok(SendOutcome::Sent),
BLE_HS_ESTALLED => Ok(SendOutcome::Stalled),
BLE_HS_EBADDATA | BLE_HS_EBUSY => {
free_mbuf(sdu);
BleError::from_raw(rc).map(|()| SendOutcome::Sent)
}
_ => BleError::from_raw(rc).map(|()| SendOutcome::Sent),
}
}
pub fn l2cap_recv_ready(&self, chan: L2capChan, sdu_size: u16) -> Result<(), BleError> {
let sdu_rx = alloc_sdu(sdu_size)?;
let rc = unsafe { ble_l2cap_recv_ready(chan.0, sdu_rx) };
if rc == BLE_HS_EBUSY as c_int || rc == BLE_HS_EINVAL as c_int {
free_mbuf(sdu_rx);
}
BleError::from_raw(rc)
}
pub fn l2cap_disconnect(&self, chan: L2capChan) -> Result<(), BleError> {
BleError::from_raw(unsafe { ble_l2cap_disconnect(chan.0) })
}
}