ChangeSet 1.1508.1.7, 2003/06/30 11:00:45-07:00, mdharm-usb@one-eyed-alien.net [PATCH] USB storage: General purpose I/O buffer allocation and management This patch makes our private I/O buffer allocated such that it's pre-mapped for DMA. We then add some logic to make sure that we don't try to re-map it. We also make the size of the buffer large enough for other sub-drivers, which will be converted shortly. drivers/usb/storage/transport.c | 13 +++++++- drivers/usb/storage/usb.c | 62 ++++++++++++++++++++++++---------------- drivers/usb/storage/usb.h | 11 ++++++- 3 files changed, 60 insertions(+), 26 deletions(-) diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Tue Jul 1 14:23:48 2003 +++ b/drivers/usb/storage/transport.c Tue Jul 1 14:23:48 2003 @@ -147,8 +147,19 @@ us->current_urb->context = &urb_done; us->current_urb->actual_length = 0; us->current_urb->error_count = 0; - us->current_urb->transfer_flags = URB_ASYNC_UNLINK; us->current_urb->status = 0; + + /* we assume that if transfer_buffer isn't us->iobuf then it + * hasn't been mapped for DMA. Yes, this is clunky, but it's + * easier than always having the caller tell us whether the + * transfer buffer has already been mapped. */ + us->current_urb->transfer_flags = + (us->current_urb->transfer_buffer == us->iobuf) + ? URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP + | URB_NO_TRANSFER_DMA_MAP + : URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP; + us->current_urb->transfer_dma = us->iobuf_dma; + us->current_urb->setup_dma = us->cr_dma; /* submit the URB */ status = usb_submit_urb(us->current_urb, GFP_NOIO); diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Tue Jul 1 14:23:48 2003 +++ b/drivers/usb/storage/usb.c Tue Jul 1 14:23:48 2003 @@ -428,7 +428,7 @@ ***********************************************************************/ /* Associate our private data with the USB device */ -static void associate_dev(struct us_data *us, struct usb_interface *intf) +static int associate_dev(struct us_data *us, struct usb_interface *intf) { US_DEBUGP("-- %s\n", __FUNCTION__); @@ -441,6 +441,22 @@ * device's reference count */ usb_set_intfdata(intf, us); usb_get_dev(us->pusb_dev); + + /* Allocate the device-related DMA-mapped buffers */ + us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr), + GFP_KERNEL, &us->cr_dma); + if (!us->cr) { + US_DEBUGP("usb_ctrlrequest allocation failed\n"); + return -ENOMEM; + } + + us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE, + GFP_KERNEL, &us->iobuf_dma); + if (!us->iobuf) { + US_DEBUGP("I/O buffer allocation failed\n"); + return -ENOMEM; + } + return 0; } /* Get the unusual_devs entries and the string descriptors */ @@ -742,25 +758,12 @@ { int p; - /* Allocate the USB control blocks */ - us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL); - if (!us->cr) { - US_DEBUGP("usb_ctrlrequest allocation failed\n"); - return -ENOMEM; - } - us->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!us->current_urb) { US_DEBUGP("URB allocation failed\n"); return -ENOMEM; } - us->iobuf = kmalloc(US_IOBUF_SIZE, GFP_KERNEL); - if (!us->iobuf) { - US_DEBUGP("I/O buffer allocation failed\n"); - return -ENOMEM; - } - /* Lock the device while we carry out the next two operations */ down(&us->dev_semaphore); @@ -810,8 +813,24 @@ { US_DEBUGP("-- %s\n", __FUNCTION__); down(&us->dev_semaphore); + + /* Free the device-related DMA-mapped buffers */ + if (us->cr) { + usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, + us->cr_dma); + us->cr = NULL; + } + if (us->iobuf) { + usb_buffer_free(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, + us->iobuf_dma); + us->iobuf = NULL; + } + + /* Remove our private data from the interface and decrement the + * device's reference count */ usb_set_intfdata(us->pusb_intf, NULL); usb_put_dev(us->pusb_dev); + us->pusb_dev = NULL; us->pusb_intf = NULL; up(&us->dev_semaphore); @@ -850,18 +869,11 @@ us->extra_destructor(us->extra); } - /* Destroy the extra data */ - if (us->extra) { + /* Free the extra data and the URB */ + if (us->extra) kfree(us->extra); - } - - /* Free the USB control blocks */ - if (us->iobuf) - kfree(us->iobuf); if (us->current_urb) usb_free_urb(us->current_urb); - if (us->cr) - kfree(us->cr); /* Free the structure itself */ kfree(us); @@ -892,7 +904,9 @@ init_completion(&(us->notify)); /* Associate the us_data structure with the USB device */ - associate_dev(us, intf); + result = associate_dev(us, intf); + if (result) + goto BadDevice; /* * Get the unusual_devs entries and the descriptors diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h Tue Jul 1 14:23:48 2003 +++ b/drivers/usb/storage/usb.h Tue Jul 1 14:23:48 2003 @@ -92,7 +92,14 @@ #define USB_STOR_STRING_LEN 32 -#define US_IOBUF_SIZE 32 /* Big enough for bulk-only CBW */ +/* + * We provide a DMA-mapped I/O buffer for use with small USB transfers. + * It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a + * 31-byte buffer. But Freecom needs a 64-byte buffer, so that's the + * size we'll allocate. + */ + +#define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */ typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*); typedef int (*trans_reset)(struct us_data*); @@ -147,6 +154,8 @@ struct usb_ctrlrequest *cr; /* control requests */ struct usb_sg_request current_sg; /* scatter-gather req. */ unsigned char *iobuf; /* I/O buffer */ + dma_addr_t cr_dma; /* buffer DMA addresses */ + dma_addr_t iobuf_dma; /* mutual exclusion structures */ struct semaphore sema; /* to sleep thread on */