Skip to content

Commit b833060

Browse files
committed
Add explicit version for linklayer bindings
in generated crates
1 parent 88c3311 commit b833060

File tree

4 files changed

+381
-2
lines changed

4 files changed

+381
-2
lines changed

stm32-bindings-gen/res/ble/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ build = "build.rs"
1010
path = "src/lib.rs"
1111

1212
[dependencies]
13-
linklayer-bindings = { path = "../linklayer-bindings" }
13+
linklayer-bindings = { version = "0.1.0", path = "../linklayer-bindings" }
1414

1515
[features]
1616
default = []

stm32-bindings-gen/res/mac/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ build = "build.rs"
1010
path = "src/lib.rs"
1111

1212
[dependencies]
13-
linklayer-bindings = { path = "../linklayer-bindings" }
13+
linklayer-bindings = { version = "0.1.0", path = "../linklayer-bindings" }
1414

1515
[features]
1616
default = []

wba-linklayer/src/ble_sys_if.rs

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
//! Safe wrappers around a subset of the BLE system interface exposed by the
2+
//! STM32 WBA wireless stack.
3+
//!
4+
//! The goal of this module is to minimise the amount of ad-hoc `unsafe` code
5+
//! required by higher layers while still providing zero-cost access to the
6+
//! underlying C APIs. Only a carefully-curated portion of the enormous BLE
7+
//! surface is exposed here; additional helpers can be added incrementally as
8+
//! the need arises.
9+
10+
use core::marker::PhantomData;
11+
use core::ptr::{self, NonNull};
12+
use core::slice;
13+
14+
use crate::ffi;
15+
16+
/// Result type returned by helpers that wrap `ble_stat_t`-style status values.
17+
pub type BleResult<T = ()> = Result<T, BleStatus>;
18+
19+
/// Commonly observed BLE controller status codes.
20+
///
21+
/// The values are derived from the constants defined in `ble_defs.h`. Any
22+
/// status that is not modelled explicitly will be surfaced through
23+
/// [`BleStatus::Other`].
24+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25+
pub enum BleStatus {
26+
Success,
27+
Busy,
28+
Pending,
29+
InvalidParameters,
30+
InsufficientResources,
31+
OutOfMemory,
32+
Timeout,
33+
Other(u8),
34+
}
35+
36+
impl BleStatus {
37+
/// Convert a raw `ble_stat_t` (as returned by the vendor APIs) into the
38+
/// strongly-typed status representation.
39+
#[inline]
40+
pub fn from_raw(value: ffi::ble_stat_t) -> Self {
41+
match (value & 0xFF) as u8 {
42+
0x00 => Self::Success,
43+
0x93 => Self::Busy,
44+
0x95 => Self::Pending,
45+
0x92 => Self::InvalidParameters,
46+
0x64 => Self::InsufficientResources,
47+
0x98 => Self::OutOfMemory,
48+
0xFF => Self::Timeout,
49+
other => Self::Other(other),
50+
}
51+
}
52+
53+
/// Returns the raw 8-bit status code.
54+
#[inline]
55+
pub const fn code(self) -> u8 {
56+
match self {
57+
Self::Success => 0x00,
58+
Self::Busy => 0x93,
59+
Self::Pending => 0x95,
60+
Self::InvalidParameters => 0x92,
61+
Self::InsufficientResources => 0x64,
62+
Self::OutOfMemory => 0x98,
63+
Self::Timeout => 0xFF,
64+
Self::Other(code) => code,
65+
}
66+
}
67+
68+
/// Converts the status into a `Result`, treating [`BleStatus::Success`] as
69+
/// the success case.
70+
#[inline]
71+
pub fn into_result(self) -> BleResult<()> {
72+
match self {
73+
Self::Success => Ok(()),
74+
other => Err(other),
75+
}
76+
}
77+
}
78+
79+
/// Callback used by the controller to hand HCI buffers back to the host
80+
/// transport.
81+
pub type HostCallback = unsafe extern "C" fn(*mut ffi::ble_buff_hdr_t) -> u8;
82+
83+
/// Callback invoked when the controller fails to enqueue a buffer because the
84+
/// host queue is full.
85+
pub type HostQueueFullCallback = unsafe extern "C" fn(*mut ffi::ble_buff_hdr_t);
86+
87+
/// Callback signature used for vendor-specific HCI command handling.
88+
pub type ExternalCustomCallback = unsafe extern "C" fn(
89+
ocf: u16,
90+
packet: *mut u8,
91+
event_packet: *mut u8,
92+
params_length: *mut u8,
93+
return_command_type: *mut ffi::hci_return_command_type,
94+
) -> ffi::ble_stat_t;
95+
96+
pub type DispatchTable = ffi::hci_dispatch_tbl;
97+
98+
/// RAII wrapper around the vendor-provided `ble_buff_hdr_t` buffers.
99+
///
100+
/// Buffers are automatically returned to the controller when dropped.
101+
pub struct BleBuffer {
102+
ptr: NonNull<ffi::ble_buff_hdr_t>,
103+
_not_send_sync: PhantomData<core::cell::Cell<ffi::ble_buff_hdr_t>>,
104+
}
105+
106+
impl BleBuffer {
107+
/// Attempts to allocate a fresh buffer from the controller.
108+
pub fn allocate() -> Option<Self> {
109+
let ptr = unsafe { ffi::hci_alloc_msg() };
110+
NonNull::new(ptr).map(|ptr| Self {
111+
ptr,
112+
_not_send_sync: PhantomData,
113+
})
114+
}
115+
116+
/// Wrap an existing raw pointer returned by the vendor middleware.
117+
///
118+
/// # Safety
119+
///
120+
/// The caller must ensure that `ptr` is either null (in which case `None`
121+
/// is returned) or a valid buffer obtained from the controller and that it
122+
/// remains valid for the lifetime of the returned wrapper.
123+
pub unsafe fn from_raw(ptr: *mut ffi::ble_buff_hdr_t) -> Option<Self> {
124+
NonNull::new(ptr).map(|ptr| Self {
125+
ptr,
126+
_not_send_sync: PhantomData,
127+
})
128+
}
129+
130+
#[inline]
131+
fn header(&self) -> &ffi::ble_buff_hdr_t {
132+
unsafe { self.ptr.as_ref() }
133+
}
134+
135+
#[inline]
136+
fn header_mut(&mut self) -> &mut ffi::ble_buff_hdr_t {
137+
unsafe { self.ptr.as_mut() }
138+
}
139+
140+
#[inline]
141+
pub fn len(&self) -> usize {
142+
self.header().data_size as usize
143+
}
144+
145+
#[inline]
146+
pub fn is_empty(&self) -> bool {
147+
self.len() == 0
148+
}
149+
150+
#[inline]
151+
pub fn capacity(&self) -> usize {
152+
self.header().total_len as usize
153+
}
154+
155+
#[inline]
156+
pub fn offset(&self) -> usize {
157+
self.header().data_offset as usize
158+
}
159+
160+
#[inline]
161+
pub fn set_len(&mut self, len: usize) {
162+
debug_assert!(len <= self.capacity());
163+
debug_assert!(len <= u16::MAX as usize);
164+
self.header_mut().data_size = len as u16;
165+
}
166+
167+
#[inline]
168+
pub fn set_offset(&mut self, offset: usize) {
169+
debug_assert!(offset <= self.capacity());
170+
debug_assert!(offset <= u16::MAX as usize);
171+
self.header_mut().data_offset = offset as u16;
172+
}
173+
174+
fn payload_parts(&self) -> Option<(NonNull<u8>, usize)> {
175+
let header = self.header();
176+
let len = header.data_size as usize;
177+
if len == 0 {
178+
return None;
179+
}
180+
let base = NonNull::new(header.buff_start)?;
181+
let offset = header.data_offset as usize;
182+
let end = offset.checked_add(len)?;
183+
if header.total_len != 0 {
184+
debug_assert!(end <= header.total_len as usize);
185+
}
186+
let ptr = unsafe { base.as_ptr().add(offset) };
187+
NonNull::new(ptr).map(|ptr| (ptr, len))
188+
}
189+
190+
/// Borrow the packet payload as an immutable slice.
191+
#[inline]
192+
pub fn payload(&self) -> Option<&[u8]> {
193+
self.payload_parts()
194+
.map(|(ptr, len)| unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, len) })
195+
}
196+
197+
/// Borrow the packet payload as a mutable slice.
198+
#[inline]
199+
pub fn payload_mut(&mut self) -> Option<&mut [u8]> {
200+
self.payload_parts()
201+
.map(|(ptr, len)| unsafe { slice::from_raw_parts_mut(ptr.as_ptr(), len) })
202+
}
203+
204+
/// Overwrite the payload with `data`, adjusting length/offset as needed.
205+
///
206+
/// Returns `Err(())` when the payload does not fit in the buffer.
207+
pub fn write_payload(&mut self, data: &[u8]) -> Result<(), ()> {
208+
if data.len() > self.capacity() {
209+
return Err(());
210+
}
211+
212+
self.set_offset(0);
213+
self.set_len(data.len());
214+
215+
match self.payload_mut() {
216+
Some(slot) => {
217+
slot.copy_from_slice(data);
218+
Ok(())
219+
}
220+
None => {
221+
if data.is_empty() {
222+
Ok(())
223+
} else {
224+
Err(())
225+
}
226+
}
227+
}
228+
}
229+
230+
/// Returns the raw pointer without relinquishing ownership.
231+
#[inline]
232+
pub fn as_ptr(&self) -> *const ffi::ble_buff_hdr_t {
233+
self.ptr.as_ptr()
234+
}
235+
236+
/// Returns the raw mutable pointer without relinquishing ownership.
237+
#[inline]
238+
pub fn as_mut_ptr(&mut self) -> *mut ffi::ble_buff_hdr_t {
239+
self.ptr.as_ptr()
240+
}
241+
242+
/// Consumes the wrapper and returns the raw pointer, preventing the
243+
/// destructor from freeing it.
244+
#[inline]
245+
pub fn into_raw(self) -> *mut ffi::ble_buff_hdr_t {
246+
let ptr = self.ptr.as_ptr();
247+
core::mem::forget(self);
248+
ptr
249+
}
250+
}
251+
252+
impl Drop for BleBuffer {
253+
fn drop(&mut self) {
254+
unsafe { ffi::hci_free_msg(self.ptr.as_ptr()) };
255+
}
256+
}
257+
258+
/// Initialise the BLE controller glue logic.
259+
///
260+
/// # Safety
261+
///
262+
/// The supplied callback must adhere to the constraints expected by the
263+
/// controller (no unwinding across the FFI boundary, ISR safety where
264+
/// applicable, etc.).
265+
pub unsafe fn init_controller(callback: HostCallback) {
266+
unsafe {
267+
ffi::ll_sys_ble_cntrl_init(Some(callback));
268+
}
269+
}
270+
271+
/// Initialise the controller <-> host transport layer.
272+
pub fn init_transport(callback: HostCallback) -> BleResult {
273+
let status = unsafe { ffi::ll_hci_init(Some(callback)) };
274+
BleStatus::from_raw(status).into_result()
275+
}
276+
277+
pub fn init_with_dispatch_table(table: &DispatchTable) -> BleResult {
278+
let status = unsafe { ffi::ll_intf_init(table as *const _) };
279+
BleStatus::from_raw(status).into_result()
280+
}
281+
282+
pub fn init_default_interface() -> BleResult {
283+
match dispatch_table() {
284+
Some(table) => init_with_dispatch_table(table),
285+
None => Err(BleStatus::InsufficientResources),
286+
}
287+
}
288+
289+
pub fn dispatch_table() -> Option<&'static DispatchTable> {
290+
let mut table: *const DispatchTable = ptr::null();
291+
unsafe {
292+
ffi::hci_get_dis_tbl(&mut table);
293+
}
294+
if table.is_null() {
295+
None
296+
} else {
297+
Some(unsafe { &*table })
298+
}
299+
}
300+
301+
pub fn reset_interface() -> BleResult {
302+
let status = unsafe { ffi::ll_intf_reset() };
303+
BleStatus::from_raw(status).into_result()
304+
}
305+
306+
pub fn reset_system() {
307+
unsafe { ffi::ll_sys_reset() };
308+
}
309+
310+
/// Register an optional callback that is invoked when the host queue is full.
311+
pub unsafe fn register_queue_full_callback(callback: Option<HostQueueFullCallback>) {
312+
unsafe {
313+
ffi::hci_rgstr_hst_cbk_ll_queue_full(callback);
314+
}
315+
}
316+
317+
/// Register the primary host callback that receives HCI buffers.
318+
pub unsafe fn register_host_callback(callback: Option<HostCallback>) {
319+
unsafe {
320+
ffi::hci_rgstr_hst_cbk(callback);
321+
}
322+
}
323+
324+
/// Register or clear the vendor-specific HCI command handler.
325+
pub unsafe fn register_external_custom_callback(callback: Option<ExternalCustomCallback>) -> bool {
326+
unsafe { ffi::hci_rgstr_ble_external_custom_cbk(callback) != 0 }
327+
}
328+
329+
/// Prepare the vendor event queues. This must be called once before invoking
330+
/// any of the queue-related helpers.
331+
pub fn init_event_queues() {
332+
unsafe { ffi::hci_init_events_queues() };
333+
}
334+
335+
/// Enqueue a packet for delivery to the host.
336+
pub fn queue_send_packet(buffer: BleBuffer) -> BleResult {
337+
let ptr = buffer.into_raw();
338+
let status = BleStatus::from_raw(unsafe { ffi::hci_queue_send_pckt(ptr) as ffi::ble_stat_t });
339+
if status == BleStatus::Success {
340+
Ok(())
341+
} else {
342+
unsafe { ffi::hci_free_msg(ptr) };
343+
Err(status)
344+
}
345+
}
346+
347+
/// Helper that allocates a buffer and enqueues it after copying `payload` into it.
348+
///
349+
/// Returns [`BleStatus::InsufficientResources`] if a buffer cannot be obtained or
350+
/// [`BleStatus::InvalidParameters`] when the payload exceeds the buffer capacity.
351+
pub fn queue_packet(payload: &[u8]) -> BleResult {
352+
let mut buffer = BleBuffer::allocate().ok_or(BleStatus::InsufficientResources)?;
353+
buffer
354+
.write_payload(payload)
355+
.map_err(|_| BleStatus::InvalidParameters)?;
356+
queue_send_packet(buffer)
357+
}
358+
359+
/// Update the LE event mask used by the controller.
360+
pub fn set_le_event_mask(mask: &mut [u8; 8]) {
361+
unsafe { ffi::hci_ll_set_le_event_mask(mask.as_mut_ptr()) };
362+
}
363+
364+
/// Update the classic/Bluetooth event mask used by the controller.
365+
pub fn set_event_mask(mask: &mut [u8; 8]) {
366+
unsafe { ffi::hci_ll_set_event_mask(mask.as_mut_ptr()) };
367+
}
368+
369+
/// Update the page 2 event mask used by the controller.
370+
pub fn set_event_mask_page2(mask: &mut [u8; 8]) {
371+
unsafe { ffi::hci_ll_set_event_mask_page2(mask.as_mut_ptr()) };
372+
}
373+
374+
/// Update the custom event mask used by the controller.
375+
pub fn set_custom_event_mask(mask: u8) {
376+
unsafe { ffi::hci_ll_set_custom_event_mask(mask) };
377+
}

0 commit comments

Comments
 (0)