diff options
author | Márton Németh <nm127@freemail.hu> | 2010-01-28 04:39:49 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-02-26 13:10:49 -0500 |
commit | 0274d42e052ecd9bc6b5f69fbfc8792ce2cc0fb6 (patch) | |
tree | c16f3c1d5f17956ce82b425473e13f5a27cfb240 | |
parent | 2abf6dd8e8754db6b18a4d55d3e4425c0a22d280 (diff) |
V4L/DVB: gspca - main: Add input support for interrupt endpoints.
Signed-off-by: Márton Németh <nm127@freemail.hu>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 203 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.h | 13 |
2 files changed, 215 insertions, 1 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 6a6e22c61779..c70d33bf6aad 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c | |||
@@ -3,6 +3,9 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) | 4 | * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) |
5 | * | 5 | * |
6 | * Camera button input handling by Márton Németh | ||
7 | * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> | ||
8 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | 9 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | 10 | * under the terms of the GNU General Public License as published by the |
8 | * Free Software Foundation; either version 2 of the License, or (at your | 11 | * Free Software Foundation; either version 2 of the License, or (at your |
@@ -37,6 +40,9 @@ | |||
37 | 40 | ||
38 | #include "gspca.h" | 41 | #include "gspca.h" |
39 | 42 | ||
43 | #include <linux/input.h> | ||
44 | #include <linux/usb/input.h> | ||
45 | |||
40 | /* global values */ | 46 | /* global values */ |
41 | #define DEF_NURBS 3 /* default number of URBs */ | 47 | #define DEF_NURBS 3 /* default number of URBs */ |
42 | #if DEF_NURBS > MAX_NURBS | 48 | #if DEF_NURBS > MAX_NURBS |
@@ -104,6 +110,182 @@ static const struct vm_operations_struct gspca_vm_ops = { | |||
104 | .close = gspca_vm_close, | 110 | .close = gspca_vm_close, |
105 | }; | 111 | }; |
106 | 112 | ||
113 | /* | ||
114 | * Input and interrupt endpoint handling functions | ||
115 | */ | ||
116 | #ifdef CONFIG_INPUT | ||
117 | static void int_irq(struct urb *urb) | ||
118 | { | ||
119 | struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; | ||
120 | int ret; | ||
121 | |||
122 | ret = urb->status; | ||
123 | switch (ret) { | ||
124 | case 0: | ||
125 | if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, | ||
126 | urb->transfer_buffer, urb->actual_length) < 0) { | ||
127 | PDEBUG(D_ERR, "Unknown packet received"); | ||
128 | } | ||
129 | break; | ||
130 | |||
131 | case -ENOENT: | ||
132 | case -ECONNRESET: | ||
133 | case -ENODEV: | ||
134 | case -ESHUTDOWN: | ||
135 | /* Stop is requested either by software or hardware is gone, | ||
136 | * keep the ret value non-zero and don't resubmit later. | ||
137 | */ | ||
138 | break; | ||
139 | |||
140 | default: | ||
141 | PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status); | ||
142 | urb->status = 0; | ||
143 | ret = 0; | ||
144 | } | ||
145 | |||
146 | if (ret == 0) { | ||
147 | ret = usb_submit_urb(urb, GFP_ATOMIC); | ||
148 | if (ret < 0) | ||
149 | PDEBUG(D_ERR, "Resubmit URB failed with error %i", ret); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static int gspca_input_connect(struct gspca_dev *dev) | ||
154 | { | ||
155 | struct input_dev *input_dev; | ||
156 | int err = 0; | ||
157 | |||
158 | dev->input_dev = NULL; | ||
159 | if (dev->sd_desc->int_pkt_scan) { | ||
160 | input_dev = input_allocate_device(); | ||
161 | if (!input_dev) | ||
162 | return -ENOMEM; | ||
163 | |||
164 | usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); | ||
165 | strlcat(dev->phys, "/input0", sizeof(dev->phys)); | ||
166 | |||
167 | input_dev->name = dev->sd_desc->name; | ||
168 | input_dev->phys = dev->phys; | ||
169 | |||
170 | usb_to_input_id(dev->dev, &input_dev->id); | ||
171 | |||
172 | input_dev->evbit[0] = BIT_MASK(EV_KEY); | ||
173 | input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); | ||
174 | input_dev->dev.parent = &dev->dev->dev; | ||
175 | |||
176 | err = input_register_device(input_dev); | ||
177 | if (err) { | ||
178 | PDEBUG(D_ERR, "Input device registration failed " | ||
179 | "with error %i", err); | ||
180 | input_dev->dev.parent = NULL; | ||
181 | input_free_device(input_dev); | ||
182 | } else { | ||
183 | dev->input_dev = input_dev; | ||
184 | } | ||
185 | } else | ||
186 | err = -EINVAL; | ||
187 | |||
188 | return err; | ||
189 | } | ||
190 | |||
191 | static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, | ||
192 | struct usb_endpoint_descriptor *ep) | ||
193 | { | ||
194 | unsigned int buffer_len; | ||
195 | int interval; | ||
196 | struct urb *urb; | ||
197 | struct usb_device *dev; | ||
198 | void *buffer = NULL; | ||
199 | int ret = -EINVAL; | ||
200 | |||
201 | buffer_len = ep->wMaxPacketSize; | ||
202 | interval = ep->bInterval; | ||
203 | PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " | ||
204 | "buffer_len=%u, interval=%u", | ||
205 | ep->bEndpointAddress, buffer_len, interval); | ||
206 | |||
207 | dev = gspca_dev->dev; | ||
208 | |||
209 | urb = usb_alloc_urb(0, GFP_KERNEL); | ||
210 | if (!urb) { | ||
211 | ret = -ENOMEM; | ||
212 | goto error; | ||
213 | } | ||
214 | |||
215 | buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize, | ||
216 | GFP_KERNEL, &urb->transfer_dma); | ||
217 | if (!buffer) { | ||
218 | ret = -ENOMEM; | ||
219 | goto error_buffer; | ||
220 | } | ||
221 | usb_fill_int_urb(urb, dev, | ||
222 | usb_rcvintpipe(dev, ep->bEndpointAddress), | ||
223 | buffer, buffer_len, | ||
224 | int_irq, (void *)gspca_dev, interval); | ||
225 | gspca_dev->int_urb = urb; | ||
226 | ret = usb_submit_urb(urb, GFP_KERNEL); | ||
227 | if (ret < 0) { | ||
228 | PDEBUG(D_ERR, "submit URB failed with error %i", ret); | ||
229 | goto error_submit; | ||
230 | } | ||
231 | return ret; | ||
232 | |||
233 | error_submit: | ||
234 | usb_buffer_free(dev, | ||
235 | urb->transfer_buffer_length, | ||
236 | urb->transfer_buffer, | ||
237 | urb->transfer_dma); | ||
238 | error_buffer: | ||
239 | usb_free_urb(urb); | ||
240 | error: | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static int gspca_input_create_urb(struct gspca_dev *gspca_dev) | ||
245 | { | ||
246 | int ret = -EINVAL; | ||
247 | struct usb_interface *intf; | ||
248 | struct usb_host_interface *intf_desc; | ||
249 | struct usb_endpoint_descriptor *ep; | ||
250 | int i; | ||
251 | |||
252 | if (gspca_dev->sd_desc->int_pkt_scan) { | ||
253 | intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); | ||
254 | intf_desc = intf->cur_altsetting; | ||
255 | for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { | ||
256 | ep = &intf_desc->endpoint[i].desc; | ||
257 | if (usb_endpoint_dir_in(ep) && | ||
258 | usb_endpoint_xfer_int(ep)) { | ||
259 | |||
260 | ret = alloc_and_submit_int_urb(gspca_dev, ep); | ||
261 | break; | ||
262 | } | ||
263 | } | ||
264 | } | ||
265 | return ret; | ||
266 | } | ||
267 | |||
268 | static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) | ||
269 | { | ||
270 | struct urb *urb; | ||
271 | |||
272 | urb = gspca_dev->int_urb; | ||
273 | if (urb) { | ||
274 | gspca_dev->int_urb = NULL; | ||
275 | usb_kill_urb(urb); | ||
276 | usb_buffer_free(gspca_dev->dev, | ||
277 | urb->transfer_buffer_length, | ||
278 | urb->transfer_buffer, | ||
279 | urb->transfer_dma); | ||
280 | usb_free_urb(urb); | ||
281 | } | ||
282 | } | ||
283 | #else | ||
284 | #define gspca_input_connect(gspca_dev) 0 | ||
285 | #define gspca_input_create_urb(gspca_dev) 0 | ||
286 | #define gspca_input_destroy_urb(gspca_dev) | ||
287 | #endif | ||
288 | |||
107 | /* get the current input frame buffer */ | 289 | /* get the current input frame buffer */ |
108 | struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) | 290 | struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) |
109 | { | 291 | { |
@@ -483,11 +665,13 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) | |||
483 | i, ep->desc.bEndpointAddress); | 665 | i, ep->desc.bEndpointAddress); |
484 | gspca_dev->alt = i; /* memorize the current alt setting */ | 666 | gspca_dev->alt = i; /* memorize the current alt setting */ |
485 | if (gspca_dev->nbalt > 1) { | 667 | if (gspca_dev->nbalt > 1) { |
668 | gspca_input_destroy_urb(gspca_dev); | ||
486 | ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); | 669 | ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); |
487 | if (ret < 0) { | 670 | if (ret < 0) { |
488 | err("set alt %d err %d", i, ret); | 671 | err("set alt %d err %d", i, ret); |
489 | return NULL; | 672 | ep = NULL; |
490 | } | 673 | } |
674 | gspca_input_create_urb(gspca_dev); | ||
491 | } | 675 | } |
492 | return ep; | 676 | return ep; |
493 | } | 677 | } |
@@ -698,7 +882,9 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) | |||
698 | if (gspca_dev->sd_desc->stopN) | 882 | if (gspca_dev->sd_desc->stopN) |
699 | gspca_dev->sd_desc->stopN(gspca_dev); | 883 | gspca_dev->sd_desc->stopN(gspca_dev); |
700 | destroy_urbs(gspca_dev); | 884 | destroy_urbs(gspca_dev); |
885 | gspca_input_destroy_urb(gspca_dev); | ||
701 | gspca_set_alt0(gspca_dev); | 886 | gspca_set_alt0(gspca_dev); |
887 | gspca_input_create_urb(gspca_dev); | ||
702 | } | 888 | } |
703 | 889 | ||
704 | /* always call stop0 to free the subdriver's resources */ | 890 | /* always call stop0 to free the subdriver's resources */ |
@@ -2121,6 +2307,11 @@ int gspca_dev_probe(struct usb_interface *intf, | |||
2121 | 2307 | ||
2122 | usb_set_intfdata(intf, gspca_dev); | 2308 | usb_set_intfdata(intf, gspca_dev); |
2123 | PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); | 2309 | PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); |
2310 | |||
2311 | ret = gspca_input_connect(gspca_dev); | ||
2312 | if (ret == 0) | ||
2313 | ret = gspca_input_create_urb(gspca_dev); | ||
2314 | |||
2124 | return 0; | 2315 | return 0; |
2125 | out: | 2316 | out: |
2126 | kfree(gspca_dev->usb_buf); | 2317 | kfree(gspca_dev->usb_buf); |
@@ -2138,6 +2329,7 @@ EXPORT_SYMBOL(gspca_dev_probe); | |||
2138 | void gspca_disconnect(struct usb_interface *intf) | 2329 | void gspca_disconnect(struct usb_interface *intf) |
2139 | { | 2330 | { |
2140 | struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | 2331 | struct gspca_dev *gspca_dev = usb_get_intfdata(intf); |
2332 | struct input_dev *input_dev; | ||
2141 | 2333 | ||
2142 | PDEBUG(D_PROBE, "%s disconnect", | 2334 | PDEBUG(D_PROBE, "%s disconnect", |
2143 | video_device_node_name(&gspca_dev->vdev)); | 2335 | video_device_node_name(&gspca_dev->vdev)); |
@@ -2149,6 +2341,13 @@ void gspca_disconnect(struct usb_interface *intf) | |||
2149 | wake_up_interruptible(&gspca_dev->wq); | 2341 | wake_up_interruptible(&gspca_dev->wq); |
2150 | } | 2342 | } |
2151 | 2343 | ||
2344 | gspca_input_destroy_urb(gspca_dev); | ||
2345 | input_dev = gspca_dev->input_dev; | ||
2346 | if (input_dev) { | ||
2347 | gspca_dev->input_dev = NULL; | ||
2348 | input_unregister_device(input_dev); | ||
2349 | } | ||
2350 | |||
2152 | /* the device is freed at exit of this function */ | 2351 | /* the device is freed at exit of this function */ |
2153 | gspca_dev->dev = NULL; | 2352 | gspca_dev->dev = NULL; |
2154 | mutex_unlock(&gspca_dev->usb_lock); | 2353 | mutex_unlock(&gspca_dev->usb_lock); |
@@ -2174,6 +2373,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) | |||
2174 | if (gspca_dev->sd_desc->stopN) | 2373 | if (gspca_dev->sd_desc->stopN) |
2175 | gspca_dev->sd_desc->stopN(gspca_dev); | 2374 | gspca_dev->sd_desc->stopN(gspca_dev); |
2176 | destroy_urbs(gspca_dev); | 2375 | destroy_urbs(gspca_dev); |
2376 | gspca_input_destroy_urb(gspca_dev); | ||
2177 | gspca_set_alt0(gspca_dev); | 2377 | gspca_set_alt0(gspca_dev); |
2178 | if (gspca_dev->sd_desc->stop0) | 2378 | if (gspca_dev->sd_desc->stop0) |
2179 | gspca_dev->sd_desc->stop0(gspca_dev); | 2379 | gspca_dev->sd_desc->stop0(gspca_dev); |
@@ -2187,6 +2387,7 @@ int gspca_resume(struct usb_interface *intf) | |||
2187 | 2387 | ||
2188 | gspca_dev->frozen = 0; | 2388 | gspca_dev->frozen = 0; |
2189 | gspca_dev->sd_desc->init(gspca_dev); | 2389 | gspca_dev->sd_desc->init(gspca_dev); |
2390 | gspca_input_create_urb(gspca_dev); | ||
2190 | if (gspca_dev->streaming) | 2391 | if (gspca_dev->streaming) |
2191 | return gspca_init_transfer(gspca_dev); | 2392 | return gspca_init_transfer(gspca_dev); |
2192 | return 0; | 2393 | return 0; |
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 53c034ea84ad..0ed254b496a5 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h | |||
@@ -91,6 +91,9 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *, | |||
91 | typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, | 91 | typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, |
92 | u8 *data, | 92 | u8 *data, |
93 | int len); | 93 | int len); |
94 | typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev, | ||
95 | u8 *data, | ||
96 | int len); | ||
94 | 97 | ||
95 | struct ctrl { | 98 | struct ctrl { |
96 | struct v4l2_queryctrl qctrl; | 99 | struct v4l2_queryctrl qctrl; |
@@ -126,6 +129,9 @@ struct sd_desc { | |||
126 | cam_reg_op get_register; | 129 | cam_reg_op get_register; |
127 | #endif | 130 | #endif |
128 | cam_ident_op get_chip_ident; | 131 | cam_ident_op get_chip_ident; |
132 | #ifdef CONFIG_INPUT | ||
133 | cam_int_pkt_op int_pkt_scan; | ||
134 | #endif | ||
129 | }; | 135 | }; |
130 | 136 | ||
131 | /* packet types when moving from iso buf to frame buf */ | 137 | /* packet types when moving from iso buf to frame buf */ |
@@ -148,6 +154,10 @@ struct gspca_dev { | |||
148 | struct module *module; /* subdriver handling the device */ | 154 | struct module *module; /* subdriver handling the device */ |
149 | struct usb_device *dev; | 155 | struct usb_device *dev; |
150 | struct file *capt_file; /* file doing video capture */ | 156 | struct file *capt_file; /* file doing video capture */ |
157 | #ifdef CONFIG_INPUT | ||
158 | struct input_dev *input_dev; | ||
159 | char phys[64]; /* physical device path */ | ||
160 | #endif | ||
151 | 161 | ||
152 | struct cam cam; /* device information */ | 162 | struct cam cam; /* device information */ |
153 | const struct sd_desc *sd_desc; /* subdriver description */ | 163 | const struct sd_desc *sd_desc; /* subdriver description */ |
@@ -157,6 +167,9 @@ struct gspca_dev { | |||
157 | #define USB_BUF_SZ 64 | 167 | #define USB_BUF_SZ 64 |
158 | __u8 *usb_buf; /* buffer for USB exchanges */ | 168 | __u8 *usb_buf; /* buffer for USB exchanges */ |
159 | struct urb *urb[MAX_NURBS]; | 169 | struct urb *urb[MAX_NURBS]; |
170 | #ifdef CONFIG_INPUT | ||
171 | struct urb *int_urb; | ||
172 | #endif | ||
160 | 173 | ||
161 | __u8 *frbuf; /* buffer for nframes */ | 174 | __u8 *frbuf; /* buffer for nframes */ |
162 | struct gspca_frame frame[GSPCA_MAX_FRAMES]; | 175 | struct gspca_frame frame[GSPCA_MAX_FRAMES]; |