david/ipxe
Archived
1
0

[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>
This commit is contained in:
Michael Brown 2015-05-08 18:02:50 +01:00
parent 5e1e2069fd
commit e4783add79
2 changed files with 55 additions and 50 deletions

View File

@ -43,6 +43,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** List of USB buses */ /** List of USB buses */
struct list_head usb_buses = LIST_HEAD_INIT ( 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 * 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, void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
int rc ) { int rc ) {
struct usb_device *usb = ep->usb; struct usb_device *usb = ep->usb;
struct usb_bus *bus = usb->port->hub->bus;
/* Decrement fill level */ /* Decrement fill level */
assert ( ep->fill > 0 ); 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 ), usb->name, usb_endpoint_name ( ep->address ),
strerror ( rc ) ); strerror ( rc ) );
list_del ( &ep->halted ); list_del ( &ep->halted );
list_add_tail ( &ep->halted, &bus->halted ); list_add_tail ( &ep->halted, &usb_halted );
} }
/* Report completion */ /* 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 * @v port USB port
* @ret rc Return status code * @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; struct usb_hub *hub = port->hub;
int rc; int rc;
@ -1621,43 +1626,26 @@ static int usb_hotplug ( struct usb_port *port ) {
* @v port USB port * @v port USB port
*/ */
void usb_port_changed ( struct usb_port *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 */ /* Record hub port status change */
list_del ( &port->changed ); 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 ) { static void usb_hotplug ( void ) {
struct usb_endpoint *ep;
struct usb_port *port; 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 /* Handle any changed ports, allowing for the fact that the
* port list may change as we perform hotplug actions. * port list may change as we perform hotplug actions.
*/ */
while ( ! list_empty ( &bus->changed ) ) { while ( ! list_empty ( &usb_changed ) ) {
/* Get first changed port */ /* Get first changed port */
port = list_first_entry ( &bus->changed, struct usb_port, port = list_first_entry ( &usb_changed, struct usb_port,
changed ); changed );
assert ( port != NULL ); assert ( port != NULL );
@ -1666,13 +1654,39 @@ static void usb_step ( struct usb_bus *bus ) {
INIT_LIST_HEAD ( &port->changed ); INIT_LIST_HEAD ( &port->changed );
/* Perform appropriate hotplug action */ /* 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 */ /** USB process */
static struct process_descriptor usb_process_desc = PERMANENT_PROCESS ( usb_process, usb_step );
PROC_DESC ( struct usb_bus, process, usb_step );
/****************************************************************************** /******************************************************************************
* *
@ -1755,10 +1769,10 @@ int register_usb_hub ( struct usb_hub *hub ) {
/* Delay to allow ports to stabilise */ /* Delay to allow ports to stabilise */
mdelay ( USB_PORT_DELAY_MS ); mdelay ( USB_PORT_DELAY_MS );
/* Attach any devices already present */ /* Mark all ports as changed */
for ( i = 1 ; i <= hub->ports ; i++ ) { for ( i = 1 ; i <= hub->ports ; i++ ) {
port = usb_port ( hub, i ); port = usb_port ( hub, i );
usb_hotplug ( port ); usb_port_changed ( port );
} }
/* Some hubs seem to defer reporting device connections until /* 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 * Poll the bus once now in order to pick up any such
* connections. * connections.
*/ */
usb_step ( bus ); usb_poll ( bus );
/* Attach any devices already present */
usb_hotplug();
return 0; return 0;
@ -1862,9 +1879,6 @@ struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports,
bus->op = op; bus->op = op;
INIT_LIST_HEAD ( &bus->devices ); INIT_LIST_HEAD ( &bus->devices );
INIT_LIST_HEAD ( &bus->hubs ); 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; bus->host = &bus->op->bus;
/* Allocate root hub */ /* Allocate root hub */
@ -1904,9 +1918,6 @@ int register_usb_bus ( struct usb_bus *bus ) {
if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 ) if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 )
goto err_register_hub; goto err_register_hub;
/* Start bus process */
process_add ( &bus->process );
return 0; return 0;
unregister_usb_hub ( bus->hub ); unregister_usb_hub ( bus->hub );
@ -1926,10 +1937,6 @@ void unregister_usb_bus ( struct usb_bus *bus ) {
/* Sanity checks */ /* Sanity checks */
assert ( bus->hub != NULL ); assert ( bus->hub != NULL );
assert ( process_running ( &bus->process ) );
/* Stop bus process */
process_del ( &bus->process );
/* Unregister root hub */ /* Unregister root hub */
unregister_usb_hub ( bus->hub ); unregister_usb_hub ( bus->hub );
@ -1943,7 +1950,6 @@ void unregister_usb_bus ( struct usb_bus *bus ) {
/* Sanity checks */ /* Sanity checks */
assert ( list_empty ( &bus->devices ) ); assert ( list_empty ( &bus->devices ) );
assert ( list_empty ( &bus->hubs ) ); 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 * @v bus USB bus
*/ */
void free_usb_bus ( struct usb_bus *bus ) { void free_usb_bus ( struct usb_bus *bus ) {
struct usb_endpoint *ep;
struct usb_port *port;
/* Sanity checks */ /* Sanity checks */
assert ( list_empty ( &bus->devices ) ); assert ( list_empty ( &bus->devices ) );
assert ( list_empty ( &bus->hubs ) ); 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 root hub */
free_usb_hub ( bus->hub ); free_usb_hub ( bus->hub );

View File

@ -922,12 +922,6 @@ struct usb_bus {
struct list_head devices; struct list_head devices;
/** List of hubs */ /** List of hubs */
struct list_head hubs; struct list_head hubs;
/** List of changed ports */
struct list_head changed;
/** List of halted endpoints */
struct list_head halted;
/** Process */
struct process process;
/** Host controller operations */ /** Host controller operations */
struct usb_bus_host_operations *host; struct usb_bus_host_operations *host;