]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Allow usb_stream() to enforce a terminating short packet
authorMichael Brown <mcb30@ipxe.org>
Tue, 10 Feb 2015 13:45:57 +0000 (13:45 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 10 Feb 2015 13:49:27 +0000 (13:49 +0000)
Some USB endpoints require that a short packet be used to terminate
transfers, since they have no other way to determine message
boundaries.  If the message length happens to be an exact multiple of
the USB packet size, then this requires the use of an additional
zero-length packet.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/bus/usb.c
src/drivers/net/ncm.c
src/drivers/usb/usbhub.c
src/drivers/usb/xhci.c
src/include/ipxe/usb.h

index 5a338a5e0da534157d599a359d36a2dc3f6fd0c7..8900324aafc3369e2c96f3432a855739d6e5d3be 100644 (file)
@@ -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 ) );
index 0fc3ab794b550da9d101d6b7e7b7d7c348a87628..e05559bb8bf539f9703b85a34c4c4dad6a830285 100644 (file)
@@ -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 */
index ecac460a0761b21a0b3f3e082388f446ed141a79..9c88531bb13f7a7f3edf2acdefc2aa6594dfa45b 100644 (file)
@@ -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 */
index 49901275d4d66ebc6e0afd200024752cb73679a5..bf8cf1c8ae04126d3ab492c30fb76d5b2c48cf86 100644 (file)
@@ -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 */
index e21ca1c432c4f615e72933e17609d7d74fbcd72f..1f5a85ecd7ef9c15e80dd2a5f6bfe3e8a9322c4c 100644 (file)
@@ -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 );