]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Add "usbscan" command for iterating over USB devices
authorMichael Brown <mcb30@ipxe.org>
Thu, 17 Oct 2024 13:05:25 +0000 (14:05 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 17 Oct 2024 13:18:22 +0000 (14:18 +0100)
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>
src/config/config.c
src/config/general.h
src/drivers/bus/usb.c
src/hci/commands/usb_cmd.c [new file with mode: 0644]
src/include/ipxe/errfile.h
src/include/ipxe/usb.h

index f532c1d2d285db8eb37f7beced6b3baa801e6709..0f950eb9dcc551518fe6dc7487d3a58a0e4cbc6a 100644 (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
index b82e5602cb35656d996dbd9383583107be87e91c..763a34aa0e4a8cfd87f1058aab0efb74b4c87173 100644 (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
index 428ae26c185b8b6cac7e1bb47fa2aa9cc9e5bbe9..e1e7d51f71e4587ae0febf081c1f9f48e49e8024 100644 (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
diff --git a/src/hci/commands/usb_cmd.c b/src/hci/commands/usb_cmd.c
new file mode 100644 (file)
index 0000000..d1086fd
--- /dev/null
@@ -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,
+       },
+};
index 42bc17789ef2c17243ef7ecd7266ccafd7371ced..01a2be6543706fc7e3d06c10c69f91ce98c6096d 100644 (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 )
 
 /** @} */
 
index 911247ede15661a71d463a4fd046b1ec7b2a1020..d9891b75742d75d78fa6dfa9b5b20933eb1694c6 100644 (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 );