1/*
2 *      uvc_status.c  --  USB Video Class driver - Status endpoint
3 *
4 *      Copyright (C) 2005-2009
5 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
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/input.h>
16#include <linux/slab.h>
17#include <linux/usb.h>
18#include <linux/usb/input.h>
19
20#include "uvcvideo.h"
21
22/* --------------------------------------------------------------------------
23 * Input device
24 */
25#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
26static int uvc_input_init(struct uvc_device *dev)
27{
28	struct input_dev *input;
29	int ret;
30
31	input = input_allocate_device();
32	if (input == NULL)
33		return -ENOMEM;
34
35	usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));
36	strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
37
38	input->name = dev->name;
39	input->phys = dev->input_phys;
40	usb_to_input_id(dev->udev, &input->id);
41	input->dev.parent = &dev->intf->dev;
42
43	__set_bit(EV_KEY, input->evbit);
44	__set_bit(KEY_CAMERA, input->keybit);
45
46	if ((ret = input_register_device(input)) < 0)
47		goto error;
48
49	dev->input = input;
50	return 0;
51
52error:
53	input_free_device(input);
54	return ret;
55}
56
57static void uvc_input_cleanup(struct uvc_device *dev)
58{
59	if (dev->input)
60		input_unregister_device(dev->input);
61}
62
63static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
64	int value)
65{
66	if (dev->input) {
67		input_report_key(dev->input, code, value);
68		input_sync(dev->input);
69	}
70}
71
72#else
73#define uvc_input_init(dev)
74#define uvc_input_cleanup(dev)
75#define uvc_input_report_key(dev, code, value)
76#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
77
78/* --------------------------------------------------------------------------
79 * Status interrupt endpoint
80 */
81static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
82{
83	if (len < 3) {
84		uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
85				"received.\n");
86		return;
87	}
88
89	if (data[2] == 0) {
90		if (len < 4)
91			return;
92		uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
93			data[1], data[3] ? "pressed" : "released", len);
94		uvc_input_report_key(dev, KEY_CAMERA, data[3]);
95	} else {
96		uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x "
97			"len %d.\n", data[1], data[2], data[3], len);
98	}
99}
100
101static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
102{
103	char *attrs[3] = { "value", "info", "failure" };
104
105	if (len < 6 || data[2] != 0 || data[4] > 2) {
106		uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
107				"received.\n");
108		return;
109	}
110
111	uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
112		data[1], data[3], attrs[data[4]], len);
113}
114
115static void uvc_status_complete(struct urb *urb)
116{
117	struct uvc_device *dev = urb->context;
118	int len, ret;
119
120	switch (urb->status) {
121	case 0:
122		break;
123
124	case -ENOENT:		/* usb_kill_urb() called. */
125	case -ECONNRESET:	/* usb_unlink_urb() called. */
126	case -ESHUTDOWN:	/* The endpoint is being disabled. */
127	case -EPROTO:		/* Device is disconnected (reported by some
128				 * host controller). */
129		return;
130
131	default:
132		uvc_printk(KERN_WARNING, "Non-zero status (%d) in status "
133			"completion handler.\n", urb->status);
134		return;
135	}
136
137	len = urb->actual_length;
138	if (len > 0) {
139		switch (dev->status[0] & 0x0f) {
140		case UVC_STATUS_TYPE_CONTROL:
141			uvc_event_control(dev, dev->status, len);
142			break;
143
144		case UVC_STATUS_TYPE_STREAMING:
145			uvc_event_streaming(dev, dev->status, len);
146			break;
147
148		default:
149			uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
150				"type %u.\n", dev->status[0]);
151			break;
152		}
153	}
154
155	/* Resubmit the URB. */
156	urb->interval = dev->int_ep->desc.bInterval;
157	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
158		uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
159			ret);
160	}
161}
162
163int uvc_status_init(struct uvc_device *dev)
164{
165	struct usb_host_endpoint *ep = dev->int_ep;
166	unsigned int pipe;
167	int interval;
168
169	if (ep == NULL)
170		return 0;
171
172	uvc_input_init(dev);
173
174	dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
175	if (dev->status == NULL)
176		return -ENOMEM;
177
178	dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
179	if (dev->int_urb == NULL) {
180		kfree(dev->status);
181		return -ENOMEM;
182	}
183
184	pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
185
186	/* For high-speed interrupt endpoints, the bInterval value is used as
187	 * an exponent of two. Some developers forgot about it.
188	 */
189	interval = ep->desc.bInterval;
190	if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&
191	    (dev->quirks & UVC_QUIRK_STATUS_INTERVAL))
192		interval = fls(interval) - 1;
193
194	usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
195		dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
196		dev, interval);
197
198	return 0;
199}
200
201void uvc_status_cleanup(struct uvc_device *dev)
202{
203	usb_kill_urb(dev->int_urb);
204	usb_free_urb(dev->int_urb);
205	kfree(dev->status);
206	uvc_input_cleanup(dev);
207}
208
209int uvc_status_start(struct uvc_device *dev)
210{
211	if (dev->int_urb == NULL)
212		return 0;
213
214	return usb_submit_urb(dev->int_urb, GFP_KERNEL);
215}
216
217void uvc_status_stop(struct uvc_device *dev)
218{
219	usb_kill_urb(dev->int_urb);
220}
221
222int uvc_status_suspend(struct uvc_device *dev)
223{
224	if (atomic_read(&dev->users))
225		usb_kill_urb(dev->int_urb);
226
227	return 0;
228}
229
230int uvc_status_resume(struct uvc_device *dev)
231{
232	if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
233		return 0;
234
235	return usb_submit_urb(dev->int_urb, GFP_NOIO);
236}
237
238