esp32_nimble/l2cap/
l2cap_client.rs

1use alloc::boxed::Box;
2use core::borrow::BorrowMut;
3use esp_idf_svc::hal::task::block_on;
4use esp_idf_svc::sys;
5
6use super::{L2cap, ReceivedData};
7use crate::{BLEClient, BLEError, Channel, Signal, ble, utilities::voidp_to_ref};
8
9#[allow(clippy::type_complexity)]
10pub struct L2capClient {
11    l2cap: L2cap,
12    coc_chan: *mut sys::ble_l2cap_chan,
13    signal: Signal<u32>,
14    channel: Channel<ReceivedData, 1>,
15}
16
17impl L2capClient {
18    pub async fn connect(
19        ble_client: &BLEClient,
20        psm: u16,
21        mtu: u16,
22    ) -> Result<Box<Self>, BLEError> {
23        let mut ret = Box::new(Self {
24            l2cap: Default::default(),
25            coc_chan: core::ptr::null_mut(),
26            signal: Signal::new(),
27            channel: Channel::new(),
28        });
29
30        ret.l2cap.init(mtu, 3)?;
31
32        unsafe {
33            ble!(sys::ble_l2cap_connect(
34                ble_client.conn_handle(),
35                psm,
36                mtu,
37                ret.l2cap.sdu_rx().0,
38                Some(Self::blecent_l2cap_coc_event_cb),
39                ret.borrow_mut() as *mut Self as _,
40            ))?;
41        }
42
43        ble!(ret.signal.wait().await)?;
44
45        Ok(ret)
46    }
47
48    pub async fn disconnect(&mut self) -> Result<(), BLEError> {
49        if self.coc_chan.is_null() {
50            return Ok(());
51        }
52
53        ble!(unsafe { sys::ble_l2cap_disconnect(self.coc_chan) })?;
54        let _ = self.signal.wait().await;
55
56        Ok(())
57    }
58
59    pub fn tx(&mut self, data: &[u8]) -> Result<(), BLEError> {
60        self.l2cap.tx(self.coc_chan, data)
61    }
62
63    pub async fn rx(&mut self) -> ReceivedData {
64        self.channel.receive().await
65    }
66
67    pub(crate) extern "C" fn blecent_l2cap_coc_event_cb(
68        _event: *mut sys::ble_l2cap_event,
69        arg: *mut core::ffi::c_void,
70    ) -> i32 {
71        let event = unsafe { &*_event };
72        let client = unsafe { voidp_to_ref::<Self>(arg) };
73
74        match event.type_ as _ {
75            sys::BLE_L2CAP_EVENT_COC_CONNECTED => {
76                let connect = unsafe { event.__bindgen_anon_1.connect };
77                if connect.status > 0 {
78                    ::log::warn!("LE COC error: {}", connect.status);
79                    client.signal.signal(connect.status as _);
80
81                    return 0;
82                }
83
84                client.coc_chan = connect.chan;
85                client.signal.signal(0);
86                0
87            }
88            sys::BLE_L2CAP_EVENT_COC_DISCONNECTED => {
89                let disconnect = unsafe { event.__bindgen_anon_1.disconnect };
90                ::log::debug!("LE CoC disconnected: {:?}", disconnect.chan);
91                client.coc_chan = core::ptr::null_mut();
92                client.signal.signal(0);
93                0
94            }
95            sys::BLE_L2CAP_EVENT_COC_DATA_RECEIVED => {
96                let receive = unsafe { event.__bindgen_anon_1.receive };
97                if !receive.sdu_rx.is_null() {
98                    let _ = client.channel.try_send(ReceivedData::from_raw(receive));
99                }
100                client.l2cap.ble_l2cap_recv_ready(receive.chan);
101                0
102            }
103            _ => 0,
104        }
105    }
106}
107
108impl Drop for L2capClient {
109    fn drop(&mut self) {
110        block_on(async {
111            let _ = self.disconnect().await;
112        });
113    }
114}