aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2015-02-12 15:14:04 +0000
committerMichael Brown <mcb30@ipxe.org>2015-02-13 01:10:51 +0000
commit17fc79425ef27f508cf8527c2adf160cef1888c8 (patch)
treeaa05c85ced9448237b52c3c5c44cb0ffbbc83199 /src
parent5de134662d3ca9245473a88849d4af770a55e994 (diff)
downloadipxe-17fc79425ef27f508cf8527c2adf160cef1888c8.tar.gz
[usb] Provide generic framework for refilling receive endpoints
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>
Diffstat (limited to 'src')
-rw-r--r--src/drivers/bus/usb.c128
-rw-r--r--src/include/ipxe/usb.h40
2 files changed, 168 insertions, 0 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c
index 397e5a84d..c3440cb31 100644
--- a/src/drivers/bus/usb.c
+++ b/src/drivers/bus/usb.c
@@ -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 ) {
@@ -504,6 +521,117 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
/******************************************************************************
*
+ * 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
*
******************************************************************************
diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h
index 8e1824160..63358024d 100644
--- a/src/include/ipxe/usb.h
+++ b/src/include/ipxe/usb.h
@@ -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 */
@@ -554,6 +563,37 @@ 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
*
* A USB function represents an association of interfaces within a USB