10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * uvc_status.c -- USB Video Class driver - Status endpoint 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2009 50c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 60c0d06caSMauro Carvalho Chehab * 70c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 80c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 90c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 100c0d06caSMauro Carvalho Chehab * (at your option) any later version. 110c0d06caSMauro Carvalho Chehab * 120c0d06caSMauro Carvalho Chehab */ 130c0d06caSMauro Carvalho Chehab 140c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 150c0d06caSMauro Carvalho Chehab #include <linux/input.h> 160c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 170c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 180c0d06caSMauro Carvalho Chehab #include <linux/usb/input.h> 190c0d06caSMauro Carvalho Chehab 200c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 210c0d06caSMauro Carvalho Chehab 220c0d06caSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 230c0d06caSMauro Carvalho Chehab * Input device 240c0d06caSMauro Carvalho Chehab */ 250c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV 260c0d06caSMauro Carvalho Chehab static int uvc_input_init(struct uvc_device *dev) 270c0d06caSMauro Carvalho Chehab { 280c0d06caSMauro Carvalho Chehab struct input_dev *input; 290c0d06caSMauro Carvalho Chehab int ret; 300c0d06caSMauro Carvalho Chehab 310c0d06caSMauro Carvalho Chehab input = input_allocate_device(); 320c0d06caSMauro Carvalho Chehab if (input == NULL) 330c0d06caSMauro Carvalho Chehab return -ENOMEM; 340c0d06caSMauro Carvalho Chehab 350c0d06caSMauro Carvalho Chehab usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); 360c0d06caSMauro Carvalho Chehab strlcat(dev->input_phys, "/button", sizeof(dev->input_phys)); 370c0d06caSMauro Carvalho Chehab 380c0d06caSMauro Carvalho Chehab input->name = dev->name; 390c0d06caSMauro Carvalho Chehab input->phys = dev->input_phys; 400c0d06caSMauro Carvalho Chehab usb_to_input_id(dev->udev, &input->id); 410c0d06caSMauro Carvalho Chehab input->dev.parent = &dev->intf->dev; 420c0d06caSMauro Carvalho Chehab 430c0d06caSMauro Carvalho Chehab __set_bit(EV_KEY, input->evbit); 440c0d06caSMauro Carvalho Chehab __set_bit(KEY_CAMERA, input->keybit); 450c0d06caSMauro Carvalho Chehab 460c0d06caSMauro Carvalho Chehab if ((ret = input_register_device(input)) < 0) 470c0d06caSMauro Carvalho Chehab goto error; 480c0d06caSMauro Carvalho Chehab 490c0d06caSMauro Carvalho Chehab dev->input = input; 500c0d06caSMauro Carvalho Chehab return 0; 510c0d06caSMauro Carvalho Chehab 520c0d06caSMauro Carvalho Chehab error: 530c0d06caSMauro Carvalho Chehab input_free_device(input); 540c0d06caSMauro Carvalho Chehab return ret; 550c0d06caSMauro Carvalho Chehab } 560c0d06caSMauro Carvalho Chehab 570c0d06caSMauro Carvalho Chehab static void uvc_input_cleanup(struct uvc_device *dev) 580c0d06caSMauro Carvalho Chehab { 590c0d06caSMauro Carvalho Chehab if (dev->input) 600c0d06caSMauro Carvalho Chehab input_unregister_device(dev->input); 610c0d06caSMauro Carvalho Chehab } 620c0d06caSMauro Carvalho Chehab 630c0d06caSMauro Carvalho Chehab static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, 640c0d06caSMauro Carvalho Chehab int value) 650c0d06caSMauro Carvalho Chehab { 660c0d06caSMauro Carvalho Chehab if (dev->input) { 670c0d06caSMauro Carvalho Chehab input_report_key(dev->input, code, value); 680c0d06caSMauro Carvalho Chehab input_sync(dev->input); 690c0d06caSMauro Carvalho Chehab } 700c0d06caSMauro Carvalho Chehab } 710c0d06caSMauro Carvalho Chehab 720c0d06caSMauro Carvalho Chehab #else 730c0d06caSMauro Carvalho Chehab #define uvc_input_init(dev) 740c0d06caSMauro Carvalho Chehab #define uvc_input_cleanup(dev) 750c0d06caSMauro Carvalho Chehab #define uvc_input_report_key(dev, code, value) 760c0d06caSMauro Carvalho Chehab #endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */ 770c0d06caSMauro Carvalho Chehab 780c0d06caSMauro Carvalho Chehab /* -------------------------------------------------------------------------- 790c0d06caSMauro Carvalho Chehab * Status interrupt endpoint 800c0d06caSMauro Carvalho Chehab */ 810c0d06caSMauro Carvalho Chehab static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) 820c0d06caSMauro Carvalho Chehab { 830c0d06caSMauro Carvalho Chehab if (len < 3) { 840c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " 850c0d06caSMauro Carvalho Chehab "received.\n"); 860c0d06caSMauro Carvalho Chehab return; 870c0d06caSMauro Carvalho Chehab } 880c0d06caSMauro Carvalho Chehab 890c0d06caSMauro Carvalho Chehab if (data[2] == 0) { 900c0d06caSMauro Carvalho Chehab if (len < 4) 910c0d06caSMauro Carvalho Chehab return; 920c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", 930c0d06caSMauro Carvalho Chehab data[1], data[3] ? "pressed" : "released", len); 940c0d06caSMauro Carvalho Chehab uvc_input_report_key(dev, KEY_CAMERA, data[3]); 950c0d06caSMauro Carvalho Chehab } else { 96*0393e735SLaurent Pinchart uvc_trace(UVC_TRACE_STATUS, 97*0393e735SLaurent Pinchart "Stream %u error event %02x len %d.\n", 98*0393e735SLaurent Pinchart data[1], data[2], len); 990c0d06caSMauro Carvalho Chehab } 1000c0d06caSMauro Carvalho Chehab } 1010c0d06caSMauro Carvalho Chehab 1020c0d06caSMauro Carvalho Chehab static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) 1030c0d06caSMauro Carvalho Chehab { 1040c0d06caSMauro Carvalho Chehab char *attrs[3] = { "value", "info", "failure" }; 1050c0d06caSMauro Carvalho Chehab 1060c0d06caSMauro Carvalho Chehab if (len < 6 || data[2] != 0 || data[4] > 2) { 1070c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " 1080c0d06caSMauro Carvalho Chehab "received.\n"); 1090c0d06caSMauro Carvalho Chehab return; 1100c0d06caSMauro Carvalho Chehab } 1110c0d06caSMauro Carvalho Chehab 1120c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", 1130c0d06caSMauro Carvalho Chehab data[1], data[3], attrs[data[4]], len); 1140c0d06caSMauro Carvalho Chehab } 1150c0d06caSMauro Carvalho Chehab 1160c0d06caSMauro Carvalho Chehab static void uvc_status_complete(struct urb *urb) 1170c0d06caSMauro Carvalho Chehab { 1180c0d06caSMauro Carvalho Chehab struct uvc_device *dev = urb->context; 1190c0d06caSMauro Carvalho Chehab int len, ret; 1200c0d06caSMauro Carvalho Chehab 1210c0d06caSMauro Carvalho Chehab switch (urb->status) { 1220c0d06caSMauro Carvalho Chehab case 0: 1230c0d06caSMauro Carvalho Chehab break; 1240c0d06caSMauro Carvalho Chehab 1250c0d06caSMauro Carvalho Chehab case -ENOENT: /* usb_kill_urb() called. */ 1260c0d06caSMauro Carvalho Chehab case -ECONNRESET: /* usb_unlink_urb() called. */ 1270c0d06caSMauro Carvalho Chehab case -ESHUTDOWN: /* The endpoint is being disabled. */ 1280c0d06caSMauro Carvalho Chehab case -EPROTO: /* Device is disconnected (reported by some 1290c0d06caSMauro Carvalho Chehab * host controller). */ 1300c0d06caSMauro Carvalho Chehab return; 1310c0d06caSMauro Carvalho Chehab 1320c0d06caSMauro Carvalho Chehab default: 1330c0d06caSMauro Carvalho Chehab uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " 1340c0d06caSMauro Carvalho Chehab "completion handler.\n", urb->status); 1350c0d06caSMauro Carvalho Chehab return; 1360c0d06caSMauro Carvalho Chehab } 1370c0d06caSMauro Carvalho Chehab 1380c0d06caSMauro Carvalho Chehab len = urb->actual_length; 1390c0d06caSMauro Carvalho Chehab if (len > 0) { 1400c0d06caSMauro Carvalho Chehab switch (dev->status[0] & 0x0f) { 1410c0d06caSMauro Carvalho Chehab case UVC_STATUS_TYPE_CONTROL: 1420c0d06caSMauro Carvalho Chehab uvc_event_control(dev, dev->status, len); 1430c0d06caSMauro Carvalho Chehab break; 1440c0d06caSMauro Carvalho Chehab 1450c0d06caSMauro Carvalho Chehab case UVC_STATUS_TYPE_STREAMING: 1460c0d06caSMauro Carvalho Chehab uvc_event_streaming(dev, dev->status, len); 1470c0d06caSMauro Carvalho Chehab break; 1480c0d06caSMauro Carvalho Chehab 1490c0d06caSMauro Carvalho Chehab default: 1500c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_STATUS, "Unknown status event " 1510c0d06caSMauro Carvalho Chehab "type %u.\n", dev->status[0]); 1520c0d06caSMauro Carvalho Chehab break; 1530c0d06caSMauro Carvalho Chehab } 1540c0d06caSMauro Carvalho Chehab } 1550c0d06caSMauro Carvalho Chehab 1560c0d06caSMauro Carvalho Chehab /* Resubmit the URB. */ 1570c0d06caSMauro Carvalho Chehab urb->interval = dev->int_ep->desc.bInterval; 1580c0d06caSMauro Carvalho Chehab if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { 1590c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", 1600c0d06caSMauro Carvalho Chehab ret); 1610c0d06caSMauro Carvalho Chehab } 1620c0d06caSMauro Carvalho Chehab } 1630c0d06caSMauro Carvalho Chehab 1640c0d06caSMauro Carvalho Chehab int uvc_status_init(struct uvc_device *dev) 1650c0d06caSMauro Carvalho Chehab { 1660c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = dev->int_ep; 1670c0d06caSMauro Carvalho Chehab unsigned int pipe; 1680c0d06caSMauro Carvalho Chehab int interval; 1690c0d06caSMauro Carvalho Chehab 1700c0d06caSMauro Carvalho Chehab if (ep == NULL) 1710c0d06caSMauro Carvalho Chehab return 0; 1720c0d06caSMauro Carvalho Chehab 1730c0d06caSMauro Carvalho Chehab uvc_input_init(dev); 1740c0d06caSMauro Carvalho Chehab 1750c0d06caSMauro Carvalho Chehab dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); 1760c0d06caSMauro Carvalho Chehab if (dev->status == NULL) 1770c0d06caSMauro Carvalho Chehab return -ENOMEM; 1780c0d06caSMauro Carvalho Chehab 1790c0d06caSMauro Carvalho Chehab dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); 1800c0d06caSMauro Carvalho Chehab if (dev->int_urb == NULL) { 1810c0d06caSMauro Carvalho Chehab kfree(dev->status); 1820c0d06caSMauro Carvalho Chehab return -ENOMEM; 1830c0d06caSMauro Carvalho Chehab } 1840c0d06caSMauro Carvalho Chehab 1850c0d06caSMauro Carvalho Chehab pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); 1860c0d06caSMauro Carvalho Chehab 1870c0d06caSMauro Carvalho Chehab /* For high-speed interrupt endpoints, the bInterval value is used as 1880c0d06caSMauro Carvalho Chehab * an exponent of two. Some developers forgot about it. 1890c0d06caSMauro Carvalho Chehab */ 1900c0d06caSMauro Carvalho Chehab interval = ep->desc.bInterval; 1910c0d06caSMauro Carvalho Chehab if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && 1920c0d06caSMauro Carvalho Chehab (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) 1930c0d06caSMauro Carvalho Chehab interval = fls(interval) - 1; 1940c0d06caSMauro Carvalho Chehab 1950c0d06caSMauro Carvalho Chehab usb_fill_int_urb(dev->int_urb, dev->udev, pipe, 1960c0d06caSMauro Carvalho Chehab dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete, 1970c0d06caSMauro Carvalho Chehab dev, interval); 1980c0d06caSMauro Carvalho Chehab 1990c0d06caSMauro Carvalho Chehab return 0; 2000c0d06caSMauro Carvalho Chehab } 2010c0d06caSMauro Carvalho Chehab 2020c0d06caSMauro Carvalho Chehab void uvc_status_cleanup(struct uvc_device *dev) 2030c0d06caSMauro Carvalho Chehab { 2040c0d06caSMauro Carvalho Chehab usb_kill_urb(dev->int_urb); 2050c0d06caSMauro Carvalho Chehab usb_free_urb(dev->int_urb); 2060c0d06caSMauro Carvalho Chehab kfree(dev->status); 2070c0d06caSMauro Carvalho Chehab uvc_input_cleanup(dev); 2080c0d06caSMauro Carvalho Chehab } 2090c0d06caSMauro Carvalho Chehab 21017706f56SLaurent Pinchart int uvc_status_start(struct uvc_device *dev, gfp_t flags) 2110c0d06caSMauro Carvalho Chehab { 2120c0d06caSMauro Carvalho Chehab if (dev->int_urb == NULL) 2130c0d06caSMauro Carvalho Chehab return 0; 2140c0d06caSMauro Carvalho Chehab 21517706f56SLaurent Pinchart return usb_submit_urb(dev->int_urb, flags); 2160c0d06caSMauro Carvalho Chehab } 2170c0d06caSMauro Carvalho Chehab 2180c0d06caSMauro Carvalho Chehab void uvc_status_stop(struct uvc_device *dev) 2190c0d06caSMauro Carvalho Chehab { 2200c0d06caSMauro Carvalho Chehab usb_kill_urb(dev->int_urb); 2210c0d06caSMauro Carvalho Chehab } 222