Merge pull request #114 from kevinmehall/rustix
Update Rustix to 1.0, use it for udev netlink
This commit is contained in:
commit
a243514f58
4 changed files with 77 additions and 127 deletions
|
|
@ -22,8 +22,8 @@ env_logger = "0.10.0"
|
|||
futures-lite = "1.13.0"
|
||||
|
||||
[target.'cfg(any(target_os="linux", target_os="android"))'.dependencies]
|
||||
rustix = { version = "0.38.17", features = ["fs", "event", "net"] }
|
||||
libc = "0.2.155"
|
||||
rustix = { version = "1.0.1", features = ["fs", "event", "net"] }
|
||||
linux-raw-sys = { version = "0.9.2", features = ["ioctl"] }
|
||||
|
||||
[target.'cfg(target_os="windows")'.dependencies]
|
||||
windows-sys = { version = "0.59.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"] }
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@ use once_cell::sync::OnceCell;
|
|||
use rustix::{
|
||||
event::epoll::{self, EventData, EventFlags},
|
||||
fd::{AsFd, BorrowedFd, OwnedFd},
|
||||
io::retry_on_intr,
|
||||
io::Errno,
|
||||
};
|
||||
use slab::Slab;
|
||||
use std::{
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
sync::{Arc, Mutex},
|
||||
task::Waker,
|
||||
thread,
|
||||
|
|
@ -87,10 +88,14 @@ pub(super) fn unregister_fd(fd: BorrowedFd) {
|
|||
|
||||
fn event_loop() {
|
||||
let epoll_fd = EPOLL_FD.get().unwrap();
|
||||
let mut event_list = epoll::EventVec::with_capacity(4);
|
||||
let mut event_buf = [MaybeUninit::<epoll::Event>::uninit(); 4];
|
||||
loop {
|
||||
retry_on_intr(|| epoll::wait(epoll_fd, &mut event_list, -1)).unwrap();
|
||||
for event in &event_list {
|
||||
let events = match epoll::wait(epoll_fd, &mut event_buf, None) {
|
||||
Ok((events, _)) => events,
|
||||
Err(Errno::INTR) => &mut [],
|
||||
Err(e) => panic!("epoll::wait failed: {e}"),
|
||||
};
|
||||
for event in events {
|
||||
match Tag::from_event_data(event.data) {
|
||||
Tag::Device(id) => Device::handle_usb_epoll(id),
|
||||
Tag::Waker(id) => {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
use libc::{sockaddr, sockaddr_nl, socklen_t, AF_NETLINK, MSG_DONTWAIT};
|
||||
use log::{error, trace, warn};
|
||||
use rustix::{
|
||||
fd::{AsFd, AsRawFd, OwnedFd},
|
||||
net::{netlink, socket_with, AddressFamily, SocketFlags, SocketType},
|
||||
};
|
||||
use std::{
|
||||
io::ErrorKind,
|
||||
mem,
|
||||
os::{raw::c_void, unix::prelude::BorrowedFd},
|
||||
path::Path,
|
||||
task::Poll,
|
||||
fd::{AsFd, OwnedFd},
|
||||
io::Errno,
|
||||
net::{
|
||||
bind,
|
||||
netlink::{self, SocketAddrNetlink},
|
||||
recvfrom, socket_with, AddressFamily, RecvFlags, SocketFlags, SocketType,
|
||||
},
|
||||
};
|
||||
use std::{mem::MaybeUninit, os::unix::prelude::BorrowedFd, path::Path, task::Poll};
|
||||
|
||||
use crate::{hotplug::HotplugEvent, Error};
|
||||
|
||||
|
|
@ -31,23 +29,7 @@ impl LinuxHotplugWatch {
|
|||
SocketFlags::CLOEXEC,
|
||||
Some(netlink::KOBJECT_UEVENT),
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
// rustix doesn't support netlink yet (pending https://github.com/bytecodealliance/rustix/pull/1004)
|
||||
// so use libc for now.
|
||||
let mut addr: sockaddr_nl = mem::zeroed();
|
||||
addr.nl_family = AF_NETLINK as u16;
|
||||
addr.nl_groups = UDEV_MULTICAST_GROUP;
|
||||
let r = libc::bind(
|
||||
fd.as_raw_fd(),
|
||||
&addr as *const sockaddr_nl as *const sockaddr,
|
||||
mem::size_of_val(&addr) as socklen_t,
|
||||
);
|
||||
if r != 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
bind(&fd, &SocketAddrNetlink::new(0, UDEV_MULTICAST_GROUP))?;
|
||||
Ok(LinuxHotplugWatch {
|
||||
fd: Async::new(fd)?,
|
||||
})
|
||||
|
|
@ -67,40 +49,28 @@ impl LinuxHotplugWatch {
|
|||
}
|
||||
|
||||
fn try_receive_event(fd: BorrowedFd) -> Option<HotplugEvent> {
|
||||
let mut buf = [0; 8192];
|
||||
let mut buf = [MaybeUninit::uninit(); 8192];
|
||||
|
||||
let received = unsafe {
|
||||
let mut addr: sockaddr_nl = mem::zeroed();
|
||||
let mut addrlen: socklen_t = mem::size_of_val(&addr) as socklen_t;
|
||||
let r = libc::recvfrom(
|
||||
fd.as_raw_fd(),
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf.len(),
|
||||
MSG_DONTWAIT,
|
||||
&mut addr as *mut sockaddr_nl as *mut sockaddr,
|
||||
&mut addrlen,
|
||||
);
|
||||
if r >= 0 {
|
||||
Ok((r as usize, addr.nl_groups))
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
let (data, src) = match recvfrom(fd, &mut buf, RecvFlags::DONTWAIT) {
|
||||
Ok(((buf, _), _, src)) => (buf, src),
|
||||
Err(Errno::AGAIN | Errno::INTR) => return None,
|
||||
Err(e) => {
|
||||
error!("udev netlink socket recvfrom failed with {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
match received {
|
||||
// udev messages will normally be sent to a multicast group, which only
|
||||
// root can send to. Reject unicast messages that may be from anywhere.
|
||||
Ok((size, groups)) if groups == UDEV_MULTICAST_GROUP => parse_packet(&buf[..size]),
|
||||
Ok((_, src)) => {
|
||||
// udev messages will normally be sent to a multicast group, which only
|
||||
// root can send to. Reject unicast messages that may be from anywhere.
|
||||
match src.map(SocketAddrNetlink::try_from).transpose() {
|
||||
Ok(Some(nl)) if nl.groups() == UDEV_MULTICAST_GROUP => {}
|
||||
src => {
|
||||
warn!("udev netlink socket received message from {src:?}");
|
||||
None
|
||||
}
|
||||
Err(e) if e.kind() == ErrorKind::WouldBlock => None,
|
||||
Err(e) => {
|
||||
error!("udev netlink socket recvfrom failed with {e}");
|
||||
None
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
parse_packet(data)
|
||||
}
|
||||
|
||||
fn parse_packet(buf: &[u8]) -> Option<HotplugEvent> {
|
||||
|
|
|
|||
|
|
@ -4,29 +4,31 @@
|
|||
//! [usbfs]: https://www.kernel.org/doc/html/latest/driver-api/usb/usb.html#the-usb-character-device-nodes
|
||||
//! [uapi]: https://github.com/torvalds/linux/blob/master/tools/include/uapi/linux/usbdevice_fs.h
|
||||
#![allow(dead_code)]
|
||||
use std::{
|
||||
ffi::{c_int, c_uchar, c_uint, c_void},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::ffi::{c_int, c_uchar, c_uint, c_void};
|
||||
|
||||
use linux_raw_sys::ioctl::{
|
||||
USBDEVFS_CLAIMINTERFACE, USBDEVFS_CLEAR_HALT, USBDEVFS_CONNECT, USBDEVFS_CONTROL,
|
||||
USBDEVFS_DISCARDURB, USBDEVFS_DISCONNECT, USBDEVFS_DISCONNECT_CLAIM, USBDEVFS_GET_SPEED,
|
||||
USBDEVFS_IOCTL, USBDEVFS_REAPURBNDELAY, USBDEVFS_RELEASEINTERFACE, USBDEVFS_RESET,
|
||||
USBDEVFS_SETCONFIGURATION, USBDEVFS_SETINTERFACE, USBDEVFS_SUBMITURB,
|
||||
};
|
||||
use rustix::{
|
||||
fd::AsFd,
|
||||
io,
|
||||
ioctl::{self, CompileTimeOpcode, Ioctl, IoctlOutput},
|
||||
ioctl::{self, Ioctl, IoctlOutput, Opcode},
|
||||
};
|
||||
|
||||
pub fn set_configuration<Fd: AsFd>(fd: Fd, configuration: u8) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadOpcode<b'U', 5, c_uint>, c_uint>::new(configuration.into());
|
||||
ioctl::Setter::<{ USBDEVFS_SETCONFIGURATION as _ }, c_uint>::new(configuration.into());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn claim_interface<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadOpcode<b'U', 15, c_uint>, c_uint>::new(interface.into());
|
||||
let ctl = ioctl::Setter::<{ USBDEVFS_CLAIMINTERFACE as _ }, c_uint>::new(interface.into());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +36,7 @@ pub fn claim_interface<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
|||
pub fn release_interface<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadOpcode<b'U', 16, c_uint>, c_uint>::new(interface.into());
|
||||
ioctl::Setter::<{ USBDEVFS_RELEASEINTERFACE as _ }, c_uint>::new(interface.into());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +59,7 @@ pub fn detach_and_claim_interface<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result
|
|||
|
||||
dc.driver[0..6].copy_from_slice(b"usbfs\0");
|
||||
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_DISCONNECT_CLAIM, DetachAndClaim>::new(dc);
|
||||
let ctl = ioctl::Setter::<{ USBDEVFS_DISCONNECT_CLAIM as _ }, DetachAndClaim>::new(dc);
|
||||
|
||||
ioctl::ioctl(&fd, ctl)
|
||||
}
|
||||
|
|
@ -70,37 +72,15 @@ struct UsbFsIoctl {
|
|||
data: *mut c_void,
|
||||
}
|
||||
|
||||
/// Opcodes used in ioctl with the usb device fs.
|
||||
///
|
||||
/// Taken from https://github.com/torvalds/linux/blob/e9680017b2dc8686a908ea1b51941a91b6da9f1d/include/uapi/linux/usbdevice_fs.h#L187
|
||||
// We repeat the USBDEVFS_ prefix to help keep the same names as what linux uses.
|
||||
// This makes the code more searchable.
|
||||
// TODO: Move the rest of the opcodes into here.
|
||||
#[allow(non_camel_case_types)]
|
||||
mod opcodes {
|
||||
use super::*;
|
||||
|
||||
pub type USBDEVFS_IOCTL = ioctl::ReadWriteOpcode<b'U', 18, UsbFsIoctl>;
|
||||
pub type USBDEVFS_DISCONNECT_CLAIM = ioctl::ReadOpcode<b'U', 27, DetachAndClaim>;
|
||||
|
||||
/// These opcodes are nested inside a [`USBDEVFS_IOCTL`] operation.
|
||||
pub mod nested {
|
||||
use super::*;
|
||||
|
||||
pub type USBDEVFS_DISCONNECT = ioctl::NoneOpcode<b'U', 22, ()>;
|
||||
pub type USBDEVFS_CONNECT = ioctl::NoneOpcode<b'U', 23, ()>;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
let command = UsbFsIoctl {
|
||||
interface: interface.into(),
|
||||
// NOTE: Cast needed since on android this type is i32 vs u32 on linux
|
||||
ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw() as _,
|
||||
ioctl_code: USBDEVFS_DISCONNECT as _,
|
||||
data: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_IOCTL, UsbFsIoctl>::new(command);
|
||||
let ctl = ioctl::Setter::<{ USBDEVFS_IOCTL as _ }, UsbFsIoctl>::new(command);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,11 +88,11 @@ pub fn detach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
|||
pub fn attach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
let command = UsbFsIoctl {
|
||||
interface: interface.into(),
|
||||
ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw() as _,
|
||||
ioctl_code: USBDEVFS_CONNECT as _,
|
||||
data: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_IOCTL, UsbFsIoctl>::new(command);
|
||||
let ctl = ioctl::Setter::<{ USBDEVFS_IOCTL as _ }, UsbFsIoctl>::new(command);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
@ -125,22 +105,20 @@ struct SetAltSetting {
|
|||
|
||||
pub fn set_interface<Fd: AsFd>(fd: Fd, interface: u8, alt_setting: u8) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl = ioctl::Setter::<ioctl::ReadOpcode<b'U', 4, SetAltSetting>, SetAltSetting>::new(
|
||||
SetAltSetting {
|
||||
let ctl =
|
||||
ioctl::Setter::<{ USBDEVFS_SETINTERFACE as _ }, SetAltSetting>::new(SetAltSetting {
|
||||
interface: interface.into(),
|
||||
alt_setting: alt_setting.into(),
|
||||
},
|
||||
);
|
||||
});
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PassPtr<Opcode, Input> {
|
||||
pub struct PassPtr<const OPCODE: Opcode, Input> {
|
||||
input: *mut Input,
|
||||
_opcode: PhantomData<Opcode>,
|
||||
}
|
||||
|
||||
impl<Opcode: CompileTimeOpcode, Input> PassPtr<Opcode, Input> {
|
||||
impl<const OPCODE: Opcode, Input> PassPtr<OPCODE, Input> {
|
||||
/// Create a new pointer setter-style `ioctl` object.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
@ -150,18 +128,18 @@ impl<Opcode: CompileTimeOpcode, Input> PassPtr<Opcode, Input> {
|
|||
/// get.
|
||||
#[inline]
|
||||
pub unsafe fn new(input: *mut Input) -> Self {
|
||||
Self {
|
||||
input,
|
||||
_opcode: PhantomData,
|
||||
}
|
||||
Self { input }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Opcode: CompileTimeOpcode, Input> Ioctl for PassPtr<Opcode, Input> {
|
||||
unsafe impl<const OPCODE: Opcode, Input> Ioctl for PassPtr<OPCODE, Input> {
|
||||
type Output = ();
|
||||
|
||||
const IS_MUTATING: bool = false;
|
||||
const OPCODE: rustix::ioctl::Opcode = Opcode::OPCODE;
|
||||
|
||||
fn opcode(&self) -> ioctl::Opcode {
|
||||
OPCODE
|
||||
}
|
||||
|
||||
fn as_ptr(&mut self) -> *mut c_void {
|
||||
self.input as *mut c_void
|
||||
|
|
@ -174,28 +152,28 @@ unsafe impl<Opcode: CompileTimeOpcode, Input> Ioctl for PassPtr<Opcode, Input> {
|
|||
|
||||
pub unsafe fn submit_urb<Fd: AsFd>(fd: Fd, urb: *mut Urb) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl = PassPtr::<ioctl::ReadOpcode<b'U', 10, Urb>, Urb>::new(urb);
|
||||
let ctl = PassPtr::<{ USBDEVFS_SUBMITURB as _ }, Urb>::new(urb);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reap_urb_ndelay<Fd: AsFd>(fd: Fd) -> io::Result<*mut Urb> {
|
||||
unsafe {
|
||||
let ctl = ioctl::Getter::<ioctl::WriteOpcode<b'U', 13, *mut Urb>, *mut Urb>::new();
|
||||
let ctl = ioctl::Getter::<{ USBDEVFS_REAPURBNDELAY as _ }, *mut Urb>::new();
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn discard_urb<Fd: AsFd>(fd: Fd, urb: *mut Urb) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl = PassPtr::<ioctl::NoneOpcode<b'U', 11, ()>, Urb>::new(urb);
|
||||
let ctl = PassPtr::<{ USBDEVFS_DISCARDURB as _ }, Urb>::new(urb);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset<Fd: AsFd>(fd: Fd) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl = ioctl::NoArg::<ioctl::NoneOpcode<b'U', 20, ()>>::new();
|
||||
let ctl = ioctl::NoArg::<{ USBDEVFS_RESET as _ }>::new();
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
@ -229,26 +207,25 @@ pub struct Urb {
|
|||
// + variable size array of iso_packet_desc
|
||||
}
|
||||
|
||||
pub struct Transfer<Opcode, Input> {
|
||||
pub struct Transfer<const OPCODE: Opcode, Input> {
|
||||
input: Input,
|
||||
_opcode: PhantomData<Opcode>,
|
||||
}
|
||||
|
||||
impl<Opcode: CompileTimeOpcode, Input> Transfer<Opcode, Input> {
|
||||
impl<const OPCODE: Opcode, Input> Transfer<OPCODE, Input> {
|
||||
#[inline]
|
||||
pub unsafe fn new(input: Input) -> Self {
|
||||
Self {
|
||||
input,
|
||||
_opcode: PhantomData,
|
||||
}
|
||||
Self { input }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Opcode: CompileTimeOpcode, Input> Ioctl for Transfer<Opcode, Input> {
|
||||
unsafe impl<const OPCODE: Opcode, Input> Ioctl for Transfer<OPCODE, Input> {
|
||||
type Output = usize;
|
||||
|
||||
const IS_MUTATING: bool = true;
|
||||
const OPCODE: rustix::ioctl::Opcode = Opcode::OPCODE;
|
||||
|
||||
fn opcode(&self) -> ioctl::Opcode {
|
||||
OPCODE
|
||||
}
|
||||
|
||||
fn as_ptr(&mut self) -> *mut c_void {
|
||||
&mut self.input as *mut Input as *mut c_void
|
||||
|
|
@ -273,23 +250,21 @@ pub struct CtrlTransfer {
|
|||
|
||||
pub fn control<Fd: AsFd>(fd: Fd, transfer: CtrlTransfer) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let ctl =
|
||||
Transfer::<ioctl::ReadWriteOpcode<b'U', 0, CtrlTransfer>, CtrlTransfer>::new(transfer);
|
||||
let ctl = Transfer::<{ USBDEVFS_CONTROL as _ }, CtrlTransfer>::new(transfer);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_halt<Fd: AsFd>(fd: Fd, endpoint: u8) -> io::Result<()> {
|
||||
unsafe {
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadOpcode<b'U', 21, c_uint>, c_uint>::new(endpoint.into());
|
||||
let ctl = ioctl::Setter::<{ USBDEVFS_CLEAR_HALT as _ }, c_uint>::new(endpoint.into());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_speed<Fd: AsFd>(fd: Fd) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let ctl = Transfer::<ioctl::NoneOpcode<b'U', 31, ()>, ()>::new(());
|
||||
let ctl = Transfer::<{ USBDEVFS_GET_SPEED as _ }, ()>::new(());
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue