diff options
author | Michael Brown | 2015-05-08 19:02:50 +0200 |
---|---|---|
committer | Michael Brown | 2015-05-09 21:08:50 +0200 |
commit | e4783add79e27af4e70cc13785236d2da616ecae (patch) | |
tree | ea6fda606ff4a402f685de76c333c1d6de743464 /src/drivers/bus | |
parent | [usb] Maintain a list of all USB buses (diff) | |
download | ipxe-e4783add79e27af4e70cc13785236d2da616ecae.tar.gz ipxe-e4783add79e27af4e70cc13785236d2da616ecae.tar.xz ipxe-e4783add79e27af4e70cc13785236d2da616ecae.zip |
[usb] Maintain single lists of halted endpoints and changed ports
When an EHCI hotplug action results in the controller disowning the
port, it will result in a hotplug action on the corresponding UHCI or
OHCI controller. Allow such hotplug actions to be carried out as part
of the same call to usb_step() or usb_register_bus(), by maintaining a
single central list of changed ports.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/bus')
-rw-r--r-- | src/drivers/bus/usb.c | 99 |
1 files changed, 55 insertions, 44 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 3484cba1..16f18259 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -43,6 +43,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** List of USB buses */ struct list_head usb_buses = LIST_HEAD_INIT ( usb_buses ); +/** List of changed ports */ +static struct list_head usb_changed = LIST_HEAD_INIT ( usb_changed ); + +/** List of halted endpoints */ +static struct list_head usb_halted = LIST_HEAD_INIT ( usb_halted ); + /****************************************************************************** * * Utility functions @@ -560,7 +566,6 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct usb_device *usb = ep->usb; - struct usb_bus *bus = usb->port->hub->bus; /* Decrement fill level */ assert ( ep->fill > 0 ); @@ -572,7 +577,7 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, usb->name, usb_endpoint_name ( ep->address ), strerror ( rc ) ); list_del ( &ep->halted ); - list_add_tail ( &ep->halted, &bus->halted ); + list_add_tail ( &ep->halted, &usb_halted ); } /* Report completion */ @@ -1576,12 +1581,12 @@ static void usb_detached ( struct usb_port *port ) { } /** - * Handle newly attached or detached USB devices + * Handle newly attached or detached USB device * * @v port USB port * @ret rc Return status code */ -static int usb_hotplug ( struct usb_port *port ) { +static int usb_hotplugged ( struct usb_port *port ) { struct usb_hub *hub = port->hub; int rc; @@ -1621,43 +1626,26 @@ static int usb_hotplug ( struct usb_port *port ) { * @v port USB port */ void usb_port_changed ( struct usb_port *port ) { - struct usb_hub *hub = port->hub; - struct usb_bus *bus = hub->bus; /* Record hub port status change */ list_del ( &port->changed ); - list_add_tail ( &port->changed, &bus->changed ); + list_add_tail ( &port->changed, &usb_changed ); } /** - * USB process + * Handle newly attached or detached USB device * - * @v bus USB bus */ -static void usb_step ( struct usb_bus *bus ) { - struct usb_endpoint *ep; +static void usb_hotplug ( void ) { struct usb_port *port; - /* Poll bus */ - usb_poll ( bus ); - - /* Attempt to reset first halted endpoint in list, if any. We - * do not attempt to process the complete list, since this - * would require extra code to allow for the facts that the - * halted endpoint list may change as we do so, and that - * resetting an endpoint may fail. - */ - if ( ( ep = list_first_entry ( &bus->halted, struct usb_endpoint, - halted ) ) != NULL ) - usb_endpoint_reset ( ep ); - /* Handle any changed ports, allowing for the fact that the * port list may change as we perform hotplug actions. */ - while ( ! list_empty ( &bus->changed ) ) { + while ( ! list_empty ( &usb_changed ) ) { /* Get first changed port */ - port = list_first_entry ( &bus->changed, struct usb_port, + port = list_first_entry ( &usb_changed, struct usb_port, changed ); assert ( port != NULL ); @@ -1666,13 +1654,39 @@ static void usb_step ( struct usb_bus *bus ) { INIT_LIST_HEAD ( &port->changed ); /* Perform appropriate hotplug action */ - usb_hotplug ( port ); + usb_hotplugged ( port ); } } +/** + * USB process + * + * @v process USB process + */ +static void usb_step ( struct process *process __unused ) { + struct usb_bus *bus; + struct usb_endpoint *ep; + + /* Poll all buses */ + for_each_usb_bus ( bus ) + usb_poll ( bus ); + + /* Attempt to reset first halted endpoint in list, if any. We + * do not attempt to process the complete list, since this + * would require extra code to allow for the facts that the + * halted endpoint list may change as we do so, and that + * resetting an endpoint may fail. + */ + if ( ( ep = list_first_entry ( &usb_halted, struct usb_endpoint, + halted ) ) != NULL ) + usb_endpoint_reset ( ep ); + + /* Handle any changed ports */ + usb_hotplug(); +} + /** USB process */ -static struct process_descriptor usb_process_desc = - PROC_DESC ( struct usb_bus, process, usb_step ); +PERMANENT_PROCESS ( usb_process, usb_step ); /****************************************************************************** * @@ -1755,10 +1769,10 @@ int register_usb_hub ( struct usb_hub *hub ) { /* Delay to allow ports to stabilise */ mdelay ( USB_PORT_DELAY_MS ); - /* Attach any devices already present */ + /* Mark all ports as changed */ for ( i = 1 ; i <= hub->ports ; i++ ) { port = usb_port ( hub, i ); - usb_hotplug ( port ); + usb_port_changed ( port ); } /* Some hubs seem to defer reporting device connections until @@ -1766,7 +1780,10 @@ int register_usb_hub ( struct usb_hub *hub ) { * Poll the bus once now in order to pick up any such * connections. */ - usb_step ( bus ); + usb_poll ( bus ); + + /* Attach any devices already present */ + usb_hotplug(); return 0; @@ -1862,9 +1879,6 @@ struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports, bus->op = op; INIT_LIST_HEAD ( &bus->devices ); INIT_LIST_HEAD ( &bus->hubs ); - INIT_LIST_HEAD ( &bus->changed ); - INIT_LIST_HEAD ( &bus->halted ); - process_init_stopped ( &bus->process, &usb_process_desc, NULL ); bus->host = &bus->op->bus; /* Allocate root hub */ @@ -1904,9 +1918,6 @@ int register_usb_bus ( struct usb_bus *bus ) { if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 ) goto err_register_hub; - /* Start bus process */ - process_add ( &bus->process ); - return 0; unregister_usb_hub ( bus->hub ); @@ -1926,10 +1937,6 @@ void unregister_usb_bus ( struct usb_bus *bus ) { /* Sanity checks */ assert ( bus->hub != NULL ); - assert ( process_running ( &bus->process ) ); - - /* Stop bus process */ - process_del ( &bus->process ); /* Unregister root hub */ unregister_usb_hub ( bus->hub ); @@ -1943,7 +1950,6 @@ void unregister_usb_bus ( struct usb_bus *bus ) { /* Sanity checks */ assert ( list_empty ( &bus->devices ) ); assert ( list_empty ( &bus->hubs ) ); - assert ( ! process_running ( &bus->process ) ); } /** @@ -1952,11 +1958,16 @@ void unregister_usb_bus ( struct usb_bus *bus ) { * @v bus USB bus */ void free_usb_bus ( struct usb_bus *bus ) { + struct usb_endpoint *ep; + struct usb_port *port; /* Sanity checks */ assert ( list_empty ( &bus->devices ) ); assert ( list_empty ( &bus->hubs ) ); - assert ( ! process_running ( &bus->process ) ); + list_for_each_entry ( ep, &usb_halted, halted ) + assert ( ep->usb->port->hub->bus != bus ); + list_for_each_entry ( port, &usb_changed, changed ) + assert ( port->hub->bus != bus ); /* Free root hub */ free_usb_hub ( bus->hub ); |