mirror of https://github.com/ipxe/ipxe.git
[usb] Add basic support for USB hubs
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/34/head
parent
018b13dcec
commit
e17e771a13
|
@ -83,6 +83,7 @@ SRCDIRS += drivers/block
|
|||
SRCDIRS += drivers/nvs
|
||||
SRCDIRS += drivers/bitbash
|
||||
SRCDIRS += drivers/infiniband
|
||||
SRCDIRS += drivers/usb
|
||||
SRCDIRS += interface/pxe interface/efi interface/smbios
|
||||
SRCDIRS += interface/bofm
|
||||
SRCDIRS += interface/xen
|
||||
|
|
|
@ -1646,3 +1646,6 @@ struct usb_port * usb_root_hub_port ( struct usb_device *usb ) {
|
|||
|
||||
return usb->port;
|
||||
}
|
||||
|
||||
/* Drag in hub driver */
|
||||
REQUIRE_OBJECT ( usbhub );
|
||||
|
|
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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 (at your option) 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.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER );
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <byteswap.h>
|
||||
#include <ipxe/usb.h>
|
||||
#include "usbhub.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
* USB hub driver
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Refill interrupt ring
|
||||
*
|
||||
* @v hubdev Hub device
|
||||
*/
|
||||
static void hub_refill ( struct usb_hub_device *hubdev ) {
|
||||
struct io_buffer *iobuf;
|
||||
size_t mtu = hubdev->intr.mtu;
|
||||
int rc;
|
||||
|
||||
/* Enqueue any available I/O buffers */
|
||||
while ( ( iobuf = list_first_entry ( &hubdev->intrs, struct io_buffer,
|
||||
list ) ) ) {
|
||||
|
||||
/* Reset size */
|
||||
iob_put ( iobuf, ( mtu - iob_len ( iobuf ) ) );
|
||||
|
||||
/* Enqueue I/O buffer */
|
||||
if ( ( rc = usb_stream ( &hubdev->intr, iobuf ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not enqueue interrupt: "
|
||||
"%s\n", hubdev->name, strerror ( rc ) );
|
||||
/* Leave in available list and wait for next refill */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove from available list */
|
||||
list_del ( &iobuf->list );
|
||||
}
|
||||
|
||||
/* Stop refill process */
|
||||
process_del ( &hubdev->refill );
|
||||
}
|
||||
|
||||
/** Refill process descriptor */
|
||||
static struct process_descriptor hub_refill_desc =
|
||||
PROC_DESC ( struct usb_hub_device, refill, hub_refill );
|
||||
|
||||
/**
|
||||
* Complete interrupt transfer
|
||||
*
|
||||
* @v ep USB endpoint
|
||||
* @v iobuf I/O buffer
|
||||
* @v rc Completion status code
|
||||
*/
|
||||
static void hub_complete ( struct usb_endpoint *ep,
|
||||
struct io_buffer *iobuf, int rc ) {
|
||||
struct usb_hub_device *hubdev =
|
||||
container_of ( ep, struct usb_hub_device, intr );
|
||||
struct usb_hub *hub = hubdev->hub;
|
||||
uint8_t *data = iobuf->data;
|
||||
unsigned int bits = ( 8 * iob_len ( iobuf ) );
|
||||
unsigned int i;
|
||||
|
||||
/* Ignore packets cancelled when the endpoint closes */
|
||||
if ( ! ep->open )
|
||||
goto done;
|
||||
|
||||
/* Ignore packets with errors */
|
||||
if ( rc != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s interrupt failed: %s\n",
|
||||
hubdev->name, strerror ( rc ) );
|
||||
DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Report any port status changes */
|
||||
for ( i = 1 ; i <= hub->ports ; i++ ) {
|
||||
|
||||
/* Sanity check */
|
||||
if ( i > bits ) {
|
||||
DBGC ( hubdev, "HUB %s underlength interrupt:\n",
|
||||
hubdev->name );
|
||||
DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Report port status change if applicable */
|
||||
if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
|
||||
DBGC2 ( hubdev, "HUB %s port %d status changed\n",
|
||||
hubdev->name, i );
|
||||
usb_port_changed ( usb_port ( hub, i ) );
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
/* Return I/O buffer to available list */
|
||||
list_add_tail ( &iobuf->list, &hubdev->intrs );
|
||||
|
||||
/* Start refill process */
|
||||
process_add ( &hubdev->refill );
|
||||
}
|
||||
|
||||
/** Interrupt endpoint operations */
|
||||
static struct usb_endpoint_driver_operations usb_hub_intr_operations = {
|
||||
.complete = hub_complete,
|
||||
};
|
||||
|
||||
/**
|
||||
* Open hub
|
||||
*
|
||||
* @v hub USB hub
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_open ( struct usb_hub *hub ) {
|
||||
struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
struct io_buffer *iobuf;
|
||||
struct io_buffer *tmp;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Ensure ports are powered */
|
||||
for ( i = 1 ; i <= hub->ports ; i++ ) {
|
||||
if ( ( rc = usb_hub_set_port_feature ( usb, i,
|
||||
USB_HUB_PORT_POWER,
|
||||
0 ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s port %d could not apply power: "
|
||||
"%s\n", hubdev->name, i, strerror ( rc ) );
|
||||
goto err_power;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate I/O buffers */
|
||||
for ( i = 0 ; i < USB_HUB_INTR_FILL ; i++ ) {
|
||||
iobuf = alloc_iob ( hubdev->intr.mtu );
|
||||
if ( ! iobuf ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc_iob;
|
||||
}
|
||||
list_add ( &iobuf->list, &hubdev->intrs );
|
||||
}
|
||||
|
||||
/* Open interrupt endpoint */
|
||||
if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not register interrupt: %s\n",
|
||||
hubdev->name, strerror ( rc ) );
|
||||
goto err_open;
|
||||
}
|
||||
|
||||
/* Start refill process */
|
||||
process_add ( &hubdev->refill );
|
||||
|
||||
/* Refill interrupt ring */
|
||||
hub_refill ( hubdev );
|
||||
|
||||
return 0;
|
||||
|
||||
usb_endpoint_close ( &hubdev->intr );
|
||||
err_open:
|
||||
err_alloc_iob:
|
||||
list_for_each_entry_safe ( iobuf, tmp, &hubdev->intrs, list ) {
|
||||
list_del ( &iobuf->list );
|
||||
free_iob ( iobuf );
|
||||
}
|
||||
err_power:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close hub
|
||||
*
|
||||
* @v hub USB hub
|
||||
*/
|
||||
static void hub_close ( struct usb_hub *hub ) {
|
||||
struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
|
||||
struct io_buffer *iobuf;
|
||||
struct io_buffer *tmp;
|
||||
|
||||
/* Close interrupt endpoint */
|
||||
usb_endpoint_close ( &hubdev->intr );
|
||||
|
||||
/* Stop refill process */
|
||||
process_del ( &hubdev->refill );
|
||||
|
||||
/* Free I/O buffers */
|
||||
list_for_each_entry_safe ( iobuf, tmp, &hubdev->intrs, list ) {
|
||||
list_del ( &iobuf->list );
|
||||
free_iob ( iobuf );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable port
|
||||
*
|
||||
* @v hub USB hub
|
||||
* @v port USB port
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
|
||||
struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
struct usb_hub_port_status status;
|
||||
unsigned int current;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Initiate reset if applicable */
|
||||
if ( ( hub->protocol < USB_PROTO_3_0 ) &&
|
||||
( ( rc = usb_hub_set_port_feature ( usb, port->address,
|
||||
USB_HUB_PORT_RESET, 0 ) )!=0)){
|
||||
DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n",
|
||||
hubdev->name, port->address, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for port to become enabled */
|
||||
for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) {
|
||||
|
||||
/* Check for port being enabled */
|
||||
if ( ( rc = usb_hub_get_port_status ( usb, port->address,
|
||||
&status ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s port %d could not get status: "
|
||||
"%s\n", hubdev->name, port->address,
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
current = le16_to_cpu ( status.current );
|
||||
if ( current & ( 1 << USB_HUB_PORT_ENABLE ) )
|
||||
return 0;
|
||||
|
||||
/* Delay */
|
||||
mdelay ( 1 );
|
||||
}
|
||||
|
||||
DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n",
|
||||
hubdev->name, port->address );
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable port
|
||||
*
|
||||
* @v hub USB hub
|
||||
* @v port USB port
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
|
||||
struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
int rc;
|
||||
|
||||
/* Disable port */
|
||||
if ( ( rc = usb_hub_clear_port_feature ( usb, port->address,
|
||||
USB_HUB_PORT_ENABLE, 0 ) )!=0){
|
||||
DBGC ( hubdev, "HUB %s port %d could not disable: %s\n",
|
||||
hubdev->name, port->address, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear port status change bits
|
||||
*
|
||||
* @v hubdev USB hub device
|
||||
* @v port Port number
|
||||
* @v changed Port status change bits
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_clear_changes ( struct usb_hub_device *hubdev,
|
||||
unsigned int port, uint16_t changed ) {
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
unsigned int bit;
|
||||
unsigned int feature;
|
||||
int rc;
|
||||
|
||||
/* Clear each set bit */
|
||||
for ( bit = 0 ; bit < 16 ; bit++ ) {
|
||||
|
||||
/* Skip unset bits */
|
||||
if ( ! ( changed & ( 1 << bit ) ) )
|
||||
continue;
|
||||
|
||||
/* Skip unused features */
|
||||
feature = USB_HUB_C_FEATURE ( bit );
|
||||
if ( ! ( hubdev->features & ( 1 << feature ) ) )
|
||||
continue;
|
||||
|
||||
/* Clear bit */
|
||||
if ( ( rc = usb_hub_clear_port_feature ( usb, port,
|
||||
feature, 0 ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s port %d could not clear feature "
|
||||
"%d: %s\n", hubdev->name, port, feature,
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update port speed
|
||||
*
|
||||
* @v hub USB hub
|
||||
* @v port USB port
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
|
||||
struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
struct usb_hub_port_status status;
|
||||
unsigned int current;
|
||||
unsigned int changed;
|
||||
int rc;
|
||||
|
||||
/* Get port status */
|
||||
if ( ( rc = usb_hub_get_port_status ( usb, port->address,
|
||||
&status ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s port %d could not get status: %s\n",
|
||||
hubdev->name, port->address, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
current = le16_to_cpu ( status.current );
|
||||
changed = le16_to_cpu ( status.changed );
|
||||
DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n",
|
||||
hubdev->name, port->address, changed, current );
|
||||
|
||||
/* Update port speed */
|
||||
if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) {
|
||||
if ( hub->protocol >= USB_PROTO_3_0 ) {
|
||||
port->speed = USB_SPEED_SUPER;
|
||||
} else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) {
|
||||
port->speed = USB_SPEED_LOW;
|
||||
} else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) {
|
||||
port->speed = USB_SPEED_HIGH;
|
||||
} else {
|
||||
port->speed = USB_SPEED_FULL;
|
||||
}
|
||||
} else {
|
||||
port->speed = USB_SPEED_NONE;
|
||||
}
|
||||
|
||||
/* Clear port status change bits */
|
||||
if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** USB hub operations */
|
||||
static struct usb_hub_driver_operations hub_operations = {
|
||||
.open = hub_open,
|
||||
.close = hub_close,
|
||||
.enable = hub_enable,
|
||||
.disable = hub_disable,
|
||||
.speed = hub_speed,
|
||||
};
|
||||
|
||||
/**
|
||||
* Probe USB hub
|
||||
*
|
||||
* @v func USB function
|
||||
* @v config Configuration descriptor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int hub_probe ( struct usb_function *func,
|
||||
struct usb_configuration_descriptor *config ) {
|
||||
struct usb_device *usb = func->usb;
|
||||
struct usb_bus *bus = usb->port->hub->bus;
|
||||
struct usb_hub_device *hubdev;
|
||||
struct usb_interface_descriptor *interface;
|
||||
union usb_hub_descriptor desc;
|
||||
unsigned int depth;
|
||||
unsigned int ports;
|
||||
int enhanced;
|
||||
int rc;
|
||||
|
||||
/* Allocate and initialise structure */
|
||||
hubdev = zalloc ( sizeof ( *hubdev ) );
|
||||
if ( ! hubdev ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
enhanced = ( usb->port->protocol >= USB_PROTO_3_0 );
|
||||
hubdev->name = func->name;
|
||||
hubdev->usb = usb;
|
||||
hubdev->features =
|
||||
( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES );
|
||||
usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations );
|
||||
INIT_LIST_HEAD ( &hubdev->intrs );
|
||||
process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL );
|
||||
|
||||
/* Locate hub interface descriptor */
|
||||
interface = usb_interface_descriptor ( config, func->interface[0], 0 );
|
||||
if ( ! interface ) {
|
||||
DBGC ( hubdev, "HUB %s has no interface descriptor\n",
|
||||
hubdev->name );
|
||||
rc = -EINVAL;
|
||||
goto err_interface;
|
||||
}
|
||||
|
||||
/* Locate interrupt endpoint descriptor */
|
||||
if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface,
|
||||
USB_INTERRUPT, 0 ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: "
|
||||
"%s\n", hubdev->name, strerror ( rc ) );
|
||||
goto err_endpoint;
|
||||
}
|
||||
|
||||
/* Set hub depth */
|
||||
depth = usb_depth ( usb );
|
||||
if ( enhanced ) {
|
||||
if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not set hub depth to %d: "
|
||||
"%s\n", hubdev->name, depth, strerror ( rc ) );
|
||||
goto err_set_hub_depth;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get hub descriptor */
|
||||
if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n",
|
||||
hubdev->name, strerror ( rc ) );
|
||||
goto err_hub_descriptor;
|
||||
}
|
||||
ports = desc.basic.ports;
|
||||
DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name,
|
||||
ports, depth, ( enhanced ? " (enhanced)" : "" ) );
|
||||
|
||||
/* Allocate hub */
|
||||
hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations );
|
||||
if ( ! hubdev->hub ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc_hub;
|
||||
}
|
||||
usb_hub_set_drvdata ( hubdev->hub, hubdev );
|
||||
|
||||
/* Register hub */
|
||||
if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) {
|
||||
DBGC ( hubdev, "HUB %s could not register: %s\n",
|
||||
hubdev->name, strerror ( rc ) );
|
||||
goto err_register_hub;
|
||||
}
|
||||
|
||||
usb_func_set_drvdata ( func, hubdev );
|
||||
return 0;
|
||||
|
||||
unregister_usb_hub ( hubdev->hub );
|
||||
err_register_hub:
|
||||
free_usb_hub ( hubdev->hub );
|
||||
err_alloc_hub:
|
||||
err_hub_descriptor:
|
||||
err_set_hub_depth:
|
||||
err_endpoint:
|
||||
err_interface:
|
||||
free ( hubdev );
|
||||
err_alloc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove USB hub
|
||||
*
|
||||
* @v func USB function
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static void hub_remove ( struct usb_function *func ) {
|
||||
struct usb_hub_device *hubdev = usb_func_get_drvdata ( func );
|
||||
struct usb_hub *hub = hubdev->hub;
|
||||
struct usb_device *usb = hubdev->usb;
|
||||
struct usb_port *port;
|
||||
unsigned int i;
|
||||
|
||||
/* If hub has been unplugged, mark all ports as unplugged */
|
||||
if ( usb->port->speed == USB_SPEED_NONE ) {
|
||||
for ( i = 1 ; i <= hub->ports ; i++ ) {
|
||||
port = usb_port ( hub, i );
|
||||
port->speed = USB_SPEED_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unregister hub */
|
||||
unregister_usb_hub ( hubdev->hub );
|
||||
assert ( ! process_running ( &hubdev->refill ) );
|
||||
assert ( list_empty ( &hubdev->intrs ) );
|
||||
|
||||
/* Free hub */
|
||||
free_usb_hub ( hubdev->hub );
|
||||
|
||||
/* Free hub device */
|
||||
free ( hubdev );
|
||||
}
|
||||
|
||||
/** USB hub device IDs */
|
||||
static struct usb_device_id hub_ids[] = {
|
||||
{
|
||||
.name = "hub-1",
|
||||
.vendor = USB_ANY_ID,
|
||||
.product = USB_ANY_ID,
|
||||
.class = {
|
||||
.class = USB_CLASS_HUB,
|
||||
.subclass = 0,
|
||||
.protocol = 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "hub-2",
|
||||
.vendor = USB_ANY_ID,
|
||||
.product = USB_ANY_ID,
|
||||
.class = {
|
||||
.class = USB_CLASS_HUB,
|
||||
.subclass = 0,
|
||||
.protocol = 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/** USB hub driver */
|
||||
struct usb_driver usb_hub_driver __usb_driver = {
|
||||
.ids = hub_ids,
|
||||
.id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ),
|
||||
.probe = hub_probe,
|
||||
.remove = hub_remove,
|
||||
};
|
|
@ -0,0 +1,248 @@
|
|||
#ifndef _USBHUB_H
|
||||
#define _USBHUB_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* USB hubs
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER );
|
||||
|
||||
#include <ipxe/usb.h>
|
||||
#include <ipxe/list.h>
|
||||
#include <ipxe/process.h>
|
||||
|
||||
/** Request recipient is a port */
|
||||
#define USB_HUB_RECIP_PORT ( 3 << 0 )
|
||||
|
||||
/** A basic USB hub descriptor */
|
||||
struct usb_hub_descriptor_basic {
|
||||
/** Descriptor header */
|
||||
struct usb_descriptor_header header;
|
||||
/** Number of ports */
|
||||
uint8_t ports;
|
||||
/** Characteristics */
|
||||
uint16_t characteristics;
|
||||
/** Power-on delay (in 2ms intervals */
|
||||
uint8_t delay;
|
||||
/** Controller current (in mA) */
|
||||
uint8_t current;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** A basic USB hub descriptor */
|
||||
#define USB_HUB_DESCRIPTOR 41
|
||||
|
||||
/** An enhanced USB hub descriptor */
|
||||
struct usb_hub_descriptor_enhanced {
|
||||
/** Basic USB hub descriptor */
|
||||
struct usb_hub_descriptor_basic basic;
|
||||
/** Header decode latency */
|
||||
uint8_t latency;
|
||||
/** Maximum delay */
|
||||
uint16_t delay;
|
||||
/** Removable device bitmask */
|
||||
uint16_t removable;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** An enhanced USB hub descriptor */
|
||||
#define USB_HUB_DESCRIPTOR_ENHANCED 42
|
||||
|
||||
/** A USB hub descriptor */
|
||||
union usb_hub_descriptor {
|
||||
/** Descriptor header */
|
||||
struct usb_descriptor_header header;
|
||||
/** Basic hub descriptor */
|
||||
struct usb_hub_descriptor_basic basic;
|
||||
/** Enhanced hub descriptor */
|
||||
struct usb_hub_descriptor_enhanced enhanced;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Port status */
|
||||
struct usb_hub_port_status {
|
||||
/** Current status */
|
||||
uint16_t current;
|
||||
/** Changed status */
|
||||
uint16_t changed;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Current connect status feature */
|
||||
#define USB_HUB_PORT_CONNECTION 0
|
||||
|
||||
/** Port enabled/disabled feature */
|
||||
#define USB_HUB_PORT_ENABLE 1
|
||||
|
||||
/** Port reset feature */
|
||||
#define USB_HUB_PORT_RESET 4
|
||||
|
||||
/** Port power feature */
|
||||
#define USB_HUB_PORT_POWER 8
|
||||
|
||||
/** Low-speed device attached */
|
||||
#define USB_HUB_PORT_LOW_SPEED 9
|
||||
|
||||
/** High-speed device attached */
|
||||
#define USB_HUB_PORT_HIGH_SPEED 10
|
||||
|
||||
/** Connect status changed */
|
||||
#define USB_HUB_C_PORT_CONNECTION 16
|
||||
|
||||
/** Port enable/disable changed */
|
||||
#define USB_HUB_C_PORT_ENABLE 17
|
||||
|
||||
/** Suspend changed */
|
||||
#define USB_HUB_C_PORT_SUSPEND 18
|
||||
|
||||
/** Over-current indicator changed */
|
||||
#define USB_HUB_C_PORT_OVER_CURRENT 19
|
||||
|
||||
/** Reset changed */
|
||||
#define USB_HUB_C_PORT_RESET 20
|
||||
|
||||
/** Link state changed */
|
||||
#define USB_HUB_C_PORT_LINK_STATE 25
|
||||
|
||||
/** Configuration error */
|
||||
#define USB_HUB_C_PORT_CONFIG_ERROR 26
|
||||
|
||||
/** Calculate feature from change bit number */
|
||||
#define USB_HUB_C_FEATURE( bit ) ( 16 + (bit) )
|
||||
|
||||
/** USB features */
|
||||
#define USB_HUB_FEATURES \
|
||||
( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \
|
||||
( 1 << USB_HUB_C_PORT_ENABLE ) | \
|
||||
( 1 << USB_HUB_C_PORT_SUSPEND ) | \
|
||||
( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \
|
||||
( 1 << USB_HUB_C_PORT_RESET ) )
|
||||
|
||||
/** USB features for enhanced hubs */
|
||||
#define USB_HUB_FEATURES_ENHANCED \
|
||||
( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \
|
||||
( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \
|
||||
( 1 << USB_HUB_C_PORT_RESET ) | \
|
||||
( 1 << USB_HUB_C_PORT_LINK_STATE ) | \
|
||||
( 1 << USB_HUB_C_PORT_CONFIG_ERROR ) )
|
||||
|
||||
/** Set hub depth */
|
||||
#define USB_HUB_SET_HUB_DEPTH \
|
||||
( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE | \
|
||||
USB_REQUEST_TYPE ( 12 ) )
|
||||
|
||||
/**
|
||||
* Get hub descriptor
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v enhanced Hub is an enhanced hub
|
||||
* @v data Hub descriptor to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
usb_hub_get_descriptor ( struct usb_device *usb, int enhanced,
|
||||
union usb_hub_descriptor *data ) {
|
||||
unsigned int desc;
|
||||
size_t len;
|
||||
|
||||
/* Determine descriptor type and length */
|
||||
desc = ( enhanced ? USB_HUB_DESCRIPTOR_ENHANCED : USB_HUB_DESCRIPTOR );
|
||||
len = ( enhanced ? sizeof ( data->enhanced ) : sizeof ( data->basic ) );
|
||||
|
||||
return usb_get_descriptor ( usb, USB_TYPE_CLASS, desc, 0, 0,
|
||||
&data->header, len );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get port status
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v port Port address
|
||||
* @v status Port status descriptor to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
usb_hub_get_port_status ( struct usb_device *usb, unsigned int port,
|
||||
struct usb_hub_port_status *status ) {
|
||||
|
||||
return usb_get_status ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
|
||||
port, status, sizeof ( *status ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear port feature
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v port Port address
|
||||
* @v feature Feature to clear
|
||||
* @v index Index (when clearing a port indicator)
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
usb_hub_clear_port_feature ( struct usb_device *usb, unsigned int port,
|
||||
unsigned int feature, unsigned int index ) {
|
||||
|
||||
return usb_clear_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
|
||||
feature, ( ( index << 8 ) | port ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set port feature
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v port Port address
|
||||
* @v feature Feature to clear
|
||||
* @v index Index (when clearing a port indicator)
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
usb_hub_set_port_feature ( struct usb_device *usb, unsigned int port,
|
||||
unsigned int feature, unsigned int index ) {
|
||||
|
||||
return usb_set_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
|
||||
feature, ( ( index << 8 ) | port ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hub depth
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v depth Hub depth
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
usb_hub_set_hub_depth ( struct usb_device *usb, unsigned int depth ) {
|
||||
|
||||
return usb_control ( usb, USB_HUB_SET_HUB_DEPTH, depth, 0, NULL, 0 );
|
||||
}
|
||||
|
||||
/** A USB hub device */
|
||||
struct usb_hub_device {
|
||||
/** Name */
|
||||
const char *name;
|
||||
/** USB device */
|
||||
struct usb_device *usb;
|
||||
/** USB hub */
|
||||
struct usb_hub *hub;
|
||||
/** Features */
|
||||
unsigned int features;
|
||||
|
||||
/** Interrupt endpoint */
|
||||
struct usb_endpoint intr;
|
||||
/** Recycled interrupt I/O buffers */
|
||||
struct list_head intrs;
|
||||
/** Interrupt endpoint refill process */
|
||||
struct process refill;
|
||||
};
|
||||
|
||||
/** Interrupt ring fill level
|
||||
*
|
||||
* This is a policy decision.
|
||||
*/
|
||||
#define USB_HUB_INTR_FILL 4
|
||||
|
||||
/** Maximum time to wait for port to become enabled
|
||||
*
|
||||
* This is a policy decision.
|
||||
*/
|
||||
#define USB_HUB_ENABLE_MAX_WAIT_MS 100
|
||||
|
||||
#endif /* _USBHUB_H */
|
|
@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||
#define ERRFILE_linux ( ERRFILE_DRIVER | 0x00050000 )
|
||||
#define ERRFILE_pcivpd ( ERRFILE_DRIVER | 0x00060000 )
|
||||
#define ERRFILE_usb ( ERRFILE_DRIVER | 0x00070000 )
|
||||
#define ERRFILE_usbhub ( ERRFILE_DRIVER | 0x00080000 )
|
||||
|
||||
#define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 )
|
||||
#define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 )
|
||||
|
|
Loading…
Reference in New Issue