From 975e76214cd2516eb6cfff4c3eec581872645e88 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 19 Sep 2013 01:50:59 +0200 Subject: [PATCH 31/53] 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_video.c | 147 ++++++++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 5 +- 4 files changed, 165 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 4b5b3e8..6808bcc 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2536,6 +2536,18 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_FORCE_Y8 }, + /* 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) }, {} diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index f552ab9..7132ad4 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -139,6 +139,7 @@ static void uvc_status_complete(struct urb *urb) 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 @@ int uvc_status_init(struct uvc_device *dev) } 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. diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f839654..b30aab6 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -21,6 +21,11 @@ #include #include #include +#include +#include +#include +#include +#include #include @@ -1089,9 +1094,149 @@ static void uvc_video_decode_data(struct uvc_streaming *stream, } } +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"); @@ -1504,6 +1649,8 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream, 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) { diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 816dd1a..d0aa324 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -152,7 +152,9 @@ #define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400 #define UVC_QUIRK_FORCE_Y8 0x00000800 - +#define UVC_QUIRK_MOTION 0x00001000 +#define UVC_QUIRK_SINGLE_ISO 0x00002000 + /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 #define UVC_FMT_FLAG_STREAM 0x00000002 @@ -550,6 +552,7 @@ struct uvc_device { __u8 *status; struct input_dev *input; char input_phys[64]; + int motion; }; enum uvc_handle_state { -- 1.7.10.4