diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_status.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_status.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c new file mode 100644 index 000000000000..be9084e5eace --- /dev/null +++ b/drivers/media/video/uvc/uvc_status.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * uvc_status.c -- USB Video Class driver - Status endpoint | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 | ||
5 | * Laurent Pinchart (laurent.pinchart@skynet.be) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/version.h> | ||
16 | #include <linux/input.h> | ||
17 | #include <linux/usb.h> | ||
18 | #include <linux/usb/input.h> | ||
19 | |||
20 | #include "uvcvideo.h" | ||
21 | |||
22 | /* -------------------------------------------------------------------------- | ||
23 | * Input device | ||
24 | */ | ||
25 | static int uvc_input_init(struct uvc_device *dev) | ||
26 | { | ||
27 | struct usb_device *udev = dev->udev; | ||
28 | struct input_dev *input; | ||
29 | char *phys = NULL; | ||
30 | int ret; | ||
31 | |||
32 | input = input_allocate_device(); | ||
33 | if (input == NULL) | ||
34 | return -ENOMEM; | ||
35 | |||
36 | phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath), | ||
37 | GFP_KERNEL); | ||
38 | if (phys == NULL) { | ||
39 | ret = -ENOMEM; | ||
40 | goto error; | ||
41 | } | ||
42 | sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath); | ||
43 | |||
44 | input->name = dev->name; | ||
45 | input->phys = phys; | ||
46 | usb_to_input_id(udev, &input->id); | ||
47 | input->dev.parent = &dev->intf->dev; | ||
48 | |||
49 | set_bit(EV_KEY, input->evbit); | ||
50 | set_bit(BTN_0, input->keybit); | ||
51 | |||
52 | if ((ret = input_register_device(input)) < 0) | ||
53 | goto error; | ||
54 | |||
55 | dev->input = input; | ||
56 | return 0; | ||
57 | |||
58 | error: | ||
59 | input_free_device(input); | ||
60 | kfree(phys); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | static void uvc_input_cleanup(struct uvc_device *dev) | ||
65 | { | ||
66 | if (dev->input) | ||
67 | input_unregister_device(dev->input); | ||
68 | } | ||
69 | |||
70 | /* -------------------------------------------------------------------------- | ||
71 | * Status interrupt endpoint | ||
72 | */ | ||
73 | static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) | ||
74 | { | ||
75 | if (len < 3) { | ||
76 | uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " | ||
77 | "received.\n"); | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | if (data[2] == 0) { | ||
82 | if (len < 4) | ||
83 | return; | ||
84 | uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", | ||
85 | data[1], data[3] ? "pressed" : "released", len); | ||
86 | if (dev->input) | ||
87 | input_report_key(dev->input, BTN_0, data[3]); | ||
88 | } else { | ||
89 | uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " | ||
90 | "len %d.\n", data[1], data[2], data[3], len); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) | ||
95 | { | ||
96 | char *attrs[3] = { "value", "info", "failure" }; | ||
97 | |||
98 | if (len < 6 || data[2] != 0 || data[4] > 2) { | ||
99 | uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " | ||
100 | "received.\n"); | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", | ||
105 | data[1], data[3], attrs[data[4]], len); | ||
106 | } | ||
107 | |||
108 | static void uvc_status_complete(struct urb *urb) | ||
109 | { | ||
110 | struct uvc_device *dev = urb->context; | ||
111 | int len, ret; | ||
112 | |||
113 | switch (urb->status) { | ||
114 | case 0: | ||
115 | break; | ||
116 | |||
117 | case -ENOENT: /* usb_kill_urb() called. */ | ||
118 | case -ECONNRESET: /* usb_unlink_urb() called. */ | ||
119 | case -ESHUTDOWN: /* The endpoint is being disabled. */ | ||
120 | case -EPROTO: /* Device is disconnected (reported by some | ||
121 | * host controller). */ | ||
122 | return; | ||
123 | |||
124 | default: | ||
125 | uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " | ||
126 | "completion handler.\n", urb->status); | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | len = urb->actual_length; | ||
131 | if (len > 0) { | ||
132 | switch (dev->status[0] & 0x0f) { | ||
133 | case UVC_STATUS_TYPE_CONTROL: | ||
134 | uvc_event_control(dev, dev->status, len); | ||
135 | break; | ||
136 | |||
137 | case UVC_STATUS_TYPE_STREAMING: | ||
138 | uvc_event_streaming(dev, dev->status, len); | ||
139 | break; | ||
140 | |||
141 | default: | ||
142 | uvc_printk(KERN_INFO, "unknown event type %u.\n", | ||
143 | dev->status[0]); | ||
144 | break; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /* Resubmit the URB. */ | ||
149 | urb->interval = dev->int_ep->desc.bInterval; | ||
150 | if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { | ||
151 | uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", | ||
152 | ret); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | int uvc_status_init(struct uvc_device *dev) | ||
157 | { | ||
158 | struct usb_host_endpoint *ep = dev->int_ep; | ||
159 | unsigned int pipe; | ||
160 | int interval; | ||
161 | |||
162 | if (ep == NULL) | ||
163 | return 0; | ||
164 | |||
165 | uvc_input_init(dev); | ||
166 | |||
167 | dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
168 | if (dev->int_urb == NULL) | ||
169 | return -ENOMEM; | ||
170 | |||
171 | pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); | ||
172 | |||
173 | /* For high-speed interrupt endpoints, the bInterval value is used as | ||
174 | * an exponent of two. Some developers forgot about it. | ||
175 | */ | ||
176 | interval = ep->desc.bInterval; | ||
177 | if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && | ||
178 | (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) | ||
179 | interval = fls(interval) - 1; | ||
180 | |||
181 | usb_fill_int_urb(dev->int_urb, dev->udev, pipe, | ||
182 | dev->status, sizeof dev->status, uvc_status_complete, | ||
183 | dev, interval); | ||
184 | |||
185 | return usb_submit_urb(dev->int_urb, GFP_KERNEL); | ||
186 | } | ||
187 | |||
188 | void uvc_status_cleanup(struct uvc_device *dev) | ||
189 | { | ||
190 | usb_kill_urb(dev->int_urb); | ||
191 | usb_free_urb(dev->int_urb); | ||
192 | uvc_input_cleanup(dev); | ||
193 | } | ||
194 | |||
195 | int uvc_status_suspend(struct uvc_device *dev) | ||
196 | { | ||
197 | usb_kill_urb(dev->int_urb); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | int uvc_status_resume(struct uvc_device *dev) | ||
202 | { | ||
203 | if (dev->int_urb == NULL) | ||
204 | return 0; | ||
205 | |||
206 | return usb_submit_urb(dev->int_urb, GFP_KERNEL); | ||
207 | } | ||