@@ -7,6 +7,7 @@ Description: UVC function directory
streaming_maxburst 0..15 (ss only)
streaming_maxpacket 1..1023 (fs), 1..3072 (hs/ss)
streaming_interval 1..16
+ streaming_bulk_mult 0..0x3fffffU
=================== =============================
What: /config/usb-gadget/gadget/functions/uvc.name/control
@@ -787,6 +787,10 @@ The uvc function provides these attributes in its function directory:
streaming_maxpacket maximum packet size this endpoint is capable of
sending or receiving when this configuration is
selected
+ streaming_bulk_mult Multiples to configure max_payload_size. If it's
+ 0, the transport mode is isochronous; otherwise
+ the transport mode is bulk and max_payload_size
+ is equal to streaming_bulk_mult * 1024.
=================== ================================================
There are also "control" and "streaming" subdirectories, each of which contain
@@ -30,6 +30,8 @@
#include "uvc_v4l2.h"
#include "uvc_video.h"
+#define USBDHDR(p) ((struct usb_descriptor_header *)(p))
+
unsigned int uvc_gadget_trace_param;
module_param_named(trace, uvc_gadget_trace_param, uint, 0644);
MODULE_PARM_DESC(trace, "Trace level bitmask");
@@ -178,19 +180,19 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
*/
};
-static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
+static const struct usb_descriptor_header *uvc_fs_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_fs_streaming_ep,
NULL,
};
-static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
+static const struct usb_descriptor_header *uvc_hs_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_hs_streaming_ep,
NULL,
};
-static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
+static const struct usb_descriptor_header *uvc_ss_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_ss_streaming_ep,
(struct usb_descriptor_header *) &uvc_ss_streaming_comp,
@@ -251,9 +253,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
void uvc_function_setup_continue(struct uvc_device *uvc)
{
+ struct f_uvc_opts *opts = fi_to_f_uvc_opts(uvc->func.fi);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
- usb_composite_setup_continue(cdev);
+ /* delayed_status in bulk mode is 0, so it doesn't need to continue. */
+ if (!opts->streaming_bulk_mult)
+ usb_composite_setup_continue(cdev);
}
static int
@@ -278,6 +283,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
struct usb_composite_dev *cdev = f->config->cdev;
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+ struct f_uvc_opts *opts = fi_to_f_uvc_opts(f->fi);
int ret;
uvcg_info(f, "%s(%u, %u)\n", __func__, interface, alt);
@@ -310,49 +316,72 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (interface != uvc->streaming_intf)
return -EINVAL;
- /* TODO
- if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
- return alt ? -EINVAL : 0;
- */
+ if (opts->streaming_bulk_mult) {
+ switch (alt) {
+ case 0:
+ if (uvc->state != UVC_STATE_CONNECTED)
+ return 0;
- switch (alt) {
- case 0:
- if (uvc->state != UVC_STATE_STREAMING)
- return 0;
+ if (!uvc->video.ep)
+ return -EINVAL;
- if (uvc->video.ep)
+ uvcg_info(f, "reset UVC\n");
usb_ep_disable(uvc->video.ep);
- memset(&v4l2_event, 0, sizeof(v4l2_event));
- v4l2_event.type = UVC_EVENT_STREAMOFF;
- v4l2_event_queue(&uvc->vdev, &v4l2_event);
-
- uvc->state = UVC_STATE_CONNECTED;
- return 0;
+ ret = config_ep_by_speed(f->config->cdev->gadget,
+ &(uvc->func), uvc->video.ep);
+ if (ret)
+ return ret;
+ usb_ep_enable(uvc->video.ep);
- case 1:
- if (uvc->state != UVC_STATE_CONNECTED)
+ memset(&v4l2_event, 0, sizeof(v4l2_event));
+ v4l2_event.type = UVC_EVENT_STREAMOFF;
+ v4l2_event_queue(&uvc->vdev, &v4l2_event);
return 0;
- if (!uvc->video.ep)
+ default:
return -EINVAL;
+ }
+ } else {
+ switch (alt) {
+ case 0:
+ if (uvc->state != UVC_STATE_STREAMING)
+ return 0;
+
+ if (uvc->video.ep)
+ usb_ep_disable(uvc->video.ep);
+
+ memset(&v4l2_event, 0, sizeof(v4l2_event));
+ v4l2_event.type = UVC_EVENT_STREAMOFF;
+ v4l2_event_queue(&uvc->vdev, &v4l2_event);
- uvcg_info(f, "reset UVC\n");
- usb_ep_disable(uvc->video.ep);
+ uvc->state = UVC_STATE_CONNECTED;
+ return 0;
- ret = config_ep_by_speed(f->config->cdev->gadget,
- &(uvc->func), uvc->video.ep);
- if (ret)
- return ret;
- usb_ep_enable(uvc->video.ep);
+ case 1:
+ if (uvc->state != UVC_STATE_CONNECTED)
+ return 0;
- memset(&v4l2_event, 0, sizeof(v4l2_event));
- v4l2_event.type = UVC_EVENT_STREAMON;
- v4l2_event_queue(&uvc->vdev, &v4l2_event);
- return USB_GADGET_DELAYED_STATUS;
+ if (!uvc->video.ep)
+ return -EINVAL;
- default:
- return -EINVAL;
+ uvcg_info(f, "reset UVC\n");
+ usb_ep_disable(uvc->video.ep);
+
+ ret = config_ep_by_speed(f->config->cdev->gadget,
+ &(uvc->func), uvc->video.ep);
+ if (ret)
+ return ret;
+ usb_ep_enable(uvc->video.ep);
+
+ memset(&v4l2_event, 0, sizeof(v4l2_event));
+ v4l2_event.type = UVC_EVENT_STREAMON;
+ v4l2_event_queue(&uvc->vdev, &v4l2_event);
+ return USB_GADGET_DELAYED_STATUS;
+
+ default:
+ return -EINVAL;
+ }
}
}
@@ -593,62 +622,96 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
unsigned int max_packet_size;
struct usb_ep *ep;
struct f_uvc_opts *opts;
+ int i = 0;
int ret = -EINVAL;
uvcg_info(f, "%s()\n", __func__);
opts = fi_to_f_uvc_opts(f->fi);
- /* Sanity check the streaming endpoint module parameters.
- */
- opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
- opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
- opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
-
- /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
- if (opts->streaming_maxburst &&
- (opts->streaming_maxpacket % 1024) != 0) {
- opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);
- uvcg_info(f, "overriding streaming_maxpacket to %d\n",
- opts->streaming_maxpacket);
- }
- /* Fill in the FS/HS/SS Video Streaming specific descriptors from the
- * module parameters.
- *
- * NOTE: We assume that the user knows what they are doing and won't
- * give parameters that their UDC doesn't support.
- */
- if (opts->streaming_maxpacket <= 1024) {
- max_packet_mult = 1;
- max_packet_size = opts->streaming_maxpacket;
- } else if (opts->streaming_maxpacket <= 2048) {
- max_packet_mult = 2;
- max_packet_size = opts->streaming_maxpacket / 2;
+ /* Handle different transfer mode for stream endpoints */
+ if (opts->streaming_bulk_mult) {
+ uvc_fs_streaming_ep.bmAttributes = USB_ENDPOINT_XFER_BULK;
+ uvc_hs_streaming_ep.bmAttributes = uvc_fs_streaming_ep.bmAttributes;
+ uvc_ss_streaming_ep.bmAttributes = uvc_fs_streaming_ep.bmAttributes;
+
+ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+
+ uvc_fs_streaming_ep.wMaxPacketSize = cpu_to_le16(64);
+ uvc_fs_streaming_ep.bInterval = 0;
+
+ uvc_hs_streaming_ep.wMaxPacketSize = cpu_to_le16(512);
+ uvc_hs_streaming_ep.bInterval = 0;
+
+ uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(1024);
+ uvc_ss_streaming_ep.bInterval = 0;
+
+ uvc_ss_streaming_comp.bmAttributes = 0;
+ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
+ uvc_ss_streaming_comp.wBytesPerInterval = 0;
+
+ uvc->video.max_payload_size = opts->streaming_bulk_mult * 1024;
} else {
- max_packet_mult = 3;
- max_packet_size = opts->streaming_maxpacket / 3;
- }
+ uvc_fs_streaming_ep.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
+ | USB_ENDPOINT_XFER_ISOC;
+ uvc_hs_streaming_ep.bmAttributes = uvc_fs_streaming_ep.bmAttributes;
+ uvc_ss_streaming_ep.bmAttributes = uvc_fs_streaming_ep.bmAttributes;
+
+ /* Sanity check the streaming endpoint module parameters.
+ */
+ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
+ opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
+ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+
+ /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
+ if (opts->streaming_maxburst &&
+ (opts->streaming_maxpacket % 1024) != 0) {
+ opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);
+ uvcg_info(f, "overriding streaming_maxpacket to %d\n",
+ opts->streaming_maxpacket);
+ }
- uvc_fs_streaming_ep.wMaxPacketSize =
- cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
- uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
+ /* Fill in the FS/HS/SS Video Streaming specific descriptors from the
+ * module parameters.
+ *
+ * NOTE: We assume that the user knows what they are doing and won't
+ * give parameters that their UDC doesn't support.
+ */
+
+ if (opts->streaming_maxpacket <= 1024) {
+ max_packet_mult = 0;
+ max_packet_size = opts->streaming_maxpacket;
+ } else if (opts->streaming_maxpacket <= 2048) {
+ max_packet_mult = 1;
+ max_packet_size = opts->streaming_maxpacket / 2;
+ } else {
+ max_packet_mult = 2;
+ max_packet_size = opts->streaming_maxpacket / 3;
+ }
- uvc_hs_streaming_ep.wMaxPacketSize =
- cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
+ uvc_fs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
+ uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
- /* A high-bandwidth endpoint must specify a bInterval value of 1 */
- if (max_packet_mult > 1)
- uvc_hs_streaming_ep.bInterval = 1;
- else
- uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
+ uvc_hs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(max_packet_size | (max_packet_mult << 11));
+ /* A high-bandwidth endpoint must specify a bInterval value of 1 */
+ if (max_packet_mult > 0)
+ uvc_hs_streaming_ep.bInterval = 1;
+ else
+ uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
+
+ uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
+ uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
- uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
- uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
- uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
- uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
- uvc_ss_streaming_comp.wBytesPerInterval =
- cpu_to_le16(max_packet_size * max_packet_mult *
- (opts->streaming_maxburst + 1));
+ uvc_ss_streaming_comp.bmAttributes = max_packet_mult;
+ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
+ uvc_ss_streaming_comp.wBytesPerInterval =
+ cpu_to_le16(max_packet_size * (max_packet_mult + 1) *
+ (opts->streaming_maxburst + 1));
+
+ uvc->video.max_payload_size = 0;
+ }
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
@@ -662,7 +725,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
&uvc_ss_streaming_comp);
else if (gadget_is_dualspeed(cdev->gadget))
- ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
+ ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_hs_streaming_ep, NULL);
else
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
@@ -703,6 +766,28 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc->streaming_intf = ret;
opts->streaming_interface = ret;
+ /* Handle different transfer mode for descriptors */
+ i = 0;
+ if (opts->streaming_bulk_mult) {
+ uvc_streaming_intf_alt0.bNumEndpoints = 1;
+ } else {
+ uvc_streaming_intf_alt0.bNumEndpoints = 0;
+
+ uvc_fs_streaming[i] = USBDHDR(&uvc_streaming_intf_alt1);
+ uvc_hs_streaming[i] = USBDHDR(&uvc_streaming_intf_alt1);
+ uvc_ss_streaming[i] = USBDHDR(&uvc_streaming_intf_alt1);
+ ++i;
+ }
+ uvc_fs_streaming[i] = USBDHDR(&uvc_fs_streaming_ep);
+ uvc_hs_streaming[i] = USBDHDR(&uvc_hs_streaming_ep);
+ uvc_ss_streaming[i] = USBDHDR(&uvc_ss_streaming_ep);
+ ++i;
+ uvc_fs_streaming[i] = NULL;
+ uvc_hs_streaming[i] = NULL;
+ uvc_ss_streaming[i] = USBDHDR(&uvc_ss_streaming_comp);
+ ++i;
+ uvc_ss_streaming[i] = NULL;
+
/* Copy descriptors */
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
if (IS_ERR(f->fs_descriptors)) {
@@ -866,6 +951,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
opts->streaming_interval = 1;
opts->streaming_maxpacket = 1024;
+ opts->streaming_bulk_mult = 0;
ret = uvcg_attach_configfs(opts);
if (ret < 0) {
@@ -24,6 +24,7 @@ struct f_uvc_opts {
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
+ unsigned int streaming_bulk_mult;
unsigned int control_interface;
unsigned int streaming_interface;
@@ -2422,6 +2422,7 @@ UVC_ATTR(f_uvc_opts_, cname, cname)
UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16);
UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072);
UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15);
+UVCG_OPTS_ATTR(streaming_bulk_mult, streaming_bulk_mult, 0x3fffffU);
#undef UVCG_OPTS_ATTR
@@ -2429,6 +2430,7 @@ static struct configfs_attribute *uvc_attrs[] = {
&f_uvc_opts_attr_streaming_interval,
&f_uvc_opts_attr_streaming_maxpacket,
&f_uvc_opts_attr_streaming_maxburst,
+ &f_uvc_opts_attr_streaming_bulk_mult,
NULL,
};
@@ -135,7 +135,9 @@ int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
queue->queue.lock = lock;
- if (cdev->gadget->sg_supported) {
+
+ /* UDC supports scatter gather and transfer mode isn't bulk. */
+ if (cdev->gadget->sg_supported && !video->max_payload_size) {
queue->queue.mem_ops = &vb2_dma_sg_memops;
queue->use_sg = 1;
} else {
@@ -119,6 +119,14 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
bpl = format->bpp * fmt->fmt.pix.width / 8;
imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
+ /*
+ * Bulk mode only allocates memory once, so user should give the
+ * maximum image size in all formats and kernel should not decrease
+ * the imagesize.
+ */
+ if (video->max_payload_size && imagesize < fmt->fmt.pix.sizeimage)
+ imagesize = fmt->fmt.pix.sizeimage;
+
video->fcc = format->fcc;
video->bpp = format->bpp;
video->width = fmt->fmt.pix.width;