aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/bus/usb.c29
-rw-r--r--src/drivers/usb/ehci.c25
-rw-r--r--src/drivers/usb/ehci.h20
-rw-r--r--src/drivers/usb/xhci.c12
-rw-r--r--src/include/ipxe/usb.h4
5 files changed, 41 insertions, 49 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c
index f53f7461..2191867a 100644
--- a/src/drivers/bus/usb.c
+++ b/src/drivers/bus/usb.c
@@ -460,16 +460,22 @@ static int usb_endpoint_mtu ( struct usb_endpoint *ep, size_t mtu ) {
* @v index Index parameter
* @v iobuf I/O buffer
* @ret rc Return status code
+ *
+ * The I/O buffer must have sufficient headroom to contain a setup
+ * packet.
*/
int usb_message ( struct usb_endpoint *ep, unsigned int request,
unsigned int value, unsigned int index,
struct io_buffer *iobuf ) {
struct usb_device *usb = ep->usb;
struct usb_port *port = usb->port;
- struct usb_setup_packet packet;
+ struct usb_setup_packet *packet;
size_t len = iob_len ( iobuf );
int rc;
+ /* Sanity check */
+ assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) );
+
/* Fail immediately if device has been unplugged */
if ( port->speed == USB_SPEED_NONE )
return -ENODEV;
@@ -484,13 +490,14 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request,
memset ( iobuf->data, 0, len );
/* Construct setup packet */
- packet.request = cpu_to_le16 ( request );
- packet.value = cpu_to_le16 ( value );
- packet.index = cpu_to_le16 ( index );
- packet.len = cpu_to_le16 ( len );
+ packet = iob_push ( iobuf, sizeof ( *packet ) );
+ packet->request = cpu_to_le16 ( request );
+ packet->value = cpu_to_le16 ( value );
+ packet->index = cpu_to_le16 ( index );
+ packet->len = cpu_to_le16 ( len );
/* Enqueue message transfer */
- if ( ( rc = ep->host->message ( ep, &packet, iobuf ) ) != 0 ) {
+ if ( ( rc = ep->host->message ( ep, iobuf ) ) != 0 ) {
DBGC ( usb, "USB %s %s could not enqueue message transfer: "
"%s\n", usb->name, usb_endpoint_name ( ep->address ),
strerror ( rc ) );
@@ -734,19 +741,23 @@ int usb_control ( struct usb_device *usb, unsigned int request,
size_t len ) {
struct usb_bus *bus = usb->port->hub->bus;
struct usb_endpoint *ep = &usb->control;
- struct usb_control_pseudo_header *pshdr;
struct io_buffer *iobuf;
struct io_buffer *cmplt;
+ union {
+ struct usb_setup_packet setup;
+ struct usb_control_pseudo_header pshdr;
+ } *headroom;
+ struct usb_control_pseudo_header *pshdr;
unsigned int i;
int rc;
/* Allocate I/O buffer */
- iobuf = alloc_iob ( sizeof ( *pshdr ) + len );
+ iobuf = alloc_iob ( sizeof ( *headroom ) + len );
if ( ! iobuf ) {
rc = -ENOMEM;
goto err_alloc;
}
- iob_reserve ( iobuf, sizeof ( *pshdr ) );
+ iob_reserve ( iobuf, sizeof ( *headroom ) );
iob_put ( iobuf, len );
if ( request & USB_DIR_IN ) {
memset ( data, 0, len );
diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c
index 4bff3fef..e33b0e19 100644
--- a/src/drivers/usb/ehci.c
+++ b/src/drivers/usb/ehci.c
@@ -528,8 +528,6 @@ static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring,
/* Fail if any portion is unreachable */
for ( i = 0 ; i < count ; i++ ) {
- if ( xfer->flags & EHCI_FL_IMMEDIATE )
- continue;
phys = ( virt_to_phys ( xfer[i].data ) + xfer[i].len - 1 );
if ( ( phys > 0xffffffffUL ) && ( ! ehci->addr64 ) )
return -ENOTSUP;
@@ -547,16 +545,9 @@ static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring,
desc->len = cpu_to_le16 ( xfer->len | toggle );
desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX );
- /* Copy data to immediate data buffer (if requested) */
+ /* Populate buffer pointers */
data = xfer->data;
len = xfer->len;
- if ( xfer->flags & EHCI_FL_IMMEDIATE ) {
- assert ( len <= sizeof ( desc->immediate ) );
- memcpy ( desc->immediate, data, len );
- data = desc->immediate;
- }
-
- /* Populate buffer pointers */
for ( i = 0 ; len ; i++ ) {
/* Calculate length of this fragment */
@@ -1103,28 +1094,32 @@ static int ehci_endpoint_mtu ( struct usb_endpoint *ep ) {
* Enqueue message transfer
*
* @v ep USB endpoint
- * @v packet Setup packet
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int ehci_endpoint_message ( struct usb_endpoint *ep,
- struct usb_setup_packet *packet,
struct io_buffer *iobuf ) {
struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
struct ehci_device *ehci = endpoint->ehci;
- unsigned int input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) );
+ struct usb_setup_packet *packet;
+ unsigned int input;
struct ehci_transfer xfers[3];
struct ehci_transfer *xfer = xfers;
- size_t len = iob_len ( iobuf );
+ size_t len;
int rc;
/* Construct setup stage */
+ assert ( iob_len ( iobuf ) >= sizeof ( *packet ) );
+ packet = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *packet ) );
xfer->data = packet;
xfer->len = sizeof ( *packet );
- xfer->flags = ( EHCI_FL_IMMEDIATE | EHCI_FL_PID_SETUP );
+ xfer->flags = EHCI_FL_PID_SETUP;
xfer++;
/* Construct data stage, if applicable */
+ len = iob_len ( iobuf );
+ input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) );
if ( len ) {
xfer->data = iobuf->data;
xfer->len = len;
diff --git a/src/drivers/usb/ehci.h b/src/drivers/usb/ehci.h
index d575727d..7ad1e649 100644
--- a/src/drivers/usb/ehci.h
+++ b/src/drivers/usb/ehci.h
@@ -241,21 +241,8 @@ struct ehci_transfer_descriptor {
uint32_t low[5];
/** Extended buffer pointers (high 32 bits) */
uint32_t high[5];
-
- /** Immediate data buffer
- *
- * This is not part of the hardware data structure. Transfer
- * descriptors must be aligned to a 32-byte boundary. Create
- * an array of descriptors therefore requires 12 bytes of
- * padding at the end of each descriptor.
- *
- * We can use this padding as an immediate data buffer (for
- * setup packets). This avoids the need for separate
- * allocations. As a bonus, there is no need to check this
- * buffer for reachability, since it is contained within a
- * transfer descriptor which must already be reachable.
- */
- uint8_t immediate[12];
+ /** Reserved */
+ uint8_t reserved[12];
} __attribute__ (( packed ));
/** Transaction error */
@@ -483,9 +470,6 @@ struct ehci_transfer {
unsigned int flags;
};
-/** Copy data to immediate data buffer */
-#define EHCI_FL_IMMEDIATE 0x0100
-
/** Set initial data toggle */
#define EHCI_FL_TOGGLE 0x8000
diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c
index 523a1c0b..831e8e6e 100644
--- a/src/drivers/usb/xhci.c
+++ b/src/drivers/usb/xhci.c
@@ -2472,16 +2472,15 @@ static int xhci_endpoint_mtu ( struct usb_endpoint *ep ) {
* Enqueue message transfer
*
* @v ep USB endpoint
- * @v packet Setup packet
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int xhci_endpoint_message ( struct usb_endpoint *ep,
- struct usb_setup_packet *packet,
struct io_buffer *iobuf ) {
struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- unsigned int input = ( le16_to_cpu ( packet->request ) & USB_DIR_IN );
- size_t len = iob_len ( iobuf );
+ struct usb_setup_packet *packet;
+ unsigned int input;
+ size_t len;
union xhci_trb trbs[ 1 /* setup */ + 1 /* possible data */ +
1 /* status */ ];
union xhci_trb *trb = trbs;
@@ -2495,11 +2494,16 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
/* Construct setup stage TRB */
memset ( trbs, 0, sizeof ( trbs ) );
+ assert ( iob_len ( iobuf ) >= sizeof ( *packet ) );
+ packet = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *packet ) );
setup = &(trb++)->setup;
memcpy ( &setup->packet, packet, sizeof ( setup->packet ) );
setup->len = cpu_to_le32 ( sizeof ( *packet ) );
setup->flags = XHCI_TRB_IDT;
setup->type = XHCI_TRB_SETUP;
+ len = iob_len ( iobuf );
+ input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) );
if ( len )
setup->direction = ( input ? XHCI_SETUP_IN : XHCI_SETUP_OUT );
diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h
index b3803cd1..991a6f44 100644
--- a/src/include/ipxe/usb.h
+++ b/src/include/ipxe/usb.h
@@ -433,12 +433,10 @@ struct usb_endpoint_host_operations {
/** Enqueue message transfer
*
* @v ep USB endpoint
- * @v packet Setup packet
- * @v iobuf I/O buffer (if any)
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
int ( * message ) ( struct usb_endpoint *ep,
- struct usb_setup_packet *setup,
struct io_buffer *iobuf );
/** Enqueue stream transfer
*