diff --git a/src/winrtble/ble/device.rs b/src/winrtble/ble/device.rs index aeba8276..c0729ef6 100644 --- a/src/winrtble/ble/device.rs +++ b/src/winrtble/ble/device.rs @@ -19,7 +19,7 @@ use tokio::time::timeout; use windows::{ Devices::Bluetooth::{ BluetoothCacheMode, BluetoothConnectionStatus, BluetoothLEDevice, - BluetoothLEPreferredConnectionParameters, + BluetoothLEPreferredConnectionParameters, BluetoothLEPreferredConnectionParametersRequest, GenericAttributeProfile::{ GattCharacteristic, GattCommunicationStatus, GattDescriptor, GattDeviceService, GattDeviceServicesResult, GattSession, @@ -41,6 +41,7 @@ pub struct BLEDevice { connection_token: i64, pdu_change_token: i64, services: Vec, + preferred_parameters_request: Option, } impl BLEDevice { @@ -89,6 +90,7 @@ impl BLEDevice { connection_token, pdu_change_token, services: vec![], + preferred_parameters_request: None, }) } @@ -220,7 +222,7 @@ impl BLEDevice { } pub fn request_connection_parameters( - &self, + &mut self, preset: crate::api::ConnectionParameterPreset, ) -> Result<()> { let winrt_error = |e| Error::Other(format!("{:?}", e).into()); @@ -244,7 +246,12 @@ impl BLEDevice { // BluetoothLEPreferredConnectionParametersRequestStatus: // Unspecified = 0, Success = 1, DeviceNotAvailable = 2, AccessDenied = 3 match status.0 { - 1 => Ok(()), + 1 => { + if let Some(previous) = self.preferred_parameters_request.replace(result) { + let _ = previous.Close(); + } + Ok(()) + } 2 | 3 => Err(Error::NotSupported(format!( "request_connection_parameters not supported (status {:?})", status @@ -273,6 +280,10 @@ impl BLEDevice { .collect(); self.services = services; debug!("services {:?}", self.services.len()); + } else { + // Do not turn an unreachable/protocol failure into an empty service + // list and a misleading "missing characteristic" error in callers. + utils::to_error(status)?; } Ok(self.services.as_slice()) } @@ -280,6 +291,9 @@ impl BLEDevice { impl Drop for BLEDevice { fn drop(&mut self) { + if let Some(request) = self.preferred_parameters_request.take() { + let _ = request.Close(); + } let result = self .gatt_session .RemoveMaxPduSizeChanged(self.pdu_change_token); diff --git a/src/winrtble/peripheral.rs b/src/winrtble/peripheral.rs index 4d1b210d..871b7d61 100644 --- a/src/winrtble/peripheral.rs +++ b/src/winrtble/peripheral.rs @@ -721,8 +721,8 @@ impl ApiPeripheral for Peripheral { } async fn request_connection_parameters(&self, preset: ConnectionParameterPreset) -> Result<()> { - let device = self.shared.device.lock().await; - match &*device { + let mut device = self.shared.device.lock().await; + match &mut *device { Some(device) => device.request_connection_parameters(preset), None => Err(Error::NotConnected), } diff --git a/src/winrtble/utils.rs b/src/winrtble/utils.rs index 8ee8e07a..c6dba1a1 100644 --- a/src/winrtble/utils.rs +++ b/src/winrtble/utils.rs @@ -42,15 +42,41 @@ pub fn to_descriptor_value( ) -> GattClientCharacteristicConfigurationDescriptorValue { let notify = GattCharacteristicProperties::Notify; let indicate = GattCharacteristicProperties::Indicate; - if properties & indicate == indicate { - GattClientCharacteristicConfigurationDescriptorValue::Indicate - } else if properties & notify == notify { + if properties & notify == notify { GattClientCharacteristicConfigurationDescriptorValue::Notify + } else if properties & indicate == indicate { + GattClientCharacteristicConfigurationDescriptorValue::Indicate } else { GattClientCharacteristicConfigurationDescriptorValue::None } } +#[cfg(test)] +mod notification_mode_tests { + use super::*; + #[test] + fn prefer_notifications_when_both_modes_are_advertised() { + let notify = GattCharacteristicProperties::Notify; + let indicate = GattCharacteristicProperties::Indicate; + assert_eq!( + to_descriptor_value(notify | indicate), + GattClientCharacteristicConfigurationDescriptorValue::Notify + ); + assert_eq!( + to_descriptor_value(notify), + GattClientCharacteristicConfigurationDescriptorValue::Notify + ); + assert_eq!( + to_descriptor_value(indicate), + GattClientCharacteristicConfigurationDescriptorValue::Indicate + ); + assert_eq!( + to_descriptor_value(GattCharacteristicProperties::None), + GattClientCharacteristicConfigurationDescriptorValue::None + ); + } +} + pub fn to_uuid(uuid: &GUID) -> Uuid { let guid_s = format!("{:?}", uuid); Uuid::from_str(&guid_s).unwrap()