Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a disconnect_reason method to Connection #248

Merged
merged 4 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nrf-softdevice/src/ble/central.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ where
Err(_) => {
raw::sd_ble_gap_disconnect(
conn_handle,
raw::BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION as _,
raw::BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES as _,
);
Err(ConnectError::NoFreeConn)
}
Expand Down
94 changes: 76 additions & 18 deletions nrf-softdevice/src/ble/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use core::iter::FusedIterator;

use raw::ble_gap_conn_params_t;

use super::PhySet;
use super::{HciStatus, PhySet};
#[cfg(feature = "ble-central")]
use crate::ble::gap::default_security_params;
#[cfg(feature = "ble-sec")]
use crate::ble::security::SecurityHandler;
use crate::ble::types::{Address, AddressType, Role, SecurityMode};
use crate::util::get_union_field;
use crate::{raw, RawError};

#[cfg(any(feature = "s113", feature = "s132", feature = "s140"))]
Expand All @@ -22,6 +23,40 @@ pub(crate) struct OutOfConnsError;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DisconnectedError;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum ConnHandleState {
Disconnected(HciStatus),
Connected(u16),
}

impl ConnHandleState {
const fn to_result(self) -> Result<u16, DisconnectedError> {
match self {
ConnHandleState::Disconnected(_) => Err(DisconnectedError),
ConnHandleState::Connected(handle) => Ok(handle),
}
}

const fn handle(self) -> Option<u16> {
match self {
ConnHandleState::Disconnected(_) => None,
ConnHandleState::Connected(handle) => Some(handle),
}
}

const fn is_connected(&self) -> bool {
matches!(self, ConnHandleState::Connected(_))
}

const fn disconnect_reason(self) -> Option<HciStatus> {
match self {
ConnHandleState::Disconnected(reason) => Some(reason),
ConnHandleState::Connected(_) => None,
}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SetConnParamsError {
Expand Down Expand Up @@ -199,7 +234,7 @@ pub(crate) struct ConnectionState {
// However, disconnection is not complete until the event GAP_DISCONNECTED.
// so there's a small gap of time where the ConnectionState is not "free" even if refcount=0.
pub refcount: u8,
pub conn_handle: Option<u16>,
pub conn_handle: ConnHandleState,

pub disconnecting: bool,
pub role: Role,
Expand All @@ -226,7 +261,7 @@ impl ConnectionState {
// can go into .bss instead of .data, which saves flash space.
Self {
refcount: 0,
conn_handle: None,
conn_handle: ConnHandleState::Disconnected(HciStatus::SUCCESS),
#[cfg(feature = "ble-central")]
role: Role::Central,
#[cfg(not(feature = "ble-central"))]
Expand All @@ -251,26 +286,32 @@ impl ConnectionState {
}
}
pub(crate) fn check_connected(&mut self) -> Result<u16, DisconnectedError> {
self.conn_handle.ok_or(DisconnectedError)
self.conn_handle.to_result()
}

pub(crate) fn disconnect(&mut self) -> Result<(), DisconnectedError> {
self.disconnect_with_reason(HciStatus::REMOTE_USER_TERMINATED_CONNECTION)
}

pub(crate) fn disconnect_with_reason(&mut self, reason: HciStatus) -> 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) };
let ret = unsafe { raw::sd_ble_gap_disconnect(conn_handle, reason.into()) };
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");
pub(crate) fn on_disconnected(&mut self, ble_evt: *const raw::ble_evt_t) {
let conn_handle = unwrap!(
self.conn_handle.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");
Expand All @@ -283,17 +324,26 @@ impl ConnectionState {

ibh.set(None);

self.conn_handle = None;
let reason = unsafe {
assert_eq!(
u32::from((*ble_evt).header.evt_id),
raw::BLE_GAP_EVTS_BLE_GAP_EVT_DISCONNECTED,
"bug: on_disconnected called with non-disconnect event"
);
let gap_evt = get_union_field(ble_evt, &(*ble_evt).evt.gap_evt);
HciStatus::new(gap_evt.params.disconnected.reason)
};
self.conn_handle = ConnHandleState::Disconnected(reason);

// Signal possible in-progess operations that the connection has disconnected.
#[cfg(feature = "ble-gatt-client")]
crate::ble::gatt_client::portal(conn_handle).call(_ble_evt);
crate::ble::gatt_client::portal(conn_handle).call(ble_evt);
#[cfg(feature = "ble-gatt-client")]
crate::ble::gatt_client::hvx_portal(conn_handle).call(_ble_evt);
crate::ble::gatt_client::hvx_portal(conn_handle).call(ble_evt);
#[cfg(feature = "ble-gatt-server")]
crate::ble::gatt_server::portal(conn_handle).call(_ble_evt);
crate::ble::gatt_server::portal(conn_handle).call(ble_evt);
#[cfg(feature = "ble-l2cap")]
crate::ble::l2cap::portal(conn_handle).call(_ble_evt);
crate::ble::l2cap::portal(conn_handle).call(ble_evt);

trace!("conn {:?}: disconnected", _index);
}
Expand Down Expand Up @@ -346,7 +396,7 @@ impl Drop for Connection {
);

if state.refcount == 0 {
if state.conn_handle.is_some() {
if state.conn_handle.is_connected() {
trace!("conn {:?}: dropped, disconnecting", self.index);
// We still leave conn_handle set, because the connection is
// not really disconnected until we get GAP_DISCONNECTED event.
Expand Down Expand Up @@ -382,8 +432,16 @@ impl Connection {
self.with_state(|state| state.disconnect())
}

pub fn disconnect_with_reason(&self, reason: HciStatus) -> Result<(), DisconnectedError> {
self.with_state(|state| state.disconnect_with_reason(reason))
}

pub fn disconnect_reason(&self) -> Option<HciStatus> {
self.with_state(|state| state.conn_handle.disconnect_reason())
}

pub fn handle(&self) -> Option<u16> {
self.with_state(|state| state.conn_handle)
self.with_state(|state| state.conn_handle.handle())
}

pub fn from_handle(conn_handle: u16) -> Option<Connection> {
Expand All @@ -405,7 +463,7 @@ impl Connection {
// Initialize
*state = ConnectionState {
refcount: 1,
conn_handle: Some(conn_handle),
conn_handle: ConnHandleState::Connected(conn_handle),
role,
peer_address,
security_mode: SecurityMode::Open,
Expand Down Expand Up @@ -727,7 +785,7 @@ impl Iterator for ConnectionIter {
unsafe {
for (i, s) in STATES[n..].iter().enumerate() {
let state = &mut *s.get();
if state.conn_handle.is_some() {
if state.conn_handle.is_connected() {
let index = (n + i) as u8;
state.refcount =
unwrap!(state.refcount.checked_add(1), "Too many references to same connection");
Expand Down Expand Up @@ -769,7 +827,7 @@ fn allocate_index<T>(f: impl FnOnce(u8, &mut ConnectionState) -> T) -> Result<T,
unsafe {
for (i, s) in STATES.iter().enumerate() {
let state = &mut *s.get();
if state.refcount == 0 && state.conn_handle.is_none() {
if state.refcount == 0 && !state.conn_handle.is_connected() {
return Ok(f(i as u8, state));
}
}
Expand Down
2 changes: 1 addition & 1 deletion nrf-softdevice/src/ble/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ where
Err(_) => {
raw::sd_ble_gap_disconnect(
conn_handle,
raw::BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION as _,
raw::BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES as _,
);
Err(AdvertiseError::NoFreeConn)
}
Expand Down
119 changes: 119 additions & 0 deletions nrf-softdevice/src/ble/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,122 @@ error_codes! {
/// ATT Common Profile and Service Error: Out Of Range.
(ATTERR_CPS_OUT_OF_RANGE, BLE_GATT_STATUS_ATTERR_CPS_OUT_OF_RANGE, "Out of Range");
}

#[derive(PartialEq, Eq, Clone, Copy)]
pub struct HciStatus(u8);

impl HciStatus {
pub const fn new(status: u8) -> Self {
Self(status)
}
}

impl From<u8> for HciStatus {
fn from(value: u8) -> Self {
Self(value)
}
}

impl From<HciStatus> for u8 {
fn from(value: HciStatus) -> Self {
value.0
}
}

macro_rules! hci_status_codes {
(
$(
$(#[$docs:meta])*
($konst:ident, $raw:expr, $phrase:expr);
)+
) => {
impl HciStatus {
$(
$(#[$docs])*
pub const $konst: HciStatus = HciStatus($raw as u8);
)+
}

#[cfg(feature = "defmt")]
impl defmt::Format for HciStatus {
fn format(&self, fmt: defmt::Formatter) {
match *self {
$(
Self::$konst => defmt::write!(fmt, $phrase),
)+
_ => defmt::write!(fmt, "Unknown HCI status: 0x{:02x}", self.0),
}
}
}

impl core::fmt::Debug for HciStatus {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
$(
Self::$konst => core::write!(fmt, $phrase),
)+
_ => core::write!(fmt, "Unknown HCI status: 0x{:02x}", self.0),
}
}
}
}
}

hci_status_codes! {
/// Success
(SUCCESS, raw::BLE_HCI_STATUS_CODE_SUCCESS, "Success");
/// Unknown HCI Command
(UNKNOWN_BTLE_COMMAND, raw::BLE_HCI_STATUS_CODE_UNKNOWN_BTLE_COMMAND, "Unknown HCI Command");
/// Unknown Connection Identifier
(UNKNOWN_CONNECTION_IDENTIFIER, raw::BLE_HCI_STATUS_CODE_UNKNOWN_CONNECTION_IDENTIFIER, "Unknown Connection Identifier");
/// Authentication Failure
(AUTHENTICATION_FAILURE, raw::BLE_HCI_AUTHENTICATION_FAILURE, "Authentication Failure");
/// PIN Or Key Missing
(PIN_OR_KEY_MISSING, raw::BLE_HCI_STATUS_CODE_PIN_OR_KEY_MISSING, "PIN Or Key Missing");
/// Memory Capacity Exceeded
(MEMORY_CAPACITY_EXCEEDED, raw::BLE_HCI_MEMORY_CAPACITY_EXCEEDED, "Memory Capacity Exceeded");
/// Connection Timeout
(CONNECTION_TIMEOUT, raw::BLE_HCI_CONNECTION_TIMEOUT, "Connection Timeout");
/// Command Disallowed
(COMMAND_DISALLOWED, raw::BLE_HCI_STATUS_CODE_COMMAND_DISALLOWED, "Command Disallowed");
/// Invalid HCI Command Parameters
(INVALID_BTLE_COMMAND_PARAMETERS, raw::BLE_HCI_STATUS_CODE_INVALID_BTLE_COMMAND_PARAMETERS, "Invalid HCI Command Parameters");
/// Remote User Terminated Connection
(REMOTE_USER_TERMINATED_CONNECTION, raw::BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION, "Remote User Terminated Connection");
/// Remote Device Terminated Connection due to Low Resources
(REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES, raw::BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES, "Remote Device Terminated Connection due to Low Resources");
/// Remote Device Terminated Connection due to Power Off
(REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF, raw::BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF, "Remote Device Terminated Connection due to Power Off");
/// Connection Terminated by Local Host
(LOCAL_HOST_TERMINATED_CONNECTION, raw::BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION, "Connection Terminated by Local Host");
/// Unsupported Remote Feature
(UNSUPPORTED_REMOTE_FEATURE, raw::BLE_HCI_UNSUPPORTED_REMOTE_FEATURE, "Unsupported Remote Feature");
/// Invalid LMP Parameters
(INVALID_LMP_PARAMETERS, raw::BLE_HCI_STATUS_CODE_INVALID_LMP_PARAMETERS, "Invalid LMP Parameters");
/// Unspecified Error
(UNSPECIFIED_ERROR, raw::BLE_HCI_STATUS_CODE_UNSPECIFIED_ERROR, "Unspecified Error");
/// LMP Response Timeout
(LMP_RESPONSE_TIMEOUT, raw::BLE_HCI_STATUS_CODE_LMP_RESPONSE_TIMEOUT, "LMP Response Timeout");
/// LMP Error Transaction Collision
(LMP_ERROR_TRANSACTION_COLLISION, raw::BLE_HCI_STATUS_CODE_LMP_ERROR_TRANSACTION_COLLISION, "LMP Error Transaction Collision");
/// LMP PDU Not Allowed
(LMP_PDU_NOT_ALLOWED, raw::BLE_HCI_STATUS_CODE_LMP_PDU_NOT_ALLOWED, "LMP PDU Not Allowed");
/// Instant Passed
(INSTANT_PASSED, raw::BLE_HCI_INSTANT_PASSED, "Instant Passed");
/// Pairing With Unit Key Not Supported
(PAIRING_WITH_UNIT_KEY_UNSUPPORTED, raw::BLE_HCI_PAIRING_WITH_UNIT_KEY_UNSUPPORTED, "Pairing With Unit Key Not Supported");
/// Different Transaction Collision
(DIFFERENT_TRANSACTION_COLLISION, raw::BLE_HCI_DIFFERENT_TRANSACTION_COLLISION, "Different Transaction Collision");
/// Parameter Out Of Mandatory Range
(PARAMETER_OUT_OF_MANDATORY_RANGE, raw::BLE_HCI_PARAMETER_OUT_OF_MANDATORY_RANGE, "Parameter Out Of Mandatory Range");
/// Controller Busy
(CONTROLLER_BUSY, raw::BLE_HCI_CONTROLLER_BUSY, "Controller Busy");
/// Unacceptable Connection Parameters
(CONN_INTERVAL_UNACCEPTABLE, raw::BLE_HCI_CONN_INTERVAL_UNACCEPTABLE, "Unacceptable Connection Parameters");
/// Advertising Timeout
(DIRECTED_ADVERTISER_TIMEOUT, raw::BLE_HCI_DIRECTED_ADVERTISER_TIMEOUT, "Advertising Timeout");
/// Connection Terminated due to MIC Failure
(CONN_TERMINATED_DUE_TO_MIC_FAILURE, raw::BLE_HCI_CONN_TERMINATED_DUE_TO_MIC_FAILURE, "Connection Terminated due to MIC Failure");
/// Connection Failed to be Established
(CONN_FAILED_TO_BE_ESTABLISHED, raw::BLE_HCI_CONN_FAILED_TO_BE_ESTABLISHED, "Connection Failed to be Established");
}
Loading