Re: [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]

 



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.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)");
> +

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