mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-10-26 05:44:24 +00:00
usb: support for listing attached usb devices
Originally, crosvm would list details about an attached usb device for a given port. This change allows getting details about multiple ports at once. This is intended to simplify command line usage and downstream consumers like concierge. TEST=various vmc commands Chrome UI for handling USB devices BUG=chromium:831850 Change-Id: I55681a7fea7425c897a22a579dcc15567683ef54 Reviewed-on: https://chromium-review.googlesource.com/1529765 Commit-Ready: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
parent
d99cd0ae0b
commit
aff94ca6da
3 changed files with 65 additions and 32 deletions
|
@ -18,7 +18,10 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||
use std::time::Duration;
|
||||
use sys_util::net::UnixSeqpacket;
|
||||
use sys_util::{error, WatchingEvents};
|
||||
use vm_control::{UsbControlCommand, UsbControlResult, UsbControlSocket};
|
||||
use vm_control::{
|
||||
UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
|
||||
USB_CONTROL_MAX_PORTS,
|
||||
};
|
||||
|
||||
const SOCKET_TIMEOUT_MS: u64 = 2000;
|
||||
|
||||
|
@ -142,6 +145,27 @@ impl ProviderInner {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {
|
||||
let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();
|
||||
for (result_index, &port_id) in ports.iter().enumerate() {
|
||||
match self.usb_hub.get_port(port_id).and_then(|p| {
|
||||
p.get_backend_device()
|
||||
.as_ref()
|
||||
.map(|d| (d.get_vid(), d.get_pid()))
|
||||
}) {
|
||||
Some((vendor_id, product_id)) => {
|
||||
devices[result_index] = UsbControlAttachedDevice {
|
||||
port: port_id,
|
||||
vendor_id,
|
||||
product_id,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
UsbControlResult::Devices(devices)
|
||||
}
|
||||
|
||||
fn on_event_helper(&self) -> Result<()> {
|
||||
let cmd = self.sock.recv().map_err(Error::ReadControlSock)?;
|
||||
match cmd {
|
||||
|
@ -287,23 +311,8 @@ impl ProviderInner {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
UsbControlCommand::ListDevice { port } => {
|
||||
let port_number = port;
|
||||
let result = match self.usb_hub.get_port(port_number) {
|
||||
Some(port) => match port.get_backend_device().as_ref() {
|
||||
Some(device) => {
|
||||
let vid = device.get_vid();
|
||||
let pid = device.get_pid();
|
||||
UsbControlResult::Device {
|
||||
port: port_number,
|
||||
vid,
|
||||
pid,
|
||||
}
|
||||
}
|
||||
None => UsbControlResult::NoSuchDevice,
|
||||
},
|
||||
None => UsbControlResult::NoSuchPort,
|
||||
};
|
||||
UsbControlCommand::ListDevice { ports } => {
|
||||
let result = self.handle_list_devices(ports);
|
||||
// The send failure will be logged, but event loop still think the event is
|
||||
// handled.
|
||||
let _ = self.sock.send(&result).map_err(Error::WriteControlSock);
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -28,7 +28,7 @@ use sys_util::{
|
|||
};
|
||||
use vm_control::{
|
||||
BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
|
||||
VmControlRequestSocket, VmRequest, VmResponse,
|
||||
VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
|
||||
};
|
||||
|
||||
use crate::argument::{print_help, set_arguments, Argument};
|
||||
|
@ -1105,14 +1105,12 @@ fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
|
|||
}
|
||||
}
|
||||
|
||||
fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
|
||||
let port: u8 = args
|
||||
.next()
|
||||
.map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
|
||||
p.parse::<u8>()
|
||||
.map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
|
||||
})?;
|
||||
let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { port });
|
||||
fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
|
||||
let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
|
||||
for (index, port) in ports.iter_mut().enumerate() {
|
||||
*port = index as u8
|
||||
}
|
||||
let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
|
||||
let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
|
||||
match response {
|
||||
VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
|
||||
|
@ -1121,9 +1119,9 @@ fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
|
|||
}
|
||||
|
||||
fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
|
||||
if args.len() < 3 {
|
||||
if args.len() < 2 {
|
||||
print_help("crosvm usb",
|
||||
"[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list PORT] VM_SOCKET...", &[]);
|
||||
"[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,13 @@ impl Default for VmRunMode {
|
|||
}
|
||||
}
|
||||
|
||||
/// The maximum number of devices that can be listed in one `UsbControlCommand`.
|
||||
///
|
||||
/// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
|
||||
/// necessary for correctness. Importing that value directly would be overkill because it would
|
||||
/// require adding a big dependency for a single const.
|
||||
pub const USB_CONTROL_MAX_PORTS: usize = 16;
|
||||
|
||||
#[derive(MsgOnSocket, Debug)]
|
||||
pub enum BalloonControlCommand {
|
||||
/// Set the size of the VM's balloon.
|
||||
|
@ -129,10 +136,23 @@ pub enum UsbControlCommand {
|
|||
port: u8,
|
||||
},
|
||||
ListDevice {
|
||||
port: u8,
|
||||
ports: [u8; USB_CONTROL_MAX_PORTS],
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(MsgOnSocket, Copy, Clone, Debug, Default)]
|
||||
pub struct UsbControlAttachedDevice {
|
||||
pub port: u8,
|
||||
pub vendor_id: u16,
|
||||
pub product_id: u16,
|
||||
}
|
||||
|
||||
impl UsbControlAttachedDevice {
|
||||
fn valid(self) -> bool {
|
||||
self.port != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MsgOnSocket, Debug)]
|
||||
pub enum UsbControlResult {
|
||||
Ok { port: u8 },
|
||||
|
@ -140,7 +160,7 @@ pub enum UsbControlResult {
|
|||
NoSuchDevice,
|
||||
NoSuchPort,
|
||||
FailedToOpenDevice,
|
||||
Device { port: u8, vid: u16, pid: u16 },
|
||||
Devices([UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS]),
|
||||
}
|
||||
|
||||
impl Display for UsbControlResult {
|
||||
|
@ -153,7 +173,13 @@ impl Display for UsbControlResult {
|
|||
NoSuchDevice => write!(f, "no_such_device"),
|
||||
NoSuchPort => write!(f, "no_such_port"),
|
||||
FailedToOpenDevice => write!(f, "failed_to_open_device"),
|
||||
Device { port, vid, pid } => write!(f, "device {} {:04x} {:04x}", port, vid, pid),
|
||||
Devices(devices) => {
|
||||
write!(f, "devices")?;
|
||||
for d in devices.iter().filter(|d| d.valid()) {
|
||||
write!(f, " {} {:04x} {:04x}", d.port, d.vendor_id, d.product_id)?;
|
||||
}
|
||||
std::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue