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.sha...@st.com>
---
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@vger.kernel.org/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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to