Re: [RFC] pass sg lists to HCDs if supported

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here's a new version of the patch:

No new fields are added to struct urb.  urb->transfer_buffer points to a
struct sg_table if urb->transfer_flags & URB_HAS_SG.

The sg table stores both the original number of entries and the number
of DMA mapped entries allowing HCDs to choose whether to iterate over
the DMA mapped entries or the original unmapped ones.

David
-- 
David Vrabel, Senior Software Engineer, Drivers
CSR, Churchill House, Cambridge Business Park,  Tel: +44 (0)1223 692562
Cowley Road, Cambridge, CB4 0WZ                 http://www.csr.com/
Index: linux-working/drivers/usb/core/message.c
===================================================================
--- linux-working.orig/drivers/usb/core/message.c	2009-01-30 16:36:47.000000000 +0000
+++ linux-working/drivers/usb/core/message.c	2009-02-04 18:37:33.000000000 +0000
@@ -330,60 +330,24 @@
 }
 
 
-/**
- * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request
- * @io: request block being initialized.  until usb_sg_wait() returns,
- *	treat this as a pointer to an opaque block of memory,
- * @dev: the usb device that will send or receive the data
- * @pipe: endpoint "pipe" used to transfer the data
- * @period: polling rate for interrupt endpoints, in frames or
- * 	(for high speed endpoints) microframes; ignored for bulk
- * @sg: scatterlist entries
- * @nents: how many entries in the scatterlist
- * @length: how many bytes to send from the scatterlist, or zero to
- * 	send every byte identified in the list.
- * @mem_flags: SLAB_* flags affecting memory allocations in this call
- *
- * Returns zero for success, else a negative errno value.  This initializes a
- * scatter/gather request, allocating resources such as I/O mappings and urb
- * memory (except maybe memory used by USB controller drivers).
- *
- * The request must be issued using usb_sg_wait(), which waits for the I/O to
- * complete (or to be canceled) and then cleans up all resources allocated by
- * usb_sg_init().
- *
- * The request may be canceled with usb_sg_cancel(), either before or after
- * usb_sg_wait() is called.
- */
-int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
-		unsigned pipe, unsigned	period, struct scatterlist *sg,
-		int nents, size_t length, gfp_t mem_flags)
+static int
+usb_sg_init_without_sg(struct usb_sg_request *io, struct usb_device *dev,
+		       unsigned pipe, unsigned period, struct scatterlist *sg,
+		       size_t length, gfp_t mem_flags)
 {
 	int i;
 	int urb_flags;
 	int dma;
 
-	if (!io || !dev || !sg
-			|| usb_pipecontrol(pipe)
-			|| usb_pipeisoc(pipe)
-			|| nents <= 0)
-		return -EINVAL;
-
-	spin_lock_init(&io->lock);
-	io->dev = dev;
-	io->pipe = pipe;
-	io->sg = sg;
-	io->nents = nents;
-
 	/* not all host controllers use DMA (like the mainstream pci ones);
 	 * they can use PIO (sl811) or be software over another transport.
 	 */
 	dma = (dev->dev.dma_mask != NULL);
 	if (dma)
 		io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe),
-						sg, nents);
+						sg, io->nents);
 	else
-		io->entries = nents;
+		io->entries = io->nents;
 
 	/* initialize all the urbs we'll use */
 	if (io->entries <= 0)
@@ -467,6 +431,122 @@
 	sg_clean(io);
 	return -ENOMEM;
 }
+
+static int
+usb_sg_init_with_sg(struct usb_sg_request *io, struct usb_device *dev,
+		    unsigned pipe, unsigned period, struct scatterlist *sg,
+		    size_t length, gfp_t mem_flags)
+{
+	struct sg_table *sgt;
+
+	if (length == 0) {
+		struct scatterlist *s;
+		int i;
+		for_each_sg(sg, s, io->nents, i) {
+			length += sg->length;
+		}
+	}
+
+	/* initialize one urb with transfer_buffer pointing to a new sg_table */
+	io->urbs = kmalloc(sizeof(struct urb *) + sizeof(struct sg_table), mem_flags);
+	if (!io->urbs)
+		return -ENOMEM;
+
+	sgt = (struct sg_table *)&io->urbs[1];
+	sgt->sgl = sg;
+	sgt->orig_nents = io->nents;
+
+	/* not all host controllers use DMA (like the mainstream pci ones);
+	 * they can use PIO (sl811) or be software over another transport.
+	 */
+	if (dev->dev.dma_mask != NULL)
+		sgt->nents = usb_buffer_map_sg(dev, usb_pipein(pipe), sg, io->nents);
+	else
+		sgt->nents = sgt->orig_nents;
+	if (sgt->nents <= 0)
+		return sgt->nents;
+
+	io->urbs[0] = usb_alloc_urb(0, mem_flags);
+	if (!io->urbs[0])
+		return -ENOMEM;
+	io->entries = 1;
+
+	io->urbs[0]->dev = NULL;
+	io->urbs[0]->pipe = pipe;
+	io->urbs[0]->interval = period;
+	io->urbs[0]->transfer_flags = URB_HAS_SG;
+
+	io->urbs[0]->complete = sg_complete;
+	io->urbs[0]->context = io;
+
+	io->urbs[0]->transfer_buffer = sgt;
+	io->urbs[0]->transfer_buffer_length = length;
+
+	return 0;
+}
+
+/**
+ * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request
+ * @io: request block being initialized.  until usb_sg_wait() returns,
+ *	treat this as a pointer to an opaque block of memory,
+ * @dev: the usb device that will send or receive the data
+ * @pipe: endpoint "pipe" used to transfer the data
+ * @period: polling rate for interrupt endpoints, in frames or
+ * 	(for high speed endpoints) microframes; ignored for bulk
+ * @sg: scatterlist entries
+ * @nents: how many entries in the scatterlist
+ * @length: how many bytes to send from the scatterlist, or zero to
+ * 	send every byte identified in the list.
+ * @mem_flags: SLAB_* flags affecting memory allocations in this call
+ *
+ * Returns zero for success, else a negative errno value.  This initializes a
+ * scatter/gather request, allocating resources such as I/O mappings and urb
+ * memory (except maybe memory used by USB controller drivers).
+ *
+ * The request must be issued using usb_sg_wait(), which waits for the I/O to
+ * complete (or to be canceled) and then cleans up all resources allocated by
+ * usb_sg_init().
+ *
+ * The request may be canceled with usb_sg_cancel(), either before or after
+ * usb_sg_wait() is called.
+ */
+int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
+		unsigned pipe, unsigned	period, struct scatterlist *sg,
+		int nents, size_t length, gfp_t mem_flags)
+{
+	int ret;
+
+	if (!io || !dev || !sg
+			|| usb_pipecontrol(pipe)
+			|| usb_pipeisoc(pipe)
+			|| nents <= 0)
+		return -EINVAL;
+
+	spin_lock_init(&io->lock);
+	io->dev = dev;
+	io->pipe = pipe;
+	io->sg = sg;
+	io->nents = nents;
+	io->entries = 0;
+
+	if (dev->bus->sg_tablesize) {
+		ret = usb_sg_init_with_sg(io, dev, pipe, period, sg, length, mem_flags);
+	} else {
+		ret = usb_sg_init_without_sg(io, dev, pipe, period, sg, length, mem_flags);
+	}
+	if (ret < 0) {
+		sg_clean(io);
+		return ret;
+	}
+
+	/* transaction state */
+	io->count = io->entries;
+	io->status = 0;
+	io->bytes = 0;
+	init_completion(&io->complete);
+
+	return 0;
+}
 EXPORT_SYMBOL_GPL(usb_sg_init);
 
 /**
Index: linux-working/drivers/usb/storage/usb.c
===================================================================
--- linux-working.orig/drivers/usb/storage/usb.c	2009-01-30 16:02:51.000000000 +0000
+++ linux-working/drivers/usb/storage/usb.c	2009-01-30 17:39:07.000000000 +0000
@@ -970,6 +970,15 @@
 	 * Allow 16-byte CDBs and thus > 2TB
 	 */
 	host->max_cmd_len = 16;
+	/*
+	 * If the USB HCD supports sg lists, limit sg_tablesize.
+	 */
+	{
+		struct usb_device *usb_dev = interface_to_usbdev(intf);
+		if (usb_dev->bus->sg_tablesize) {
+			host->sg_tablesize = usb_dev->bus->sg_tablesize;
+		}
+	}
 	us = host_to_us(host);
 	memset(us, 0, sizeof(struct us_data));
 	mutex_init(&(us->dev_mutex));
Index: linux-working/include/linux/usb.h
===================================================================
--- linux-working.orig/include/linux/usb.h	2009-01-30 16:27:35.000000000 +0000
+++ linux-working/include/linux/usb.h	2009-02-04 18:34:07.000000000 +0000
@@ -306,6 +306,7 @@
 	u8 otg_port;			/* 0, or number of OTG/HNP port */
 	unsigned is_b_host:1;		/* true during some HNP roleswitches */
 	unsigned b_hnp_enable:1;	/* OTG: did A-Host enable HNP? */
+	int sg_tablesize;               /* 0 or largest number of sg list entries */
 
 	int devnum_next;		/* Next open device number in
 					 * round-robin allocation */
@@ -1114,6 +1115,7 @@
 					 * ignored */
 #define URB_NO_TRANSFER_DMA_MAP	0x0004	/* urb->transfer_dma valid on submit */
 #define URB_NO_SETUP_DMA_MAP	0x0008	/* urb->setup_dma valid on submit */
+#define URB_HAS_SG		0x0010	/* urb->transfer_buffer points to a struct sg_table */
 #define URB_NO_FSBR		0x0020	/* UHCI-specific */
 #define URB_ZERO_PACKET		0x0040	/* Finish bulk OUT with short packet */
 #define URB_NO_INTERRUPT	0x0080	/* HINT: no non-error interrupt
@@ -1351,6 +1353,15 @@
 					/* (in) ISO ONLY */
 };
 
+/**
+ * usb_urb_has_sg - does the urb have a sg_table instead of a
+ * transfer_buffer?
+ */
+static inline int usb_urb_has_sg(struct urb *urb)
+{
+	return urb->transfer_flags & URB_HAS_SG;
+}
+
 /* ----------------------------------------------------------------------- */
 
 /**

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux