]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Provide generic framework for refilling receive endpoints
authorMichael Brown <mcb30@ipxe.org>
Thu, 12 Feb 2015 15:14:04 +0000 (15:14 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 13 Feb 2015 01:10:51 +0000 (01:10 +0000)
Provide a generic framework for allocating, refilling, and optionally
recycling I/O buffers used by bulk IN and interrupt endpoints.

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

index 397e5a84d01ed91775122b8684f3d9623cbe8c50..c3440cb31285de6c6d07679e00ab0c36441dc713 100644 (file)
@@ -313,6 +313,8 @@ int usb_endpoint_open ( struct usb_endpoint *ep ) {
  err_open:
        usb->ep[idx] = NULL;
  err_already:
+       if ( ep->max )
+               usb_flush ( ep );
        return rc;
 }
 
@@ -331,9 +333,14 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
        /* Close endpoint */
        ep->open = 0;
        ep->host->close ( ep );
+       assert ( ep->fill == 0 );
 
        /* Remove from endpoint list */
        usb->ep[idx] = NULL;
+
+       /* Discard any recycled buffers, if applicable */
+       if ( ep->max )
+               usb_flush ( ep );
 }
 
 /**
@@ -443,6 +450,9 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request,
                return rc;
        }
 
+       /* Increment fill level */
+       ep->fill++;
+
        return 0;
 }
 
@@ -476,6 +486,9 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
                return rc;
        }
 
+       /* Increment fill level */
+       ep->fill++;
+
        return 0;
 }
 
@@ -490,6 +503,10 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
                        int rc ) {
        struct usb_device *usb = ep->usb;
 
+       /* Decrement fill level */
+       assert ( ep->fill > 0 );
+       ep->fill--;
+
        /* Record error (if any) */
        ep->rc = rc;
        if ( ( rc != 0 ) && ep->open ) {
@@ -502,6 +519,117 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
        ep->driver->complete ( ep, iobuf, rc );
 }
 
+/******************************************************************************
+ *
+ * Endpoint refilling
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Prefill endpoint recycled buffer list
+ *
+ * @v ep               USB endpoint
+ * @ret rc             Return status code
+ */
+int usb_prefill ( struct usb_endpoint *ep ) {
+       struct io_buffer *iobuf;
+       size_t len = ( ep->len ? ep->len : ep->mtu );
+       unsigned int fill;
+       int rc;
+
+       /* Sanity checks */
+       assert ( ep->fill == 0 );
+       assert ( ep->max > 0 );
+       assert ( list_empty ( &ep->recycled ) );
+
+       /* Fill recycled buffer list */
+       for ( fill = 0 ; fill < ep->max ; fill++ ) {
+
+               /* Allocate I/O buffer */
+               iobuf = alloc_iob ( len );
+               if ( ! iobuf ) {
+                       rc = -ENOMEM;
+                       goto err_alloc;
+               }
+
+               /* Add to recycled buffer list */
+               list_add_tail ( &iobuf->list, &ep->recycled );
+       }
+
+       return 0;
+
+ err_alloc:
+       usb_flush ( ep );
+       return rc;
+}
+
+/**
+ * Refill endpoint
+ *
+ * @v ep               USB endpoint
+ * @ret rc             Return status code
+ */
+int usb_refill ( struct usb_endpoint *ep ) {
+       struct io_buffer *iobuf;
+       size_t len = ( ep->len ? ep->len : ep->mtu );
+       int rc;
+
+       /* Sanity checks */
+       assert ( ep->open );
+       assert ( ep->max > 0 );
+
+       /* Refill endpoint */
+       while ( ep->fill < ep->max ) {
+
+               /* Get or allocate buffer */
+               if ( list_empty ( &ep->recycled ) ) {
+                       /* Recycled buffer list is empty; allocate new buffer */
+                       iobuf = alloc_iob ( len );
+                       if ( ! iobuf )
+                               return -ENOMEM;
+               } else {
+                       /* Get buffer from recycled buffer list */
+                       iobuf = list_first_entry ( &ep->recycled,
+                                                  struct io_buffer, list );
+                       assert ( iobuf != NULL );
+                       list_del ( &iobuf->list );
+               }
+
+               /* Reset buffer to maximum size */
+               assert ( iob_len ( iobuf ) <= len );
+               iob_put ( iobuf, ( len - iob_len ( iobuf ) ) );
+
+               /* Enqueue buffer */
+               if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) {
+                       list_add ( &iobuf->list, &ep->recycled );
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Discard endpoint recycled buffer list
+ *
+ * @v ep               USB endpoint
+ */
+void usb_flush ( struct usb_endpoint *ep ) {
+       struct io_buffer *iobuf;
+       struct io_buffer *tmp;
+
+       /* Sanity checks */
+       assert ( ! ep->open );
+       assert ( ep->max > 0 );
+
+       /* Free all I/O buffers */
+       list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) {
+               list_del ( &iobuf->list );
+               free_iob ( iobuf );
+       }
+}
+
 /******************************************************************************
  *
  * Control endpoint
index 8e18241603216f28e0b54b9e84083048c5c391cc..63358024d76e3e726d0fbbd143501fdd7b5f75be 100644 (file)
@@ -379,6 +379,8 @@ struct usb_endpoint {
        int open;
        /** Current failure state (if any) */
        int rc;
+       /** Buffer fill level */
+       unsigned int fill;
 
        /** Host controller operations */
        struct usb_endpoint_host_operations *host;
@@ -386,6 +388,13 @@ struct usb_endpoint {
        void *priv;
        /** Driver operations */
        struct usb_endpoint_driver_operations *driver;
+
+       /** Recycled I/O buffer list */
+       struct list_head recycled;
+       /** Refill buffer length */
+       size_t len;
+       /** Maximum fill level */
+       unsigned int max;
 };
 
 /** USB endpoint host controller operations */
@@ -553,6 +562,37 @@ extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
 extern void usb_complete_err ( struct usb_endpoint *ep,
                               struct io_buffer *iobuf, int rc );
 
+/**
+ * Initialise USB endpoint refill
+ *
+ * @v ep               USB endpoint
+ * @v len              Refill buffer length (or zero to use endpoint's MTU)
+ * @v max              Maximum fill level
+ */
+static inline __attribute__ (( always_inline )) void
+usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) {
+
+       INIT_LIST_HEAD ( &ep->recycled );
+       ep->len = len;
+       ep->max = max;
+}
+
+/**
+ * Recycle I/O buffer
+ *
+ * @v ep               USB endpoint
+ * @v iobuf            I/O buffer
+ */
+static inline __attribute__ (( always_inline )) void
+usb_recycle ( struct usb_endpoint *ep, struct io_buffer *iobuf ) {
+
+       list_add_tail ( &iobuf->list, &ep->recycled );
+}
+
+extern int usb_prefill ( struct usb_endpoint *ep );
+extern int usb_refill ( struct usb_endpoint *ep );
+extern void usb_flush ( struct usb_endpoint *ep );
+
 /**
  * A USB function
  *