summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c11
-rw-r--r--drivers/usb/gadget/function/f_ncm.c60
-rw-r--r--drivers/usb/gadget/function/f_printer.c12
-rw-r--r--drivers/usb/gadget/function/f_tcm.c4
-rw-r--r--drivers/usb/gadget/function/f_uvc.c37
-rw-r--r--drivers/usb/gadget/function/rndis.c4
-rw-r--r--drivers/usb/gadget/function/u_ether.c8
-rw-r--r--drivers/usb/gadget/function/u_serial.c4
-rw-r--r--drivers/usb/gadget/function/uvc.h3
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c294
-rw-r--r--drivers/usb/gadget/function/uvc_video.c9
12 files changed, 390 insertions, 62 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index e0fa4b186ec6..73dc10a77cde 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2645,10 +2645,10 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
unsigned i = 0;
vla_group(d);
vla_item(d, struct usb_gadget_strings *, stringtabs,
- lang_count + 1);
+ size_add(lang_count, 1));
vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
vla_item(d, struct usb_string, strings,
- lang_count*(needed_count+1));
+ size_mul(lang_count, (needed_count + 1)));
char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
@@ -3700,7 +3700,7 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name)
existing = _ffs_do_find_dev(name);
if (!existing)
- strlcpy(dev->name, name, ARRAY_SIZE(dev->name));
+ strscpy(dev->name, name, ARRAY_SIZE(dev->name));
else if (existing != dev)
ret = -EBUSY;
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 925e99f9775c..3abf7f586e2a 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2662,11 +2662,16 @@ static ssize_t forced_eject_store(struct device *dev,
}
static DEVICE_ATTR_RW(nofua);
-/* mode wil be set in fsg_lun_attr_is_visible() */
-static DEVICE_ATTR(ro, 0, ro_show, ro_store);
-static DEVICE_ATTR(file, 0, file_show, file_store);
static DEVICE_ATTR_WO(forced_eject);
+/*
+ * Mode of the ro and file attribute files will be overridden in
+ * fsg_lun_dev_is_visible() depending on if this is a cdrom, or if it is a
+ * removable device.
+ */
+static DEVICE_ATTR_RW(ro);
+static DEVICE_ATTR_RW(file);
+
/****************************** FSG COMMON ******************************/
static void fsg_lun_release(struct device *dev)
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index dc8f078f918c..c36bcfa0e9b4 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -450,39 +450,35 @@ struct ndp_parser_opts {
unsigned next_ndp_index;
};
-#define INIT_NDP16_OPTS { \
- .nth_sign = USB_CDC_NCM_NTH16_SIGN, \
- .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
- .nth_size = sizeof(struct usb_cdc_ncm_nth16), \
- .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
- .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \
- .ndplen_align = 4, \
- .dgram_item_len = 1, \
- .block_length = 1, \
- .ndp_index = 1, \
- .reserved1 = 0, \
- .reserved2 = 0, \
- .next_ndp_index = 1, \
- }
-
-
-#define INIT_NDP32_OPTS { \
- .nth_sign = USB_CDC_NCM_NTH32_SIGN, \
- .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
- .nth_size = sizeof(struct usb_cdc_ncm_nth32), \
- .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
- .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \
- .ndplen_align = 8, \
- .dgram_item_len = 2, \
- .block_length = 2, \
- .ndp_index = 2, \
- .reserved1 = 1, \
- .reserved2 = 2, \
- .next_ndp_index = 2, \
- }
+static const struct ndp_parser_opts ndp16_opts = {
+ .nth_sign = USB_CDC_NCM_NTH16_SIGN,
+ .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,
+ .nth_size = sizeof(struct usb_cdc_ncm_nth16),
+ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16),
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe16),
+ .ndplen_align = 4,
+ .dgram_item_len = 1,
+ .block_length = 1,
+ .ndp_index = 1,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .next_ndp_index = 1,
+};
-static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
-static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+static const struct ndp_parser_opts ndp32_opts = {
+ .nth_sign = USB_CDC_NCM_NTH32_SIGN,
+ .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,
+ .nth_size = sizeof(struct usb_cdc_ncm_nth32),
+ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32),
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe32),
+ .ndplen_align = 8,
+ .dgram_item_len = 2,
+ .block_length = 2,
+ .ndp_index = 2,
+ .reserved1 = 1,
+ .reserved2 = 2,
+ .next_ndp_index = 2,
+};
static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
{
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index abec5c58f525..a881c69b1f2b 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -89,7 +89,7 @@ struct printer_dev {
u8 printer_cdev_open;
wait_queue_head_t wait;
unsigned q_len;
- char *pnp_string; /* We don't own memory! */
+ char **pnp_string; /* We don't own memory! */
struct usb_function function;
};
@@ -1000,16 +1000,16 @@ static int printer_func_setup(struct usb_function *f,
if ((wIndex>>8) != dev->interface)
break;
- if (!dev->pnp_string) {
+ if (!*dev->pnp_string) {
value = 0;
break;
}
- value = strlen(dev->pnp_string);
+ value = strlen(*dev->pnp_string);
buf[0] = (value >> 8) & 0xFF;
buf[1] = value & 0xFF;
- memcpy(buf + 2, dev->pnp_string, value);
+ memcpy(buf + 2, *dev->pnp_string, value);
DBG(dev, "1284 PNP String: %x %s\n", value,
- dev->pnp_string);
+ *dev->pnp_string);
break;
case GET_PORT_STATUS: /* Get Port Status */
@@ -1475,7 +1475,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
kref_init(&dev->kref);
++opts->refcnt;
dev->minor = opts->minor;
- dev->pnp_string = opts->pnp_string;
+ dev->pnp_string = &opts->pnp_string;
dev->q_len = opts->q_len;
mutex_unlock(&opts->lock);
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 8e17ac831be0..658e2e21fdd0 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -2306,7 +2306,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
-static int tcm_init(void)
+static int __init tcm_init(void)
{
int ret;
@@ -2322,7 +2322,7 @@ static int tcm_init(void)
}
module_init(tcm_init);
-static void tcm_exit(void)
+static void __exit tcm_exit(void)
{
target_unregister_template(&usbg_ops);
usb_function_unregister(&tcmusb_func);
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 71669e0e4d00..6e196e06181e 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -421,7 +421,7 @@ uvc_register_video(struct uvc_device *uvc)
int ret;
/* TODO reference counting. */
- memset(&uvc->vdev, 0, sizeof(uvc->video));
+ memset(&uvc->vdev, 0, sizeof(uvc->vdev));
uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops;
@@ -430,7 +430,7 @@ uvc_register_video(struct uvc_device *uvc)
uvc->vdev.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
+ strscpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
video_set_drvdata(&uvc->vdev, uvc);
@@ -888,6 +888,7 @@ static void uvc_free(struct usb_function *f)
struct uvc_device *uvc = to_uvc(f);
struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
func_inst);
+ config_item_put(&uvc->header->item);
--opts->refcnt;
kfree(uvc);
}
@@ -897,10 +898,14 @@ static void uvc_function_unbind(struct usb_configuration *c,
{
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
+ struct uvc_video *video = &uvc->video;
long wait_ret = 1;
uvcg_info(f, "%s()\n", __func__);
+ if (video->async_wq)
+ destroy_workqueue(video->async_wq);
+
/*
* If we know we're connected via v4l2, then there should be a cleanup
* of the device from userspace either via UVC_EVENT_DISCONNECT or
@@ -941,6 +946,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
struct uvc_device *uvc;
struct f_uvc_opts *opts;
struct uvc_descriptor_header **strm_cls;
+ struct config_item *streaming, *header, *h;
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
if (uvc == NULL)
@@ -973,6 +979,28 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->desc.fs_streaming = opts->fs_streaming;
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
+
+ streaming = config_group_find_item(&opts->func_inst.group, "streaming");
+ if (!streaming)
+ goto err_config;
+
+ header = config_group_find_item(to_config_group(streaming), "header");
+ config_item_put(streaming);
+ if (!header)
+ goto err_config;
+
+ h = config_group_find_item(to_config_group(header), "h");
+ config_item_put(header);
+ if (!h)
+ goto err_config;
+
+ uvc->header = to_uvcg_streaming_header(h);
+ if (!uvc->header->linked) {
+ mutex_unlock(&opts->lock);
+ kfree(uvc);
+ return ERR_PTR(-EBUSY);
+ }
+
++opts->refcnt;
mutex_unlock(&opts->lock);
@@ -988,6 +1016,11 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->func.bind_deactivated = true;
return &uvc->func;
+
+err_config:
+ mutex_unlock(&opts->lock);
+ kfree(uvc);
+ return ERR_PTR(-ENOENT);
}
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 713efd9aefde..29bf8664bf58 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -869,7 +869,7 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser);
static inline int rndis_get_nr(void)
{
- return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL);
+ return ida_simple_get(&rndis_ida, 0, 1000, GFP_KERNEL);
}
static inline void rndis_put_nr(int nr)
@@ -1105,7 +1105,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
"used : %s\n"
"state : %s\n"
"medium : 0x%08X\n"
- "speed : %d\n"
+ "speed : %u\n"
"cable : %s\n"
"vendor ID : 0x%08X\n"
"vendor : %s\n",
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 7887def05dc2..e06022873df1 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -144,10 +144,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
{
struct eth_dev *dev = netdev_priv(net);
- strlcpy(p->driver, "g_ether", sizeof(p->driver));
- strlcpy(p->version, UETH__VERSION, sizeof(p->version));
- strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
- strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+ strscpy(p->driver, "g_ether", sizeof(p->driver));
+ strscpy(p->version, UETH__VERSION, sizeof(p->version));
+ strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
+ strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
}
/* REVISIT can also support:
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 6f68cbeeee7c..7538279f9817 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -1443,7 +1443,7 @@ void gserial_resume(struct gserial *gser)
}
EXPORT_SYMBOL_GPL(gserial_resume);
-static int userial_init(void)
+static int __init userial_init(void)
{
struct tty_driver *driver;
unsigned i;
@@ -1496,7 +1496,7 @@ fail:
}
module_init(userial_init);
-static void userial_cleanup(void)
+static void __exit userial_cleanup(void)
{
tty_unregister_driver(gs_tty_driver);
tty_driver_kref_put(gs_tty_driver);
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 58e383afdd44..40226b1f7e14 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -88,6 +88,7 @@ struct uvc_video {
struct usb_ep *ep;
struct work_struct pump;
+ struct workqueue_struct *async_wq;
/* Frame parameters */
u8 bpp;
@@ -133,6 +134,8 @@ struct uvc_device {
bool func_connected;
wait_queue_head_t func_connected_queue;
+ struct uvcg_streaming_header *header;
+
/* Descriptors */
struct {
const struct uvc_descriptor_header * const *fs_control;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index fd8f73bb726d..c4ed48d6b8a4 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -18,12 +18,161 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-uvc.h>
#include "f_uvc.h"
#include "uvc.h"
#include "uvc_queue.h"
#include "uvc_video.h"
#include "uvc_v4l2.h"
+#include "uvc_configfs.h"
+
+static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat)
+{
+ char guid[16] = UVC_GUID_FORMAT_MJPEG;
+ struct uvc_format_desc *format;
+ struct uvcg_uncompressed *unc;
+
+ if (uformat->type == UVCG_UNCOMPRESSED) {
+ unc = to_uvcg_uncompressed(&uformat->group.cg_item);
+ if (!unc)
+ return ERR_PTR(-EINVAL);
+
+ memcpy(guid, unc->desc.guidFormat, sizeof(guid));
+ }
+
+ format = uvc_format_by_guid(guid);
+ if (!format)
+ return ERR_PTR(-EINVAL);
+
+ return format;
+}
+
+static int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat,
+ struct uvcg_frame *uframe)
+{
+ struct uvcg_uncompressed *u;
+
+ if (uformat->type == UVCG_UNCOMPRESSED) {
+ u = to_uvcg_uncompressed(&uformat->group.cg_item);
+ if (!u)
+ return 0;
+
+ return u->desc.bBitsPerPixel * uframe->frame.w_width / 8;
+ }
+
+ return 0;
+}
+
+static int uvc_get_frame_size(struct uvcg_format *uformat,
+ struct uvcg_frame *uframe)
+{
+ unsigned int bpl = uvc_v4l2_get_bytesperline(uformat, uframe);
+
+ return bpl ? bpl * uframe->frame.w_height :
+ uframe->frame.dw_max_video_frame_buffer_size;
+}
+
+static struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index)
+{
+ struct uvcg_format_ptr *format;
+ struct uvcg_format *uformat = NULL;
+ int i = 1;
+
+ list_for_each_entry(format, &uvc->header->formats, entry) {
+ if (index == i) {
+ uformat = format->fmt;
+ break;
+ }
+ i++;
+ }
+
+ return uformat;
+}
+
+static struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc,
+ struct uvcg_format *uformat,
+ int index)
+{
+ struct uvcg_format_ptr *format;
+ struct uvcg_frame_ptr *frame;
+ struct uvcg_frame *uframe = NULL;
+
+ list_for_each_entry(format, &uvc->header->formats, entry) {
+ if (format->fmt->type != uformat->type)
+ continue;
+ list_for_each_entry(frame, &format->fmt->frames, entry) {
+ if (index == frame->frm->frame.b_frame_index) {
+ uframe = frame->frm;
+ break;
+ }
+ }
+ }
+
+ return uframe;
+}
+
+static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
+ u32 pixelformat)
+{
+ struct uvcg_format_ptr *format;
+ struct uvcg_format *uformat = NULL;
+
+ list_for_each_entry(format, &uvc->header->formats, entry) {
+ struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
+
+ if (fmtdesc->fcc == pixelformat) {
+ uformat = format->fmt;
+ break;
+ }
+ }
+
+ return uformat;
+}
+
+static struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc,
+ struct uvcg_format *uformat,
+ u16 rw, u16 rh)
+{
+ struct uvc_video *video = &uvc->video;
+ struct uvcg_format_ptr *format;
+ struct uvcg_frame_ptr *frame;
+ struct uvcg_frame *uframe = NULL;
+ unsigned int d, maxd;
+
+ /* Find the closest image size. The distance between image sizes is
+ * the size in pixels of the non-overlapping regions between the
+ * requested size and the frame-specified size.
+ */
+ maxd = (unsigned int)-1;
+
+ list_for_each_entry(format, &uvc->header->formats, entry) {
+ if (format->fmt->type != uformat->type)
+ continue;
+
+ list_for_each_entry(frame, &format->fmt->frames, entry) {
+ u16 w, h;
+
+ w = frame->frm->frame.w_width;
+ h = frame->frm->frame.w_height;
+
+ d = min(w, rw) * min(h, rh);
+ d = w*h + rw*rh - 2*d;
+ if (d < maxd) {
+ maxd = d;
+ uframe = frame->frm;
+ }
+
+ if (maxd == 0)
+ break;
+ }
+ }
+
+ if (!uframe)
+ uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh);
+
+ return uframe;
+}
/* --------------------------------------------------------------------------
* Requests handling
@@ -67,9 +216,9 @@ uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
struct uvc_device *uvc = video_get_drvdata(vdev);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
- strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
- strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
+ strscpy(cap->driver, "g_uvc", sizeof(cap->driver));
+ strscpy(cap->card, cdev->gadget->name, sizeof(cap->card));
+ strscpy(cap->bus_info, dev_name(&cdev->gadget->dev),
sizeof(cap->bus_info));
return 0;
}
@@ -135,6 +284,139 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
}
static int
+uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
+ struct uvcg_format *uformat;
+ struct uvcg_frame *uframe;
+ u8 *fcc;
+
+ if (fmt->type != video->queue.queue.type)
+ return -EINVAL;
+
+ fcc = (u8 *)&fmt->fmt.pix.pixelformat;
+ uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n",
+ fmt->fmt.pix.pixelformat,
+ fcc[0], fcc[1], fcc[2], fcc[3],
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+
+ uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat);
+ if (!uformat)
+ return -EINVAL;
+
+ uframe = find_closest_frame_by_size(uvc, uformat,
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+ if (!uframe)
+ return -EINVAL;
+
+ fmt->fmt.pix.width = uframe->frame.w_width;
+ fmt->fmt.pix.height = uframe->frame.w_height;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
+ fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
+ fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int
+uvc_v4l2_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvcg_format *uformat = NULL;
+ struct uvcg_frame *uframe = NULL;
+ struct uvcg_frame_ptr *frame;
+
+ uformat = find_format_by_pix(uvc, fival->pixel_format);
+ if (!uformat)
+ return -EINVAL;
+
+ list_for_each_entry(frame, &uformat->frames, entry) {
+ if (frame->frm->frame.w_width == fival->width &&
+ frame->frm->frame.w_height == fival->height) {
+ uframe = frame->frm;
+ break;
+ }
+ }
+ if (!uframe)
+ return -EINVAL;
+
+ if (fival->index >= uframe->frame.b_frame_interval_type)
+ return -EINVAL;
+
+ fival->discrete.numerator =
+ uframe->dw_frame_interval[fival->index];
+
+ /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.denominator = 10000000;
+ v4l2_simplify_fraction(&fival->discrete.numerator,
+ &fival->discrete.denominator, 8, 333);
+
+ return 0;
+}
+
+static int
+uvc_v4l2_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvcg_format *uformat = NULL;
+ struct uvcg_frame *uframe = NULL;
+
+ uformat = find_format_by_pix(uvc, fsize->pixel_format);
+ if (!uformat)
+ return -EINVAL;
+
+ if (fsize->index >= uformat->num_frames)
+ return -EINVAL;
+
+ uframe = find_frame_by_index(uvc, uformat, fsize->index + 1);
+ if (!uframe)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = uframe->frame.w_width;
+ fsize->discrete.height = uframe->frame.w_height;
+
+ return 0;
+}
+
+static int
+uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_format_desc *fmtdesc;
+ struct uvcg_format *uformat;
+
+ if (f->index >= uvc->header->num_fmt)
+ return -EINVAL;
+
+ uformat = find_format_by_index(uvc, f->index + 1);
+ if (!uformat)
+ return -EINVAL;
+
+ if (uformat->type != UVCG_UNCOMPRESSED)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+
+ fmtdesc = to_uvc_format(uformat);
+ f->pixelformat = fmtdesc->fcc;
+
+ strscpy(f->description, fmtdesc->name, sizeof(f->description));
+ f->description[strlen(fmtdesc->name) - 1] = 0;
+
+ return 0;
+}
+
+static int
uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
struct video_device *vdev = video_devdata(file);
@@ -170,7 +452,7 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
return ret;
if (uvc->state == UVC_STATE_STREAMING)
- schedule_work(&video->pump);
+ queue_work(video->async_wq, &video->pump);
return ret;
}
@@ -298,8 +580,12 @@ uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_querycap = uvc_v4l2_querycap,
+ .vidioc_try_fmt_vid_out = uvc_v4l2_try_format,
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
+ .vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals,
+ .vidioc_enum_framesizes = uvc_v4l2_enum_framesizes,
+ .vidioc_enum_fmt_vid_out = uvc_v4l2_enum_format,
.vidioc_reqbufs = uvc_v4l2_reqbufs,
.vidioc_querybuf = uvc_v4l2_querybuf,
.vidioc_qbuf = uvc_v4l2_qbuf,
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index c00ce0e91f5d..bb037fcc90e6 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -277,7 +277,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&video->req_lock, flags);
if (uvc->state == UVC_STATE_STREAMING)
- schedule_work(&video->pump);
+ queue_work(video->async_wq, &video->pump);
}
static int
@@ -485,7 +485,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
video->req_int_count = 0;
- schedule_work(&video->pump);
+ queue_work(video->async_wq, &video->pump);
return ret;
}
@@ -499,6 +499,11 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
spin_lock_init(&video->req_lock);
INIT_WORK(&video->pump, uvcg_video_pump);
+ /* Allocate a work queue for asynchronous video pump handler. */
+ video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0);
+ if (!video->async_wq)
+ return -EINVAL;
+
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16;