Hi Bhupesh,

Thank you for the patch.

On Wednesday 17 April 2013 09:44:25 Bhupesh Sharma wrote:
> 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)");
> +

Can't this be queried automatically from the UDC at runtime ?

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

Don't you need one header per request ?

It would probably be easier to create a uvc_request structure to group the 
request, the request header and the request buffer:

struct uvc_request{
        struct usb_request *req;
        __u8 *header;
        __u8 *buffer;
};

and have a table of those in the uvc_video structure.

>       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;
> +}

Instead of duplicating the code could you just pass the header pointer to 
uvc_video_encode_header() ? It might be even better to pass a uvc_request 
structure pointer instead of a data pointer and move the sg_mode check to 
uvc_video_encode_header() instead of spreading it around the code.

> +
> +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;
> +}

Same comments here as for uvc_video_encode_header(). I think the code will be 
cleaner if you move the sg_mode checks to the encode* functions.

> +
>  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;
-- 
Regards,

Laurent Pinchart

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