diff options
| author | Michael Brown | 2015-03-16 16:37:39 +0100 |
|---|---|---|
| committer | Michael Brown | 2015-03-16 16:42:29 +0100 |
| commit | 838ab97ce3bda9db0b6d5beb98f8c5bc16582be9 (patch) | |
| tree | 0ce8b7eb9f24f1b6fb66fc74dbd7facdce028eba | |
| parent | [ncm] Respect maximum transfer size of the bus (diff) | |
| download | ipxe-838ab97ce3bda9db0b6d5beb98f8c5bc16582be9.tar.gz ipxe-838ab97ce3bda9db0b6d5beb98f8c5bc16582be9.tar.xz ipxe-838ab97ce3bda9db0b6d5beb98f8c5bc16582be9.zip | |
[usb] Add functions for manual device address assignment
Signed-off-by: Michael Brown <mcb30@ipxe.org>
| -rw-r--r-- | src/drivers/bus/usb.c | 44 | ||||
| -rw-r--r-- | src/include/ipxe/usb.h | 26 |
2 files changed, 70 insertions, 0 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 57a253320..548aa7b08 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <strings.h> #include <unistd.h> #include <errno.h> #include <assert.h> @@ -1851,6 +1852,49 @@ void free_usb_bus ( struct usb_bus *bus ) { /****************************************************************************** * + * USB address assignment + * + ****************************************************************************** + */ + +/** + * Allocate device address + * + * @v bus USB bus + * @ret address Device address, or negative error + */ +int usb_alloc_address ( struct usb_bus *bus ) { + unsigned int address; + + /* Find first free device address */ + address = ffsll ( ~bus->addresses ); + if ( ! address ) + return -ENOENT; + + /* Mark address as used */ + bus->addresses |= ( 1ULL << ( address - 1 ) ); + + return address; +} + +/** + * Free device address + * + * @v bus USB bus + * @v address Device address + */ +void usb_free_address ( struct usb_bus *bus, unsigned int address ) { + + /* Sanity check */ + assert ( address > 0 ); + assert ( bus->addresses & ( 1ULL << ( address - 1 ) ) ); + + /* Mark address as free */ + bus->addresses &= ~( 1ULL << ( address - 1 ) ); +} + +/****************************************************************************** + * * USB bus topology * ****************************************************************************** diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index fd027d188..70038368b 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -867,6 +867,17 @@ struct usb_bus { /** Largest transfer allowed on the bus */ size_t mtu; + /** Address in-use mask + * + * This is used only by buses which perform manual address + * assignment. USB allows for addresses in the range [1,127]. + * We use a simple bitmask which restricts us to the range + * [1,64]; this is unlikely to be a problem in practice. For + * comparison: controllers which perform autonomous address + * assignment (such as xHCI) typically allow for only 32 + * devices per bus anyway. + */ + unsigned long long addresses; /** Root hub */ struct usb_hub *hub; @@ -1022,6 +1033,19 @@ usb_set_feature ( struct usb_device *usb, unsigned int type, } /** + * Set address + * + * @v usb USB device + * @v address Device address + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_address ( struct usb_device *usb, unsigned int address ) { + + return usb_control ( usb, USB_SET_ADDRESS, address, 0, NULL, 0 ); +} + +/** * Get USB descriptor * * @v usb USB device @@ -1148,6 +1172,8 @@ 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 int usb_alloc_address ( struct usb_bus *bus ); +extern void usb_free_address ( struct usb_bus *bus, unsigned int address ); extern unsigned int usb_route_string ( struct usb_device *usb ); extern unsigned int usb_depth ( struct usb_device *usb ); extern struct usb_port * usb_root_hub_port ( struct usb_device *usb ); |
