aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/bus/usb.c6
-rw-r--r--src/drivers/net/ncm.c4
-rw-r--r--src/drivers/usb/usbhub.c2
-rw-r--r--src/drivers/usb/xhci.c22
-rw-r--r--src/include/ipxe/usb.h8
5 files changed, 28 insertions, 14 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c
index 5a338a5e..8900324a 100644
--- a/src/drivers/bus/usb.c
+++ b/src/drivers/bus/usb.c
@@ -451,9 +451,11 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request,
*
* @v ep USB endpoint
* @v iobuf I/O buffer
+ * @v terminate Terminate using a short packet
* @ret rc Return status code
*/
-int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf ) {
+int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int terminate ) {
struct usb_device *usb = ep->usb;
struct usb_port *port = usb->port;
int rc;
@@ -467,7 +469,7 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf ) {
return rc;
/* Enqueue stream transfer */
- if ( ( rc = ep->host->stream ( ep, iobuf ) ) != 0 ) {
+ if ( ( rc = ep->host->stream ( ep, iobuf, terminate ) ) != 0 ) {
DBGC ( usb, "USB %s %s could not enqueue stream transfer: %s\n",
usb->name, usb_endpoint_name ( ep->address ),
strerror ( rc ) );
diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c
index 0fc3ab79..e05559bb 100644
--- a/src/drivers/net/ncm.c
+++ b/src/drivers/net/ncm.c
@@ -145,7 +145,7 @@ static int ncm_rx_refill ( struct ncm_device *ncm, struct ncm_rx_ring *ring ) {
iob_put ( iobuf, ( ring->mtu - iob_len ( iobuf ) ) );
/* Enqueue I/O buffer */
- if ( ( rc = usb_stream ( &ring->ep, iobuf ) ) != 0 ) {
+ if ( ( rc = usb_stream ( &ring->ep, iobuf, 0 ) ) != 0 ) {
DBGC ( ncm, "NCM %p could not enqueue %s: %s\n", ncm,
ncm_rx_name ( ncm, ring ), strerror ( rc ) );
/* Leave in recycled list and wait for next refill */
@@ -548,7 +548,7 @@ static int ncm_out_transmit ( struct ncm_device *ncm,
memset ( &header->desc[1], 0, sizeof ( header->desc[1] ) );
/* Enqueue I/O buffer */
- if ( ( rc = usb_stream ( &ncm->out.ep, iobuf ) ) != 0 )
+ if ( ( rc = usb_stream ( &ncm->out.ep, iobuf, 0 ) ) != 0 )
return rc;
/* Increment sequence number */
diff --git a/src/drivers/usb/usbhub.c b/src/drivers/usb/usbhub.c
index ecac460a..9c88531b 100644
--- a/src/drivers/usb/usbhub.c
+++ b/src/drivers/usb/usbhub.c
@@ -52,7 +52,7 @@ static void hub_refill ( struct usb_hub_device *hubdev ) {
iob_put ( iobuf, ( mtu - iob_len ( iobuf ) ) );
/* Enqueue I/O buffer */
- if ( ( rc = usb_stream ( &hubdev->intr, iobuf ) ) != 0 ) {
+ if ( ( rc = usb_stream ( &hubdev->intr, iobuf, 0 ) ) != 0 ) {
DBGC ( hubdev, "HUB %s could not enqueue interrupt: "
"%s\n", hubdev->name, strerror ( rc ) );
/* Leave in available list and wait for next refill */
diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c
index 49901275..bf8cf1c8 100644
--- a/src/drivers/usb/xhci.c
+++ b/src/drivers/usb/xhci.c
@@ -2457,27 +2457,37 @@ static int xhci_endpoint_message ( struct usb_endpoint *ep,
*
* @v ep USB endpoint
* @v iobuf I/O buffer
+ * @v terminate Terminate using a short packet
* @ret rc Return status code
*/
static int xhci_endpoint_stream ( struct usb_endpoint *ep,
- struct io_buffer *iobuf ) {
+ struct io_buffer *iobuf, int terminate ) {
struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- union xhci_trb trb;
+ union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ];
+ union xhci_trb *trb = trbs;
struct xhci_trb_normal *normal;
+ size_t len = iob_len ( iobuf );
int rc;
/* Profile stream transfers */
profile_start ( &xhci_stream_profiler );
/* Construct normal TRBs */
- normal = &trb.normal;
+ memset ( &trbs, 0, sizeof ( trbs ) );
+ normal = &(trb++)->normal;
normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) );
- normal->len = cpu_to_le32 ( iob_len ( iobuf ) );
- normal->flags = XHCI_TRB_IOC;
+ normal->len = cpu_to_le32 ( len );
normal->type = XHCI_TRB_NORMAL;
+ if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) {
+ normal->flags = XHCI_TRB_CH;
+ normal = &(trb++)->normal;
+ normal->type = XHCI_TRB_NORMAL;
+ }
+ normal->flags = XHCI_TRB_IOC;
/* Enqueue TRBs */
- if ( ( rc = xhci_enqueue ( &endpoint->ring, iobuf, &trb ) ) != 0 )
+ if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs,
+ ( trb - trbs ) ) ) != 0 )
return rc;
/* Ring the doorbell */
diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h
index e21ca1c4..1f5a85ec 100644
--- a/src/include/ipxe/usb.h
+++ b/src/include/ipxe/usb.h
@@ -428,10 +428,11 @@ struct usb_endpoint_host_operations {
*
* @v ep USB endpoint
* @v iobuf I/O buffer
+ * @v terminate Terminate using a short packet
* @ret rc Return status code
*/
- int ( * stream ) ( struct usb_endpoint *ep,
- struct io_buffer *iobuf );
+ int ( * stream ) ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int terminate );
};
/** USB endpoint driver operations */
@@ -547,7 +548,8 @@ extern void usb_endpoint_close ( struct usb_endpoint *ep );
extern int usb_message ( struct usb_endpoint *ep, unsigned int request,
unsigned int value, unsigned int index,
struct io_buffer *iobuf );
-extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf );
+extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int terminate );
extern void usb_complete_err ( struct usb_endpoint *ep,
struct io_buffer *iobuf, int rc );