/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::io; use std::mem; use std::ptr; use std::slice; use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; use crate::util::io_err; extern crate libc; extern crate winapi; use winapi::shared::{guiddef, minwindef, ntdef, windef}; use winapi::shared::{hidclass, hidpi, hidusage}; use winapi::um::{handleapi, setupapi}; #[link(name = "setupapi")] extern "system" { fn SetupDiGetClassDevsW( ClassGuid: *const guiddef::GUID, Enumerator: ntdef::PCSTR, hwndParent: windef::HWND, flags: minwindef::DWORD, ) -> setupapi::HDEVINFO; fn SetupDiDestroyDeviceInfoList(DeviceInfoSet: setupapi::HDEVINFO) -> minwindef::BOOL; fn SetupDiEnumDeviceInterfaces( DeviceInfoSet: setupapi::HDEVINFO, DeviceInfoData: setupapi::PSP_DEVINFO_DATA, InterfaceClassGuid: *const guiddef::GUID, MemberIndex: minwindef::DWORD, DeviceInterfaceData: setupapi::PSP_DEVICE_INTERFACE_DATA, ) -> minwindef::BOOL; fn SetupDiGetDeviceInterfaceDetailW( DeviceInfoSet: setupapi::HDEVINFO, DeviceInterfaceData: setupapi::PSP_DEVICE_INTERFACE_DATA, DeviceInterfaceDetailData: setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W, DeviceInterfaceDetailDataSize: minwindef::DWORD, RequiredSize: minwindef::PDWORD, DeviceInfoData: setupapi::PSP_DEVINFO_DATA, ) -> minwindef::BOOL; } #[link(name = "hid")] extern "system" { fn HidD_GetPreparsedData( HidDeviceObject: ntdef::HANDLE, PreparsedData: *mut hidpi::PHIDP_PREPARSED_DATA, ) -> ntdef::BOOLEAN; fn HidD_FreePreparsedData(PreparsedData: hidpi::PHIDP_PREPARSED_DATA) -> ntdef::BOOLEAN; fn HidP_GetCaps( PreparsedData: hidpi::PHIDP_PREPARSED_DATA, Capabilities: hidpi::PHIDP_CAPS, ) -> ntdef::NTSTATUS; } fn from_wide_ptr(ptr: *const u16, len: usize) -> String { assert!(!ptr.is_null() && len % 2 == 0); let slice = unsafe { slice::from_raw_parts(ptr, len / 2) }; OsString::from_wide(slice).to_string_lossy().into_owned() } pub struct DeviceInfoSet { set: setupapi::HDEVINFO, } impl DeviceInfoSet { pub fn new() -> io::Result { let flags = setupapi::DIGCF_PRESENT | setupapi::DIGCF_DEVICEINTERFACE; let set = unsafe { SetupDiGetClassDevsW( &hidclass::GUID_DEVINTERFACE_HID, ptr::null_mut(), ptr::null_mut(), flags, ) }; if set == handleapi::INVALID_HANDLE_VALUE { return Err(io_err("SetupDiGetClassDevsW failed!")); } Ok(Self { set }) } pub fn get(&self) -> setupapi::HDEVINFO { self.set } pub fn devices(&self) -> DeviceInfoSetIter { DeviceInfoSetIter::new(self) } } impl Drop for DeviceInfoSet { fn drop(&mut self) { let _ = unsafe { SetupDiDestroyDeviceInfoList(self.set) }; } } pub struct DeviceInfoSetIter<'a> { set: &'a DeviceInfoSet, index: minwindef::DWORD, } impl<'a> DeviceInfoSetIter<'a> { fn new(set: &'a DeviceInfoSet) -> Self { Self { set, index: 0 } } } impl<'a> Iterator for DeviceInfoSetIter<'a> { type Item = String; fn next(&mut self) -> Option { let mut device_interface_data = mem::MaybeUninit::::zeroed(); unsafe { (*device_interface_data.as_mut_ptr()).cbSize = mem::size_of::() as minwindef::UINT; } let rv = unsafe { SetupDiEnumDeviceInterfaces( self.set.get(), ptr::null_mut(), &hidclass::GUID_DEVINTERFACE_HID, self.index, device_interface_data.as_mut_ptr(), ) }; if rv == 0 { return None; // We're past the last device index. } // Determine the size required to hold a detail struct. let mut required_size = 0; unsafe { SetupDiGetDeviceInterfaceDetailW( self.set.get(), device_interface_data.as_mut_ptr(), ptr::null_mut(), required_size, &mut required_size, ptr::null_mut(), ) }; if required_size == 0 { return None; // An error occurred. } let detail = DeviceInterfaceDetailData::new(required_size as usize)?; let rv = unsafe { SetupDiGetDeviceInterfaceDetailW( self.set.get(), device_interface_data.as_mut_ptr(), detail.get(), required_size, ptr::null_mut(), ptr::null_mut(), ) }; if rv == 0 { return None; // An error occurred. } self.index += 1; Some(detail.path()) } } struct DeviceInterfaceDetailData { data: setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W, path_len: usize, } impl DeviceInterfaceDetailData { fn new(size: usize) -> Option { let mut cb_size = mem::size_of::(); if cfg!(target_pointer_width = "32") { cb_size = 4 + 2; // 4-byte uint + default TCHAR size. size_of is inaccurate. } if size < cb_size { warn!("DeviceInterfaceDetailData is too small. {}", size); return None; } let data = unsafe { libc::malloc(size) as setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W }; if data.is_null() { return None; } // Set total size of the structure. unsafe { (*data).cbSize = cb_size as minwindef::UINT }; // Compute offset of `SP_DEVICE_INTERFACE_DETAIL_DATA_W.DevicePath`. let offset = memoffset::offset_of!(setupapi::SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath); Some(Self { data, path_len: size - offset, }) } fn get(&self) -> setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W { self.data } fn path(&self) -> String { unsafe { from_wide_ptr(ptr::addr_of!((*self.data).DevicePath[0]), self.path_len - 2) } } } impl Drop for DeviceInterfaceDetailData { fn drop(&mut self) { unsafe { libc::free(self.data as *mut libc::c_void) }; } } pub struct DeviceCapabilities { caps: hidpi::HIDP_CAPS, } impl DeviceCapabilities { pub fn new(handle: ntdef::HANDLE) -> io::Result { let mut preparsed_data = ptr::null_mut(); let rv = unsafe { HidD_GetPreparsedData(handle, &mut preparsed_data) }; if rv == 0 || preparsed_data.is_null() { return Err(io_err("HidD_GetPreparsedData failed!")); } let mut caps = mem::MaybeUninit::::uninit(); unsafe { let rv = HidP_GetCaps(preparsed_data, caps.as_mut_ptr()); HidD_FreePreparsedData(preparsed_data); if rv != hidpi::HIDP_STATUS_SUCCESS { return Err(io_err("HidP_GetCaps failed!")); } Ok(Self { caps: caps.assume_init(), }) } } pub fn usage(&self) -> hidusage::USAGE { self.caps.Usage } pub fn usage_page(&self) -> hidusage::USAGE { self.caps.UsagePage } }