#![allow(non_camel_case_types, non_snake_case)]
use core::ptr::NonNull;
use winapi::{
ctypes::c_void,
shared::{
minwindef::{BOOL, DWORD, ULONG, USHORT, UCHAR, LPVOID, TRUE},
ntdef::{LPWSTR, LPCWSTR, WCHAR, ULONGLONG, HANDLE},
windef::{HWND},
},
um::{
minwinbase::{SYSTEMTIME},
handleapi::{CloseHandle},
},
STRUCT
};
use crate::Addr;
pub const BLUETOOTH_MAX_NAME_SIZE: usize = 248;
pub type PFN_DEVICE_CALLBACK = Option<unsafe extern "system" fn(
pvParam: LPVOID,
pDevice: *const BLUETOOTH_DEVICE_INFO,
)>;
pub type PBLUETOOTH_DEVICE_INFO = *mut BLUETOOTH_DEVICE_INFO;
pub type PBLUETOOTH_RADIO_INFO = *mut BLUETOOTH_RADIO_INFO;
pub type HBLUETOOTH_RADIO_FIND = LPVOID;
STRUCT!{struct BLUETOOTH_ADDRESS{
inner: ULONGLONG,
}}
STRUCT!{struct BLUETOOTH_DEVICE_INFO{
dwSize: DWORD,
Address: BLUETOOTH_ADDRESS,
ulClassofDevice: ULONG,
fConnected: BOOL,
fRemembered: BOOL,
fAuthenticated: BOOL,
stLastSeen: SYSTEMTIME,
stLastUsed: SYSTEMTIME,
szName: [WCHAR; BLUETOOTH_MAX_NAME_SIZE],
}}
STRUCT!{struct BLUETOOTH_COD_PAIRS{
ulCODMask: ULONG,
pcszDescription: LPCWSTR,
}}
STRUCT!{struct BLUETOOTH_SELECT_DEVICE_PARAMS {
dwSize: DWORD,
cNumOfClasses: ULONG,
prgClassOfDevices: *mut BLUETOOTH_COD_PAIRS,
pszInfo: LPWSTR,
hwndParent: HWND,
fForceAuthentication: BOOL,
fShowAuthenticated: BOOL,
fShowRemembered: BOOL,
fShowUnknown: BOOL,
fAddNewDeviceWizard: BOOL,
fSkipServicesPage: BOOL,
pfnDeviceCallback: PFN_DEVICE_CALLBACK,
pvParam: LPVOID,
cNumDevices: DWORD,
pDevices: PBLUETOOTH_DEVICE_INFO,
}}
STRUCT!{struct BLUETOOTH_DEVICE_SEARCH_PARAMS {
dwSize: DWORD,
fReturnAuthenticated: BOOL,
fReturnRemembered: BOOL,
fReturnUnknown: BOOL,
fReturnConnected: BOOL,
fIssueInquiry: BOOL,
cTimeoutMultiplier: UCHAR,
hRadio: HANDLE,
}}
STRUCT!{struct BLUETOOTH_RADIO_INFO {
dwSize: DWORD,
address: BLUETOOTH_ADDRESS,
szName: [WCHAR; BLUETOOTH_MAX_NAME_SIZE],
ulClassofDevice: ULONG,
lmpSubversion: USHORT,
manufacturer: USHORT,
}}
STRUCT!{struct BLUETOOTH_FIND_RADIO_PARAMS {
dwSize: DWORD,
}}
extern "system" {
pub fn BluetoothFindFirstRadio (
pbtfrp: *const BLUETOOTH_FIND_RADIO_PARAMS,
phRadio: *mut HANDLE,
) -> HBLUETOOTH_RADIO_FIND;
pub fn BluetoothFindNextRadio (
hFind: HBLUETOOTH_RADIO_FIND,
phRadio: *mut HANDLE,
) -> BOOL;
pub fn BluetoothGetRadioInfo (
hRadio: HANDLE,
pRadioInfo: PBLUETOOTH_RADIO_INFO,
) -> DWORD;
pub fn BluetoothFindRadioClose (
hFind: HBLUETOOTH_RADIO_FIND
) -> BOOL;
}
macro_rules! create_struct {
($struct_name: ident, $ptr_name: ident, $struct_type: ty) => {
let mut $struct_name: $struct_type = core::mem::zeroed();
$struct_name.dwSize = core::mem::size_of::<$struct_type>() as DWORD;
let $ptr_name = &$struct_name as *const _ as *mut $struct_type;
};
}
#[derive(Debug)]
pub struct Radio {
hRadio: HANDLE,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct RadioInfo {
addr: Addr,
name: String,
class: u32,
subversion: u16,
manufacturer: u16,
}
fn ull_to_addr(src: ULONGLONG) -> Addr {
Addr::from(unsafe { *(&src as *const u64 as *const [u8; 6]) })
}
impl RadioInfo {
unsafe fn from_radio_handle(hRadio: HANDLE) -> Self {
create_struct!(radioInfo, pRadioInfo, BLUETOOTH_RADIO_INFO);
BluetoothGetRadioInfo(hRadio, pRadioInfo);
let addr = ull_to_addr(radioInfo.address.inner);
let name = String::from_utf16_lossy(&radioInfo.szName).trim_end_matches(|c| c=='\0').to_string();
let class = radioInfo.ulClassofDevice as u32;
let subversion = radioInfo.lmpSubversion as u16;
let manufacturer = radioInfo.manufacturer as u16;
Self { addr, name, class, subversion, manufacturer }
}
pub fn addr(&self) -> Addr {
self.addr
}
pub fn name(&self) -> &str {
&self.name
}
pub fn class(&self) -> u32 {
self.class
}
pub fn subversion(&self) -> u16 {
self.subversion
}
pub fn manufacturer(&self) -> u16 {
self.manufacturer
}
}
impl Radio {
fn new(hRadio: HANDLE) -> Self {
Self { hRadio }
}
pub fn info(&self) -> RadioInfo {
unsafe { RadioInfo::from_radio_handle(self.hRadio) }
}
}
impl Drop for Radio {
fn drop(&mut self) {
unsafe {
CloseHandle(self.hRadio);
}
}
}
#[derive(Debug)]
pub struct Radios {
last_radio: HANDLE,
handle_find_radio: Option<NonNull<c_void>>,
}
impl Iterator for Radios {
type Item = Radio;
fn next(&mut self) -> Option<Self::Item> {
self.handle_find_radio.map(|handle| unsafe {
let ans = Radio::new(self.last_radio.clone());
let hFindRadio = handle.as_ptr();
let phRadio = &self.last_radio as *const _ as *mut _;
if BluetoothFindNextRadio(hFindRadio, phRadio) != TRUE {
self.handle_find_radio = None;
}
ans
})
}
}
impl Drop for Radios {
fn drop(&mut self) {
self.handle_find_radio.map(|handle| unsafe {
let hFindRadio = handle.as_ptr();
BluetoothFindRadioClose(hFindRadio);
});
}
}
pub fn radios() -> Radios {
unsafe {
let hRadio: HANDLE = core::ptr::null_mut();
let phRadio = &hRadio as *const _ as *mut _;
create_struct!(btfrp, pbtfrp, BLUETOOTH_FIND_RADIO_PARAMS);
let hFindRadio = BluetoothFindFirstRadio(pbtfrp, phRadio);
Radios {
last_radio: hRadio,
handle_find_radio: NonNull::new(hFindRadio),
}
}
}