From e1d1bcb86be500a02edc46c0939d81f6cb8b6eec Mon Sep 17 00:00:00 2001 From: blogic Date: Thu, 19 Sep 2013 05:57:00 +0000 Subject: [PATCH] ralink: make dcs930 uvc camera work Signed-off-by: John Crispin git-svn-id: svn://svn.openwrt.org/openwrt/trunk@38054 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../0205-uvc-add-iPassion-iP2970-support.patch | 255 +++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 target/linux/ramips/patches-3.10/0205-uvc-add-iPassion-iP2970-support.patch diff --git a/target/linux/ramips/patches-3.10/0205-uvc-add-iPassion-iP2970-support.patch b/target/linux/ramips/patches-3.10/0205-uvc-add-iPassion-iP2970-support.patch new file mode 100644 index 0000000000..5c5cf7f596 --- /dev/null +++ b/target/linux/ramips/patches-3.10/0205-uvc-add-iPassion-iP2970-support.patch @@ -0,0 +1,255 @@ +From be8d5b55f93b8ccb3a6b5cfb1e858a59aeca2d6c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 19 Sep 2013 01:50:59 +0200 +Subject: [PATCH] uvc: add iPassion iP2970 support + +Signed-off-by: John Crispin +--- + drivers/media/usb/uvc/uvc_driver.c | 12 +++++++++ + drivers/media/usb/uvc/uvc_status.c | 2 ++ + drivers/media/usb/uvc/uvc_v4l2.c | 1 + + drivers/media/usb/uvc/uvc_video.c | 50 +++++++++++++++++++++++++++++++----- + drivers/media/usb/uvc/uvcvideo.h | 3 +++ + 5 files changed, 61 insertions(+), 7 deletions(-) + +Index: linux-3.10.12/drivers/media/usb/uvc/uvc_driver.c +=================================================================== +--- linux-3.10.12.orig/drivers/media/usb/uvc/uvc_driver.c 2013-09-14 15:55:12.000000000 +0200 ++++ linux-3.10.12/drivers/media/usb/uvc/uvc_driver.c 2013-09-19 04:30:11.825592123 +0200 +@@ -2420,6 +2420,20 @@ + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, ++ ++/* iPassion iP2970 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x1B3B, ++ .idProduct = 0x2970, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 0, ++ .driver_info = UVC_QUIRK_PROBE_MINMAX ++ | UVC_QUIRK_STREAM_NO_FID ++ | UVC_QUIRK_MOTION ++ | UVC_QUIRK_SINGLE_ISO }, ++ + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +Index: linux-3.10.12/drivers/media/usb/uvc/uvc_status.c +=================================================================== +--- linux-3.10.12.orig/drivers/media/usb/uvc/uvc_status.c 2013-09-14 15:55:12.000000000 +0200 ++++ linux-3.10.12/drivers/media/usb/uvc/uvc_status.c 2013-09-19 01:52:55.721188742 +0200 +@@ -139,6 +139,7 @@ + switch (dev->status[0] & 0x0f) { + case UVC_STATUS_TYPE_CONTROL: + uvc_event_control(dev, dev->status, len); ++ dev->motion = 1; + break; + + case UVC_STATUS_TYPE_STREAMING: +@@ -182,6 +183,7 @@ + } + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); ++ dev->motion = 0; + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. +Index: linux-3.10.12/drivers/media/usb/uvc/uvc_video.c +=================================================================== +--- linux-3.10.12.orig/drivers/media/usb/uvc/uvc_video.c 2013-09-14 15:55:12.000000000 +0200 ++++ linux-3.10.12/drivers/media/usb/uvc/uvc_video.c 2013-09-19 07:31:28.642057093 +0200 +@@ -21,6 +21,11 @@ + #include + #include + #include ++#include ++#include ++#include ++#include ++#include + + #include + +@@ -1074,9 +1079,149 @@ + } + } + ++struct bh_priv { ++ unsigned long seen; ++}; ++ ++struct bh_event { ++ const char *name; ++ struct sk_buff *skb; ++ struct work_struct work; ++}; ++ ++#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, "webcam", ##args ) ++#define BH_DBG(fmt, args...) do {} while (0) ++#define BH_SKB_SIZE 2048 ++ ++extern u64 uevent_next_seqnum(void); ++static int seen = 0; ++ ++static int bh_event_add_var(struct bh_event *event, int argv, ++ const char *format, ...) ++{ ++ static char buf[128]; ++ char *s; ++ va_list args; ++ int len; ++ ++ if (argv) ++ return 0; ++ ++ va_start(args, format); ++ len = vsnprintf(buf, sizeof(buf), format, args); ++ va_end(args); ++ ++ if (len >= sizeof(buf)) { ++ BH_ERR("buffer size too small\n"); ++ WARN_ON(1); ++ return -ENOMEM; ++ } ++ ++ s = skb_put(event->skb, len + 1); ++ strcpy(s, buf); ++ ++ BH_DBG("added variable '%s'\n", s); ++ ++ return 0; ++} ++ ++static int motion_hotplug_fill_event(struct bh_event *event) ++{ ++ int s = jiffies; ++ int ret; ++ ++ if (!seen) ++ seen = jiffies; ++ ++ ret = bh_event_add_var(event, 0, "HOME=%s", "/"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "PATH=%s", ++ "/sbin:/bin:/usr/sbin:/usr/bin"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "SUBSYSTEM=usb"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "ACTION=motion"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "SEEN=%d", s - seen); ++ if (ret) ++ return ret; ++ seen = s; ++ ++ ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum()); ++ ++ return ret; ++} ++ ++static void motion_hotplug_work(struct work_struct *work) ++{ ++ struct bh_event *event = container_of(work, struct bh_event, work); ++ int ret = 0; ++ ++ event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL); ++ if (!event->skb) ++ goto out_free_event; ++ ++ ret = bh_event_add_var(event, 0, "%s@", "add"); ++ if (ret) ++ goto out_free_skb; ++ ++ ret = motion_hotplug_fill_event(event); ++ if (ret) ++ goto out_free_skb; ++ ++ NETLINK_CB(event->skb).dst_group = 1; ++ broadcast_uevent(event->skb, 0, 1, GFP_KERNEL); ++ ++out_free_skb: ++ if (ret) { ++ BH_ERR("work error %d\n", ret); ++ kfree_skb(event->skb); ++ } ++out_free_event: ++ kfree(event); ++} ++ ++static int motion_hotplug_create_event(void) ++{ ++ struct bh_event *event; ++ ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ if (!event) ++ return -ENOMEM; ++ ++ event->name = "motion"; ++ ++ INIT_WORK(&event->work, (void *)(void *)motion_hotplug_work); ++ schedule_work(&event->work); ++ ++ return 0; ++} ++ ++#define MOTION_FLAG_OFFSET 4 + static void uvc_video_decode_end(struct uvc_streaming *stream, + struct uvc_buffer *buf, const __u8 *data, int len) + { ++ if ((stream->dev->quirks & UVC_QUIRK_MOTION) && ++ (data[len - 2] == 0xff) && (data[len - 1] == 0xd9)) { ++ u8 *mem; ++ buf->state = UVC_BUF_STATE_READY; ++ mem = (u8 *) (buf->mem + MOTION_FLAG_OFFSET); ++ if ( stream->dev->motion ) { ++ stream->dev->motion = 0; ++ motion_hotplug_create_event(); ++ } else { ++ *mem &= 0x7f; ++ } ++ } ++ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); +@@ -1477,6 +1622,8 @@ + if (npackets == 0) + return -ENOMEM; + ++ if (stream->dev->quirks & UVC_QUIRK_SINGLE_ISO) ++ npackets = 1; + size = npackets * psize; + + for (i = 0; i < UVC_URBS; ++i) { +Index: linux-3.10.12/drivers/media/usb/uvc/uvcvideo.h +=================================================================== +--- linux-3.10.12.orig/drivers/media/usb/uvc/uvcvideo.h 2013-09-14 15:55:12.000000000 +0200 ++++ linux-3.10.12/drivers/media/usb/uvc/uvcvideo.h 2013-09-19 04:36:51.801609222 +0200 +@@ -137,6 +137,8 @@ + #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 + #define UVC_QUIRK_PROBE_DEF 0x00000100 + #define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 ++#define UVC_QUIRK_MOTION 0x00000400 ++#define UVC_QUIRK_SINGLE_ISO 0x00000800 + + /* Format flags */ + #define UVC_FMT_FLAG_COMPRESSED 0x00000001 +@@ -538,6 +540,7 @@ + __u8 *status; + struct input_dev *input; + char input_phys[64]; ++ int motion; + }; + + enum uvc_handle_state { -- 2.11.0