diff options
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_driver.c | 11 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_queue.c | 2 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 207 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 14 |
4 files changed, 226 insertions, 8 deletions
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a240d43d15d..1d131720b6d 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -23,6 +23,7 @@ * codec can't handle MJPEG data. */ +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> @@ -32,7 +33,6 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/version.h> -#include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> @@ -2139,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Dell XPS m1530 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05a9, + .idProduct = 0x2640, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 518f77d3a4d..8f54e24e3f3 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -126,7 +126,7 @@ void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int drop_corrupted) { queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 2ae4f880ea0..ff2cdddf9bc 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -11,6 +11,7 @@ * */ +#include <linux/compat.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/list.h> @@ -1012,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) default: uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); - return -EINVAL; + return -ENOTTY; } return ret; @@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file, return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } +#ifdef CONFIG_COMPAT +struct uvc_xu_control_mapping32 { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + __u32 v4l2_type; + __u32 data_type; + + compat_caddr_t menu_info; + __u32 menu_count; + + __u32 reserved[4]; +}; + +static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, + const struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || + __get_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + memset(kp->reserved, 0, sizeof(kp->reserved)); + + if (kp->menu_count == 0) { + kp->menu_info = NULL; + return 0; + } + + if (__get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); + if (kmenus == NULL) + return -EFAULT; + kp->menu_info = kmenus; + + if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, + struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus = kp->menu_info; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || + __put_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + __clear_user(up->reserved, sizeof(up->reserved)); + + if (kp->menu_count == 0) + return 0; + + if (get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +struct uvc_xu_control_query32 { + __u8 unit; + __u8 selector; + __u8 query; + __u16 size; + compat_caddr_t data; +}; + +static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, + const struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) { + kp->data = NULL; + return 0; + } + + if (__get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + kdata = compat_alloc_user_space(kp->size); + if (kdata == NULL) + return -EFAULT; + kp->data = kdata; + + if (copy_in_user(kdata, udata, kp->size)) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, + struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata = kp->data; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) + return 0; + + if (get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + if (copy_in_user(udata, kdata, kp->size)) + return -EFAULT; + + return 0; +} + +#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) +#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) + +static long uvc_v4l2_compat_ioctl32(struct file *file, + unsigned int cmd, unsigned long arg) +{ + union { + struct uvc_xu_control_mapping xmap; + struct uvc_xu_control_query xqry; + } karg; + void __user *up = compat_ptr(arg); + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP32: + cmd = UVCIOC_CTRL_MAP; + ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY32: + cmd = UVCIOC_CTRL_QUERY; + ret = uvc_v4l2_get_xu_query(&karg.xqry, up); + break; + + default: + return -ENOIOCTLCMD; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if (ret < 0) + return ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP: + ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY: + ret = uvc_v4l2_put_xu_query(&karg.xqry, up); + break; + } + + return ret; +} +#endif + static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = { .open = uvc_v4l2_open, .release = uvc_v4l2_release, .unlocked_ioctl = uvc_v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = uvc_v4l2_compat_ioctl32, +#endif .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index c7e69b8f81c..4a44f9a1bae 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -611,9 +611,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream, delta_stc = buf->pts - (1UL << 31); x1 = first->dev_stc - delta_stc; x2 = last->dev_stc - delta_stc; + if (x1 == x2) + goto done; + y1 = (first->dev_sof + 2048) << 16; y2 = (last->dev_sof + 2048) << 16; - if (y2 < y1) y2 += 2048 << 16; @@ -631,14 +633,16 @@ void uvc_video_clock_update(struct uvc_streaming *stream, x1, x2, y1, y2, clock->sof_offset); /* Second step, SOF to host clock conversion. */ - ts = timespec_sub(last->host_ts, first->host_ts); x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; x2 = (uvc_video_clock_host_sof(last) + 2048) << 16; - y1 = NSEC_PER_SEC; - y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; - if (x2 < x1) x2 += 2048 << 16; + if (x1 == x2) + goto done; + + ts = timespec_sub(last->host_ts, first->host_ts); + y1 = NSEC_PER_SEC; + y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; /* Interpolated and host SOF timestamps can wrap around at slightly * different times. Handle this by adding or removing 2048 to or from |