windows: implement transfers

This commit is contained in:
Kevin Mehall 2023-10-07 11:06:01 -06:00
parent 5de605874e
commit d4f322fe18
12 changed files with 682 additions and 82 deletions

View file

@ -21,4 +21,4 @@ env_logger = "0.10.0"
rustix = { version = "0.38.17", features = ["fs", "event"] }
[target.'cfg(target_os="windows")'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_Devices_Usb", "Win32_Devices_DeviceAndDriverInstallation", "Win32_Foundation", "Win32_Devices_Properties", "Win32_Storage_FileSystem", "Win32_Security", "Win32_System_IO"] }
windows-sys = { version = "0.48.0", features = ["Win32_Devices_Usb", "Win32_Devices_DeviceAndDriverInstallation", "Win32_Foundation", "Win32_Devices_Properties", "Win32_Storage_FileSystem", "Win32_Security", "Win32_System_IO", "Win32_System_Registry", "Win32_System_Com"] }

View file

@ -15,9 +15,10 @@ A new pure-Rust library for cross-platform low-level access to USB devices.
a specific interface, not a device as a whole. `nusb`'s API makes working with interfaces
a required step so that it can map directly to Windows APIs.
### Current status
### :construction: Current status
:construction: Control, bulk and interrupt transfers work on Linux, minimally tested
* Linux: Control, bulk and interrupt transfers work, minimally tested
* Windows: Control, bulk and interrupt transfers work, minimally tested
### License
MIT or Apache 2.0, at your option

View file

@ -1,4 +1,5 @@
use std::{
collections::HashMap,
ffi::{OsStr, OsString},
fmt::Display,
str::FromStr,
@ -23,6 +24,9 @@ pub struct DeviceInfo {
#[cfg(target_os = "windows")]
pub(crate) driver: Option<String>,
#[cfg(target_os = "windows")]
pub(crate) interfaces: HashMap<u8, OsString>,
pub(crate) bus_number: u8,
pub(crate) device_address: u8,

View file

@ -1,42 +1,82 @@
use std::sync::Arc;
use std::{
collections::HashMap,
ffi::OsString,
io::{self, ErrorKind},
os::windows::prelude::OwnedHandle,
sync::Arc,
};
use log::{debug, error};
use windows_sys::Win32::{
Devices::Usb::{WinUsb_Free, WinUsb_Initialize, WINUSB_INTERFACE_HANDLE},
Foundation::FALSE,
};
use crate::{
transfer::{EndpointType, TransferHandle},
DeviceInfo, Error,
};
pub(crate) struct WindowsDevice {}
use super::util::{create_file, raw_handle};
pub(crate) struct WindowsDevice {
interface_paths: HashMap<u8, OsString>,
}
impl WindowsDevice {
pub(crate) fn from_device_info(d: &DeviceInfo) -> Result<Arc<WindowsDevice>, Error> {
todo!()
debug!("Creating device for {:?}", d.instance_id);
Ok(Arc::new(WindowsDevice {
interface_paths: d.interfaces.clone(),
}))
}
pub(crate) fn set_configuration(&self, configuration: u8) -> Result<(), Error> {
todo!()
pub(crate) fn set_configuration(&self, _configuration: u8) -> Result<(), Error> {
Err(io::Error::new(
ErrorKind::Unsupported,
"set_configuration not supported by WinUSB",
))
}
pub(crate) fn reset(&self) -> Result<(), Error> {
todo!()
Err(io::Error::new(
ErrorKind::Unsupported,
"reset not supported by WinUSB",
))
}
pub(crate) fn claim_interface(
self: &Arc<Self>,
interface: u8,
) -> Result<Arc<WindowsInterface>, Error> {
todo!()
}
}
let path = self.interface_paths.get(&interface).ok_or_else(|| {
Error::new(ErrorKind::NotFound, "interface not found or not compatible")
})?;
impl Drop for WindowsDevice {
fn drop(&mut self) {
todo!()
let handle = create_file(path)?;
super::events::register(&handle)?;
let winusb_handle = unsafe {
let mut h = 0;
if WinUsb_Initialize(raw_handle(&handle), &mut h) == FALSE {
error!("WinUsb_Initialize failed: {:?}", io::Error::last_os_error());
return Err(io::Error::last_os_error());
}
h
};
Ok(Arc::new(WindowsInterface {
handle,
winusb_handle,
}))
}
}
pub(crate) struct WindowsInterface {
pub(crate) interface: u8,
pub(crate) device: Arc<WindowsDevice>,
pub(crate) handle: OwnedHandle,
pub(crate) winusb_handle: WINUSB_INTERFACE_HANDLE,
}
impl WindowsInterface {
@ -47,18 +87,12 @@ impl WindowsInterface {
) -> TransferHandle<super::TransferData> {
TransferHandle::new(super::TransferData::new(self.clone(), endpoint, ep_type))
}
pub(crate) unsafe fn submit_urb(&self, urb: *mut ()) {
todo!()
}
pub(crate) unsafe fn cancel_urb(&self, urb: *mut ()) {
todo!()
}
}
impl Drop for WindowsInterface {
fn drop(&mut self) {
todo!()
unsafe {
WinUsb_Free(self.winusb_handle);
}
}
}

View file

@ -1,11 +1,11 @@
use std::io::ErrorKind;
use std::{collections::HashMap, ffi::OsString, io::ErrorKind};
use log::debug;
use log::{debug, error};
use windows_sys::Win32::Devices::{
Properties::{
DEVPKEY_Device_Address, DEVPKEY_Device_BusNumber, DEVPKEY_Device_DeviceDesc,
DEVPKEY_Device_FriendlyName, DEVPKEY_Device_InstanceId, DEVPKEY_Device_Manufacturer,
DEVPKEY_Device_Parent, DEVPKEY_Device_Service, DEVPKEY_NAME,
DEVPKEY_Device_Address, DEVPKEY_Device_BusNumber, DEVPKEY_Device_Children,
DEVPKEY_Device_FriendlyName, DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId,
DEVPKEY_Device_Manufacturer, DEVPKEY_Device_Parent, DEVPKEY_Device_Service,
},
Usb::{GUID_DEVINTERFACE_USB_DEVICE, USB_DEVICE_SPEED},
};
@ -18,13 +18,12 @@ use super::{
};
pub fn list_devices() -> Result<impl Iterator<Item = DeviceInfo>, Error> {
let dset =
DeviceInfoSet::get_by_setup_class(GUID_DEVINTERFACE_USB_DEVICE, None).map_err(|_| {
Error::new(
ErrorKind::UnexpectedEof,
String::from("failed to list devices"),
)
})?;
let dset = DeviceInfoSet::get(Some(GUID_DEVINTERFACE_USB_DEVICE), None).map_err(|_| {
Error::new(
ErrorKind::UnexpectedEof,
String::from("failed to list devices"),
)
})?;
let devs: Vec<_> = dset.iter_devices().flat_map(probe_device).collect();
Ok(devs.into_iter())
@ -66,13 +65,31 @@ pub fn probe_device(dev: setupapi::DeviceInfo) -> Option<DeviceInfo> {
let driver = dev
.get_string_property(DEVPKEY_Device_Service)
.and_then(|s| s.into_string().ok());
.and_then(|s| s.into_string().ok())
.unwrap_or_default();
let mut interfaces = HashMap::new();
if driver.eq_ignore_ascii_case("usbccgp") {
let children = dev
.get_string_list_property(DEVPKEY_Device_Children)
.unwrap_or_default();
interfaces.extend(children.into_iter().flat_map(probe_interface));
} else if driver.eq_ignore_ascii_case("winusb") {
let intf_dev = dev.interfaces(GUID_DEVINTERFACE_USB_DEVICE).next();
if let Some(path) = intf_dev.and_then(|i| i.get_path()) {
interfaces.insert(0, path);
} else {
error!("Failed to find path for winusb device");
}
}
Some(DeviceInfo {
instance_id,
parent_instance_id,
interfaces,
port_number,
driver,
driver: Some(driver).filter(|s| !s.is_empty()),
bus_number: bus_number as u8,
device_address: info.DeviceAddress as u8,
vendor_id: info.DeviceDescriptor.idVendor,
@ -88,6 +105,50 @@ pub fn probe_device(dev: setupapi::DeviceInfo) -> Option<DeviceInfo> {
})
}
fn probe_interface(c_id: OsString) -> Option<(u8, OsString)> {
debug!("Probing interface {c_id:?} of composite device");
let iset = DeviceInfoSet::get(None, Some(&c_id)).ok()?;
let Some(intf_dev) = iset.iter_devices().next() else {
debug!("Interface not found in SetupAPI");
return None;
};
let driver = intf_dev.get_string_property(DEVPKEY_Device_Service);
if !driver
.as_ref()
.is_some_and(|d| d.eq_ignore_ascii_case("winusb"))
{
return None;
}
let hw_ids = intf_dev.get_string_list_property(DEVPKEY_Device_HardwareIds);
let Some(intf_num) = hw_ids
.as_deref()
.unwrap_or_default()
.iter()
.find_map(|id| id.to_str()?.rsplit_once("&MI_")?.1.parse::<u8>().ok())
else {
error!("Failed to parse interface number in hardware IDs: {hw_ids:?}");
return None;
};
let reg_key = intf_dev.registry_key().unwrap();
let guid = reg_key.query_value_guid("DeviceInterfaceGUIDs").unwrap();
let Some(intf) = intf_dev.interfaces(guid).next() else {
error!("Failed to find interface");
return None;
};
let Some(path) = intf.get_path() else {
error!("Failed to find interface path");
return None;
};
Some((intf_num, path))
}
fn map_speed(speed: u8) -> Option<Speed> {
#![allow(non_upper_case_globals)]
use windows_sys::Win32::Devices::Usb::{

View file

@ -0,0 +1,103 @@
use log::error;
use once_cell::sync::OnceCell;
use std::{
os::windows::{
io::HandleOrNull,
prelude::{OwnedHandle, RawHandle},
},
thread,
};
use windows_sys::Win32::{
Foundation::{GetLastError, FALSE, INVALID_HANDLE_VALUE},
System::IO::{CreateIoCompletionPort, GetQueuedCompletionStatusEx, OVERLAPPED_ENTRY},
};
use crate::Error;
use super::util::raw_handle;
struct IoCompletionPort(OwnedHandle);
impl IoCompletionPort {
fn new() -> Result<IoCompletionPort, Error> {
unsafe {
let port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
match HandleOrNull::from_raw_handle(port as RawHandle).try_into() {
Ok(handle) => Ok(IoCompletionPort(handle)),
Err(_) => {
let err = GetLastError();
error!("CreateIoCompletionPort (create) failed: {err:?}");
Err(std::io::Error::from_raw_os_error(err as i32))
}
}
}
}
fn register(&self, handle: &OwnedHandle) -> Result<(), Error> {
unsafe {
let r = CreateIoCompletionPort(raw_handle(handle), raw_handle(&self.0), 0, 0);
if r == 0 {
let err = std::io::Error::last_os_error();
error!("CreateIoCompletionPort (register) failed: {err:?}");
Err(err)
} else {
Ok(())
}
}
}
fn wait(&self, events: &mut Vec<OVERLAPPED_ENTRY>) -> Result<(), Error> {
unsafe {
let mut event_count = 0;
let r = GetQueuedCompletionStatusEx(
raw_handle(&self.0),
events.as_mut_ptr(),
events
.capacity()
.try_into()
.expect("events capacity should fit in u32"),
&mut event_count,
u32::MAX,
0,
);
if r == FALSE {
let err = std::io::Error::last_os_error();
error!("GetQueuedCompletionStatusEx failed: {err:?}");
Err(err)
} else {
events.set_len(event_count as usize);
Ok(())
}
}
}
}
static IOCP_HANDLE: OnceCell<IoCompletionPort> = OnceCell::new();
pub(super) fn register(usb_fd: &OwnedHandle) -> Result<(), Error> {
let mut start_thread = false;
let iocp = IOCP_HANDLE.get_or_try_init(|| {
start_thread = true;
IoCompletionPort::new()
})?;
if start_thread {
thread::spawn(event_loop);
}
iocp.register(usb_fd)
}
fn event_loop() {
let iocp = IOCP_HANDLE.get().unwrap();
let mut event_list = Vec::with_capacity(8);
loop {
event_list.clear();
iocp.wait(&mut event_list).unwrap();
for event in &event_list {
super::transfer::handle_event(event.lpOverlapped);
}
}
}

View file

@ -5,7 +5,7 @@ use std::{
ptr::null_mut,
};
use log::{debug, error};
use log::error;
use windows_sys::Win32::{
Devices::Usb::{
GUID_DEVINTERFACE_USB_HUB, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
@ -25,8 +25,7 @@ pub struct HubHandle(OwnedHandle);
impl HubHandle {
pub fn by_instance_id(instance_id: &OsStr) -> Option<HubHandle> {
let devs =
DeviceInfoSet::get_by_setup_class(GUID_DEVINTERFACE_USB_HUB, Some(instance_id)).ok()?;
let devs = DeviceInfoSet::get(Some(GUID_DEVINTERFACE_USB_HUB), Some(instance_id)).ok()?;
let Some(hub_interface) = devs.iter_interfaces(GUID_DEVINTERFACE_USB_HUB).next() else {
error!("Failed to find hub interface");
return None;

View file

@ -1,6 +1,8 @@
mod enumeration;
pub use enumeration::list_devices;
mod events;
mod device;
pub(crate) use device::WindowsDevice as Device;
pub(crate) use device::WindowsInterface as Interface;

View file

@ -4,13 +4,14 @@ use std::{
alloc,
alloc::Layout,
ffi::{OsStr, OsString},
io::{self, ErrorKind},
mem::{self, size_of},
os::windows::prelude::OsStrExt,
os::windows::prelude::{OsStrExt, OsStringExt},
ptr::{addr_of_mut, null, null_mut},
slice,
};
use log::{debug, error};
use log::error;
use windows_sys::{
core::GUID,
Win32::{
@ -18,12 +19,20 @@ use windows_sys::{
DeviceAndDriverInstallation::{
SetupDiDestroyDeviceInfoList, SetupDiEnumDeviceInfo, SetupDiEnumDeviceInterfaces,
SetupDiGetClassDevsW, SetupDiGetDeviceInterfaceDetailW, SetupDiGetDevicePropertyW,
DIGCF_DEVICEINTERFACE, DIGCF_PRESENT, SP_DEVICE_INTERFACE_DATA,
SetupDiOpenDevRegKey, DICS_FLAG_GLOBAL, DIGCF_ALLCLASSES, DIGCF_DEVICEINTERFACE,
DIGCF_PRESENT, DIREG_DEV, SP_DEVICE_INTERFACE_DATA,
SP_DEVICE_INTERFACE_DETAIL_DATA_W, SP_DEVINFO_DATA,
},
Properties::{DEVPROPKEY, DEVPROPTYPE, DEVPROP_TYPE_STRING, DEVPROP_TYPE_UINT32},
Properties::{
DEVPROPKEY, DEVPROPTYPE, DEVPROP_TYPE_STRING, DEVPROP_TYPE_STRING_LIST,
DEVPROP_TYPE_UINT32,
},
},
Foundation::{GetLastError, ERROR_SUCCESS, FALSE, INVALID_HANDLE_VALUE, S_OK, TRUE},
System::{
Com::IIDFromString,
Registry::{RegCloseKey, RegQueryValueExW, HKEY, KEY_READ, REG_MULTI_SZ, REG_SZ},
},
Foundation::{GetLastError, FALSE, INVALID_HANDLE_VALUE, TRUE},
},
};
@ -35,20 +44,26 @@ pub struct DeviceInfoSet {
}
impl DeviceInfoSet {
pub fn get_by_setup_class(guid: GUID, enumerator: Option<&OsStr>) -> Result<DeviceInfoSet, ()> {
pub fn get(
class: Option<GUID>,
enumerator: Option<&OsStr>,
) -> Result<DeviceInfoSet, io::Error> {
let enumerator: Option<Vec<u16>> =
enumerator.map(|e| e.encode_wide().chain(Some(0)).collect());
let handle = unsafe {
SetupDiGetClassDevsW(
&guid,
class.as_ref().map_or(null(), |g| g as *const _),
enumerator.as_ref().map_or(null(), |s| s.as_ptr()),
0,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT,
if class.is_some() { 0 } else { DIGCF_ALLCLASSES }
| DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT,
)
};
if handle == INVALID_HANDLE_VALUE {
error!("SetupDiGetClassDevsW failed: {}", unsafe { GetLastError() });
Err(())
let err = io::Error::last_os_error();
error!("SetupDiGetClassDevsW failed: {}", err);
Err(err)
} else {
Ok(DeviceInfoSet { handle })
}
@ -137,6 +152,38 @@ impl<'a> DeviceInfo<'a> {
}
}
pub fn get_string_list_property(&self, pkey: DEVPROPKEY) -> Option<Vec<OsString>> {
let mut property_type: DEVPROPTYPE = unsafe { mem::zeroed() };
let mut buffer = [0u16; 4096];
let mut size: u32 = 0; // in bytes
let r = unsafe {
SetupDiGetDevicePropertyW(
self.set.handle,
&self.device_info,
&pkey,
&mut property_type,
buffer.as_mut_ptr() as *mut u8,
(buffer.len() * mem::size_of::<u16>()) as u32,
&mut size,
0,
)
};
if r == TRUE && property_type == DEVPROP_TYPE_STRING_LIST {
let buffer = &buffer[..(size as usize / mem::size_of::<u16>())];
Some(
buffer
.split(|&c| c == 0)
.filter(|e| e.len() > 0)
.map(|s| OsString::from_wide(s))
.collect(),
)
} else {
None
}
}
pub fn get_u32_property(&self, pkey: DEVPROPKEY) -> Option<u32> {
let mut property_type: DEVPROPTYPE = unsafe { mem::zeroed() };
let mut buffer: u32 = 0;
@ -154,13 +201,32 @@ impl<'a> DeviceInfo<'a> {
)
};
if r == 1 && property_type == DEVPROP_TYPE_UINT32 {
if r == TRUE && property_type == DEVPROP_TYPE_UINT32 {
Some(buffer)
} else {
None
}
}
pub fn registry_key(&self) -> Result<RegKey, io::Error> {
unsafe {
let key = SetupDiOpenDevRegKey(
self.set.handle,
&self.device_info,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_READ,
);
if key == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(RegKey(key))
}
}
}
pub fn interfaces(&self, interface_class_guid: GUID) -> DeviceInfoSetInterfaceIter {
DeviceInfoSetInterfaceIter {
set: self.set,
@ -269,3 +335,69 @@ impl<'a> InterfaceInfo<'a> {
}
}
}
pub struct RegKey(HKEY);
impl RegKey {
pub fn query_value_guid(&self, value_name: &str) -> Result<GUID, io::Error> {
unsafe {
let value_name: Vec<u16> = OsStr::new(value_name)
.encode_wide()
.chain(Some(0))
.collect();
let mut ty = 0;
let mut size = 0;
// get size
let r = RegQueryValueExW(
self.0,
value_name.as_ptr(),
null_mut(),
&mut ty,
null_mut(),
&mut size,
);
if r != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(r as i32));
}
if ty != REG_MULTI_SZ && ty != REG_SZ {
return Err(io::Error::new(
ErrorKind::InvalidInput,
"registry value type not string",
));
}
let layout = Layout::from_size_align(size as usize, mem::align_of::<u16>()).unwrap();
let buf = alloc::alloc(layout);
let r = RegQueryValueExW(self.0, value_name.as_ptr(), null(), &mut ty, buf, &mut size);
if r != ERROR_SUCCESS {
alloc::dealloc(buf, layout);
return Err(io::Error::from_raw_os_error(r as i32));
}
let mut guid = GUID::from_u128(0);
let r = IIDFromString(buf as *mut u16, &mut guid);
alloc::dealloc(buf, layout);
if r == S_OK {
Ok(guid)
} else {
Err(io::Error::new(ErrorKind::InvalidData, "invalid UUID"))
}
}
}
}
impl Drop for RegKey {
fn drop(&mut self) {
unsafe {
RegCloseKey(self.0);
}
}
}

View file

@ -1,86 +1,333 @@
use std::{
ffi::c_void,
io,
mem::{self, ManuallyDrop},
ptr::null_mut,
ptr::{addr_of_mut, null_mut},
sync::Arc,
};
use log::{debug, error};
use windows_sys::Win32::{
Devices::Usb::{
WinUsb_ControlTransfer, WinUsb_GetOverlappedResult, WinUsb_ReadPipe, WinUsb_WritePipe,
WINUSB_SETUP_PACKET,
},
Foundation::{
GetLastError, ERROR_DEVICE_NOT_CONNECTED, ERROR_FILE_NOT_FOUND, ERROR_GEN_FAILURE,
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_SUCH_DEVICE, ERROR_REQUEST_ABORTED, FALSE,
TRUE, WIN32_ERROR,
},
System::IO::{CancelIoEx, OVERLAPPED},
};
use crate::transfer::{
Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit, PlatformTransfer,
RequestBuffer, ResponseBuffer, TransferStatus, SETUP_PACKET_SIZE,
notify_completion, Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit,
PlatformTransfer, RequestBuffer, ResponseBuffer, TransferStatus,
};
use super::util::raw_handle;
#[repr(C)]
pub struct TransferData {}
pub(crate) struct EventNotify {
// first member of repr(C) struct; can cast pointer between types
overlapped: OVERLAPPED,
ptr: *mut c_void,
}
pub struct TransferData {
interface: Arc<super::Interface>,
event: *mut EventNotify,
buf: *mut u8,
capacity: usize,
endpoint: u8,
ep_type: EndpointType,
submit_error: Option<WIN32_ERROR>,
}
unsafe impl Send for TransferData {}
impl TransferData {
pub(crate) fn new(
clone: std::sync::Arc<super::Interface>,
interface: std::sync::Arc<super::Interface>,
endpoint: u8,
ep_type: EndpointType,
) -> TransferData {
todo!()
}
fn fill(&mut self, v: Vec<u8>, len: usize, user_data: *mut c_void) {
todo!()
TransferData {
interface,
event: Box::into_raw(Box::new(unsafe { mem::zeroed() })),
buf: null_mut(),
capacity: 0,
endpoint,
ep_type,
submit_error: None,
}
}
/// SAFETY: requires that the transfer has completed and `length` bytes are initialized
unsafe fn take_buf(&mut self, length: usize) -> Vec<u8> {
todo!()
let v = Vec::from_raw_parts(self.buf, length, self.capacity);
self.buf = null_mut();
self.capacity = 0;
v
}
/// SAFETY: user_data must be the callback pointer passed to `submit`
unsafe fn post_submit(&mut self, r: i32, func: &str, user_data: *mut c_void) {
if r == TRUE {
error!("{func} completed synchronously")
}
let err = GetLastError();
if err != ERROR_IO_PENDING {
self.submit_error = Some(err);
error!("{func} failed: {}", io::Error::from_raw_os_error(err as _));
// Safety: Transfer was not submitted, so we still own it
// and must complete it in place of the event thread.
notify_completion::<TransferData>(user_data);
} else {
self.submit_error = None;
}
}
/// SAFETY: transfer must be completed
unsafe fn get_status(&mut self) -> (usize, TransferStatus) {
if let Some(err) = self.submit_error {
debug!(
"Transfer {:?} on endpoint {:02x} failed on submit: {}",
self.event, self.endpoint, err
);
return (0, map_error(err));
}
let mut actual_len = 0;
let r = WinUsb_GetOverlappedResult(
self.interface.winusb_handle,
self.event as *mut OVERLAPPED,
&mut actual_len,
FALSE,
);
let status = if r != 0 {
debug!(
"Transfer {:?} on endpoint {:02x} complete: {} bytes transferred",
self.event, self.endpoint, actual_len
);
TransferStatus::Complete
} else {
let err = GetLastError();
debug!(
"Transfer {:?} on endpoint {:02x} failed: {}, {} bytes transferred",
self.event, self.endpoint, err, actual_len
);
map_error(err)
};
(actual_len as usize, status)
}
}
impl Drop for TransferData {
fn drop(&mut self) {
todo!()
if !self.buf.is_null() {
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
}
unsafe { drop(Box::from_raw(self.event)) }
}
}
impl PlatformTransfer for TransferData {
fn cancel(&self) {
todo!()
debug!("Cancelling transfer {:?}", self.event);
unsafe {
let r = CancelIoEx(
raw_handle(&self.interface.handle),
self.event as *mut OVERLAPPED,
);
if r == 0 {
let err = GetLastError();
if err != ERROR_NOT_FOUND {
error!(
"CancelIoEx failed: {}",
io::Error::from_raw_os_error(err as i32)
);
}
}
}
}
}
impl PlatformSubmit<Vec<u8>> for TransferData {
unsafe fn submit(&mut self, data: Vec<u8>, user_data: *mut c_void) {
todo!()
addr_of_mut!((*self.event).ptr).write(user_data);
let mut data = ManuallyDrop::new(data);
self.buf = data.as_mut_ptr();
self.capacity = data.capacity();
let len = data.len();
debug!(
"Submit transfer {:?} on endpoint {:02X} for {} bytes OUT",
self.event, self.endpoint, len
);
let r = WinUsb_WritePipe(
self.interface.winusb_handle,
self.endpoint,
self.buf,
len.try_into().expect("transfer size should fit in u32"),
null_mut(),
self.event as *mut OVERLAPPED,
);
self.post_submit(r, "WinUsb_WritePipe", user_data);
}
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
todo!()
let (actual_len, status) = self.get_status();
let data = ResponseBuffer::from_vec(self.take_buf(0), actual_len);
Completion { data, status }
}
}
impl PlatformSubmit<RequestBuffer> for TransferData {
unsafe fn submit(&mut self, data: RequestBuffer, user_data: *mut c_void) {
todo!()
addr_of_mut!((*self.event).ptr).write(user_data);
let (buf, request_len) = data.into_vec();
let mut buf = ManuallyDrop::new(buf);
self.buf = buf.as_mut_ptr();
self.capacity = buf.capacity();
debug!(
"Submit transfer {:?} on endpoint {:02X} for {} bytes IN",
self.event, self.endpoint, request_len
);
let r = WinUsb_ReadPipe(
self.interface.winusb_handle,
self.endpoint,
self.buf,
request_len
.try_into()
.expect("transfer size should fit in u32"),
null_mut(),
self.event as *mut OVERLAPPED,
);
self.post_submit(r, "WinUsb_ReadPipe", user_data);
}
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
todo!()
let (actual_len, status) = self.get_status();
let data = self.take_buf(actual_len);
Completion { data, status }
}
}
impl PlatformSubmit<ControlIn> for TransferData {
unsafe fn submit(&mut self, data: ControlIn, user_data: *mut c_void) {
todo!()
assert_eq!(self.endpoint, 0);
assert_eq!(self.ep_type, EndpointType::Control);
addr_of_mut!((*self.event).ptr).write(user_data);
let mut buf = ManuallyDrop::new(Vec::with_capacity(data.length as usize));
self.buf = buf.as_mut_ptr();
self.capacity = buf.capacity();
debug!(
"Submit transfer {:?} on endpoint {:02X} for {} bytes ControlIN",
self.event, self.endpoint, data.length
);
let pkt = WINUSB_SETUP_PACKET {
RequestType: data.request_type(),
Request: data.request,
Value: data.value,
Index: data.index,
Length: data.length,
};
let r = WinUsb_ControlTransfer(
self.interface.winusb_handle,
pkt,
self.buf,
data.length as u32,
null_mut(),
self.event as *mut OVERLAPPED,
);
self.post_submit(r, "WinUsb_ControlTransfer", user_data);
}
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
todo!()
let (actual_len, status) = self.get_status();
let data = self.take_buf(actual_len);
Completion { data, status }
}
}
impl PlatformSubmit<ControlOut<'_>> for TransferData {
unsafe fn submit(&mut self, data: ControlOut, user_data: *mut c_void) {
todo!()
assert_eq!(self.endpoint, 0);
assert_eq!(self.ep_type, EndpointType::Control);
addr_of_mut!((*self.event).ptr).write(user_data);
let mut buf = ManuallyDrop::new(data.data.to_vec());
self.buf = buf.as_mut_ptr();
self.capacity = buf.capacity();
let len: u16 = buf
.len()
.try_into()
.expect("transfer size should fit in u16");
debug!(
"Submit transfer {:?} on endpoint {:02X} for {} bytes ControlOUT",
self.event, self.endpoint, len
);
let pkt = WINUSB_SETUP_PACKET {
RequestType: data.request_type(),
Request: data.request,
Value: data.value,
Index: data.index,
Length: len as u16,
};
let r = WinUsb_ControlTransfer(
self.interface.winusb_handle,
pkt,
self.buf,
len as u32,
null_mut(),
self.event as *mut OVERLAPPED,
);
self.post_submit(r, "WinUsb_ControlTransfer", user_data);
}
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
todo!()
let (actual_len, status) = self.get_status();
let data = ResponseBuffer::from_vec(self.take_buf(0), actual_len);
Completion { data, status }
}
}
pub(super) fn handle_event(completion: *mut OVERLAPPED) {
let completion = completion as *mut EventNotify;
debug!("Handling completion for transfer {completion:?}");
unsafe {
let p = addr_of_mut!((*completion).ptr).read();
notify_completion::<TransferData>(p)
}
}
fn map_error(err: WIN32_ERROR) -> TransferStatus {
match err {
ERROR_GEN_FAILURE => TransferStatus::Stall,
ERROR_REQUEST_ABORTED => TransferStatus::Cancelled,
ERROR_FILE_NOT_FOUND | ERROR_DEVICE_NOT_CONNECTED | ERROR_NO_SUCH_DEVICE => {
TransferStatus::Disconnected
}
_ => TransferStatus::UnknownError,
}
}

View file

@ -1,5 +1,6 @@
use std::{
ffi::{OsStr, OsString},
io,
os::windows::prelude::{
AsHandle, AsRawHandle, HandleOrInvalid, OsStrExt, OsStringExt, OwnedHandle, RawHandle,
},
@ -7,19 +8,21 @@ use std::{
};
use windows_sys::Win32::{
Foundation::{GetLastError, GENERIC_WRITE, HANDLE, WIN32_ERROR},
Storage::FileSystem::{CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_WRITE, OPEN_EXISTING},
Foundation::{GENERIC_READ, GENERIC_WRITE, HANDLE},
Storage::FileSystem::{
CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
},
};
/// Wrapper around `CreateFile`
pub fn create_file(path: &OsStr) -> Result<OwnedHandle, WIN32_ERROR> {
pub fn create_file(path: &OsStr) -> Result<OwnedHandle, io::Error> {
let wide_name: Vec<u16> = path.encode_wide().chain(Some(0)).collect();
unsafe {
let r = CreateFileW(
wide_name.as_ptr(),
GENERIC_WRITE,
FILE_SHARE_WRITE,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
null(),
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
@ -27,7 +30,7 @@ pub fn create_file(path: &OsStr) -> Result<OwnedHandle, WIN32_ERROR> {
);
HandleOrInvalid::from_raw_handle(r as RawHandle)
.try_into()
.map_err(|_| GetLastError())
.map_err(|_| io::Error::last_os_error())
}
}

View file

@ -48,6 +48,7 @@ pub struct ControlOut<'a> {
}
impl<'a> ControlOut<'a> {
#[allow(unused)]
pub(crate) fn setup_packet(&self) -> Result<[u8; SETUP_PACKET_SIZE], ()> {
Ok(pack_setup(
Direction::Out,
@ -59,6 +60,10 @@ impl<'a> ControlOut<'a> {
self.data.len().try_into().map_err(|_| ())?,
))
}
pub(crate) fn request_type(&self) -> u8 {
request_type(Direction::Out, self.control_type, self.recipient)
}
}
impl TransferRequest for ControlOut<'_> {
@ -86,6 +91,7 @@ pub struct ControlIn {
}
impl ControlIn {
#[allow(unused)]
pub(crate) fn setup_packet(&self) -> [u8; SETUP_PACKET_SIZE] {
pack_setup(
Direction::In,
@ -97,6 +103,10 @@ impl ControlIn {
self.length,
)
}
pub(crate) fn request_type(&self) -> u8 {
request_type(Direction::In, self.control_type, self.recipient)
}
}
pub(crate) const SETUP_PACKET_SIZE: usize = 8;
@ -110,7 +120,7 @@ fn pack_setup(
index: u16,
length: u16,
) -> [u8; SETUP_PACKET_SIZE] {
let bmrequesttype = ((direction as u8) << 7) | ((control_type as u8) << 5) | (recipient as u8);
let bmrequesttype = request_type(direction, control_type, recipient);
[
bmrequesttype,
@ -124,6 +134,10 @@ fn pack_setup(
]
}
fn request_type(direction: Direction, control_type: ControlType, recipient: Recipient) -> u8 {
((direction as u8) << 7) | ((control_type as u8) << 5) | (recipient as u8)
}
impl TransferRequest for ControlIn {
type Response = Vec<u8>;
}