mirror of
https://github.com/ipxe/ipxe
synced 2026-01-21 09:57:23 +03:00
[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>
This commit is contained in:
@@ -313,6 +313,8 @@ int usb_endpoint_open ( struct usb_endpoint *ep ) {
|
|||||||
err_open:
|
err_open:
|
||||||
usb->ep[idx] = NULL;
|
usb->ep[idx] = NULL;
|
||||||
err_already:
|
err_already:
|
||||||
|
if ( ep->max )
|
||||||
|
usb_flush ( ep );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,9 +333,14 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
|
|||||||
/* Close endpoint */
|
/* Close endpoint */
|
||||||
ep->open = 0;
|
ep->open = 0;
|
||||||
ep->host->close ( ep );
|
ep->host->close ( ep );
|
||||||
|
assert ( ep->fill == 0 );
|
||||||
|
|
||||||
/* Remove from endpoint list */
|
/* Remove from endpoint list */
|
||||||
usb->ep[idx] = NULL;
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Increment fill level */
|
||||||
|
ep->fill++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +486,9 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Increment fill level */
|
||||||
|
ep->fill++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,6 +503,10 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
|
|||||||
int rc ) {
|
int rc ) {
|
||||||
struct usb_device *usb = ep->usb;
|
struct usb_device *usb = ep->usb;
|
||||||
|
|
||||||
|
/* Decrement fill level */
|
||||||
|
assert ( ep->fill > 0 );
|
||||||
|
ep->fill--;
|
||||||
|
|
||||||
/* Record error (if any) */
|
/* Record error (if any) */
|
||||||
ep->rc = rc;
|
ep->rc = rc;
|
||||||
if ( ( rc != 0 ) && ep->open ) {
|
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 );
|
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
|
* Control endpoint
|
||||||
|
|||||||
@@ -379,6 +379,8 @@ struct usb_endpoint {
|
|||||||
int open;
|
int open;
|
||||||
/** Current failure state (if any) */
|
/** Current failure state (if any) */
|
||||||
int rc;
|
int rc;
|
||||||
|
/** Buffer fill level */
|
||||||
|
unsigned int fill;
|
||||||
|
|
||||||
/** Host controller operations */
|
/** Host controller operations */
|
||||||
struct usb_endpoint_host_operations *host;
|
struct usb_endpoint_host_operations *host;
|
||||||
@@ -386,6 +388,13 @@ struct usb_endpoint {
|
|||||||
void *priv;
|
void *priv;
|
||||||
/** Driver operations */
|
/** Driver operations */
|
||||||
struct usb_endpoint_driver_operations *driver;
|
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 */
|
/** 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,
|
extern void usb_complete_err ( struct usb_endpoint *ep,
|
||||||
struct io_buffer *iobuf, int rc );
|
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
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user