Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Video4Linux Colour QuickCam driver | |
3 | * Copyright 1997-2000 Philip Blundell <philb@gnu.org> | |
4 | * | |
5 | * Module parameters: | |
6 | * | |
7 | * parport=auto -- probe all parports (default) | |
8 | * parport=0 -- parport0 becomes qcam1 | |
9 | * parport=2,0,1 -- parports 2,0,1 are tried in that order | |
10 | * | |
11 | * probe=0 -- do no probing, assume camera is present | |
12 | * probe=1 -- use IEEE-1284 autoprobe data only (default) | |
13 | * probe=2 -- probe aggressively for cameras | |
14 | * | |
15 | * force_rgb=1 -- force data format to RGB (default is BGR) | |
16 | * | |
17 | * The parport parameter controls which parports will be scanned. | |
18 | * Scanning all parports causes some printers to print a garbage page. | |
d56410e0 | 19 | * -- March 14, 1999 Billy Donahue <billy@escape.com> |
1da177e4 LT |
20 | * |
21 | * Fixed data format to BGR, added force_rgb parameter. Added missing | |
22 | * parport_unregister_driver() on module removal. | |
23 | * -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com> | |
24 | */ | |
25 | ||
26 | #include <linux/module.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/errno.h> | |
29 | #include <linux/fs.h> | |
30 | #include <linux/init.h> | |
31 | #include <linux/kernel.h> | |
32 | #include <linux/slab.h> | |
33 | #include <linux/mm.h> | |
34 | #include <linux/parport.h> | |
35 | #include <linux/sched.h> | |
3593cab5 | 36 | #include <linux/mutex.h> |
168c626c | 37 | #include <linux/jiffies.h> |
d71964fb HV |
38 | #include <linux/version.h> |
39 | #include <linux/videodev2.h> | |
1da177e4 | 40 | #include <asm/uaccess.h> |
d71964fb HV |
41 | #include <media/v4l2-device.h> |
42 | #include <media/v4l2-common.h> | |
43 | #include <media/v4l2-ioctl.h> | |
1da177e4 | 44 | |
d71964fb HV |
45 | struct qcam { |
46 | struct v4l2_device v4l2_dev; | |
1da177e4 LT |
47 | struct video_device vdev; |
48 | struct pardevice *pdev; | |
49 | struct parport *pport; | |
50 | int width, height; | |
51 | int ccd_width, ccd_height; | |
52 | int mode; | |
53 | int contrast, brightness, whitebal; | |
54 | int top, left; | |
55 | unsigned int bidirectional; | |
3593cab5 | 56 | struct mutex lock; |
1da177e4 LT |
57 | }; |
58 | ||
59 | /* cameras maximum */ | |
60 | #define MAX_CAMS 4 | |
61 | ||
62 | /* The three possible QuickCam modes */ | |
63 | #define QC_MILLIONS 0x18 | |
64 | #define QC_BILLIONS 0x10 | |
65 | #define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */ | |
66 | ||
67 | /* The three possible decimations */ | |
68 | #define QC_DECIMATION_1 0 | |
69 | #define QC_DECIMATION_2 2 | |
70 | #define QC_DECIMATION_4 4 | |
71 | ||
d71964fb | 72 | #define BANNER "Colour QuickCam for Video4Linux v0.06" |
1da177e4 LT |
73 | |
74 | static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; | |
75 | static int probe = 2; | |
ff699e6b | 76 | static int force_rgb; |
1da177e4 LT |
77 | static int video_nr = -1; |
78 | ||
d71964fb HV |
79 | /* FIXME: parport=auto would never have worked, surely? --RR */ |
80 | MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n" | |
81 | "probe=<0|1|2> for camera detection method\n" | |
82 | "force_rgb=<0|1> for RGB data format (default BGR)"); | |
83 | module_param_array(parport, int, NULL, 0); | |
84 | module_param(probe, int, 0); | |
85 | module_param(force_rgb, bool, 0); | |
86 | module_param(video_nr, int, 0); | |
87 | ||
88 | static struct qcam *qcams[MAX_CAMS]; | |
89 | static unsigned int num_cams; | |
90 | ||
91 | static inline void qcam_set_ack(struct qcam *qcam, unsigned int i) | |
1da177e4 LT |
92 | { |
93 | /* note: the QC specs refer to the PCAck pin by voltage, not | |
94 | software level. PC ports have builtin inverters. */ | |
51224aa4 | 95 | parport_frob_control(qcam->pport, 8, i ? 8 : 0); |
1da177e4 LT |
96 | } |
97 | ||
d71964fb | 98 | static inline unsigned int qcam_ready1(struct qcam *qcam) |
1da177e4 | 99 | { |
51224aa4 | 100 | return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; |
1da177e4 LT |
101 | } |
102 | ||
d71964fb | 103 | static inline unsigned int qcam_ready2(struct qcam *qcam) |
1da177e4 | 104 | { |
51224aa4 | 105 | return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; |
1da177e4 LT |
106 | } |
107 | ||
d71964fb | 108 | static unsigned int qcam_await_ready1(struct qcam *qcam, int value) |
1da177e4 | 109 | { |
d71964fb | 110 | struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; |
1da177e4 LT |
111 | unsigned long oldjiffies = jiffies; |
112 | unsigned int i; | |
113 | ||
168c626c | 114 | for (oldjiffies = jiffies; |
51224aa4 | 115 | time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) |
1da177e4 LT |
116 | if (qcam_ready1(qcam) == value) |
117 | return 0; | |
118 | ||
d56410e0 | 119 | /* If the camera didn't respond within 1/25 second, poll slowly |
1da177e4 | 120 | for a while. */ |
51224aa4 | 121 | for (i = 0; i < 50; i++) { |
1da177e4 LT |
122 | if (qcam_ready1(qcam) == value) |
123 | return 0; | |
124 | msleep_interruptible(100); | |
125 | } | |
126 | ||
127 | /* Probably somebody pulled the plug out. Not much we can do. */ | |
d71964fb | 128 | v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value, |
1da177e4 LT |
129 | parport_read_status(qcam->pport), |
130 | parport_read_control(qcam->pport)); | |
131 | return 1; | |
132 | } | |
133 | ||
d71964fb | 134 | static unsigned int qcam_await_ready2(struct qcam *qcam, int value) |
1da177e4 | 135 | { |
d71964fb | 136 | struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; |
1da177e4 LT |
137 | unsigned long oldjiffies = jiffies; |
138 | unsigned int i; | |
139 | ||
168c626c | 140 | for (oldjiffies = jiffies; |
51224aa4 | 141 | time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) |
1da177e4 LT |
142 | if (qcam_ready2(qcam) == value) |
143 | return 0; | |
144 | ||
d56410e0 | 145 | /* If the camera didn't respond within 1/25 second, poll slowly |
1da177e4 | 146 | for a while. */ |
51224aa4 | 147 | for (i = 0; i < 50; i++) { |
1da177e4 LT |
148 | if (qcam_ready2(qcam) == value) |
149 | return 0; | |
150 | msleep_interruptible(100); | |
151 | } | |
152 | ||
153 | /* Probably somebody pulled the plug out. Not much we can do. */ | |
d71964fb | 154 | v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value, |
1da177e4 LT |
155 | parport_read_status(qcam->pport), |
156 | parport_read_control(qcam->pport), | |
157 | parport_read_data(qcam->pport)); | |
158 | return 1; | |
159 | } | |
160 | ||
d71964fb | 161 | static int qcam_read_data(struct qcam *qcam) |
1da177e4 LT |
162 | { |
163 | unsigned int idata; | |
51224aa4 | 164 | |
1da177e4 | 165 | qcam_set_ack(qcam, 0); |
51224aa4 HV |
166 | if (qcam_await_ready1(qcam, 1)) |
167 | return -1; | |
1da177e4 LT |
168 | idata = parport_read_status(qcam->pport) & 0xf0; |
169 | qcam_set_ack(qcam, 1); | |
51224aa4 HV |
170 | if (qcam_await_ready1(qcam, 0)) |
171 | return -1; | |
172 | idata |= parport_read_status(qcam->pport) >> 4; | |
1da177e4 LT |
173 | return idata; |
174 | } | |
175 | ||
d71964fb | 176 | static int qcam_write_data(struct qcam *qcam, unsigned int data) |
1da177e4 | 177 | { |
d71964fb | 178 | struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; |
1da177e4 | 179 | unsigned int idata; |
51224aa4 | 180 | |
1da177e4 LT |
181 | parport_write_data(qcam->pport, data); |
182 | idata = qcam_read_data(qcam); | |
51224aa4 | 183 | if (data != idata) { |
d71964fb | 184 | v4l2_warn(v4l2_dev, "sent %x but received %x\n", data, |
1da177e4 LT |
185 | idata); |
186 | return 1; | |
d56410e0 | 187 | } |
1da177e4 LT |
188 | return 0; |
189 | } | |
190 | ||
d71964fb | 191 | static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data) |
1da177e4 LT |
192 | { |
193 | if (qcam_write_data(qcam, cmd)) | |
194 | return -1; | |
195 | if (qcam_write_data(qcam, data)) | |
196 | return -1; | |
197 | return 0; | |
198 | } | |
199 | ||
d71964fb | 200 | static inline int qcam_get(struct qcam *qcam, unsigned int cmd) |
1da177e4 LT |
201 | { |
202 | if (qcam_write_data(qcam, cmd)) | |
203 | return -1; | |
204 | return qcam_read_data(qcam); | |
205 | } | |
206 | ||
d71964fb | 207 | static int qc_detect(struct qcam *qcam) |
1da177e4 LT |
208 | { |
209 | unsigned int stat, ostat, i, count = 0; | |
210 | ||
211 | /* The probe routine below is not very reliable. The IEEE-1284 | |
212 | probe takes precedence. */ | |
213 | /* XXX Currently parport provides no way to distinguish between | |
214 | "the IEEE probe was not done" and "the probe was done, but | |
215 | no device was found". Fix this one day. */ | |
216 | if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA | |
217 | && qcam->pport->probe_info[0].model | |
d56410e0 | 218 | && !strcmp(qcam->pdev->port->probe_info[0].model, |
1da177e4 LT |
219 | "Color QuickCam 2.0")) { |
220 | printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n"); | |
221 | return 1; | |
222 | } | |
d56410e0 | 223 | |
1da177e4 LT |
224 | if (probe < 2) |
225 | return 0; | |
226 | ||
227 | parport_write_control(qcam->pport, 0xc); | |
228 | ||
229 | /* look for a heartbeat */ | |
230 | ostat = stat = parport_read_status(qcam->pport); | |
51224aa4 | 231 | for (i = 0; i < 250; i++) { |
1da177e4 LT |
232 | mdelay(1); |
233 | stat = parport_read_status(qcam->pport); | |
51224aa4 HV |
234 | if (ostat != stat) { |
235 | if (++count >= 3) | |
236 | return 1; | |
1da177e4 LT |
237 | ostat = stat; |
238 | } | |
239 | } | |
240 | ||
241 | /* Reset the camera and try again */ | |
242 | parport_write_control(qcam->pport, 0xc); | |
243 | parport_write_control(qcam->pport, 0x8); | |
244 | mdelay(1); | |
245 | parport_write_control(qcam->pport, 0xc); | |
246 | mdelay(1); | |
247 | count = 0; | |
248 | ||
249 | ostat = stat = parport_read_status(qcam->pport); | |
51224aa4 | 250 | for (i = 0; i < 250; i++) { |
1da177e4 LT |
251 | mdelay(1); |
252 | stat = parport_read_status(qcam->pport); | |
51224aa4 HV |
253 | if (ostat != stat) { |
254 | if (++count >= 3) | |
255 | return 1; | |
1da177e4 LT |
256 | ostat = stat; |
257 | } | |
258 | } | |
259 | ||
260 | /* no (or flatline) camera, give up */ | |
261 | return 0; | |
262 | } | |
263 | ||
d71964fb | 264 | static void qc_reset(struct qcam *qcam) |
1da177e4 LT |
265 | { |
266 | parport_write_control(qcam->pport, 0xc); | |
267 | parport_write_control(qcam->pport, 0x8); | |
268 | mdelay(1); | |
269 | parport_write_control(qcam->pport, 0xc); | |
d56410e0 | 270 | mdelay(1); |
1da177e4 LT |
271 | } |
272 | ||
273 | /* Reset the QuickCam and program for brightness, contrast, | |
274 | * white-balance, and resolution. */ | |
275 | ||
d71964fb | 276 | static void qc_setup(struct qcam *qcam) |
1da177e4 | 277 | { |
d71964fb | 278 | qc_reset(qcam); |
1da177e4 | 279 | |
51224aa4 | 280 | /* Set the brightness. */ |
d71964fb | 281 | qcam_set(qcam, 11, qcam->brightness); |
1da177e4 LT |
282 | |
283 | /* Set the height and width. These refer to the actual | |
284 | CCD area *before* applying the selected decimation. */ | |
d71964fb HV |
285 | qcam_set(qcam, 17, qcam->ccd_height); |
286 | qcam_set(qcam, 19, qcam->ccd_width / 2); | |
1da177e4 LT |
287 | |
288 | /* Set top and left. */ | |
d71964fb HV |
289 | qcam_set(qcam, 0xd, qcam->top); |
290 | qcam_set(qcam, 0xf, qcam->left); | |
1da177e4 LT |
291 | |
292 | /* Set contrast and white balance. */ | |
d71964fb HV |
293 | qcam_set(qcam, 0x19, qcam->contrast); |
294 | qcam_set(qcam, 0x1f, qcam->whitebal); | |
d56410e0 | 295 | |
1da177e4 | 296 | /* Set the speed. */ |
d71964fb | 297 | qcam_set(qcam, 45, 2); |
1da177e4 LT |
298 | } |
299 | ||
d56410e0 | 300 | /* Read some bytes from the camera and put them in the buffer. |
1da177e4 LT |
301 | nbytes should be a multiple of 3, because bidirectional mode gives |
302 | us three bytes at a time. */ | |
303 | ||
d71964fb | 304 | static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes) |
1da177e4 LT |
305 | { |
306 | unsigned int bytes = 0; | |
307 | ||
d71964fb HV |
308 | qcam_set_ack(qcam, 0); |
309 | if (qcam->bidirectional) { | |
1da177e4 | 310 | /* It's a bidirectional port */ |
51224aa4 | 311 | while (bytes < nbytes) { |
1da177e4 LT |
312 | unsigned int lo1, hi1, lo2, hi2; |
313 | unsigned char r, g, b; | |
314 | ||
d71964fb | 315 | if (qcam_await_ready2(qcam, 1)) |
51224aa4 | 316 | return bytes; |
d71964fb HV |
317 | lo1 = parport_read_data(qcam->pport) >> 1; |
318 | hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; | |
319 | qcam_set_ack(qcam, 1); | |
320 | if (qcam_await_ready2(qcam, 0)) | |
51224aa4 | 321 | return bytes; |
d71964fb HV |
322 | lo2 = parport_read_data(qcam->pport) >> 1; |
323 | hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; | |
324 | qcam_set_ack(qcam, 0); | |
51224aa4 HV |
325 | r = lo1 | ((hi1 & 1) << 7); |
326 | g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); | |
327 | b = lo2 | ((hi2 & 1) << 7); | |
1da177e4 LT |
328 | if (force_rgb) { |
329 | buf[bytes++] = r; | |
330 | buf[bytes++] = g; | |
331 | buf[bytes++] = b; | |
332 | } else { | |
333 | buf[bytes++] = b; | |
334 | buf[bytes++] = g; | |
335 | buf[bytes++] = r; | |
336 | } | |
337 | } | |
51224aa4 | 338 | } else { |
1da177e4 LT |
339 | /* It's a unidirectional port */ |
340 | int i = 0, n = bytes; | |
341 | unsigned char rgb[3]; | |
342 | ||
51224aa4 | 343 | while (bytes < nbytes) { |
1da177e4 LT |
344 | unsigned int hi, lo; |
345 | ||
d71964fb | 346 | if (qcam_await_ready1(qcam, 1)) |
51224aa4 | 347 | return bytes; |
d71964fb HV |
348 | hi = (parport_read_status(qcam->pport) & 0xf0); |
349 | qcam_set_ack(qcam, 1); | |
350 | if (qcam_await_ready1(qcam, 0)) | |
51224aa4 | 351 | return bytes; |
d71964fb HV |
352 | lo = (parport_read_status(qcam->pport) & 0xf0); |
353 | qcam_set_ack(qcam, 0); | |
1da177e4 LT |
354 | /* flip some bits */ |
355 | rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88; | |
356 | if (i >= 2) { | |
357 | get_fragment: | |
358 | if (force_rgb) { | |
359 | buf[n++] = rgb[0]; | |
360 | buf[n++] = rgb[1]; | |
361 | buf[n++] = rgb[2]; | |
362 | } else { | |
363 | buf[n++] = rgb[2]; | |
364 | buf[n++] = rgb[1]; | |
365 | buf[n++] = rgb[0]; | |
366 | } | |
367 | } | |
368 | } | |
369 | if (i) { | |
370 | i = 0; | |
371 | goto get_fragment; | |
372 | } | |
373 | } | |
374 | return bytes; | |
375 | } | |
376 | ||
377 | #define BUFSZ 150 | |
378 | ||
d71964fb | 379 | static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) |
1da177e4 | 380 | { |
d71964fb | 381 | struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; |
1da177e4 | 382 | unsigned lines, pixelsperline, bitsperxfer; |
d71964fb | 383 | unsigned int is_bi_dir = qcam->bidirectional; |
1da177e4 LT |
384 | size_t wantlen, outptr = 0; |
385 | char tmpbuf[BUFSZ]; | |
386 | ||
387 | if (!access_ok(VERIFY_WRITE, buf, len)) | |
388 | return -EFAULT; | |
389 | ||
390 | /* Wait for camera to become ready */ | |
51224aa4 | 391 | for (;;) { |
d71964fb | 392 | int i = qcam_get(qcam, 41); |
51224aa4 | 393 | |
1da177e4 | 394 | if (i == -1) { |
d71964fb | 395 | qc_setup(qcam); |
1da177e4 LT |
396 | return -EIO; |
397 | } | |
398 | if ((i & 0x80) == 0) | |
399 | break; | |
51224aa4 | 400 | schedule(); |
1da177e4 LT |
401 | } |
402 | ||
d71964fb | 403 | if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1)) |
1da177e4 | 404 | return -EIO; |
d56410e0 | 405 | |
d71964fb HV |
406 | lines = qcam->height; |
407 | pixelsperline = qcam->width; | |
1da177e4 LT |
408 | bitsperxfer = (is_bi_dir) ? 24 : 8; |
409 | ||
51224aa4 | 410 | if (is_bi_dir) { |
1da177e4 | 411 | /* Turn the port around */ |
d71964fb | 412 | parport_data_reverse(qcam->pport); |
1da177e4 | 413 | mdelay(3); |
d71964fb HV |
414 | qcam_set_ack(qcam, 0); |
415 | if (qcam_await_ready1(qcam, 1)) { | |
416 | qc_setup(qcam); | |
1da177e4 LT |
417 | return -EIO; |
418 | } | |
d71964fb HV |
419 | qcam_set_ack(qcam, 1); |
420 | if (qcam_await_ready1(qcam, 0)) { | |
421 | qc_setup(qcam); | |
1da177e4 LT |
422 | return -EIO; |
423 | } | |
424 | } | |
425 | ||
426 | wantlen = lines * pixelsperline * 24 / 8; | |
427 | ||
51224aa4 | 428 | while (wantlen) { |
1da177e4 | 429 | size_t t, s; |
51224aa4 HV |
430 | |
431 | s = (wantlen > BUFSZ) ? BUFSZ : wantlen; | |
d71964fb | 432 | t = qcam_read_bytes(qcam, tmpbuf, s); |
51224aa4 | 433 | if (outptr < len) { |
1da177e4 | 434 | size_t sz = len - outptr; |
51224aa4 HV |
435 | |
436 | if (sz > t) | |
437 | sz = t; | |
438 | if (__copy_to_user(buf + outptr, tmpbuf, sz)) | |
1da177e4 LT |
439 | break; |
440 | outptr += sz; | |
441 | } | |
442 | wantlen -= t; | |
443 | if (t < s) | |
444 | break; | |
445 | cond_resched(); | |
446 | } | |
447 | ||
448 | len = outptr; | |
449 | ||
51224aa4 | 450 | if (wantlen) { |
d71964fb | 451 | v4l2_err(v4l2_dev, "short read.\n"); |
1da177e4 | 452 | if (is_bi_dir) |
d71964fb HV |
453 | parport_data_forward(qcam->pport); |
454 | qc_setup(qcam); | |
1da177e4 LT |
455 | return len; |
456 | } | |
457 | ||
51224aa4 | 458 | if (is_bi_dir) { |
1da177e4 | 459 | int l; |
51224aa4 | 460 | |
1da177e4 | 461 | do { |
d71964fb | 462 | l = qcam_read_bytes(qcam, tmpbuf, 3); |
1da177e4 LT |
463 | cond_resched(); |
464 | } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); | |
465 | if (force_rgb) { | |
466 | if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) | |
d71964fb | 467 | v4l2_err(v4l2_dev, "bad EOF\n"); |
1da177e4 LT |
468 | } else { |
469 | if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) | |
d71964fb | 470 | v4l2_err(v4l2_dev, "bad EOF\n"); |
1da177e4 | 471 | } |
d71964fb HV |
472 | qcam_set_ack(qcam, 0); |
473 | if (qcam_await_ready1(qcam, 1)) { | |
474 | v4l2_err(v4l2_dev, "no ack after EOF\n"); | |
475 | parport_data_forward(qcam->pport); | |
476 | qc_setup(qcam); | |
1da177e4 LT |
477 | return len; |
478 | } | |
d71964fb | 479 | parport_data_forward(qcam->pport); |
1da177e4 | 480 | mdelay(3); |
d71964fb HV |
481 | qcam_set_ack(qcam, 1); |
482 | if (qcam_await_ready1(qcam, 0)) { | |
483 | v4l2_err(v4l2_dev, "no ack to port turnaround\n"); | |
484 | qc_setup(qcam); | |
1da177e4 LT |
485 | return len; |
486 | } | |
51224aa4 | 487 | } else { |
1da177e4 | 488 | int l; |
51224aa4 | 489 | |
1da177e4 | 490 | do { |
d71964fb | 491 | l = qcam_read_bytes(qcam, tmpbuf, 1); |
1da177e4 LT |
492 | cond_resched(); |
493 | } while (l && tmpbuf[0] == 0x7e); | |
d71964fb | 494 | l = qcam_read_bytes(qcam, tmpbuf + 1, 2); |
1da177e4 LT |
495 | if (force_rgb) { |
496 | if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) | |
d71964fb | 497 | v4l2_err(v4l2_dev, "bad EOF\n"); |
1da177e4 LT |
498 | } else { |
499 | if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) | |
d71964fb | 500 | v4l2_err(v4l2_dev, "bad EOF\n"); |
1da177e4 LT |
501 | } |
502 | } | |
503 | ||
d71964fb | 504 | qcam_write_data(qcam, 0); |
1da177e4 LT |
505 | return len; |
506 | } | |
507 | ||
508 | /* | |
509 | * Video4linux interfacing | |
510 | */ | |
511 | ||
d71964fb HV |
512 | static int qcam_querycap(struct file *file, void *priv, |
513 | struct v4l2_capability *vcap) | |
1da177e4 | 514 | { |
d71964fb | 515 | struct qcam *qcam = video_drvdata(file); |
d56410e0 | 516 | |
d71964fb HV |
517 | strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); |
518 | strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card)); | |
519 | strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); | |
520 | vcap->version = KERNEL_VERSION(0, 0, 3); | |
521 | vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; | |
522 | return 0; | |
523 | } | |
51224aa4 | 524 | |
d71964fb HV |
525 | static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) |
526 | { | |
527 | if (vin->index > 0) | |
528 | return -EINVAL; | |
529 | strlcpy(vin->name, "Camera", sizeof(vin->name)); | |
530 | vin->type = V4L2_INPUT_TYPE_CAMERA; | |
531 | vin->audioset = 0; | |
532 | vin->tuner = 0; | |
533 | vin->std = 0; | |
534 | vin->status = 0; | |
535 | return 0; | |
536 | } | |
51224aa4 | 537 | |
d71964fb HV |
538 | static int qcam_g_input(struct file *file, void *fh, unsigned int *inp) |
539 | { | |
540 | *inp = 0; | |
541 | return 0; | |
542 | } | |
543 | ||
544 | static int qcam_s_input(struct file *file, void *fh, unsigned int inp) | |
545 | { | |
546 | return (inp > 0) ? -EINVAL : 0; | |
547 | } | |
548 | ||
549 | static int qcam_queryctrl(struct file *file, void *priv, | |
550 | struct v4l2_queryctrl *qc) | |
551 | { | |
552 | switch (qc->id) { | |
553 | case V4L2_CID_BRIGHTNESS: | |
554 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 240); | |
555 | case V4L2_CID_CONTRAST: | |
556 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192); | |
557 | case V4L2_CID_GAMMA: | |
558 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); | |
51224aa4 | 559 | } |
d71964fb HV |
560 | return -EINVAL; |
561 | } | |
562 | ||
563 | static int qcam_g_ctrl(struct file *file, void *priv, | |
564 | struct v4l2_control *ctrl) | |
565 | { | |
566 | struct qcam *qcam = video_drvdata(file); | |
567 | int ret = 0; | |
568 | ||
569 | switch (ctrl->id) { | |
570 | case V4L2_CID_BRIGHTNESS: | |
571 | ctrl->value = qcam->brightness; | |
572 | break; | |
573 | case V4L2_CID_CONTRAST: | |
574 | ctrl->value = qcam->contrast; | |
575 | break; | |
576 | case V4L2_CID_GAMMA: | |
577 | ctrl->value = qcam->whitebal; | |
578 | break; | |
579 | default: | |
580 | ret = -EINVAL; | |
581 | break; | |
51224aa4 | 582 | } |
d71964fb HV |
583 | return ret; |
584 | } | |
585 | ||
586 | static int qcam_s_ctrl(struct file *file, void *priv, | |
587 | struct v4l2_control *ctrl) | |
588 | { | |
589 | struct qcam *qcam = video_drvdata(file); | |
590 | int ret = 0; | |
591 | ||
592 | mutex_lock(&qcam->lock); | |
593 | switch (ctrl->id) { | |
594 | case V4L2_CID_BRIGHTNESS: | |
595 | qcam->brightness = ctrl->value; | |
596 | break; | |
597 | case V4L2_CID_CONTRAST: | |
598 | qcam->contrast = ctrl->value; | |
599 | break; | |
600 | case V4L2_CID_GAMMA: | |
601 | qcam->whitebal = ctrl->value; | |
602 | break; | |
603 | default: | |
604 | ret = -EINVAL; | |
605 | break; | |
51224aa4 | 606 | } |
d71964fb | 607 | if (ret == 0) { |
51224aa4 HV |
608 | parport_claim_or_block(qcam->pdev); |
609 | qc_setup(qcam); | |
610 | parport_release(qcam->pdev); | |
51224aa4 | 611 | } |
d71964fb HV |
612 | mutex_unlock(&qcam->lock); |
613 | return ret; | |
614 | } | |
51224aa4 | 615 | |
d71964fb HV |
616 | static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) |
617 | { | |
618 | struct qcam *qcam = video_drvdata(file); | |
619 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | |
620 | ||
621 | pix->width = qcam->width; | |
622 | pix->height = qcam->height; | |
623 | pix->pixelformat = V4L2_PIX_FMT_RGB24; | |
624 | pix->field = V4L2_FIELD_NONE; | |
625 | pix->bytesperline = 3 * qcam->width; | |
626 | pix->sizeimage = 3 * qcam->width * qcam->height; | |
627 | /* Just a guess */ | |
628 | pix->colorspace = V4L2_COLORSPACE_SRGB; | |
629 | return 0; | |
630 | } | |
631 | ||
632 | static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) | |
633 | { | |
634 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | |
635 | ||
636 | if (pix->height < 60 || pix->width < 80) { | |
637 | pix->height = 60; | |
638 | pix->width = 80; | |
639 | } else if (pix->height < 120 || pix->width < 160) { | |
640 | pix->height = 120; | |
641 | pix->width = 160; | |
642 | } else { | |
643 | pix->height = 240; | |
644 | pix->width = 320; | |
51224aa4 | 645 | } |
d71964fb HV |
646 | pix->pixelformat = V4L2_PIX_FMT_RGB24; |
647 | pix->field = V4L2_FIELD_NONE; | |
648 | pix->bytesperline = 3 * pix->width; | |
649 | pix->sizeimage = 3 * pix->width * pix->height; | |
650 | /* Just a guess */ | |
651 | pix->colorspace = V4L2_COLORSPACE_SRGB; | |
652 | return 0; | |
653 | } | |
654 | ||
655 | static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) | |
656 | { | |
657 | struct qcam *qcam = video_drvdata(file); | |
658 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | |
659 | int ret = qcam_try_fmt_vid_cap(file, fh, fmt); | |
660 | ||
661 | if (ret) | |
662 | return ret; | |
663 | switch (pix->height) { | |
664 | case 60: | |
665 | qcam->mode = QC_DECIMATION_4; | |
666 | break; | |
667 | case 120: | |
668 | qcam->mode = QC_DECIMATION_2; | |
669 | break; | |
51224aa4 | 670 | default: |
d71964fb HV |
671 | qcam->mode = QC_DECIMATION_1; |
672 | break; | |
1da177e4 | 673 | } |
d71964fb HV |
674 | |
675 | mutex_lock(&qcam->lock); | |
676 | qcam->mode |= QC_MILLIONS; | |
677 | qcam->height = pix->height; | |
678 | qcam->width = pix->width; | |
679 | parport_claim_or_block(qcam->pdev); | |
680 | qc_setup(qcam); | |
681 | parport_release(qcam->pdev); | |
682 | mutex_unlock(&qcam->lock); | |
1da177e4 LT |
683 | return 0; |
684 | } | |
685 | ||
d71964fb | 686 | static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) |
1da177e4 | 687 | { |
d71964fb HV |
688 | static struct v4l2_fmtdesc formats[] = { |
689 | { 0, 0, 0, | |
690 | "RGB 8:8:8", V4L2_PIX_FMT_RGB24, | |
691 | { 0, 0, 0, 0 } | |
692 | }, | |
693 | }; | |
694 | enum v4l2_buf_type type = fmt->type; | |
695 | ||
696 | if (fmt->index > 0) | |
697 | return -EINVAL; | |
698 | ||
699 | *fmt = formats[fmt->index]; | |
700 | fmt->type = type; | |
701 | return 0; | |
1da177e4 LT |
702 | } |
703 | ||
704 | static ssize_t qcam_read(struct file *file, char __user *buf, | |
705 | size_t count, loff_t *ppos) | |
706 | { | |
d71964fb | 707 | struct qcam *qcam = video_drvdata(file); |
1da177e4 LT |
708 | int len; |
709 | ||
3593cab5 | 710 | mutex_lock(&qcam->lock); |
1da177e4 LT |
711 | parport_claim_or_block(qcam->pdev); |
712 | /* Probably should have a semaphore against multiple users */ | |
51224aa4 | 713 | len = qc_capture(qcam, buf, count); |
1da177e4 | 714 | parport_release(qcam->pdev); |
3593cab5 | 715 | mutex_unlock(&qcam->lock); |
1da177e4 LT |
716 | return len; |
717 | } | |
718 | ||
bec43661 | 719 | static const struct v4l2_file_operations qcam_fops = { |
1da177e4 | 720 | .owner = THIS_MODULE, |
61df3c9b | 721 | .unlocked_ioctl = video_ioctl2, |
1da177e4 | 722 | .read = qcam_read, |
1da177e4 LT |
723 | }; |
724 | ||
d71964fb HV |
725 | static const struct v4l2_ioctl_ops qcam_ioctl_ops = { |
726 | .vidioc_querycap = qcam_querycap, | |
727 | .vidioc_g_input = qcam_g_input, | |
728 | .vidioc_s_input = qcam_s_input, | |
729 | .vidioc_enum_input = qcam_enum_input, | |
730 | .vidioc_queryctrl = qcam_queryctrl, | |
731 | .vidioc_g_ctrl = qcam_g_ctrl, | |
732 | .vidioc_s_ctrl = qcam_s_ctrl, | |
733 | .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, | |
734 | .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, | |
735 | .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, | |
736 | .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, | |
1da177e4 LT |
737 | }; |
738 | ||
739 | /* Initialize the QuickCam driver control structure. */ | |
740 | ||
d71964fb | 741 | static struct qcam *qcam_init(struct parport *port) |
1da177e4 | 742 | { |
d71964fb HV |
743 | struct qcam *qcam; |
744 | struct v4l2_device *v4l2_dev; | |
d56410e0 | 745 | |
d71964fb HV |
746 | qcam = kzalloc(sizeof(*qcam), GFP_KERNEL); |
747 | if (qcam == NULL) | |
1da177e4 LT |
748 | return NULL; |
749 | ||
d71964fb HV |
750 | v4l2_dev = &qcam->v4l2_dev; |
751 | strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name)); | |
752 | ||
753 | if (v4l2_device_register(NULL, v4l2_dev) < 0) { | |
754 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | |
755 | return NULL; | |
756 | } | |
757 | ||
758 | qcam->pport = port; | |
759 | qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL, | |
1da177e4 LT |
760 | NULL, 0, NULL); |
761 | ||
d71964fb | 762 | qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; |
1da177e4 | 763 | |
d71964fb HV |
764 | if (qcam->pdev == NULL) { |
765 | v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); | |
766 | kfree(qcam); | |
1da177e4 LT |
767 | return NULL; |
768 | } | |
d56410e0 | 769 | |
d71964fb HV |
770 | strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name)); |
771 | qcam->vdev.v4l2_dev = v4l2_dev; | |
772 | qcam->vdev.fops = &qcam_fops; | |
773 | qcam->vdev.ioctl_ops = &qcam_ioctl_ops; | |
774 | qcam->vdev.release = video_device_release_empty; | |
775 | video_set_drvdata(&qcam->vdev, qcam); | |
776 | ||
777 | mutex_init(&qcam->lock); | |
778 | qcam->width = qcam->ccd_width = 320; | |
779 | qcam->height = qcam->ccd_height = 240; | |
780 | qcam->mode = QC_MILLIONS | QC_DECIMATION_1; | |
781 | qcam->contrast = 192; | |
782 | qcam->brightness = 240; | |
783 | qcam->whitebal = 128; | |
784 | qcam->top = 1; | |
785 | qcam->left = 14; | |
786 | return qcam; | |
1da177e4 LT |
787 | } |
788 | ||
1da177e4 LT |
789 | static int init_cqcam(struct parport *port) |
790 | { | |
d71964fb HV |
791 | struct qcam *qcam; |
792 | struct v4l2_device *v4l2_dev; | |
1da177e4 | 793 | |
51224aa4 | 794 | if (parport[0] != -1) { |
1da177e4 LT |
795 | /* The user gave specific instructions */ |
796 | int i, found = 0; | |
51224aa4 HV |
797 | |
798 | for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) { | |
1da177e4 LT |
799 | if (parport[0] == port->number) |
800 | found = 1; | |
801 | } | |
802 | if (!found) | |
803 | return -ENODEV; | |
804 | } | |
805 | ||
806 | if (num_cams == MAX_CAMS) | |
807 | return -ENOSPC; | |
808 | ||
809 | qcam = qcam_init(port); | |
51224aa4 | 810 | if (qcam == NULL) |
1da177e4 | 811 | return -ENODEV; |
d56410e0 | 812 | |
d71964fb HV |
813 | v4l2_dev = &qcam->v4l2_dev; |
814 | ||
1da177e4 LT |
815 | parport_claim_or_block(qcam->pdev); |
816 | ||
817 | qc_reset(qcam); | |
d56410e0 | 818 | |
51224aa4 | 819 | if (probe && qc_detect(qcam) == 0) { |
1da177e4 LT |
820 | parport_release(qcam->pdev); |
821 | parport_unregister_device(qcam->pdev); | |
822 | kfree(qcam); | |
823 | return -ENODEV; | |
824 | } | |
825 | ||
826 | qc_setup(qcam); | |
827 | ||
828 | parport_release(qcam->pdev); | |
d56410e0 | 829 | |
dc60de33 | 830 | if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { |
d71964fb | 831 | v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n", |
1da177e4 LT |
832 | qcam->pport->name); |
833 | parport_unregister_device(qcam->pdev); | |
834 | kfree(qcam); | |
835 | return -ENODEV; | |
836 | } | |
837 | ||
d71964fb | 838 | v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n", |
38c7c036 | 839 | video_device_node_name(&qcam->vdev), qcam->pport->name); |
d56410e0 | 840 | |
1da177e4 LT |
841 | qcams[num_cams++] = qcam; |
842 | ||
843 | return 0; | |
844 | } | |
845 | ||
d71964fb | 846 | static void close_cqcam(struct qcam *qcam) |
1da177e4 LT |
847 | { |
848 | video_unregister_device(&qcam->vdev); | |
849 | parport_unregister_device(qcam->pdev); | |
850 | kfree(qcam); | |
851 | } | |
852 | ||
853 | static void cq_attach(struct parport *port) | |
854 | { | |
855 | init_cqcam(port); | |
856 | } | |
857 | ||
858 | static void cq_detach(struct parport *port) | |
859 | { | |
860 | /* Write this some day. */ | |
861 | } | |
862 | ||
863 | static struct parport_driver cqcam_driver = { | |
864 | .name = "cqcam", | |
865 | .attach = cq_attach, | |
866 | .detach = cq_detach, | |
867 | }; | |
868 | ||
51224aa4 | 869 | static int __init cqcam_init(void) |
1da177e4 | 870 | { |
d71964fb | 871 | printk(KERN_INFO BANNER "\n"); |
1da177e4 LT |
872 | |
873 | return parport_register_driver(&cqcam_driver); | |
874 | } | |
875 | ||
51224aa4 | 876 | static void __exit cqcam_cleanup(void) |
1da177e4 LT |
877 | { |
878 | unsigned int i; | |
879 | ||
880 | for (i = 0; i < num_cams; i++) | |
881 | close_cqcam(qcams[i]); | |
882 | ||
883 | parport_unregister_driver(&cqcam_driver); | |
884 | } | |
885 | ||
886 | MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); | |
887 | MODULE_DESCRIPTION(BANNER); | |
888 | MODULE_LICENSE("GPL"); | |
889 | ||
1da177e4 LT |
890 | module_init(cqcam_init); |
891 | module_exit(cqcam_cleanup); |