[PATCH 1/1] usb: gadget/uvc: Add support to allocate UVC payload and header as SG elements

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

 



This patch adds the support in UVC webcam gadget to allocate UVC header and
payload as Scatter-Gather elements which can be used on a SG-capable UDC
controller.

A module parameter has been introduced for the same. One can set the
module parameter 'sg_mode' to 1 to turn on this feature (by default this
feature is turned-off).

This ensures that we don't require a memcpy from CPU side to append UVC
header in front of the UVC payload atleast for SG capable UDC contollers.

Signed-off-by: Bhupesh Sharma <bhupesh.sharma@xxxxxx>
---
Note that to ease review and integration of this patch, I have rebased it
on the following patch already circulated for review last week:

[PATCH 1/1] usb: gadget/uvc: Add support for Bulk endpoint to be used as
                             Video Streaming ep
http://www.mail-archive.com/linux-usb@xxxxxxxxxxxxxxx/msg19546.html

The above patch was rebased on Laurent's UVC gadget git tree available here
(head uvc-gadget):
git://linuxtv.org/pinchartl/uvcvideo.git

This will allow the patches to be pulled into Felipe's repo in one go
after review and any subsequent rework (if required).

 drivers/usb/gadget/f_uvc.c     |    8 +++
 drivers/usb/gadget/uvc.h       |    2 +
 drivers/usb/gadget/uvc_video.c |  113 +++++++++++++++++++++++++++++++++++-----
 3 files changed, 109 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index e5953eb..ccf0253 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -50,6 +50,11 @@ module_param(bulk_streaming_ep, bool, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(bulk_streaming_ep, "0 (Use ISOC video streaming ep) / "
 					"1 (Use BULK video streaming ep)");
 
+static bool sg_mode;
+module_param(sg_mode, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(sg_mode, "0 (Don't use SG feature) / "
+			"1 (Use scatterlist for SG-capable controllers)");
+
 /* --------------------------------------------------------------------------
  * Function descriptors
  */
@@ -888,6 +893,9 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	uvc->control_req->complete = uvc_function_ep0_complete;
 	uvc->control_req->context = uvc;
 
+	/* Use SG mode ? */
+	uvc->video.sg_mode = sg_mode;
+
 	/* Avoid letting this gadget enumerate until the userspace server is
 	 * active.
 	 */
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h
index 8756853..3a54510 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/uvc.h
@@ -122,6 +122,7 @@ struct uvc_video
 	struct usb_request *req[UVC_NUM_REQUESTS];
 	__u8 *req_buffer[UVC_NUM_REQUESTS];
 	struct list_head req_free;
+	unsigned char *header_buf;
 	spinlock_t req_lock;
 
 	void (*encode) (struct usb_request *req, struct uvc_video *video,
@@ -135,6 +136,7 @@ struct uvc_video
 	unsigned int fid;
 
 	bool bulk_streaming_ep;
+	bool sg_mode;
 };
 
 enum uvc_state
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
index 87ac526..5f92f93 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/uvc_video.c
@@ -39,6 +39,25 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
 }
 
 static int
+uvc_video_encode_header_sg(struct uvc_video *video, struct uvc_buffer *buf,
+			   int len)
+{
+	unsigned char *data = video->header_buf;
+
+	*data++ = 2;
+	*data = UVC_STREAM_EOH | video->fid;
+
+	if (!video->bulk_streaming_ep) {
+		if (buf->bytesused - video->queue.buf_used <= len - 2)
+			*data |= UVC_STREAM_EOF;
+	} else {
+		*data |= UVC_STREAM_EOF;
+	}
+
+	return 2;
+}
+
+static int
 uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
 		u8 *data, int len)
 {
@@ -56,25 +75,57 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
 	return nbytes;
 }
 
+static int
+uvc_video_encode_data_sg(struct uvc_video *video, struct uvc_buffer *buf,
+			 struct scatterlist *sg, int len)
+{
+	struct uvc_video_queue *queue = &video->queue;
+	unsigned int nbytes;
+
+	/* Pass pointer of video frame data to the USB req->sg. */
+	nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
+
+	sg_set_buf(sg, (void *)(buf->mem + queue->buf_used), len);
+	queue->buf_used += nbytes;
+
+	return nbytes;
+}
+
 static void
 uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
 		struct uvc_buffer *buf)
 {
-	void *mem = req->buf;
+	struct scatterlist *sg = NULL;
+	void *mem = NULL;
 	int len = video->req_size;
 	int ret;
 
 	/* Add a header at the beginning of the payload. */
+	if (!video->sg_mode)
+		mem = req->buf;
+	else
+		sg = req->sg;
+
 	if (video->payload_size == 0) {
-		ret = uvc_video_encode_header(video, buf, mem, len);
+		if (!video->sg_mode) {
+			ret = uvc_video_encode_header(video, buf, mem, len);
+			mem += ret;
+		} else {
+			ret = uvc_video_encode_header_sg(video, buf, len);
+		}
+
 		video->payload_size += ret;
-		mem += ret;
 		len -= ret;
 	}
 
 	/* Process video data. */
 	len = min((int)(video->max_payload_size - video->payload_size), len);
-	ret = uvc_video_encode_data(video, buf, mem, len);
+	if (!video->sg_mode) {
+		ret = uvc_video_encode_data(video, buf, mem, len);
+	} else {
+		sg++;
+		ret = uvc_video_encode_data_sg(video, buf, sg, len);
+	}
 
 	video->payload_size += ret;
 	len -= ret;
@@ -100,17 +151,29 @@ static void
 uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
 		struct uvc_buffer *buf)
 {
+	struct scatterlist *sg = NULL;
 	void *mem = req->buf;
 	int len = video->req_size;
 	int ret;
 
 	/* Add the header. */
-	ret = uvc_video_encode_header(video, buf, mem, len);
+	if (!video->sg_mode)
+		ret = uvc_video_encode_header(video, buf, mem, len);
+	else
+		ret = uvc_video_encode_header_sg(video, buf, len);
+
 	mem += ret;
 	len -= ret;
 
 	/* Process video data. */
-	ret = uvc_video_encode_data(video, buf, mem, len);
+	if (!video->sg_mode) {
+		ret = uvc_video_encode_data(video, buf, mem, len);
+	} else {
+		sg = req->sg;
+		sg++;
+		ret = uvc_video_encode_data_sg(video, buf, sg, len);
+	}
+
 	len -= ret;
 
 	req->length = video->req_size - len;
@@ -212,13 +275,19 @@ uvc_video_free_requests(struct uvc_video *video)
 {
 	unsigned int i;
 
+	if (video->sg_mode && video->header_buf)
+		kfree(video->header_buf);
+
 	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+		if (video->sg_mode && video->req[i]->sg)
+			kfree(video->req[i]->sg);
+
 		if (video->req[i]) {
 			usb_ep_free_request(video->ep, video->req[i]);
 			video->req[i] = NULL;
 		}
 
-		if (video->req_buffer[i]) {
+		if (!video->sg_mode && video->req_buffer[i]) {
 			kfree(video->req_buffer[i]);
 			video->req_buffer[i] = NULL;
 		}
@@ -243,19 +312,35 @@ uvc_video_alloc_requests(struct uvc_video *video)
 			* max_t(unsigned int, video->ep->maxburst, 1)
 			* (video->ep->mult + 1);
 	else
-		req_size = video->ep->maxpacket
-			* max_t(unsigned int, video->ep->maxburst, 1);
+		req_size = max_t(unsigned int, video->imagesize,
+			video->ep->maxpacket
+			* max_t(unsigned int, video->ep->maxburst, 1));
 
-	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
-		video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
-		if (video->req_buffer[i] == NULL)
-			goto error;
+	/* Allocate a UVC header here itself for SG mode. */
+	if (video->sg_mode)
+		video->header_buf = kmalloc(2 * sizeof(video->header_buf),
+					GFP_DMA);
 
+	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
 		video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
 		if (video->req[i] == NULL)
 			goto error;
 
-		video->req[i]->buf = video->req_buffer[i];
+		if (!video->sg_mode) {
+			video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
+			if (video->req_buffer[i] == NULL)
+				goto error;
+
+			video->req[i]->buf = video->req_buffer[i];
+		} else {
+			video->req[i]->sg =
+				kmalloc(2 * sizeof(video->req[i]->sg), GFP_DMA);
+			video->req[i]->num_sgs = 2;
+			sg_init_table(video->req[i]->sg, 2);
+			sg_set_buf(video->req[i]->sg,
+					(void *)video->header_buf, 2);
+		}
+
 		video->req[i]->length = 0;
 		video->req[i]->complete = uvc_video_complete;
 		video->req[i]->context = video;
-- 
1.7.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




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

  Powered by Linux