diff options
Diffstat (limited to 'drivers/media/usb/gspca/vicam.c')
-rw-r--r-- | drivers/media/usb/gspca/vicam.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c new file mode 100644 index 000000000000..b1a64b912666 --- /dev/null +++ b/drivers/media/usb/gspca/vicam.c | |||
@@ -0,0 +1,365 @@ | |||
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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
30 | |||
31 | #define MODULE_NAME "vicam" | ||
32 | #define HEADER_SIZE 64 | ||
33 | |||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/firmware.h> | ||
37 | #include <linux/ihex.h> | ||
38 | #include "gspca.h" | ||
39 | |||
40 | #define VICAM_FIRMWARE "vicam/firmware.fw" | ||
41 | |||
42 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | ||
43 | MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | MODULE_FIRMWARE(VICAM_FIRMWARE); | ||
46 | |||
47 | struct sd { | ||
48 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
49 | struct work_struct work_struct; | ||
50 | struct workqueue_struct *work_thread; | ||
51 | }; | ||
52 | |||
53 | /* The vicam sensor has a resolution of 512 x 244, with I believe square | ||
54 | pixels, but this is forced to a 4:3 ratio by optics. So it has | ||
55 | non square pixels :( */ | ||
56 | static struct v4l2_pix_format vicam_mode[] = { | ||
57 | { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
58 | .bytesperline = 256, | ||
59 | .sizeimage = 256 * 122, | ||
60 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
61 | /* 2 modes with somewhat more square pixels */ | ||
62 | { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
63 | .bytesperline = 256, | ||
64 | .sizeimage = 256 * 200, | ||
65 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
66 | { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
67 | .bytesperline = 256, | ||
68 | .sizeimage = 256 * 240, | ||
69 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
70 | #if 0 /* This mode has extremely non square pixels, testing use only */ | ||
71 | { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
72 | .bytesperline = 512, | ||
73 | .sizeimage = 512 * 122, | ||
74 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
75 | #endif | ||
76 | { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
77 | .bytesperline = 512, | ||
78 | .sizeimage = 512 * 244, | ||
79 | .colorspace = V4L2_COLORSPACE_SRGB,}, | ||
80 | }; | ||
81 | |||
82 | static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, | ||
83 | u16 value, u16 index, u8 *data, u16 len) | ||
84 | { | ||
85 | int ret; | ||
86 | |||
87 | ret = usb_control_msg(gspca_dev->dev, | ||
88 | usb_sndctrlpipe(gspca_dev->dev, 0), | ||
89 | request, | ||
90 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
91 | value, index, data, len, 1000); | ||
92 | if (ret < 0) | ||
93 | pr_err("control msg req %02X error %d\n", request, ret); | ||
94 | |||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) | ||
99 | { | ||
100 | int ret; | ||
101 | |||
102 | ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); | ||
103 | if (ret < 0) | ||
104 | return ret; | ||
105 | |||
106 | if (state) | ||
107 | ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); | ||
108 | |||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * request and read a block of data - see warning on vicam_command. | ||
114 | */ | ||
115 | static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) | ||
116 | { | ||
117 | int ret, unscaled_height, act_len = 0; | ||
118 | u8 *req_data = gspca_dev->usb_buf; | ||
119 | s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); | ||
120 | s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); | ||
121 | |||
122 | memset(req_data, 0, 16); | ||
123 | req_data[0] = gain; | ||
124 | if (gspca_dev->width == 256) | ||
125 | req_data[1] |= 0x01; /* low nibble x-scale */ | ||
126 | if (gspca_dev->height <= 122) { | ||
127 | req_data[1] |= 0x10; /* high nibble y-scale */ | ||
128 | unscaled_height = gspca_dev->height * 2; | ||
129 | } else | ||
130 | unscaled_height = gspca_dev->height; | ||
131 | req_data[2] = 0x90; /* unknown, does not seem to do anything */ | ||
132 | if (unscaled_height <= 200) | ||
133 | req_data[3] = 0x06; /* vend? */ | ||
134 | else if (unscaled_height <= 242) /* Yes 242 not 240 */ | ||
135 | req_data[3] = 0x07; /* vend? */ | ||
136 | else /* Up to 244 lines with req_data[3] == 0x08 */ | ||
137 | req_data[3] = 0x08; /* vend? */ | ||
138 | |||
139 | if (expo < 256) { | ||
140 | /* Frame rate maxed out, use partial frame expo time */ | ||
141 | req_data[4] = 255 - expo; | ||
142 | req_data[5] = 0x00; | ||
143 | req_data[6] = 0x00; | ||
144 | req_data[7] = 0x01; | ||
145 | } else { | ||
146 | /* Modify frame rate */ | ||
147 | req_data[4] = 0x00; | ||
148 | req_data[5] = 0x00; | ||
149 | req_data[6] = expo & 0xFF; | ||
150 | req_data[7] = expo >> 8; | ||
151 | } | ||
152 | req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ | ||
153 | /* bytes 9-15 do not seem to affect exposure or image quality */ | ||
154 | |||
155 | mutex_lock(&gspca_dev->usb_lock); | ||
156 | ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); | ||
157 | mutex_unlock(&gspca_dev->usb_lock); | ||
158 | if (ret < 0) | ||
159 | return ret; | ||
160 | |||
161 | ret = usb_bulk_msg(gspca_dev->dev, | ||
162 | usb_rcvbulkpipe(gspca_dev->dev, 0x81), | ||
163 | data, size, &act_len, 10000); | ||
164 | /* successful, it returns 0, otherwise negative */ | ||
165 | if (ret < 0 || act_len != size) { | ||
166 | pr_err("bulk read fail (%d) len %d/%d\n", | ||
167 | ret, act_len, size); | ||
168 | return -EIO; | ||
169 | } | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /* This function is called as a workqueue function and runs whenever the camera | ||
174 | * is streaming data. Because it is a workqueue function it is allowed to sleep | ||
175 | * so we can use synchronous USB calls. To avoid possible collisions with other | ||
176 | * threads attempting to use the camera's USB interface we take the gspca | ||
177 | * usb_lock when performing USB operations. In practice the only thing we need | ||
178 | * to protect against is the usb_set_interface call that gspca makes during | ||
179 | * stream_off as the camera doesn't provide any controls that the user could try | ||
180 | * to change. | ||
181 | */ | ||
182 | static void vicam_dostream(struct work_struct *work) | ||
183 | { | ||
184 | struct sd *sd = container_of(work, struct sd, work_struct); | ||
185 | struct gspca_dev *gspca_dev = &sd->gspca_dev; | ||
186 | int ret, frame_sz; | ||
187 | u8 *buffer; | ||
188 | |||
189 | frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + | ||
190 | HEADER_SIZE; | ||
191 | buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); | ||
192 | if (!buffer) { | ||
193 | pr_err("Couldn't allocate USB buffer\n"); | ||
194 | goto exit; | ||
195 | } | ||
196 | |||
197 | while (gspca_dev->dev && gspca_dev->streaming) { | ||
198 | #ifdef CONFIG_PM | ||
199 | if (gspca_dev->frozen) | ||
200 | break; | ||
201 | #endif | ||
202 | ret = vicam_read_frame(gspca_dev, buffer, frame_sz); | ||
203 | if (ret < 0) | ||
204 | break; | ||
205 | |||
206 | /* Note the frame header contents seem to be completely | ||
207 | constant, they do not change with either image, or | ||
208 | settings. So we simply discard it. The frames have | ||
209 | a very similar 64 byte footer, which we don't even | ||
210 | bother reading from the cam */ | ||
211 | gspca_frame_add(gspca_dev, FIRST_PACKET, | ||
212 | buffer + HEADER_SIZE, | ||
213 | frame_sz - HEADER_SIZE); | ||
214 | gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); | ||
215 | } | ||
216 | exit: | ||
217 | kfree(buffer); | ||
218 | } | ||
219 | |||
220 | /* This function is called at probe time just before sd_init */ | ||
221 | static int sd_config(struct gspca_dev *gspca_dev, | ||
222 | const struct usb_device_id *id) | ||
223 | { | ||
224 | struct cam *cam = &gspca_dev->cam; | ||
225 | struct sd *sd = (struct sd *)gspca_dev; | ||
226 | |||
227 | /* We don't use the buffer gspca allocates so make it small. */ | ||
228 | cam->bulk = 1; | ||
229 | cam->bulk_size = 64; | ||
230 | cam->cam_mode = vicam_mode; | ||
231 | cam->nmodes = ARRAY_SIZE(vicam_mode); | ||
232 | |||
233 | INIT_WORK(&sd->work_struct, vicam_dostream); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* this function is called at probe and resume time */ | ||
239 | static int sd_init(struct gspca_dev *gspca_dev) | ||
240 | { | ||
241 | int ret; | ||
242 | const struct ihex_binrec *rec; | ||
243 | const struct firmware *uninitialized_var(fw); | ||
244 | u8 *firmware_buf; | ||
245 | |||
246 | ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, | ||
247 | &gspca_dev->dev->dev); | ||
248 | if (ret) { | ||
249 | pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); | ||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
254 | if (!firmware_buf) { | ||
255 | ret = -ENOMEM; | ||
256 | goto exit; | ||
257 | } | ||
258 | for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { | ||
259 | memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); | ||
260 | ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, | ||
261 | be16_to_cpu(rec->len)); | ||
262 | if (ret < 0) | ||
263 | break; | ||
264 | } | ||
265 | |||
266 | kfree(firmware_buf); | ||
267 | exit: | ||
268 | release_firmware(fw); | ||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | /* Set up for getting frames. */ | ||
273 | static int sd_start(struct gspca_dev *gspca_dev) | ||
274 | { | ||
275 | struct sd *sd = (struct sd *)gspca_dev; | ||
276 | int ret; | ||
277 | |||
278 | ret = vicam_set_camera_power(gspca_dev, 1); | ||
279 | if (ret < 0) | ||
280 | return ret; | ||
281 | |||
282 | /* Start the workqueue function to do the streaming */ | ||
283 | sd->work_thread = create_singlethread_workqueue(MODULE_NAME); | ||
284 | queue_work(sd->work_thread, &sd->work_struct); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | /* called on streamoff with alt==0 and on disconnect */ | ||
290 | /* the usb_lock is held at entry - restore on exit */ | ||
291 | static void sd_stop0(struct gspca_dev *gspca_dev) | ||
292 | { | ||
293 | struct sd *dev = (struct sd *)gspca_dev; | ||
294 | |||
295 | /* wait for the work queue to terminate */ | ||
296 | mutex_unlock(&gspca_dev->usb_lock); | ||
297 | /* This waits for vicam_dostream to finish */ | ||
298 | destroy_workqueue(dev->work_thread); | ||
299 | dev->work_thread = NULL; | ||
300 | mutex_lock(&gspca_dev->usb_lock); | ||
301 | |||
302 | if (gspca_dev->dev) | ||
303 | vicam_set_camera_power(gspca_dev, 0); | ||
304 | } | ||
305 | |||
306 | static int sd_init_controls(struct gspca_dev *gspca_dev) | ||
307 | { | ||
308 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; | ||
309 | |||
310 | gspca_dev->vdev.ctrl_handler = hdl; | ||
311 | v4l2_ctrl_handler_init(hdl, 2); | ||
312 | gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL, | ||
313 | V4L2_CID_EXPOSURE, 0, 2047, 1, 256); | ||
314 | gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL, | ||
315 | V4L2_CID_GAIN, 0, 255, 1, 200); | ||
316 | |||
317 | if (hdl->error) { | ||
318 | pr_err("Could not initialize controls\n"); | ||
319 | return hdl->error; | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /* Table of supported USB devices */ | ||
325 | static const struct usb_device_id device_table[] = { | ||
326 | {USB_DEVICE(0x04c1, 0x009d)}, | ||
327 | {USB_DEVICE(0x0602, 0x1001)}, | ||
328 | {} | ||
329 | }; | ||
330 | |||
331 | MODULE_DEVICE_TABLE(usb, device_table); | ||
332 | |||
333 | /* sub-driver description */ | ||
334 | static const struct sd_desc sd_desc = { | ||
335 | .name = MODULE_NAME, | ||
336 | .config = sd_config, | ||
337 | .init = sd_init, | ||
338 | .init_controls = sd_init_controls, | ||
339 | .start = sd_start, | ||
340 | .stop0 = sd_stop0, | ||
341 | }; | ||
342 | |||
343 | /* -- device connect -- */ | ||
344 | static int sd_probe(struct usb_interface *intf, | ||
345 | const struct usb_device_id *id) | ||
346 | { | ||
347 | return gspca_dev_probe(intf, id, | ||
348 | &sd_desc, | ||
349 | sizeof(struct sd), | ||
350 | THIS_MODULE); | ||
351 | } | ||
352 | |||
353 | static struct usb_driver sd_driver = { | ||
354 | .name = MODULE_NAME, | ||
355 | .id_table = device_table, | ||
356 | .probe = sd_probe, | ||
357 | .disconnect = gspca_disconnect, | ||
358 | #ifdef CONFIG_PM | ||
359 | .suspend = gspca_suspend, | ||
360 | .resume = gspca_resume, | ||
361 | .reset_resume = gspca_resume, | ||
362 | #endif | ||
363 | }; | ||
364 | |||
365 | module_usb_driver(sd_driver); | ||