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   */
