use core::cell::{Cell, UnsafeCell};
use core::iter::FusedIterator;
use raw::ble_gap_conn_params_t;
use super::PhySet;
#[cfg(feature = "ble-sec")]
use crate::ble::security::SecurityHandler;
use crate::ble::types::{Address, AddressType, Role, SecurityMode};
use crate::{raw, RawError};
#[cfg(any(feature = "s113", feature = "s132", feature = "s140"))]
const BLE_GAP_DATA_LENGTH_DEFAULT: u8 = 27; #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct OutOfConnsError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DisconnectedError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SetConnParamsError {
Disconnected,
Raw(RawError),
}
impl From<DisconnectedError> for SetConnParamsError {
fn from(_err: DisconnectedError) -> Self {
Self::Disconnected
}
}
impl From<RawError> for SetConnParamsError {
fn from(err: RawError) -> Self {
Self::Raw(err)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "ble-peripheral")]
pub enum IgnoreSlaveLatencyError {
Disconnected,
Raw(RawError),
}
#[cfg(feature = "ble-peripheral")]
impl From<DisconnectedError> for IgnoreSlaveLatencyError {
fn from(_err: DisconnectedError) -> Self {
Self::Disconnected
}
}
#[cfg(feature = "ble-peripheral")]
impl From<RawError> for IgnoreSlaveLatencyError {
fn from(err: RawError) -> Self {
Self::Raw(err)
}
}
pub enum PhyUpdateError {
Disconnected,
Raw(RawError),
}
impl From<DisconnectedError> for PhyUpdateError {
fn from(_err: DisconnectedError) -> Self {
Self::Disconnected
}
}
impl From<RawError> for PhyUpdateError {
fn from(err: RawError) -> Self {
Self::Raw(err)
}
}
pub(crate) const CONNS_MAX: usize = 20;
#[cfg(feature = "ble-sec")]
#[derive(Clone, Copy)]
pub(crate) struct EncryptionState {
pub handler: Option<&'static dyn SecurityHandler>,
pub own_enc_key: raw::ble_gap_enc_key_t,
pub peer_enc_key: raw::ble_gap_enc_key_t,
pub peer_id: raw::ble_gap_id_key_t,
}
#[cfg(feature = "ble-sec")]
const NEW_GAP_ENC_KEY: raw::ble_gap_enc_key_t = raw::ble_gap_enc_key_t {
enc_info: raw::ble_gap_enc_info_t {
ltk: [0; 16],
_bitfield_1: raw::__BindgenBitfieldUnit::new([0; 1]),
},
master_id: raw::ble_gap_master_id_t { ediv: 0, rand: [0; 8] },
};
#[cfg(feature = "ble-sec")]
const NEW_GAP_ID_KEY: raw::ble_gap_id_key_t = raw::ble_gap_id_key_t {
id_info: raw::ble_gap_irk_t { irk: [0; 16] },
id_addr_info: raw::ble_gap_addr_t {
_bitfield_1: raw::__BindgenBitfieldUnit::new([0; 1]),
addr: [0; 6],
},
};
#[cfg(feature = "ble-sec")]
const NEW_ENCRYPTION_STATE: EncryptionState = EncryptionState {
handler: None,
own_enc_key: NEW_GAP_ENC_KEY,
peer_enc_key: NEW_GAP_ENC_KEY,
peer_id: NEW_GAP_ID_KEY,
};
pub(crate) struct ConnectionState {
pub refcount: u8,
pub conn_handle: Option<u16>,
pub disconnecting: bool,
pub role: Role,
pub peer_address: Address,
pub security_mode: SecurityMode,
pub conn_params: ble_gap_conn_params_t,
#[cfg(feature = "ble-rssi")]
pub rssi: Option<i8>,
#[cfg(feature = "ble-gatt")]
pub att_mtu: u16, #[cfg(any(feature = "s113", feature = "s132", feature = "s140"))]
pub data_length_effective: u8, #[cfg(feature = "ble-sec")]
pub security: EncryptionState,
}
impl ConnectionState {
const fn dummy() -> Self {
Self {
refcount: 0,
conn_handle: None,
#[cfg(feature = "ble-central")]
role: Role::Central,
#[cfg(not(feature = "ble-central"))]
role: Role::Peripheral,
peer_address: Address::new(AddressType::Public, [0; 6]),
security_mode: SecurityMode::NoAccess,
disconnecting: false,
conn_params: ble_gap_conn_params_t {
conn_sup_timeout: 0,
max_conn_interval: 0,
min_conn_interval: 0,
slave_latency: 0,
},
#[cfg(feature = "ble-rssi")]
rssi: None,
#[cfg(feature = "ble-gatt")]
att_mtu: 0,
#[cfg(any(feature = "s113", feature = "s132", feature = "s140"))]
data_length_effective: 0,
#[cfg(feature = "ble-sec")]
security: NEW_ENCRYPTION_STATE,
}
}
pub(crate) fn check_connected(&mut self) -> Result<u16, DisconnectedError> {
self.conn_handle.ok_or(DisconnectedError)
}
pub(crate) fn disconnect(&mut self) -> Result<(), DisconnectedError> {
let conn_handle = self.check_connected()?;
if self.disconnecting {
return Ok(());
}
let ret =
unsafe { raw::sd_ble_gap_disconnect(conn_handle, raw::BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION as u8) };
unwrap!(RawError::convert(ret), "sd_ble_gap_disconnect");
self.disconnecting = true;
Ok(())
}
pub(crate) fn on_disconnected(&mut self, _ble_evt: *const raw::ble_evt_t) {
let conn_handle = unwrap!(self.conn_handle, "bug: on_disconnected when already disconnected");
let ibh = index_by_handle(conn_handle);
let _index = unwrap!(ibh.get(), "bug: conn_handle has no index");
#[cfg(all(feature = "ble-gatt-server", feature = "ble-sec"))]
if let Some(handler) = self.security.handler {
let conn = unwrap!(Connection::from_handle(conn_handle), "bug: conn_handle has no index");
handler.save_sys_attrs(&conn);
}
ibh.set(None);
self.conn_handle = None;
#[cfg(feature = "ble-gatt-client")]
crate::ble::gatt_client::portal(conn_handle).call(_ble_evt);
#[cfg(feature = "ble-gatt-server")]
crate::ble::gatt_server::portal(conn_handle).call(_ble_evt);
#[cfg(feature = "ble-l2cap")]
crate::ble::l2cap::portal(conn_handle).call(_ble_evt);
trace!("conn {:?}: disconnected", _index);
}
pub(crate) fn keyset(&mut self) -> raw::ble_gap_sec_keyset_t {
#[cfg(feature = "ble-sec")]
return raw::ble_gap_sec_keyset_t {
keys_own: raw::ble_gap_sec_keys_t {
p_enc_key: &mut self.security.own_enc_key,
p_id_key: core::ptr::null_mut(),
p_sign_key: core::ptr::null_mut(),
p_pk: core::ptr::null_mut(),
},
keys_peer: raw::ble_gap_sec_keys_t {
p_enc_key: &mut self.security.peer_enc_key,
p_id_key: &mut self.security.peer_id,
p_sign_key: core::ptr::null_mut(),
p_pk: core::ptr::null_mut(),
},
};
#[cfg(not(feature = "ble-sec"))]
return raw::ble_gap_sec_keyset_t {
keys_own: raw::ble_gap_sec_keys_t {
p_enc_key: core::ptr::null_mut(),
p_id_key: core::ptr::null_mut(),
p_sign_key: core::ptr::null_mut(),
p_pk: core::ptr::null_mut(),
},
keys_peer: raw::ble_gap_sec_keys_t {
p_enc_key: core::ptr::null_mut(),
p_id_key: core::ptr::null_mut(),
p_sign_key: core::ptr::null_mut(),
p_pk: core::ptr::null_mut(),
},
};
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Connection {
index: u8,
}
impl Drop for Connection {
fn drop(&mut self) {
self.with_state(|state| {
state.refcount = unwrap!(
state.refcount.checked_sub(1),
"bug: dropping a conn which is already at refcount 0"
);
if state.refcount == 0 {
if state.conn_handle.is_some() {
trace!("conn {:?}: dropped, disconnecting", self.index);
unwrap!(state.disconnect());
} else {
trace!("conn {:?}: dropped, already disconnected", self.index);
}
}
});
}
}
impl Clone for Connection {
fn clone(&self) -> Self {
self.with_state(|state| {
state.refcount = unwrap!(state.refcount.checked_add(1), "Too many references to same connection");
});
Self { index: self.index }
}
}
impl Connection {
pub fn role(&self) -> Role {
self.with_state(|state| state.role)
}
pub fn peer_address(&self) -> Address {
self.with_state(|state| state.peer_address)
}
pub fn disconnect(&self) -> Result<(), DisconnectedError> {
self.with_state(|state| state.disconnect())
}
pub fn handle(&self) -> Option<u16> {
self.with_state(|state| state.conn_handle)
}
pub fn from_handle(conn_handle: u16) -> Option<Connection> {
index_by_handle(conn_handle).get().map(|index| {
with_state(index, |state| {
state.refcount = unwrap!(state.refcount.checked_add(1), "Too many references to same connection");
Connection { index }
})
})
}
pub(crate) fn new(
conn_handle: u16,
role: Role,
peer_address: Address,
conn_params: ble_gap_conn_params_t,
) -> Result<Self, OutOfConnsError> {
allocate_index(|index, state| {
*state = ConnectionState {
refcount: 1,
conn_handle: Some(conn_handle),
role,
peer_address,
security_mode: SecurityMode::Open,
disconnecting: false,
conn_params,
#[cfg(feature = "ble-rssi")]
rssi: None,
#[cfg(feature = "ble-gatt")]
att_mtu: raw::BLE_GATT_ATT_MTU_DEFAULT as _,
#[cfg(any(feature = "s113", feature = "s132", feature = "s140"))]
data_length_effective: BLE_GAP_DATA_LENGTH_DEFAULT,
#[cfg(feature = "ble-sec")]
security: NEW_ENCRYPTION_STATE,
};
let ibh = index_by_handle(conn_handle);
assert!(ibh.get().is_none(), "bug: conn_handle already has index");
ibh.set(Some(index));
trace!("conn {:?}: connected", index);
Self { index }
})
}
#[cfg(feature = "ble-sec")]
pub(crate) fn with_security_handler(
conn_handle: u16,
role: Role,
peer_address: Address,
conn_params: ble_gap_conn_params_t,
handler: &'static dyn SecurityHandler,
) -> Result<Self, OutOfConnsError> {
let conn = Self::new(conn_handle, role, peer_address, conn_params)?;
conn.with_state(|state| state.security.handler = Some(handler));
Ok(conn)
}
#[cfg(feature = "ble-rssi")]
pub fn start_rssi(&self) {
if let Ok(conn_handle) = self.with_state(|state| state.check_connected()) {
let ret = unsafe { raw::sd_ble_gap_rssi_start(conn_handle, 0, 0) };
if let Err(err) = RawError::convert(ret) {
warn!("sd_ble_gap_rssi_start err {:?}", err);
}
}
}
#[cfg(feature = "ble-rssi")]
pub fn rssi(&self) -> Option<i8> {
self.with_state(|state| state.rssi)
}
pub fn conn_params(&self) -> ble_gap_conn_params_t {
with_state(self.index, |s| s.conn_params)
}
#[cfg(feature = "ble-gatt")]
pub fn att_mtu(&self) -> u16 {
with_state(self.index, |s| s.att_mtu)
}
pub fn security_mode(&self) -> SecurityMode {
with_state(self.index, |s| s.security_mode)
}
#[cfg(feature = "ble-sec")]
pub fn security_handler(&self) -> Option<&dyn SecurityHandler> {
with_state(self.index, |s| s.security.handler)
}
pub fn set_conn_params(&self, conn_params: ble_gap_conn_params_t) -> Result<(), SetConnParamsError> {
let conn_handle = self.with_state(|state| state.check_connected())?;
let ret = unsafe { raw::sd_ble_gap_conn_param_update(conn_handle, &conn_params) };
if let Err(err) = RawError::convert(ret) {
warn!("sd_ble_gap_conn_param_update err {:?}", err);
return Err(err.into());
}
Ok(())
}
#[cfg(feature = "ble-peripheral")]
pub fn ignore_slave_latency(&mut self, ignore: bool) -> Result<(), IgnoreSlaveLatencyError> {
let conn_handle = self.with_state(|state| state.check_connected())?;
let mut disable: raw::ble_gap_opt_slave_latency_disable_t = unsafe { core::mem::zeroed() };
disable.conn_handle = conn_handle;
disable.set_disable(ignore as u8); let ret = unsafe {
raw::sd_ble_opt_set(
raw::BLE_GAP_OPTS_BLE_GAP_OPT_SLAVE_LATENCY_DISABLE,
&raw::ble_opt_t {
gap_opt: raw::ble_gap_opt_t {
slave_latency_disable: disable,
},
},
)
};
if let Err(err) = RawError::convert(ret) {
warn!("ignore_slave_latency sd_ble_opt_set err {:?}", err);
return Err(err.into());
}
Ok(())
}
pub(crate) fn with_state<T>(&self, f: impl FnOnce(&mut ConnectionState) -> T) -> T {
with_state(self.index, f)
}
pub fn iter() -> ConnectionIter {
ConnectionIter(0)
}
pub fn phy_update(&mut self, tx_phys: PhySet, rx_phys: PhySet) -> Result<(), PhyUpdateError> {
let conn_handle = self.with_state(|state| state.check_connected())?;
let p_gap_phys = raw::ble_gap_phys_t {
tx_phys: tx_phys as u8,
rx_phys: rx_phys as u8,
};
let ret = unsafe { raw::sd_ble_gap_phy_update(conn_handle, &p_gap_phys) };
if let Err(err) = RawError::convert(ret) {
warn!("sd_ble_gap_phy_update err {:?}", err);
return Err(err.into());
}
Ok(())
}
}
pub struct ConnectionIter(u8);
impl Iterator for ConnectionIter {
type Item = Connection;
fn next(&mut self) -> Option<Self::Item> {
let n = usize::from(self.0);
if n < CONNS_MAX {
unsafe {
for (i, s) in STATES[n..].iter().enumerate() {
let state = &mut *s.get();
if state.conn_handle.is_some() {
let index = (n + i) as u8;
state.refcount =
unwrap!(state.refcount.checked_add(1), "Too many references to same connection");
self.0 = index + 1;
return Some(Connection { index });
}
}
}
self.0 = CONNS_MAX as u8;
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(CONNS_MAX - usize::from(self.0)))
}
}
impl FusedIterator for ConnectionIter {}
const DUMMY_STATE: UnsafeCell<ConnectionState> = UnsafeCell::new(ConnectionState::dummy());
static mut STATES: [UnsafeCell<ConnectionState>; CONNS_MAX] = [DUMMY_STATE; CONNS_MAX];
pub(crate) fn with_state_by_conn_handle<T>(conn_handle: u16, f: impl FnOnce(&mut ConnectionState) -> T) -> T {
let index = unwrap!(
index_by_handle(conn_handle).get(),
"bug: with_state_by_conn_handle on conn_handle that has no state"
);
with_state(index, f)
}
pub(crate) fn with_state<T>(index: u8, f: impl FnOnce(&mut ConnectionState) -> T) -> T {
let state = unsafe { &mut *STATES[index as usize].get() };
f(state)
}
fn allocate_index<T>(f: impl FnOnce(u8, &mut ConnectionState) -> T) -> Result<T, OutOfConnsError> {
unsafe {
for (i, s) in STATES.iter().enumerate() {
let state = &mut *s.get();
if state.refcount == 0 && state.conn_handle.is_none() {
return Ok(f(i as u8, state));
}
}
Err(OutOfConnsError)
}
}
const INDEX_NONE: Cell<Option<u8>> = Cell::new(None);
static mut INDEX_BY_HANDLE: [Cell<Option<u8>>; CONNS_MAX] = [INDEX_NONE; CONNS_MAX];
fn index_by_handle(conn_handle: u16) -> &'static Cell<Option<u8>> {
unsafe { &INDEX_BY_HANDLE[conn_handle as usize] }
}