[usb] Add "usbscan" command for iterating over USB devices

Implement a "usbscan" command as a direct analogy of the existing
"pciscan" command, allowing scripts to iterate over all detected USB
devices.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2024-10-17 14:05:25 +01:00
parent 2bf16c6ffc
commit c219b5d8a9
6 changed files with 225 additions and 2 deletions

View File

@ -302,6 +302,9 @@ REQUIRE_OBJECT ( shim_cmd );
#ifdef IMAGE_CRYPT_CMD
REQUIRE_OBJECT ( image_crypt_cmd );
#endif
#ifdef USB_CMD
REQUIRE_OBJECT ( usb_cmd );
#endif
/*
* Drag in miscellaneous objects

View File

@ -171,6 +171,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
//#define IMAGE_MEM_CMD /* Read memory command */
#define IMAGE_ARCHIVE_CMD /* Archive image management commands */
#define SHIM_CMD /* EFI shim command (or dummy command) */
//#define USB_CMD /* USB commands */
/*
* ROM-specific options

View File

@ -1323,7 +1323,8 @@ usb_probe_all ( struct usb_device *usb,
func->name = func->dev.name;
func->usb = usb;
func->dev.desc.bus_type = BUS_TYPE_USB;
func->dev.desc.location = usb->address;
func->dev.desc.location =
USB_BUSDEV ( bus->address, usb->address );
func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor );
func->dev.desc.device = le16_to_cpu ( usb->device.product );
snprintf ( func->dev.name, sizeof ( func->dev.name ),
@ -1725,6 +1726,25 @@ static void free_usb ( struct usb_device *usb ) {
free ( usb );
}
/**
* Find USB device by address
*
* @v bus USB bus
* @v address Device address
* @ret usb USB device, or NULL if not found
*/
struct usb_device * find_usb ( struct usb_bus *bus, unsigned int address ) {
struct usb_device *usb;
/* Search for a matching non-zero address */
list_for_each_entry ( usb, &bus->devices, list ) {
if ( address && ( usb->address == address ) )
return usb;
}
return NULL;
}
/******************************************************************************
*
* USB device hotplug event handling
@ -2115,6 +2135,11 @@ int register_usb_bus ( struct usb_bus *bus ) {
/* Sanity checks */
assert ( bus->hub != NULL );
/* Assign the first available bus address */
bus->address = 0;
while ( find_usb_bus ( bus->address ) != NULL )
bus->address++;
/* Open bus */
if ( ( rc = bus->host->open ( bus ) ) != 0 )
goto err_open;
@ -2187,6 +2212,23 @@ void free_usb_bus ( struct usb_bus *bus ) {
free ( bus );
}
/**
* Find USB bus by address
*
* @v address Bus address
* @ret bus USB bus, or NULL
*/
struct usb_bus * find_usb_bus ( unsigned int address ) {
struct usb_bus *bus;
for_each_usb_bus ( bus ) {
if ( bus->address == address )
return bus;
}
return NULL;
}
/**
* Find USB bus by device location
*
@ -2209,7 +2251,7 @@ struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type,
/******************************************************************************
*
* USB address assignment
* USB device addressing
*
******************************************************************************
*/
@ -2250,6 +2292,35 @@ void usb_free_address ( struct usb_bus *bus, unsigned int address ) {
bus->addresses &= ~( 1ULL << ( address - 1 ) );
}
/**
* Find next USB device
*
* @v usb USB device to fill in
* @v busdev Starting bus:dev address
* @ret busdev Bus:dev address of next USB device
* @ret rc Return status code
*/
int usb_find_next ( struct usb_device **usb, uint16_t *busdev ) {
struct usb_bus *bus;
do {
/* Find USB bus, if any */
bus = find_usb_bus ( USB_BUS ( *busdev ) );
if ( ! bus ) {
*busdev |= ( USB_BUS ( 1 ) - 1 );
continue;
}
/* Find USB device, if any */
*usb = find_usb ( bus, USB_DEV ( *busdev ) );
if ( *usb )
return 0;
} while ( ++(*busdev) );
return -ENODEV;
}
/******************************************************************************
*
* USB bus topology

122
src/hci/commands/usb_cmd.c Normal file
View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <ipxe/usb.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* USB commands
*
*/
/** "usbscan" options */
struct usbscan_options {};
/** "usbscan" option list */
static struct option_descriptor usbscan_opts[] = {};
/** "usbscan" command descriptor */
static struct command_descriptor usbscan_cmd =
COMMAND_DESC ( struct usbscan_options, usbscan_opts, 1, 1,
"<setting>" );
/**
* "usbscan" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int usbscan_exec ( int argc, char **argv ) {
struct usbscan_options opts;
struct named_setting setting;
struct usb_device *usb;
unsigned long prev;
uint16_t busdev;
int len;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &usbscan_cmd, &opts ) ) != 0 )
goto err_parse_options;
/* Parse setting name */
if ( ( rc = parse_autovivified_setting ( argv[optind],
&setting ) ) != 0 )
goto err_parse_setting;
/* Determine starting bus:dev.fn address */
if ( ( len = fetchn_setting ( setting.settings, &setting.setting,
NULL, &setting.setting, &prev ) ) < 0 ) {
/* Setting not yet defined: start searching from 00:00 */
busdev = 0;
} else {
/* Setting is defined: start searching from next location */
busdev = ( prev + 1 );
if ( ! busdev ) {
rc = -ENOENT;
goto err_end;
}
}
/* Find next existent USB device */
if ( ( rc = usb_find_next ( &usb, &busdev ) ) != 0 )
goto err_find_next;
/* Apply default type if necessary. Use ":uint16" rather than
* ":hex" to allow for easy inclusion within a
* "${usb/${location}....}" constructed setting.
*/
if ( ! setting.setting.type )
setting.setting.type = &setting_type_uint16;
/* Store setting */
if ( ( rc = storen_setting ( setting.settings, &setting.setting,
busdev ) ) != 0 ) {
printf ( "Could not store \"%s\": %s\n",
setting.setting.name, strerror ( rc ) );
goto err_store;
}
err_store:
err_end:
err_find_next:
err_parse_setting:
err_parse_options:
return rc;
}
/** USB commands */
struct command usb_commands[] __command = {
{
.name = "usbscan",
.exec = usbscan_exec,
},
};

View File

@ -423,6 +423,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_editstring ( ERRFILE_OTHER | 0x00610000 )
#define ERRFILE_widget_ui ( ERRFILE_OTHER | 0x00620000 )
#define ERRFILE_form_ui ( ERRFILE_OTHER | 0x00630000 )
#define ERRFILE_usb_cmd ( ERRFILE_OTHER | 0x00640000 )
/** @} */

View File

@ -54,6 +54,20 @@ enum usb_speed {
USB_SPEED_SUPER = USB_SPEED ( 5, 3 ),
};
/** Define a USB bus:device address
*
* @v bus Bus address
* @v dev Device address
* @ret busdev Bus:device address
*/
#define USB_BUSDEV( bus, dev ) ( ( (bus) << 8 ) | (dev) )
/** Extract USB bus address */
#define USB_BUS( busdev ) ( (busdev) >> 8 )
/** Extract USB device address */
#define USB_DEV( busdev ) ( (busdev) & 0xff )
/** USB packet IDs */
enum usb_pid {
/** IN PID */
@ -956,6 +970,12 @@ struct usb_bus {
/** Host controller operations set */
struct usb_host_operations *op;
/** Bus address
*
* This is an internal index used only to allow a USB device
* to be identified via a nominal bus:device address.
*/
unsigned int address;
/** Largest transfer allowed on the bus */
size_t mtu;
/** Address in-use mask
@ -1269,6 +1289,9 @@ extern struct usb_endpoint_companion_descriptor *
usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config,
struct usb_endpoint_descriptor *desc );
extern struct usb_device * find_usb ( struct usb_bus *bus,
unsigned int address );
extern struct usb_hub * alloc_usb_hub ( struct usb_bus *bus,
struct usb_device *usb,
unsigned int ports,
@ -1285,11 +1308,13 @@ extern struct usb_bus * alloc_usb_bus ( struct device *dev,
extern int register_usb_bus ( struct usb_bus *bus );
extern void unregister_usb_bus ( struct usb_bus *bus );
extern void free_usb_bus ( struct usb_bus *bus );
extern struct usb_bus * find_usb_bus ( unsigned int address );
extern struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type,
unsigned int location );
extern int usb_alloc_address ( struct usb_bus *bus );
extern void usb_free_address ( struct usb_bus *bus, unsigned int address );
extern int usb_find_next ( struct usb_device **usb, uint16_t *busdev );
extern unsigned int usb_route_string ( struct usb_device *usb );
extern struct usb_port * usb_root_hub_port ( struct usb_device *usb );
extern struct usb_port * usb_transaction_translator ( struct usb_device *usb );