[RFC] pass sg lists to HCDs if supported

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

 



All,

The epic USB 3.0 thread was getting a bit long so here's some actual
(untested!) code to discuss.

If an HCD supports sg lists then we simply submit one urb with the
mapped sg list (urb->sg) instead of many urbs each with an
urb->transfer_buffer.

I think this is a good first step. Later on we can look at improving the
API to allow urbs with sg lists to be submitted asynchronously which may
be more useful to drivers other than mass storage.

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-01-30 17:35:31.000000000 +0000
@@ -355,20 +355,15 @@
  * 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,
+		       int nents, 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;
@@ -467,6 +462,89 @@
 	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,
+		    int nents, size_t length, gfp_t mem_flags)
+{
+	int urb_flags;
+	int dma;
+
+	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)
+		nents = usb_buffer_map_sg(dev, usb_pipein(pipe), sg, nents);
+
+	/* initialize all the urbs we'll use */
+	if (nents <= 0)
+		return nents;
+
+	io->entries = 1;
+	io->urbs = kmalloc(io->entries * sizeof(struct urb *), mem_flags);
+	if (!io->urbs)
+		goto nomem;
+
+	urb_flags = 0;
+	if (dma)
+		urb_flags |= URB_NO_TRANSFER_DMA_MAP;
+	if (usb_pipein(pipe))
+		urb_flags |= URB_SHORT_NOT_OK;
+
+	io->urbs[0] = usb_alloc_urb(0, mem_flags);
+	if (!io->urbs[0]) {
+		io->entries = 0;
+		goto nomem;
+	}
+
+	io->urbs[0]->dev = NULL;
+	io->urbs[0]->pipe = pipe;
+	io->urbs[0]->interval = period;
+	io->urbs[0]->transfer_flags = urb_flags;
+
+	io->urbs[0]->complete = sg_complete;
+	io->urbs[0]->context = io;
+
+	io->urbs[0]->sg = sg;
+	io->urbs[0]->sg_nents = nents;
+	io->urbs[0]->transfer_buffer_length = length;
+
+	/* transaction state */
+	io->count = io->entries;
+	io->status = 0;
+	io->bytes = 0;
+	init_completion(&io->complete);
+	return 0;
+
+nomem:
+	sg_clean(io);
+	return -ENOMEM;	
+}
+
+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)
+{
+	if (!io || !dev || !sg
+			|| usb_pipecontrol(pipe)
+			|| usb_pipeisoc(pipe)
+			|| nents <= 0)
+		return -EINVAL;
+
+	if (dev->bus->sg_tablesize) {
+		return usb_sg_init_with_sg(io, dev, pipe, period, sg, nents, length, mem_flags);
+	} else {
+		return usb_sg_init_without_sg(io, dev, pipe, period, sg, nents, length, mem_flags);
+	}
+}
 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-01-30 17:30:30.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 */
@@ -1335,6 +1336,8 @@
 	int status;			/* (return) non-ISO status */
 	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
 	void *transfer_buffer;		/* (in) associated data buffer */
+	struct scatterlist *sg;         /* (in) sg list for data buffer */
+	int sg_nents;                   /* (in) number of entries in sg */
 	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
 	int transfer_buffer_length;	/* (in) data buffer length */
 	int actual_length;		/* (return) actual transfer length */
@@ -1351,6 +1354,15 @@
 					/* (in) ISO ONLY */
 };
 
+/**
+ * usb_urb_has_sg - does the urb have a scatterlist instead of a
+ * transfer_buffer?
+ */
+static inline int usb_urb_has_sg(struct urb *urb)
+{
+	return urb->sg != NULL;
+}
+
 /* ----------------------------------------------------------------------- */
 
 /**

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

  Powered by Linux