| //! Bluetooth interface shim |
| //! |
| //! This is a shim interface for calling the C++ bluetooth interface via Rust. |
| //! |
| |
| use crate::bindings::root as bindings; |
| use crate::topstack::get_dispatchers; |
| use num_traits::cast::{FromPrimitive, ToPrimitive}; |
| use std::cmp; |
| use std::fmt::{Debug, Formatter, Result}; |
| use std::mem; |
| use std::os::raw::c_char; |
| use std::sync::{Arc, Mutex}; |
| use std::vec::Vec; |
| use topshim_macros::cb_variant; |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtState { |
| Off = 0, |
| On, |
| } |
| |
| impl From<bindings::bt_state_t> for BtState { |
| fn from(item: bindings::bt_state_t) -> Self { |
| BtState::from_u32(item).unwrap_or_else(|| BtState::Off) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtTransport { |
| Auto = 0, |
| Bredr, |
| Le, |
| } |
| |
| impl From<i32> for BtTransport { |
| fn from(item: i32) -> Self { |
| BtTransport::from_i32(item).unwrap_or_else(|| BtTransport::Auto) |
| } |
| } |
| |
| impl From<BtTransport> for i32 { |
| fn from(item: BtTransport) -> Self { |
| item.to_i32().unwrap_or_else(|| 0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtSspVariant { |
| PasskeyConfirmation = 0, |
| PasskeyEntry, |
| Consent, |
| PasskeyNotification, |
| } |
| |
| impl From<bindings::bt_ssp_variant_t> for BtSspVariant { |
| fn from(item: bindings::bt_ssp_variant_t) -> Self { |
| BtSspVariant::from_u32(item).unwrap_or_else(|| BtSspVariant::PasskeyConfirmation) |
| } |
| } |
| |
| impl From<BtSspVariant> for bindings::bt_ssp_variant_t { |
| fn from(item: BtSspVariant) -> Self { |
| item.to_u32().unwrap_or_else(|| 0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtBondState { |
| NotBonded = 0, |
| Bonding, |
| Bonded, |
| } |
| |
| impl From<bindings::bt_bond_state_t> for BtBondState { |
| fn from(item: bindings::bt_bond_state_t) -> Self { |
| BtBondState::from_u32(item).unwrap_or_else(|| BtBondState::NotBonded) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtConnectionState { |
| NotConnected = 0, |
| ConnectedOnly = 1, |
| EncryptedBredr = 3, |
| EncryptedLe = 5, |
| } |
| |
| impl From<i32> for BtConnectionState { |
| fn from(item: i32) -> Self { |
| let fallback = if item > 0 { |
| BtConnectionState::ConnectedOnly |
| } else { |
| BtConnectionState::NotConnected |
| }; |
| |
| BtConnectionState::from_i32(item).unwrap_or(fallback) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtAclState { |
| Connected = 0, |
| Disconnected, |
| } |
| |
| impl From<bindings::bt_acl_state_t> for BtAclState { |
| fn from(item: bindings::bt_acl_state_t) -> Self { |
| BtAclState::from_u32(item).unwrap_or_else(|| BtAclState::Disconnected) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtDeviceType { |
| Bredr, |
| Ble, |
| Dual, |
| } |
| |
| #[derive(Clone, Debug, Eq, Hash, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtPropertyType { |
| BdName = 0x1, |
| BdAddr, |
| Uuids, |
| ClassOfDevice, |
| TypeOfDevice, |
| ServiceRecord, |
| AdapterScanMode, |
| AdapterBondedDevices, |
| AdapterDiscoveryTimeout, |
| RemoteFriendlyName, |
| RemoteRssi, |
| RemoteVersionInfo, |
| LocalLeFeatures, |
| LocalIoCaps, |
| LocalIoCapsBle, |
| DynamicAudioBuffer, |
| |
| Unknown = 0xFE, |
| RemoteDeviceTimestamp = 0xFF, |
| } |
| |
| impl From<u32> for BtPropertyType { |
| fn from(item: u32) -> Self { |
| BtPropertyType::from_u32(item).unwrap_or_else(|| BtPropertyType::Unknown) |
| } |
| } |
| |
| impl From<BtPropertyType> for u32 { |
| fn from(item: BtPropertyType) -> Self { |
| item.to_u32().unwrap_or_else(|| 0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtDiscoveryState { |
| Stopped = 0x0, |
| Started, |
| } |
| |
| impl From<u32> for BtDiscoveryState { |
| fn from(item: u32) -> Self { |
| BtDiscoveryState::from_u32(item).unwrap_or_else(|| BtDiscoveryState::Stopped) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtStatus { |
| Success = 0, |
| Fail, |
| NotReady, |
| NoMemory, |
| Busy, |
| Done, |
| Unsupported, |
| InvalidParam, |
| Unhandled, |
| AuthFailure, |
| RemoteDeviceDown, |
| AuthRejected, |
| JniEnvironmentError, |
| JniThreadAttachError, |
| WakeLockError, |
| |
| // Any statuses that couldn't be cleanly converted |
| Unknown = 0xff, |
| } |
| |
| pub fn ascii_to_string(data: &[u8], length: usize) -> String { |
| // We need to reslice data because from_utf8 tries to interpret the |
| // whole slice and not just what is before the null terminated portion |
| let ascii = data |
| .iter() |
| .enumerate() |
| .take_while(|&(pos, &c)| c != 0 && pos < length) |
| .map(|(_pos, &x)| x.clone()) |
| .collect::<Vec<u8>>(); |
| |
| return String::from_utf8(ascii).unwrap_or_default(); |
| } |
| |
| fn u32_from_bytes(item: &[u8]) -> u32 { |
| let mut u: [u8; 4] = [0; 4]; |
| u.copy_from_slice(&item[0..4]); |
| u32::from_ne_bytes(u) |
| } |
| |
| impl From<bindings::bt_status_t> for BtStatus { |
| fn from(item: bindings::bt_status_t) -> Self { |
| match BtStatus::from_u32(item) { |
| Some(x) => x, |
| _ => BtStatus::Unknown, |
| } |
| } |
| } |
| |
| impl From<bindings::bt_bdname_t> for String { |
| fn from(item: bindings::bt_bdname_t) -> Self { |
| ascii_to_string(&item.name, item.name.len()) |
| } |
| } |
| |
| #[derive(Debug, Clone)] |
| pub struct BtServiceRecord { |
| pub uuid: bindings::bluetooth::Uuid, |
| pub channel: u16, |
| pub name: String, |
| } |
| |
| impl From<bindings::bt_service_record_t> for BtServiceRecord { |
| fn from(item: bindings::bt_service_record_t) -> Self { |
| let name = item.name.iter().map(|&x| x.clone() as u8).collect::<Vec<u8>>(); |
| |
| BtServiceRecord { |
| uuid: item.uuid, |
| channel: item.channel, |
| name: ascii_to_string(name.as_slice(), name.len()), |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtScanMode { |
| None_, |
| Connectable, |
| ConnectableDiscoverable, |
| } |
| |
| impl From<bindings::bt_scan_mode_t> for BtScanMode { |
| fn from(item: bindings::bt_scan_mode_t) -> Self { |
| BtScanMode::from_u32(item).unwrap_or(BtScanMode::None_) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtIoCap { |
| Out, |
| InOut, |
| In, |
| None_, |
| KbDisp, |
| Max, |
| Unknown = 0xff, |
| } |
| |
| impl From<bindings::bt_io_cap_t> for BtIoCap { |
| fn from(item: bindings::bt_io_cap_t) -> Self { |
| BtIoCap::from_u32(item).unwrap_or(BtIoCap::Unknown) |
| } |
| } |
| |
| pub type BtHciErrorCode = u8; |
| pub type BtLocalLeFeatures = bindings::bt_local_le_features_t; |
| pub type BtPinCode = bindings::bt_pin_code_t; |
| pub type BtRemoteVersion = bindings::bt_remote_version_t; |
| pub type Uuid = bindings::bluetooth::Uuid; |
| pub type Uuid128Bit = bindings::bluetooth::Uuid_UUID128Bit; |
| |
| /// All supported Bluetooth properties after conversion. |
| #[derive(Debug, Clone)] |
| pub enum BluetoothProperty { |
| BdName(String), |
| BdAddr(RawAddress), |
| Uuids(Vec<Uuid>), |
| ClassOfDevice(u32), |
| TypeOfDevice(BtDeviceType), |
| ServiceRecord(BtServiceRecord), |
| AdapterScanMode(BtScanMode), |
| AdapterBondedDevices(Vec<RawAddress>), |
| AdapterDiscoveryTimeout(u32), |
| RemoteFriendlyName(String), |
| RemoteRssi(i8), |
| RemoteVersionInfo(BtRemoteVersion), |
| LocalLeFeatures(BtLocalLeFeatures), |
| LocalIoCaps(BtIoCap), |
| LocalIoCapsBle(BtIoCap), |
| DynamicAudioBuffer(), |
| RemoteDeviceTimestamp(), |
| |
| Unknown(), |
| } |
| |
| /// Wherever names are sent in bindings::bt_property_t, the size of the character |
| /// arrays are 256. Keep one extra byte for null termination. |
| const PROPERTY_NAME_MAX: usize = 255; |
| |
| impl BluetoothProperty { |
| pub fn get_type(&self) -> BtPropertyType { |
| match &*self { |
| BluetoothProperty::BdName(_) => BtPropertyType::BdName, |
| BluetoothProperty::BdAddr(_) => BtPropertyType::BdAddr, |
| BluetoothProperty::Uuids(_) => BtPropertyType::Uuids, |
| BluetoothProperty::ClassOfDevice(_) => BtPropertyType::ClassOfDevice, |
| BluetoothProperty::TypeOfDevice(_) => BtPropertyType::TypeOfDevice, |
| BluetoothProperty::ServiceRecord(_) => BtPropertyType::ServiceRecord, |
| BluetoothProperty::AdapterScanMode(_) => BtPropertyType::AdapterScanMode, |
| BluetoothProperty::AdapterBondedDevices(_) => BtPropertyType::AdapterBondedDevices, |
| BluetoothProperty::AdapterDiscoveryTimeout(_) => { |
| BtPropertyType::AdapterDiscoveryTimeout |
| } |
| BluetoothProperty::RemoteFriendlyName(_) => BtPropertyType::RemoteFriendlyName, |
| BluetoothProperty::RemoteRssi(_) => BtPropertyType::RemoteRssi, |
| BluetoothProperty::RemoteVersionInfo(_) => BtPropertyType::RemoteVersionInfo, |
| BluetoothProperty::LocalLeFeatures(_) => BtPropertyType::LocalLeFeatures, |
| BluetoothProperty::LocalIoCaps(_) => BtPropertyType::LocalIoCaps, |
| BluetoothProperty::LocalIoCapsBle(_) => BtPropertyType::LocalIoCapsBle, |
| BluetoothProperty::DynamicAudioBuffer() => BtPropertyType::DynamicAudioBuffer, |
| BluetoothProperty::RemoteDeviceTimestamp() => BtPropertyType::RemoteDeviceTimestamp, |
| BluetoothProperty::Unknown() => BtPropertyType::Unknown, |
| } |
| } |
| |
| fn get_len(&self) -> usize { |
| match &*self { |
| BluetoothProperty::BdName(name) => cmp::min(PROPERTY_NAME_MAX, name.len() + 1), |
| BluetoothProperty::BdAddr(addr) => addr.val.len(), |
| BluetoothProperty::Uuids(uulist) => uulist.len() * mem::size_of::<Uuid>(), |
| BluetoothProperty::ClassOfDevice(_) => mem::size_of::<u32>(), |
| BluetoothProperty::TypeOfDevice(_) => mem::size_of::<BtDeviceType>(), |
| BluetoothProperty::ServiceRecord(rec) => { |
| mem::size_of::<BtServiceRecord>() + cmp::min(PROPERTY_NAME_MAX, rec.name.len() + 1) |
| } |
| BluetoothProperty::AdapterScanMode(_) => mem::size_of::<BtScanMode>(), |
| BluetoothProperty::AdapterBondedDevices(devlist) => { |
| devlist.len() * mem::size_of::<RawAddress>() |
| } |
| BluetoothProperty::AdapterDiscoveryTimeout(_) => mem::size_of::<u32>(), |
| BluetoothProperty::RemoteFriendlyName(name) => { |
| cmp::min(PROPERTY_NAME_MAX, name.len() + 1) |
| } |
| BluetoothProperty::RemoteRssi(_) => mem::size_of::<i8>(), |
| BluetoothProperty::RemoteVersionInfo(_) => mem::size_of::<BtRemoteVersion>(), |
| BluetoothProperty::LocalLeFeatures(_) => mem::size_of::<BtLocalLeFeatures>(), |
| BluetoothProperty::LocalIoCaps(_) => mem::size_of::<BtIoCap>(), |
| BluetoothProperty::LocalIoCapsBle(_) => mem::size_of::<BtIoCap>(), |
| |
| // TODO(abps) - Figure out sizes for these |
| BluetoothProperty::DynamicAudioBuffer() => 0, |
| BluetoothProperty::RemoteDeviceTimestamp() => 0, |
| BluetoothProperty::Unknown() => 0, |
| } |
| } |
| |
| // Given a mutable array, this will copy the data to that array and return a |
| // pointer to it. |
| // |
| // The lifetime of the returned pointer is tied to that of the slice given. |
| fn get_data_ptr<'a>(&'a self, data: &'a mut [u8]) -> *mut u8 { |
| let len = self.get_len(); |
| match &*self { |
| BluetoothProperty::BdName(name) => { |
| data.copy_from_slice(&name.as_bytes()[0..len]); |
| } |
| BluetoothProperty::BdAddr(addr) => { |
| data.copy_from_slice(&addr.val); |
| } |
| BluetoothProperty::Uuids(uulist) => { |
| for (idx, &uuid) in uulist.iter().enumerate() { |
| let start = idx * mem::size_of::<Uuid>(); |
| let end = start + mem::size_of::<Uuid>(); |
| data[start..end].copy_from_slice(&uuid.uu); |
| } |
| } |
| BluetoothProperty::ClassOfDevice(cod) => { |
| data.copy_from_slice(&cod.to_ne_bytes()); |
| } |
| BluetoothProperty::TypeOfDevice(tod) => { |
| data.copy_from_slice(&BtDeviceType::to_u32(tod).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::ServiceRecord(sr) => { |
| // Do an unsafe cast to binding:: type and assign the values |
| // The underlying memory location is provided by |data| which will |
| // have enough space because it uses get_len() |
| let mut record = |
| unsafe { &mut *(data.as_mut_ptr() as *mut bindings::bt_service_record_t) }; |
| record.uuid = sr.uuid; |
| record.channel = sr.channel; |
| let name_len = len - mem::size_of::<BtServiceRecord>(); |
| record.name.copy_from_slice( |
| &(sr.name.as_bytes().iter().map(|x| *x as c_char).collect::<Vec<c_char>>()) |
| [0..name_len], |
| ); |
| } |
| BluetoothProperty::AdapterScanMode(sm) => { |
| data.copy_from_slice(&BtScanMode::to_u32(sm).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::AdapterBondedDevices(devlist) => { |
| for (idx, &dev) in devlist.iter().enumerate() { |
| let start = idx * mem::size_of::<RawAddress>(); |
| let end = idx + mem::size_of::<RawAddress>(); |
| data[start..end].copy_from_slice(&dev.val); |
| } |
| } |
| BluetoothProperty::AdapterDiscoveryTimeout(timeout) => { |
| data.copy_from_slice(&timeout.to_ne_bytes()); |
| } |
| BluetoothProperty::RemoteFriendlyName(name) => { |
| data.copy_from_slice(&name.as_bytes()[0..len]); |
| } |
| BluetoothProperty::RemoteRssi(rssi) => { |
| data[0] = *rssi as u8; |
| } |
| BluetoothProperty::RemoteVersionInfo(rvi) => { |
| let ptr: *const BtRemoteVersion = rvi; |
| let slice = unsafe { |
| std::slice::from_raw_parts(ptr as *mut u8, mem::size_of::<BtRemoteVersion>()) |
| }; |
| data.copy_from_slice(&slice); |
| } |
| BluetoothProperty::LocalLeFeatures(llf) => { |
| let ptr: *const BtLocalLeFeatures = llf; |
| let slice = unsafe { |
| std::slice::from_raw_parts(ptr as *mut u8, mem::size_of::<BtLocalLeFeatures>()) |
| }; |
| data.copy_from_slice(&slice); |
| } |
| BluetoothProperty::LocalIoCaps(iocap) => { |
| data.copy_from_slice(&BtIoCap::to_u32(iocap).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::LocalIoCapsBle(iocap) => { |
| data.copy_from_slice(&BtIoCap::to_u32(iocap).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::DynamicAudioBuffer() => (), |
| BluetoothProperty::RemoteDeviceTimestamp() => (), |
| BluetoothProperty::Unknown() => (), |
| }; |
| |
| data.as_mut_ptr() |
| } |
| } |
| |
| // TODO(abps) - Check that sizes are correct when given a BtProperty |
| impl From<bindings::bt_property_t> for BluetoothProperty { |
| fn from(prop: bindings::bt_property_t) -> Self { |
| let slice: &[u8] = |
| unsafe { std::slice::from_raw_parts(prop.val as *mut u8, prop.len as usize) }; |
| let len = prop.len as usize; |
| |
| match BtPropertyType::from(prop.type_) { |
| BtPropertyType::BdName => BluetoothProperty::BdName(ascii_to_string(slice, len)), |
| BtPropertyType::BdAddr => { |
| BluetoothProperty::BdAddr(RawAddress::from_bytes(slice).unwrap_or_default()) |
| } |
| BtPropertyType::Uuids => { |
| let count = len / mem::size_of::<Uuid>(); |
| BluetoothProperty::Uuids(ptr_to_vec(prop.val as *mut Uuid, count)) |
| } |
| BtPropertyType::ClassOfDevice => { |
| BluetoothProperty::ClassOfDevice(u32_from_bytes(slice)) |
| } |
| BtPropertyType::TypeOfDevice => BluetoothProperty::TypeOfDevice( |
| BtDeviceType::from_u32(u32_from_bytes(slice)).unwrap_or(BtDeviceType::Bredr), |
| ), |
| BtPropertyType::ServiceRecord => { |
| let v = unsafe { *(prop.val as *const bindings::bt_service_record_t) }; |
| BluetoothProperty::ServiceRecord(BtServiceRecord::from(v)) |
| } |
| BtPropertyType::AdapterScanMode => BluetoothProperty::AdapterScanMode( |
| BtScanMode::from_u32(u32_from_bytes(slice)).unwrap_or(BtScanMode::None_), |
| ), |
| BtPropertyType::AdapterBondedDevices => { |
| let count = len / mem::size_of::<RawAddress>(); |
| BluetoothProperty::AdapterBondedDevices(ptr_to_vec( |
| prop.val as *mut RawAddress, |
| count, |
| )) |
| } |
| BtPropertyType::AdapterDiscoveryTimeout => { |
| BluetoothProperty::AdapterDiscoveryTimeout(u32_from_bytes(slice)) |
| } |
| BtPropertyType::RemoteFriendlyName => { |
| BluetoothProperty::RemoteFriendlyName(ascii_to_string(slice, len)) |
| } |
| BtPropertyType::RemoteRssi => BluetoothProperty::RemoteRssi(slice[0] as i8), |
| BtPropertyType::RemoteVersionInfo => { |
| let v = unsafe { *(prop.val as *const BtRemoteVersion) }; |
| BluetoothProperty::RemoteVersionInfo(v.clone()) |
| } |
| BtPropertyType::LocalLeFeatures => { |
| let v = unsafe { *(prop.val as *const BtLocalLeFeatures) }; |
| BluetoothProperty::LocalLeFeatures(v.clone()) |
| } |
| BtPropertyType::LocalIoCaps => BluetoothProperty::LocalIoCaps( |
| BtIoCap::from_u32(u32_from_bytes(slice)).unwrap_or(BtIoCap::Unknown), |
| ), |
| BtPropertyType::LocalIoCapsBle => BluetoothProperty::LocalIoCapsBle( |
| BtIoCap::from_u32(u32_from_bytes(slice)).unwrap_or(BtIoCap::Unknown), |
| ), |
| |
| // TODO(abps) - Figure out if these values should actually have contents |
| BtPropertyType::DynamicAudioBuffer => BluetoothProperty::DynamicAudioBuffer(), |
| BtPropertyType::RemoteDeviceTimestamp => BluetoothProperty::RemoteDeviceTimestamp(), |
| _ => BluetoothProperty::Unknown(), |
| } |
| } |
| } |
| |
| impl From<BluetoothProperty> for (Box<[u8]>, bindings::bt_property_t) { |
| fn from(prop: BluetoothProperty) -> Self { |
| let dvec: Vec<u8> = vec![0; prop.get_len()]; |
| let mut data: Box<[u8]> = dvec.into_boxed_slice(); |
| let prop = bindings::bt_property_t { |
| type_: prop.get_type().into(), |
| len: prop.get_len() as i32, |
| val: prop.get_data_ptr(&mut data) as *mut std::os::raw::c_void, |
| }; |
| |
| (data, prop) |
| } |
| } |
| |
| pub enum SupportedProfiles { |
| HidHost, |
| Hfp, |
| A2dp, |
| Gatt, |
| Sdp, |
| } |
| |
| impl From<SupportedProfiles> for Vec<u8> { |
| fn from(item: SupportedProfiles) -> Self { |
| match item { |
| SupportedProfiles::HidHost => "hidhost", |
| SupportedProfiles::Hfp => "hfp", |
| SupportedProfiles::A2dp => "a2dp", |
| SupportedProfiles::Gatt => "gatt", |
| SupportedProfiles::Sdp => "sdp", |
| } |
| .bytes() |
| .chain("\0".bytes()) |
| .collect::<Vec<u8>>() |
| } |
| } |
| |
| #[cxx::bridge(namespace = bluetooth::topshim::rust)] |
| mod ffi { |
| unsafe extern "C++" { |
| include!("btif/btif_shim.h"); |
| |
| // For converting init flags from Vec<String> to const char ** |
| type InitFlags; |
| |
| // Convert flgas into an InitFlags object |
| fn ConvertFlags(flags: Vec<String>) -> UniquePtr<InitFlags>; |
| fn GetFlagsPtr(self: &InitFlags) -> *mut *const c_char; |
| } |
| } |
| |
| // Export the raw address type directly from the bindings |
| pub type FfiAddress = bindings::RawAddress; |
| |
| /// A shared address structure that has the same representation as |
| /// bindings::RawAddress. Macros `deref_ffi_address` and `cast_to_ffi_address` |
| /// are used for transforming between bindings::RawAddress at ffi boundaries. |
| #[derive(Copy, Clone, Hash, Eq, PartialEq)] |
| #[repr(C)] |
| pub struct RawAddress { |
| pub val: [u8; 6], |
| } |
| |
| impl Debug for RawAddress { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| f.write_fmt(format_args!( |
| "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", |
| self.val[0], self.val[1], self.val[2], self.val[3], self.val[4], self.val[5] |
| )) |
| } |
| } |
| |
| impl Default for RawAddress { |
| fn default() -> Self { |
| Self { val: [0; 6] } |
| } |
| } |
| |
| impl ToString for RawAddress { |
| fn to_string(&self) -> String { |
| String::from(format!( |
| "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", |
| self.val[0], self.val[1], self.val[2], self.val[3], self.val[4], self.val[5] |
| )) |
| } |
| } |
| |
| impl RawAddress { |
| /// Constructs a RawAddress from a slice of 6 bytes. |
| pub fn from_bytes(raw_addr: &[u8]) -> Option<RawAddress> { |
| if raw_addr.len() != 6 { |
| return None; |
| } |
| let mut raw: [u8; 6] = [0; 6]; |
| raw.copy_from_slice(raw_addr); |
| return Some(RawAddress { val: raw }); |
| } |
| |
| pub fn from_string<S: Into<String>>(addr: S) -> Option<RawAddress> { |
| let addr: String = addr.into(); |
| let s = addr.split(':').collect::<Vec<&str>>(); |
| |
| if s.len() != 6 { |
| return None; |
| } |
| |
| let mut raw: [u8; 6] = [0; 6]; |
| for i in 0..s.len() { |
| raw[i] = match u8::from_str_radix(s[i], 16) { |
| Ok(res) => res, |
| Err(_) => { |
| return None; |
| } |
| }; |
| } |
| |
| Some(RawAddress { val: raw }) |
| } |
| |
| pub fn to_byte_arr(&self) -> [u8; 6] { |
| self.val.clone() |
| } |
| } |
| |
| #[macro_export] |
| macro_rules! deref_ffi_address { |
| ($ffi_addr:ident) => { |
| *($ffi_addr as *mut RawAddress) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! deref_const_ffi_address { |
| ($ffi_addr:ident) => { |
| *($ffi_addr as *const RawAddress) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! cast_to_ffi_address { |
| ($raw_addr:expr) => { |
| $raw_addr as *mut FfiAddress |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! cast_to_const_ffi_address { |
| ($raw_addr:expr) => { |
| $raw_addr as *const FfiAddress |
| }; |
| } |
| |
| #[derive(Clone, Debug)] |
| pub enum BaseCallbacks { |
| AdapterState(BtState), |
| AdapterProperties(BtStatus, i32, Vec<BluetoothProperty>), |
| RemoteDeviceProperties(BtStatus, RawAddress, i32, Vec<BluetoothProperty>), |
| DeviceFound(i32, Vec<BluetoothProperty>), |
| DiscoveryState(BtDiscoveryState), |
| PinRequest(RawAddress, String, u32, bool), |
| SspRequest(RawAddress, String, u32, BtSspVariant, u32), |
| BondState(BtStatus, RawAddress, BtBondState, i32), |
| AclState(BtStatus, RawAddress, BtAclState, BtTransport, BtHciErrorCode), |
| // Unimplemented so far: |
| // thread_evt_cb |
| // dut_mode_recv_cb |
| // le_test_mode_cb |
| // energy_info_cb |
| // link_quality_report_cb |
| // generate_local_oob_data_cb |
| } |
| |
| pub struct BaseCallbacksDispatcher { |
| pub dispatch: Box<dyn Fn(BaseCallbacks) + Send>, |
| } |
| |
| type BaseCb = Arc<Mutex<BaseCallbacksDispatcher>>; |
| |
| cb_variant!(BaseCb, adapter_state_cb -> BaseCallbacks::AdapterState, u32 -> BtState); |
| cb_variant!(BaseCb, adapter_properties_cb -> BaseCallbacks::AdapterProperties, |
| u32 -> BtStatus, i32, *mut bindings::bt_property_t, { |
| let _2 = ptr_to_vec(_2, _1 as usize); |
| }); |
| cb_variant!(BaseCb, remote_device_properties_cb -> BaseCallbacks::RemoteDeviceProperties, |
| u32 -> BtStatus, *mut FfiAddress -> RawAddress, i32, *mut bindings::bt_property_t, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| let _3 = ptr_to_vec(_3, _2 as usize); |
| }); |
| cb_variant!(BaseCb, device_found_cb -> BaseCallbacks::DeviceFound, |
| i32, *mut bindings::bt_property_t, { |
| let _1 = ptr_to_vec(_1, _0 as usize); |
| }); |
| cb_variant!(BaseCb, discovery_state_cb -> BaseCallbacks::DiscoveryState, |
| bindings::bt_discovery_state_t -> BtDiscoveryState); |
| cb_variant!(BaseCb, pin_request_cb -> BaseCallbacks::PinRequest, |
| *mut FfiAddress, *mut bindings::bt_bdname_t, u32, bool, { |
| let _0 = unsafe { *(_0 as *const RawAddress)}; |
| let _1 = String::from(unsafe{*_1}); |
| }); |
| cb_variant!(BaseCb, ssp_request_cb -> BaseCallbacks::SspRequest, |
| *mut FfiAddress, *mut bindings::bt_bdname_t, u32, bindings::bt_ssp_variant_t -> BtSspVariant, u32, { |
| let _0 = unsafe { *(_0 as *const RawAddress) }; |
| let _1 = String::from(unsafe{*_1}); |
| }); |
| cb_variant!(BaseCb, bond_state_cb -> BaseCallbacks::BondState, |
| u32 -> BtStatus, *mut FfiAddress, bindings::bt_bond_state_t -> BtBondState, i32, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| cb_variant!(BaseCb, acl_state_cb -> BaseCallbacks::AclState, |
| u32 -> BtStatus, *mut FfiAddress, bindings::bt_acl_state_t -> BtAclState, i32 -> BtTransport, bindings::bt_hci_error_code_t -> BtHciErrorCode, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| |
| struct RawInterfaceWrapper { |
| pub raw: *const bindings::bt_interface_t, |
| } |
| |
| unsafe impl Send for RawInterfaceWrapper {} |
| |
| pub struct BluetoothInterface { |
| internal: RawInterfaceWrapper, |
| pub is_init: bool, |
| // Need to take ownership of callbacks so it doesn't get freed after init |
| callbacks: Option<Box<bindings::bt_callbacks_t>>, |
| } |
| |
| #[macro_export] |
| macro_rules! ccall { |
| ($self:ident,$fn_name:ident) => { |
| unsafe { |
| ((*$self.internal.raw).$fn_name.unwrap())() |
| } |
| }; |
| ($self:ident,$fn_name:ident, $($args:expr),*) => { |
| unsafe { |
| ((*$self.internal.raw).$fn_name.unwrap())($($args),*) |
| } |
| } |
| } |
| |
| impl BluetoothInterface { |
| pub fn is_initialized(&self) -> bool { |
| self.is_init |
| } |
| |
| pub fn initialize( |
| &mut self, |
| callbacks: BaseCallbacksDispatcher, |
| init_flags: Vec<String>, |
| ) -> bool { |
| // Init flags need to be converted from string to null terminated bytes |
| let converted: cxx::UniquePtr<ffi::InitFlags> = ffi::ConvertFlags(init_flags); |
| let flags = (*converted).GetFlagsPtr(); |
| |
| if get_dispatchers().lock().unwrap().set::<BaseCb>(Arc::new(Mutex::new(callbacks))) { |
| panic!("Tried to set dispatcher for BaseCallbacks but it already existed"); |
| } |
| |
| // Fill up callbacks struct to pass to init function (will be copied so |
| // no need to worry about ownership) |
| let mut callbacks = Box::new(bindings::bt_callbacks_t { |
| size: 16 * 8, |
| adapter_state_changed_cb: Some(adapter_state_cb), |
| adapter_properties_cb: Some(adapter_properties_cb), |
| remote_device_properties_cb: Some(remote_device_properties_cb), |
| device_found_cb: Some(device_found_cb), |
| discovery_state_changed_cb: Some(discovery_state_cb), |
| pin_request_cb: Some(pin_request_cb), |
| ssp_request_cb: Some(ssp_request_cb), |
| bond_state_changed_cb: Some(bond_state_cb), |
| acl_state_changed_cb: Some(acl_state_cb), |
| thread_evt_cb: None, |
| dut_mode_recv_cb: None, |
| le_test_mode_cb: None, |
| energy_info_cb: None, |
| link_quality_report_cb: None, |
| generate_local_oob_data_cb: None, |
| }); |
| |
| let rawcb: *mut bindings::bt_callbacks_t = &mut *callbacks; |
| |
| let (guest_mode, is_common_criteria_mode, config_compare_result, is_atv) = |
| (false, false, 0, false); |
| |
| let init = ccall!( |
| self, |
| init, |
| rawcb, |
| guest_mode, |
| is_common_criteria_mode, |
| config_compare_result, |
| flags, |
| is_atv |
| ); |
| |
| self.is_init = init == 0; |
| self.callbacks = Some(callbacks); |
| |
| return self.is_init; |
| } |
| |
| pub fn cleanup(&self) { |
| ccall!(self, cleanup) |
| } |
| |
| pub fn enable(&self) -> i32 { |
| ccall!(self, enable) |
| } |
| |
| pub fn disable(&self) -> i32 { |
| ccall!(self, disable) |
| } |
| |
| pub fn get_adapter_properties(&self) -> i32 { |
| ccall!(self, get_adapter_properties) |
| } |
| |
| pub fn get_adapter_property(&self, prop: BtPropertyType) -> i32 { |
| let converted_type = bindings::bt_property_type_t::from(prop); |
| ccall!(self, get_adapter_property, converted_type) |
| } |
| |
| pub fn set_adapter_property(&self, prop: BluetoothProperty) -> i32 { |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = prop.into(); |
| ccall!(self, set_adapter_property, &prop_pair.1) |
| } |
| |
| pub fn get_remote_device_properties(&self, addr: &mut RawAddress) -> i32 { |
| let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress); |
| ccall!(self, get_remote_device_properties, ffi_addr) |
| } |
| |
| pub fn get_remote_device_property( |
| &self, |
| addr: &mut RawAddress, |
| prop_type: BtPropertyType, |
| ) -> i32 { |
| let converted_type = bindings::bt_property_type_t::from(prop_type); |
| let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress); |
| ccall!(self, get_remote_device_property, ffi_addr, converted_type) |
| } |
| |
| pub fn set_remote_device_property( |
| &self, |
| addr: &mut RawAddress, |
| prop: BluetoothProperty, |
| ) -> i32 { |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = prop.into(); |
| let ffi_addr = cast_to_ffi_address!(addr as *const RawAddress); |
| ccall!(self, set_remote_device_property, ffi_addr, &prop_pair.1) |
| } |
| |
| pub fn get_remote_services(&self, addr: &mut RawAddress, transport: BtTransport) -> i32 { |
| let ffi_addr = cast_to_ffi_address!(addr as *const RawAddress); |
| ccall!(self, get_remote_services, ffi_addr, transport.to_i32().unwrap()) |
| } |
| |
| pub fn start_discovery(&self) -> i32 { |
| ccall!(self, start_discovery) |
| } |
| |
| pub fn cancel_discovery(&self) -> i32 { |
| ccall!(self, cancel_discovery) |
| } |
| |
| pub fn create_bond(&self, addr: &RawAddress, transport: BtTransport) -> i32 { |
| let ctransport: i32 = transport.into(); |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, create_bond, ffi_addr, ctransport) |
| } |
| |
| pub fn remove_bond(&self, addr: &RawAddress) -> i32 { |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, remove_bond, ffi_addr) |
| } |
| |
| pub fn cancel_bond(&self, addr: &RawAddress) -> i32 { |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, cancel_bond, ffi_addr) |
| } |
| |
| pub fn get_connection_state(&self, addr: &RawAddress) -> u32 { |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, get_connection_state, ffi_addr).to_u32().unwrap() |
| } |
| |
| pub fn pin_reply( |
| &self, |
| addr: &RawAddress, |
| accept: u8, |
| pin_len: u8, |
| pin_code: &mut BtPinCode, |
| ) -> i32 { |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, pin_reply, ffi_addr, accept, pin_len, pin_code) |
| } |
| |
| pub fn ssp_reply( |
| &self, |
| addr: &RawAddress, |
| variant: BtSspVariant, |
| accept: u8, |
| passkey: u32, |
| ) -> i32 { |
| let cvariant = bindings::bt_ssp_variant_t::from(variant); |
| let ffi_addr = cast_to_const_ffi_address!(addr as *const RawAddress); |
| ccall!(self, ssp_reply, ffi_addr, cvariant, accept, passkey) |
| } |
| |
| pub(crate) fn get_profile_interface( |
| &self, |
| profile: SupportedProfiles, |
| ) -> *const std::os::raw::c_void { |
| let cprofile = Vec::<u8>::from(profile); |
| ccall!( |
| self, |
| get_profile_interface, |
| cprofile.as_slice().as_ptr() as *const std::os::raw::c_char |
| ) |
| } |
| |
| pub(crate) fn as_raw_ptr(&self) -> *const u8 { |
| self.internal.raw as *const u8 |
| } |
| } |
| |
| pub fn get_btinterface() -> Option<BluetoothInterface> { |
| let mut ret: Option<BluetoothInterface> = None; |
| let mut ifptr: *const bindings::bt_interface_t = std::ptr::null(); |
| |
| unsafe { |
| if bindings::hal_util_load_bt_library(&mut ifptr) == 0 { |
| ret = Some(BluetoothInterface { |
| internal: RawInterfaceWrapper { raw: ifptr }, |
| is_init: false, |
| callbacks: None, |
| }); |
| } |
| } |
| |
| ret |
| } |
| |
| // Turns C-array T[] to Vec<U>. |
| pub(crate) fn ptr_to_vec<T: Copy, U: From<T>>(start: *const T, length: usize) -> Vec<U> { |
| unsafe { (0..length).map(|i| U::from(*start.offset(i as isize))).collect::<Vec<U>>() } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use std::mem; |
| |
| #[test] |
| fn test_addr_size() { |
| assert_eq!(mem::size_of::<RawAddress>(), mem::size_of::<FfiAddress>()); |
| } |
| |
| #[test] |
| fn test_offset() { |
| let r = RawAddress { val: [1, 2, 3, 4, 5, 6] }; |
| let f = FfiAddress { address: [1, 2, 3, 4, 5, 6] }; |
| assert_eq!( |
| &f as *const _ as usize - &f.address as *const _ as usize, |
| &r as *const _ as usize - &r.val as *const _ as usize |
| ); |
| } |
| |
| #[test] |
| fn test_alignment() { |
| assert_eq!(std::mem::align_of::<RawAddress>(), std::mem::align_of::<FfiAddress>()); |
| } |
| |
| fn make_bdname_from_slice(slice: &[u8]) -> bindings::bt_bdname_t { |
| // Length of slice must be less than bd_name max |
| assert!(slice.len() <= 249); |
| |
| let mut bdname = bindings::bt_bdname_t { name: [128; 249] }; |
| |
| for (i, v) in slice.iter().enumerate() { |
| bdname.name[i] = v.clone(); |
| } |
| |
| bdname |
| } |
| |
| #[test] |
| fn test_bdname_conversions() { |
| let hello_bdname = make_bdname_from_slice(&[72, 69, 76, 76, 79, 0]); |
| assert_eq!("HELLO".to_string(), String::from(hello_bdname)); |
| |
| let empty_bdname = make_bdname_from_slice(&[0]); |
| assert_eq!("".to_string(), String::from(empty_bdname)); |
| |
| let no_nullterm_bdname = make_bdname_from_slice(&[72, 69, 76, 76, 79]); |
| assert_eq!("".to_string(), String::from(no_nullterm_bdname)); |
| |
| let invalid_bdname = make_bdname_from_slice(&[128; 249]); |
| assert_eq!("".to_string(), String::from(invalid_bdname)); |
| } |
| |
| #[test] |
| fn test_ptr_to_vec() { |
| let arr: [i32; 3] = [1, 2, 3]; |
| let vec: Vec<i32> = ptr_to_vec(arr.as_ptr(), arr.len()); |
| let expected: Vec<i32> = vec![1, 2, 3]; |
| assert_eq!(expected, vec); |
| } |
| } |