#ifndef _IPXE_IOBUF_H #define _IPXE_IOBUF_H /** @file * * I/O buffers * */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include /** * Minimum I/O buffer length * * alloc_iob() will round up the allocated length to this size if * necessary. This is used on behalf of hardware that is not capable * of auto-padding. */ #define IOB_ZLEN 128 /** * A persistent I/O buffer * * This data structure encapsulates a long-lived I/O buffer. The * buffer may be passed between multiple owners, queued for possible * retransmission, etc. */ struct io_buffer { /** List of which this buffer is a member * * The list must belong to the current owner of the buffer. * Different owners may maintain different lists (e.g. a * retransmission list for TCP). */ struct list_head list; /** DMA mapping */ struct dma_mapping map; /** Start of the buffer */ void *head; /** Start of data */ void *data; /** End of data */ void *tail; /** End of the buffer */ void *end; }; /** * Reserve space at start of I/O buffer * * @v iobuf I/O buffer * @v len Length to reserve * @ret data Pointer to new start of buffer */ static inline void * iob_reserve ( struct io_buffer *iobuf, size_t len ) { iobuf->data += len; iobuf->tail += len; return iobuf->data; } #define iob_reserve( iobuf, len ) ( { \ void *__result; \ __result = iob_reserve ( (iobuf), (len) ); \ assert ( (iobuf)->tail <= (iobuf)->end ); \ __result; } ) /** * Add data to start of I/O buffer * * @v iobuf I/O buffer * @v len Length to add * @ret data Pointer to new start of buffer */ static inline void * iob_push ( struct io_buffer *iobuf, size_t len ) { iobuf->data -= len; return iobuf->data; } #define iob_push( iobuf, len ) ( { \ void *__result; \ __result = iob_push ( (iobuf), (len) ); \ assert ( (iobuf)->data >= (iobuf)->head ); \ __result; } ) /** * Remove data from start of I/O buffer * * @v iobuf I/O buffer * @v len Length to remove * @ret data Pointer to new start of buffer */ static inline void * iob_pull ( struct io_buffer *iobuf, size_t len ) { iobuf->data += len; assert ( iobuf->data <= iobuf->tail ); return iobuf->data; } #define iob_pull( iobuf, len ) ( { \ void *__result; \ __result = iob_pull ( (iobuf), (len) ); \ assert ( (iobuf)->data <= (iobuf)->tail ); \ __result; } ) /** * Add data to end of I/O buffer * * @v iobuf I/O buffer * @v len Length to add * @ret data Pointer to newly added space */ static inline void * iob_put ( struct io_buffer *iobuf, size_t len ) { void *old_tail = iobuf->tail; iobuf->tail += len; return old_tail; } #define iob_put( iobuf, len ) ( { \ void *__result; \ __result = iob_put ( (iobuf), (len) ); \ assert ( (iobuf)->tail <= (iobuf)->end ); \ __result; } ) /** * Remove data from end of I/O buffer * * @v iobuf I/O buffer * @v len Length to remove */ static inline void iob_unput ( struct io_buffer *iobuf, size_t len ) { iobuf->tail -= len; } #define iob_unput( iobuf, len ) do { \ iob_unput ( (iobuf), (len) ); \ assert ( (iobuf)->tail >= (iobuf)->data ); \ } while ( 0 ) /** * Empty an I/O buffer * * @v iobuf I/O buffer */ static inline void iob_empty ( struct io_buffer *iobuf ) { iobuf->tail = iobuf->data; } /** * Calculate length of data in an I/O buffer * * @v iobuf I/O buffer * @ret len Length of data in buffer */ static inline size_t iob_len ( struct io_buffer *iobuf ) { return ( iobuf->tail - iobuf->data ); } /** * Calculate available space at start of an I/O buffer * * @v iobuf I/O buffer * @ret len Length of data available at start of buffer */ static inline size_t iob_headroom ( struct io_buffer *iobuf ) { return ( iobuf->data - iobuf->head ); } /** * Calculate available space at end of an I/O buffer * * @v iobuf I/O buffer * @ret len Length of data available at end of buffer */ static inline size_t iob_tailroom ( struct io_buffer *iobuf ) { return ( iobuf->end - iobuf->tail ); } /** * Create a temporary I/O buffer * * @v iobuf I/O buffer * @v data Data buffer * @v len Length of data * @v max_len Length of buffer * * It is sometimes useful to use the iob_xxx() methods on temporary * data buffers. */ static inline void iob_populate ( struct io_buffer *iobuf, void *data, size_t len, size_t max_len ) { iobuf->head = iobuf->data = data; iobuf->tail = ( data + len ); iobuf->end = ( data + max_len ); } /** * Disown an I/O buffer * * @v iobuf I/O buffer * * There are many functions that take ownership of the I/O buffer they * are passed as a parameter. The caller should not retain a pointer * to the I/O buffer. Use iob_disown() to automatically nullify the * caller's pointer, e.g.: * * xfer_deliver_iob ( xfer, iob_disown ( iobuf ) ); * * This will ensure that iobuf is set to NULL for any code after the * call to xfer_deliver_iob(). */ #define iob_disown( iobuf ) ( { \ struct io_buffer *__iobuf = (iobuf); \ (iobuf) = NULL; \ __iobuf; } ) /** * Map I/O buffer for DMA * * @v iobuf I/O buffer * @v dma DMA device * @v len Length to map * @v flags Mapping flags * @ret rc Return status code */ static inline __always_inline int iob_map ( struct io_buffer *iobuf, struct dma_device *dma, size_t len, int flags ) { return dma_map ( dma, &iobuf->map, virt_to_phys ( iobuf->data ), len, flags ); } /** * Map I/O buffer for transmit DMA * * @v iobuf I/O buffer * @v dma DMA device * @ret rc Return status code */ static inline __always_inline int iob_map_tx ( struct io_buffer *iobuf, struct dma_device *dma ) { return iob_map ( iobuf, dma, iob_len ( iobuf ), DMA_TX ); } /** * Map empty I/O buffer for receive DMA * * @v iobuf I/O buffer * @v dma DMA device * @ret rc Return status code */ static inline __always_inline int iob_map_rx ( struct io_buffer *iobuf, struct dma_device *dma ) { assert ( iob_len ( iobuf ) == 0 ); return iob_map ( iobuf, dma, iob_tailroom ( iobuf ), DMA_RX ); } /** * Get I/O buffer DMA address * * @v iobuf I/O buffer * @ret addr DMA address */ static inline __always_inline physaddr_t iob_dma ( struct io_buffer *iobuf ) { return dma ( &iobuf->map, iobuf->data ); } /** * Unmap I/O buffer for DMA * * @v iobuf I/O buffer * @v dma DMA device * @ret rc Return status code */ static inline __always_inline void iob_unmap ( struct io_buffer *iobuf ) { dma_unmap ( &iobuf->map ); } extern struct io_buffer * __malloc alloc_iob_raw ( size_t len, size_t align, size_t offset ); extern struct io_buffer * __malloc alloc_iob ( size_t len ); extern void free_iob ( struct io_buffer *iobuf ); extern struct io_buffer * __malloc alloc_rx_iob ( size_t len, struct dma_device *dma ); extern void free_rx_iob ( struct io_buffer *iobuf ); extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ); extern struct io_buffer * iob_concatenate ( struct list_head *list ); extern struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ); #endif /* _IPXE_IOBUF_H */