diff options
author | Hans de Goede <hdegoede@redhat.com> | 2011-02-21 09:06:29 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-03-21 19:32:10 -0400 |
commit | 49b61ec9b5afdb739e07bc085ed12678bc932a22 (patch) | |
tree | d98dbfe9957ced5d8736c1b4b5d30d848298aa8f /drivers/media/video | |
parent | bcc6f669066d490f10484f44561f9c2ebb38c25b (diff) |
[media] gspca: Add new vicam subdriver
This is a complete rewrite of the old v4l1 vicam subdriver, featuring
multiple resolutions, gain + exposure control and still has less code.
Oh and it is a v4l2 driver rather then v4l1 ofcourse :)
Many thanks to Devin Heitmueller <dheitmueller@kernellabs.com> for
donating his 3com homeconnect to me, which made this rewrite possible.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/gspca/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/video/gspca/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/gspca/vicam.c | 381 |
3 files changed, 393 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index dda56ff834f..a20f6ae8825 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig | |||
@@ -346,6 +346,16 @@ config USB_GSPCA_VC032X | |||
346 | To compile this driver as a module, choose M here: the | 346 | To compile this driver as a module, choose M here: the |
347 | module will be called gspca_vc032x. | 347 | module will be called gspca_vc032x. |
348 | 348 | ||
349 | config USB_GSPCA_VICAM | ||
350 | tristate "ViCam USB Camera Driver" | ||
351 | depends on VIDEO_V4L2 && USB_GSPCA | ||
352 | help | ||
353 | Say Y here if you want support for the 3com homeconnect camera | ||
354 | (vicam). | ||
355 | |||
356 | To compile this driver as a module, choose M here: the | ||
357 | module will be called gspca_vicam. | ||
358 | |||
349 | config USB_GSPCA_XIRLINK_CIT | 359 | config USB_GSPCA_XIRLINK_CIT |
350 | tristate "Xirlink C-It USB Camera Driver" | 360 | tristate "Xirlink C-It USB Camera Driver" |
351 | depends on VIDEO_V4L2 && USB_GSPCA | 361 | depends on VIDEO_V4L2 && USB_GSPCA |
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 24e695b8b07..a0dbcfbab29 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile | |||
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o | |||
34 | obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o | 34 | obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o |
35 | obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o | 35 | obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o |
36 | obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o | 36 | obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o |
37 | obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o | ||
37 | obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o | 38 | obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o |
38 | obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o | 39 | obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o |
39 | 40 | ||
@@ -73,6 +74,7 @@ gspca_sunplus-objs := sunplus.o | |||
73 | gspca_t613-objs := t613.o | 74 | gspca_t613-objs := t613.o |
74 | gspca_tv8532-objs := tv8532.o | 75 | gspca_tv8532-objs := tv8532.o |
75 | gspca_vc032x-objs := vc032x.o | 76 | gspca_vc032x-objs := vc032x.o |
77 | gspca_vicam-objs := vicam.o | ||
76 | gspca_xirlink_cit-objs := xirlink_cit.o | 78 | gspca_xirlink_cit-objs := xirlink_cit.o |
77 | gspca_zc3xx-objs := zc3xx.o | 79 | gspca_zc3xx-objs := zc3xx.o |
78 | 80 | ||
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c new file mode 100644 index 00000000000..84dfbab923b --- /dev/null +++ b/drivers/media/video/gspca/vicam.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * gspca ViCam subdriver | ||
3 | * | ||
4 | * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> | ||
5 | * | ||
6 | * Based on the usbvideo vicam driver, which is: | ||
7 | * | ||
8 | * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), | ||
9 | * Christopher L Cheney (ccheney@cheney.cx), | ||
10 | * Pavel Machek (pavel@ucw.cz), | ||
11 | * John Tyner (jtyner@cs.ucr.edu), | ||
12 | * Monroe Williams (monroe@pobox.com) | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
27 | */ | ||
28 | |||
29 | #define MODULE_NAME "vicam" | ||
30 | #define HEADER_SIZE 64 | ||
31 | |||
32 | #include <linux/workqueue.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/firmware.h> | ||
35 | #include <linux/ihex.h> | ||
36 | #include "gspca.h" | ||
37 | |||
38 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | ||
39 | MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | enum e_ctrl { | ||
43 | GAIN, | ||
44 | EXPOSURE, | ||
45 | NCTRL /* number of controls */ | ||
46 | }; | ||
47 | |||
48 | struct sd { | ||
49 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
50 | struct work_struct work_struct; | ||
51 | struct workqueue_struct *work_thread; | ||
52 | struct gspca_ctrl ctrls[NCTRL]; | ||
53 | }; | ||
54 | |||
55 | /* The vicam sensor has a resolution of 512 x 244, with I believe square | ||
56 | pixels, but this is forced to a 4:3 ratio by optics. So it has | ||
57 | non square pixels :( */ | ||
58 | static struct v4l2_pix_format vicam_mode[] = { | ||
59 | { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
60 | .bytesperline = 256, | ||
61 | .sizeimage = 256 * 122, | ||
62 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
63 | /* 2 modes with somewhat more square pixels */ | ||
64 | { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
65 | .bytesperline = 256, | ||
66 | .sizeimage = 256 * 200, | ||
67 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
68 | { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
69 | .bytesperline = 256, | ||
70 | .sizeimage = 256 * 240, | ||
71 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
72 | #if 0 /* This mode has extremely non square pixels, testing use only */ | ||
73 | { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
74 | .bytesperline = 512, | ||
75 | .sizeimage = 512 * 122, | ||
76 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
77 | #endif | ||
78 | { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
79 | .bytesperline = 512, | ||
80 | .sizeimage = 512 * 244, | ||
81 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
82 | }; | ||
83 | |||
84 | static const struct ctrl sd_ctrls[] = { | ||
85 | [GAIN] = { | ||
86 | { | ||
87 | .id = V4L2_CID_GAIN, | ||
88 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
89 | .name = "Gain", | ||
90 | .minimum = 0, | ||
91 | .maximum = 255, | ||
92 | .step = 1, | ||
93 | .default_value = 200, | ||
94 | }, | ||
95 | }, | ||
96 | [EXPOSURE] = { | ||
97 | { | ||
98 | .id = V4L2_CID_EXPOSURE, | ||
99 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
100 | .name = "Exposure", | ||
101 | .minimum = 0, | ||
102 | .maximum = 2047, | ||
103 | .step = 1, | ||
104 | .default_value = 256, | ||
105 | }, | ||
106 | }, | ||
107 | }; | ||
108 | |||
109 | static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, | ||
110 | u16 value, u16 index, u8 *data, u16 len) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | ret = usb_control_msg(gspca_dev->dev, | ||
115 | usb_sndctrlpipe(gspca_dev->dev, 0), | ||
116 | request, | ||
117 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
118 | value, index, data, len, 1000); | ||
119 | if (ret < 0) | ||
120 | err("control msg req %02X error %d", request, ret); | ||
121 | |||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) | ||
126 | { | ||
127 | int ret; | ||
128 | |||
129 | ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); | ||
130 | if (ret < 0) | ||
131 | return ret; | ||
132 | |||
133 | if (state) | ||
134 | ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * request and read a block of data - see warning on vicam_command. | ||
141 | */ | ||
142 | static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) | ||
143 | { | ||
144 | struct sd *sd = (struct sd *)gspca_dev; | ||
145 | int ret, unscaled_height, act_len = 0; | ||
146 | u8 *req_data = gspca_dev->usb_buf; | ||
147 | |||
148 | memset(req_data, 0, 16); | ||
149 | req_data[0] = sd->ctrls[GAIN].val; | ||
150 | if (gspca_dev->width == 256) | ||
151 | req_data[1] |= 0x01; /* low nibble x-scale */ | ||
152 | if (gspca_dev->height <= 122) { | ||
153 | req_data[1] |= 0x10; /* high nibble y-scale */ | ||
154 | unscaled_height = gspca_dev->height * 2; | ||
155 | } else | ||
156 | unscaled_height = gspca_dev->height; | ||
157 | req_data[2] = 0x90; /* unknown, does not seem to do anything */ | ||
158 | if (unscaled_height <= 200) | ||
159 | req_data[3] = 0x06; /* vend? */ | ||
160 | else if (unscaled_height <= 242) /* Yes 242 not 240 */ | ||
161 | req_data[3] = 0x07; /* vend? */ | ||
162 | else /* Up to 244 lines with req_data[3] == 0x08 */ | ||
163 | req_data[3] = 0x08; /* vend? */ | ||
164 | |||
165 | if (sd->ctrls[EXPOSURE].val < 256) { | ||
166 | /* Frame rate maxed out, use partial frame expo time */ | ||
167 | req_data[4] = 255 - sd->ctrls[EXPOSURE].val; | ||
168 | req_data[5] = 0x00; | ||
169 | req_data[6] = 0x00; | ||
170 | req_data[7] = 0x01; | ||
171 | } else { | ||
172 | /* Modify frame rate */ | ||
173 | req_data[4] = 0x00; | ||
174 | req_data[5] = 0x00; | ||
175 | req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF; | ||
176 | req_data[7] = sd->ctrls[EXPOSURE].val >> 8; | ||
177 | } | ||
178 | req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ | ||
179 | /* bytes 9-15 do not seem to affect exposure or image quality */ | ||
180 | |||
181 | mutex_lock(&gspca_dev->usb_lock); | ||
182 | ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); | ||
183 | mutex_unlock(&gspca_dev->usb_lock); | ||
184 | if (ret < 0) | ||
185 | return ret; | ||
186 | |||
187 | ret = usb_bulk_msg(gspca_dev->dev, | ||
188 | usb_rcvbulkpipe(gspca_dev->dev, 0x81), | ||
189 | data, size, &act_len, 10000); | ||
190 | /* successful, it returns 0, otherwise negative */ | ||
191 | if (ret < 0 || act_len != size) { | ||
192 | err("bulk read fail (%d) len %d/%d", | ||
193 | ret, act_len, size); | ||
194 | return -EIO; | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* This function is called as a workqueue function and runs whenever the camera | ||
200 | * is streaming data. Because it is a workqueue function it is allowed to sleep | ||
201 | * so we can use synchronous USB calls. To avoid possible collisions with other | ||
202 | * threads attempting to use the camera's USB interface we take the gspca | ||
203 | * usb_lock when performing USB operations. In practice the only thing we need | ||
204 | * to protect against is the usb_set_interface call that gspca makes during | ||
205 | * stream_off as the camera doesn't provide any controls that the user could try | ||
206 | * to change. | ||
207 | */ | ||
208 | static void vicam_dostream(struct work_struct *work) | ||
209 | { | ||
210 | struct sd *sd = container_of(work, struct sd, work_struct); | ||
211 | struct gspca_dev *gspca_dev = &sd->gspca_dev; | ||
212 | int ret, frame_sz; | ||
213 | u8 *buffer; | ||
214 | |||
215 | frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + | ||
216 | HEADER_SIZE; | ||
217 | buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); | ||
218 | if (!buffer) { | ||
219 | err("Couldn't allocate USB buffer"); | ||
220 | goto exit; | ||
221 | } | ||
222 | |||
223 | while (gspca_dev->present && gspca_dev->streaming) { | ||
224 | ret = vicam_read_frame(gspca_dev, buffer, frame_sz); | ||
225 | if (ret < 0) | ||
226 | break; | ||
227 | |||
228 | /* Note the frame header contents seem to be completely | ||
229 | constant, they do not change with either image, or | ||
230 | settings. So we simply discard it. The frames have | ||
231 | a very similar 64 byte footer, which we don't even | ||
232 | bother reading from the cam */ | ||
233 | gspca_frame_add(gspca_dev, FIRST_PACKET, | ||
234 | buffer + HEADER_SIZE, | ||
235 | frame_sz - HEADER_SIZE); | ||
236 | gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); | ||
237 | } | ||
238 | exit: | ||
239 | kfree(buffer); | ||
240 | } | ||
241 | |||
242 | /* This function is called at probe time just before sd_init */ | ||
243 | static int sd_config(struct gspca_dev *gspca_dev, | ||
244 | const struct usb_device_id *id) | ||
245 | { | ||
246 | struct cam *cam = &gspca_dev->cam; | ||
247 | struct sd *sd = (struct sd *)gspca_dev; | ||
248 | |||
249 | /* We don't use the buffer gspca allocates so make it small. */ | ||
250 | cam->bulk = 1; | ||
251 | cam->bulk_size = 64; | ||
252 | cam->cam_mode = vicam_mode; | ||
253 | cam->nmodes = ARRAY_SIZE(vicam_mode); | ||
254 | cam->ctrls = sd->ctrls; | ||
255 | |||
256 | INIT_WORK(&sd->work_struct, vicam_dostream); | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | /* this function is called at probe and resume time */ | ||
262 | static int sd_init(struct gspca_dev *gspca_dev) | ||
263 | { | ||
264 | int ret; | ||
265 | const struct ihex_binrec *rec; | ||
266 | const struct firmware *uninitialized_var(fw); | ||
267 | u8 *firmware_buf; | ||
268 | |||
269 | ret = request_ihex_firmware(&fw, "vicam/firmware.fw", | ||
270 | &gspca_dev->dev->dev); | ||
271 | if (ret) { | ||
272 | err("Failed to load \"vicam/firmware.fw\": %d\n", ret); | ||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
277 | if (!firmware_buf) { | ||
278 | ret = -ENOMEM; | ||
279 | goto exit; | ||
280 | } | ||
281 | for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { | ||
282 | memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); | ||
283 | ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, | ||
284 | be16_to_cpu(rec->len)); | ||
285 | if (ret < 0) | ||
286 | break; | ||
287 | } | ||
288 | |||
289 | kfree(firmware_buf); | ||
290 | exit: | ||
291 | release_firmware(fw); | ||
292 | return ret; | ||
293 | } | ||
294 | |||
295 | /* Set up for getting frames. */ | ||
296 | static int sd_start(struct gspca_dev *gspca_dev) | ||
297 | { | ||
298 | struct sd *sd = (struct sd *)gspca_dev; | ||
299 | int ret; | ||
300 | |||
301 | ret = vicam_set_camera_power(gspca_dev, 1); | ||
302 | if (ret < 0) | ||
303 | return ret; | ||
304 | |||
305 | /* Start the workqueue function to do the streaming */ | ||
306 | sd->work_thread = create_singlethread_workqueue(MODULE_NAME); | ||
307 | queue_work(sd->work_thread, &sd->work_struct); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | /* called on streamoff with alt==0 and on disconnect */ | ||
313 | /* the usb_lock is held at entry - restore on exit */ | ||
314 | static void sd_stop0(struct gspca_dev *gspca_dev) | ||
315 | { | ||
316 | struct sd *dev = (struct sd *)gspca_dev; | ||
317 | |||
318 | /* wait for the work queue to terminate */ | ||
319 | mutex_unlock(&gspca_dev->usb_lock); | ||
320 | /* This waits for vicam_dostream to finish */ | ||
321 | destroy_workqueue(dev->work_thread); | ||
322 | dev->work_thread = NULL; | ||
323 | mutex_lock(&gspca_dev->usb_lock); | ||
324 | |||
325 | vicam_set_camera_power(gspca_dev, 0); | ||
326 | } | ||
327 | |||
328 | /* Table of supported USB devices */ | ||
329 | static const struct usb_device_id device_table[] = { | ||
330 | {USB_DEVICE(0x04c1, 0x009d)}, | ||
331 | {USB_DEVICE(0x0602, 0x1001)}, | ||
332 | {} | ||
333 | }; | ||
334 | |||
335 | MODULE_DEVICE_TABLE(usb, device_table); | ||
336 | |||
337 | /* sub-driver description */ | ||
338 | static const struct sd_desc sd_desc = { | ||
339 | .name = MODULE_NAME, | ||
340 | .ctrls = sd_ctrls, | ||
341 | .nctrls = ARRAY_SIZE(sd_ctrls), | ||
342 | .config = sd_config, | ||
343 | .init = sd_init, | ||
344 | .start = sd_start, | ||
345 | .stop0 = sd_stop0, | ||
346 | }; | ||
347 | |||
348 | /* -- device connect -- */ | ||
349 | static int sd_probe(struct usb_interface *intf, | ||
350 | const struct usb_device_id *id) | ||
351 | { | ||
352 | return gspca_dev_probe(intf, id, | ||
353 | &sd_desc, | ||
354 | sizeof(struct sd), | ||
355 | THIS_MODULE); | ||
356 | } | ||
357 | |||
358 | static struct usb_driver sd_driver = { | ||
359 | .name = MODULE_NAME, | ||
360 | .id_table = device_table, | ||
361 | .probe = sd_probe, | ||
362 | .disconnect = gspca_disconnect, | ||
363 | #ifdef CONFIG_PM | ||
364 | .suspend = gspca_suspend, | ||
365 | .resume = gspca_resume, | ||
366 | #endif | ||
367 | }; | ||
368 | |||
369 | /* -- module insert / remove -- */ | ||
370 | static int __init sd_mod_init(void) | ||
371 | { | ||
372 | return usb_register(&sd_driver); | ||
373 | } | ||
374 | |||
375 | static void __exit sd_mod_exit(void) | ||
376 | { | ||
377 | usb_deregister(&sd_driver); | ||
378 | } | ||
379 | |||
380 | module_init(sd_mod_init); | ||
381 | module_exit(sd_mod_exit); | ||