Skip to content

Commit

Permalink
Merge pull request #17 from oxc/default_configuration_commands
Browse files Browse the repository at this point in the history
Add support for Default System/Runtime configuration
  • Loading branch information
laptou authored Oct 15, 2020
2 parents fc7f55a + 3a39fd1 commit 16f02ac
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 2 deletions.
40 changes: 40 additions & 0 deletions src/client/params.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::hash::Hash;

use enumflags2::BitFlags;

use crate::Address;
Expand Down Expand Up @@ -199,3 +201,41 @@ pub enum PhyFlag {
LECodedTx = 1 << 13,
LECodedRx = 1 << 14,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
#[repr(u16)]
pub enum SystemConfigParameterType {
BREDRPageScanType = 0x0000,
BREDRPageScanInterval,
BREDRPageScanWindow,
BREDRInquiryScanType,
BREDRInquiryScanInterval,
BREDRInquiryScanWindow,
BREDRLinkSupervisionTimeout,
BREDRPageTimeout,
BREDRMinSniffInterval,
BREDRMaxSniffInterval,
LEAdvertisementMinInterval,
LEAdvertisementMaxInterval,
LEMultiAdvertisementRotationInterval,
LEScanningIntervalForAutoConnect,
LEScanningWindowForAutoConnect,
LEScanningIntervalForWakeScenarios,
LEScanningWindowForWakeScenarios,
LEScanningIntervalForDiscovery,
LEScanningWindowForDiscovery,
LEScanningIntervalForAdvMonitoring,
LEScanningWindowForAdvMonitoring,
LEScanningIntervalForConnect,
LEScanningWindowForConnect,
LEMinConnectionInterval,
LEMaxConnectionInterval,
LEConnectionLatency,
LEConnectionSupervisionTimeout,
LEAutoconnectTimeout,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
//#[repr(u16)] once there are known variants
#[non_exhaustive]
pub enum RuntimeConfigParameterType {}
31 changes: 31 additions & 0 deletions src/client/query.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use crate::interface::class::from_bytes as class_from_bytes;
use crate::interface::controller::ControllerInfoExt;
use crate::util::BufExt2;
Expand Down Expand Up @@ -382,4 +384,33 @@ impl<'a> BlueZClient<'a> {
})
.await
}

/// Currently no Parameter_Type values are defined and an empty list
/// will be returned.
///
/// This command can be used at any time and will return a list of
/// supported default parameters as well as their current value.
pub async fn get_default_runtime_config(
&mut self,
controller: Controller,
) -> Result<HashMap<RuntimeConfigParameterType, Vec<u8>>> {
self.exec_command(Command::ReadDefaultRuntimeConfig, controller, None, |_, param| {
let mut param = param.unwrap();
Ok(param.get_tlv_map())
})
.await
}

/// This command can be used at any time and will return a list of
/// supported default parameters as well as their current value.
pub async fn get_default_system_config(
&mut self,
controller: Controller,
) -> Result<HashMap<SystemConfigParameterType, Vec<u8>>> {
self.exec_command(Command::ReadDefaultSystemConfig, controller, None, |_, param| {
let mut param = param.unwrap();
Ok(param.get_tlv_map())
})
.await
}
}
90 changes: 90 additions & 0 deletions src/client/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,4 +796,94 @@ impl<'a> BlueZClient<'a> {
)
.await
}

/// This command is used to enable/disable Wideband Speech
/// support for a controller.
///
/// This command is only available for BR/EDR capable controllers and
/// require controller specific support.
///
/// This command can be used when the controller is not powered and
/// all settings will be programmed once powered.
///
/// In case the controller does not support Wideband Speech
/// the command will fail regardless with Not Supported error.
pub async fn set_wideband_speech(
&mut self,
controller: Controller,
enabled: bool,
) -> Result<ControllerSettings> {
let mut param = BytesMut::with_capacity(1);
param.put_u8(enabled as u8);

self.exec_command(
Command::SetWidebandSpeech,
controller,
Some(param.to_bytes()),
settings_callback,
)
.await
}

/// This command is used to set a list of default runtime parameters.
///
/// This command can be used at any time and will change the runtime
/// default. Changes however will not apply to existing connections or
/// currently active operations.
///
/// When providing unsupported values or invalid values, no parameter
/// value will be changed and all values discarded.
pub async fn set_default_runtime_config(
&mut self,
controller: Controller,
params: &[(RuntimeConfigParameterType, Vec<u8>)],
) -> Result<()> {
let size = params.iter().fold(0, |acc, (_, value)| acc + 3 + value.len());
let mut param = BytesMut::with_capacity(size);

#[allow(unreachable_code,unused_variables)] // until we have constants in RuntimeConfigParameterType
for (parameter_type, value) in params {
param.put_u16_le(unimplemented!("*parameter_type as u16"));
param.put_u8(value.len() as u8);
param.put_slice(value);
}

self.exec_command(
Command::SetDefaultSystemConfig,
controller,
Some(param.to_bytes()),
|_, _| Ok(()),
)
.await
}

/// This command is used to set a list of default controller parameters.
///
/// This command can be used when the controller is not powered and
/// all supported parameters will be programmed once powered.
///
/// When providing unsupported values or invalid values, no parameter
/// value will be changed and all values discarded.
pub async fn set_default_system_config(
&mut self,
controller: Controller,
params: &[(SystemConfigParameterType, Vec<u8>)],
) -> Result<()> {
let size = params.iter().fold(0, |acc, (_, value)| acc + 3 + value.len());
let mut param = BytesMut::with_capacity(size);

for (parameter_type, value) in params {
param.put_u16_le(*parameter_type as u16);
param.put_u8(value.len() as u8);
param.put_slice(value);
}

self.exec_command(
Command::SetDefaultSystemConfig,
controller,
Some(param.to_bytes()),
|_, _| Ok(()),
)
.await
}
}
13 changes: 13 additions & 0 deletions src/interface/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ pub enum Command {
GetPhyConfig,
SetPhyConfig,
LoadBlockedKeys,
SetWidebandSpeech,
ReadSecurityInfo,
ReadExperimentalFeaturesInfo,
SetExperimentalFeature,
ReadDefaultSystemConfig,
SetDefaultSystemConfig,
ReadDefaultRuntimeConfig,
SetDefaultRuntimeConfig,
GetDeviceFlags,
SetDeviceFlags,
ReadAdvertisementMonitorFeatures,
AddAdvertisementPatternsMonitor,
RemoveAdvertisementMonitor,
}

impl fmt::LowerHex for CommandStatus {
Expand Down
2 changes: 2 additions & 0 deletions src/interface/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub enum ControllerSetting {
Privacy = 1 << 13,
Configuration = 1 << 14,
StaticAddress = 1 << 15,
PhyConfiguration = 1 << 16,
WidebandSpeech = 1 << 17,
}

pub type ControllerSettings = BitFlags<ControllerSetting>;
27 changes: 27 additions & 0 deletions src/interface/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::interface::class::{DeviceClass, ServiceClasses};
use crate::interface::controller::ControllerSettings;
use crate::interface::{Command, CommandStatus};
use crate::Address;
use std::collections::HashMap;

#[derive(Debug)]
pub enum Event {
Expand Down Expand Up @@ -433,4 +434,30 @@ pub enum Event {
/// The event will only be sent to management sockets other than the
/// one through which the command was sent.
PhyConfigChanged { selected_phys: BitFlags<PhyFlag> },

/// This event indicates that the status of an experimental feature
/// has been changed.
///
/// The event will only be sent to management sockets other than the
/// one through which the change was triggered.
ExperimentalFeatureChanged {
uuid: [u8; 16],
flags: u32,
},

/// This event indicates the change of default system parameter values.
///
/// The event will only be sent to management sockets other than the
/// one through which the change was trigged. In addition it will
/// only be sent to sockets that have issues the Read Default System
/// Configuration command.
DefaultSystemConfigChanged { params: HashMap<SystemConfigParameterType, Vec<u8>> },

/// This event indicates the change of default runtime parameter values.
///
/// The event will only be sent to management sockets other than the
/// one through which the change was trigged. In addition it will
/// only be sent to sockets that have issues the Read Default Runtime
/// Configuration command.
DefaultRuntimeConfigChanged { params: HashMap<RuntimeConfigParameterType, Vec<u8>> },
}
12 changes: 11 additions & 1 deletion src/interface/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,17 @@ impl Response {
0x0026 => Event::PhyConfigChanged {
selected_phys: BitFlags::from_bits_truncate(buf.get_u32_le()),
},
_ => todo!("throw error instead of panicking"),
0x0027 => Event::ExperimentalFeatureChanged {
uuid: buf.get_u8x16(),
flags: buf.get_u32_le(),
},
0x0028 => Event::DefaultSystemConfigChanged {
params: buf.get_tlv_map()
},
0x0029 => Event::DefaultRuntimeConfigChanged {
params: buf.get_tlv_map()
},
_ => return Err(Error::UnknownEventCode { evt_code }),
},
})
}
Expand Down
4 changes: 3 additions & 1 deletion src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub enum Error {
#[source]
source: ::std::io::Error,
},
#[error("Command {:?} returned {:?}.", status, opcode)]
#[error("Command {:?} returned {:?}.", opcode, status)]
CommandError {
opcode: Command,
status: CommandStatus,
Expand All @@ -22,6 +22,8 @@ pub enum Error {
UnknownOpcode { opcode: u16 },
#[error("Unknown command status: {:x}.", status)]
UnknownStatus { status: u8 },
#[error("Unknown event code: {:x}.", evt_code)]
UnknownEventCode { evt_code: u16 },
#[error("Timed out.")]
TimedOut,
#[error("The socket received invalid data.")]
Expand Down
36 changes: 36 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::collections::HashMap;
use std::ffi::CString;
use std::hash::Hash;

use bytes::Buf;
use enumflags2::BitFlags;
Expand All @@ -20,6 +22,12 @@ pub(crate) trait BufExt2: Buf {
arr
}

fn get_vec_u8(&mut self, len: usize) -> Vec<u8> {
let mut ret = vec![0; len];
self.copy_to_slice(ret.as_mut_slice());
ret
}

fn get_bool(&mut self) -> bool {
self.get_u8() != 0
}
Expand Down Expand Up @@ -53,6 +61,34 @@ pub(crate) trait BufExt2: Buf {
}
return unsafe { CString::from_vec_unchecked(bytes) };
}

/// Parses a list of Type/Length/Value entries into a map keyed by type
///
/// This parses a list of mgmt_tlv entries (as defined in mgmt.h) and converts them
/// into a map of Type => Vec<u8>.
///
/// # Bytes layout
///
/// The layout as described in the mgmt-api documentation is:
/// ```plain
/// Parameter1 {
/// Parameter_Type (2 Octet)
/// Value_Length (1 Octet)
/// Value (0-255 Octets)
/// }
/// Parameter2 { }
/// ...
/// ```
///
fn get_tlv_map<T : FromPrimitive + Eq + Hash>(&mut self) -> HashMap<T, Vec<u8>> {
let mut parameters = HashMap::new();
while self.has_remaining() {
let parameter_type: T = self.get_primitive_u16_le();
let value_size = self.get_u8() as usize;
parameters.insert(parameter_type, self.get_vec_u8(value_size));
}
parameters
}
}

impl<T: Buf> BufExt2 for T {}

0 comments on commit 16f02ac

Please sign in to comment.