blob: a3989bd2f81bb99b94c49127d36d7f277ba53689 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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.
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030019 * -- March 14, 1999 Billy Donahue <billy@escape.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 *
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>
36#include <linux/videodev.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030037#include <media/v4l2-common.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020038#include <linux/mutex.h>
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <asm/uaccess.h>
41
42struct qcam_device {
43 struct video_device vdev;
44 struct pardevice *pdev;
45 struct parport *pport;
46 int width, height;
47 int ccd_width, ccd_height;
48 int mode;
49 int contrast, brightness, whitebal;
50 int top, left;
51 unsigned int bidirectional;
Ingo Molnar3593cab2006-02-07 06:49:14 -020052 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053};
54
55/* cameras maximum */
56#define MAX_CAMS 4
57
58/* The three possible QuickCam modes */
59#define QC_MILLIONS 0x18
60#define QC_BILLIONS 0x10
61#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
62
63/* The three possible decimations */
64#define QC_DECIMATION_1 0
65#define QC_DECIMATION_2 2
66#define QC_DECIMATION_4 4
67
68#define BANNER "Colour QuickCam for Video4Linux v0.05"
69
70static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
71static int probe = 2;
72static int force_rgb = 0;
73static int video_nr = -1;
74
75static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i)
76{
77 /* note: the QC specs refer to the PCAck pin by voltage, not
78 software level. PC ports have builtin inverters. */
79 parport_frob_control(qcam->pport, 8, i?8:0);
80}
81
82static inline unsigned int qcam_ready1(struct qcam_device *qcam)
83{
84 return (parport_read_status(qcam->pport) & 0x8)?1:0;
85}
86
87static inline unsigned int qcam_ready2(struct qcam_device *qcam)
88{
89 return (parport_read_data(qcam->pport) & 0x1)?1:0;
90}
91
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030092static unsigned int qcam_await_ready1(struct qcam_device *qcam,
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 int value)
94{
95 unsigned long oldjiffies = jiffies;
96 unsigned int i;
97
98 for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
99 if (qcam_ready1(qcam) == value)
100 return 0;
101
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300102 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 for a while. */
104 for (i = 0; i < 50; i++)
105 {
106 if (qcam_ready1(qcam) == value)
107 return 0;
108 msleep_interruptible(100);
109 }
110
111 /* Probably somebody pulled the plug out. Not much we can do. */
112 printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value,
113 parport_read_status(qcam->pport),
114 parport_read_control(qcam->pport));
115 return 1;
116}
117
118static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value)
119{
120 unsigned long oldjiffies = jiffies;
121 unsigned int i;
122
123 for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
124 if (qcam_ready2(qcam) == value)
125 return 0;
126
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300127 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 for a while. */
129 for (i = 0; i < 50; i++)
130 {
131 if (qcam_ready2(qcam) == value)
132 return 0;
133 msleep_interruptible(100);
134 }
135
136 /* Probably somebody pulled the plug out. Not much we can do. */
137 printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value,
138 parport_read_status(qcam->pport),
139 parport_read_control(qcam->pport),
140 parport_read_data(qcam->pport));
141 return 1;
142}
143
144static int qcam_read_data(struct qcam_device *qcam)
145{
146 unsigned int idata;
147 qcam_set_ack(qcam, 0);
148 if (qcam_await_ready1(qcam, 1)) return -1;
149 idata = parport_read_status(qcam->pport) & 0xf0;
150 qcam_set_ack(qcam, 1);
151 if (qcam_await_ready1(qcam, 0)) return -1;
152 idata |= (parport_read_status(qcam->pport) >> 4);
153 return idata;
154}
155
156static int qcam_write_data(struct qcam_device *qcam, unsigned int data)
157{
158 unsigned int idata;
159 parport_write_data(qcam->pport, data);
160 idata = qcam_read_data(qcam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300161 if (data != idata)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300163 printk(KERN_WARNING "cqcam: sent %x but received %x\n", data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 idata);
165 return 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300166 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return 0;
168}
169
170static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data)
171{
172 if (qcam_write_data(qcam, cmd))
173 return -1;
174 if (qcam_write_data(qcam, data))
175 return -1;
176 return 0;
177}
178
179static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd)
180{
181 if (qcam_write_data(qcam, cmd))
182 return -1;
183 return qcam_read_data(qcam);
184}
185
186static int qc_detect(struct qcam_device *qcam)
187{
188 unsigned int stat, ostat, i, count = 0;
189
190 /* The probe routine below is not very reliable. The IEEE-1284
191 probe takes precedence. */
192 /* XXX Currently parport provides no way to distinguish between
193 "the IEEE probe was not done" and "the probe was done, but
194 no device was found". Fix this one day. */
195 if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
196 && qcam->pport->probe_info[0].model
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300197 && !strcmp(qcam->pdev->port->probe_info[0].model,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 "Color QuickCam 2.0")) {
199 printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
200 return 1;
201 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 if (probe < 2)
204 return 0;
205
206 parport_write_control(qcam->pport, 0xc);
207
208 /* look for a heartbeat */
209 ostat = stat = parport_read_status(qcam->pport);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300210 for (i=0; i<250; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 {
212 mdelay(1);
213 stat = parport_read_status(qcam->pport);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300214 if (ostat != stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 {
216 if (++count >= 3) return 1;
217 ostat = stat;
218 }
219 }
220
221 /* Reset the camera and try again */
222 parport_write_control(qcam->pport, 0xc);
223 parport_write_control(qcam->pport, 0x8);
224 mdelay(1);
225 parport_write_control(qcam->pport, 0xc);
226 mdelay(1);
227 count = 0;
228
229 ostat = stat = parport_read_status(qcam->pport);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300230 for (i=0; i<250; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 {
232 mdelay(1);
233 stat = parport_read_status(qcam->pport);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300234 if (ostat != stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 {
236 if (++count >= 3) return 1;
237 ostat = stat;
238 }
239 }
240
241 /* no (or flatline) camera, give up */
242 return 0;
243}
244
245static void qc_reset(struct qcam_device *qcam)
246{
247 parport_write_control(qcam->pport, 0xc);
248 parport_write_control(qcam->pport, 0x8);
249 mdelay(1);
250 parport_write_control(qcam->pport, 0xc);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300251 mdelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252}
253
254/* Reset the QuickCam and program for brightness, contrast,
255 * white-balance, and resolution. */
256
257static void qc_setup(struct qcam_device *q)
258{
259 qc_reset(q);
260
261 /* Set the brightness. */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300262 qcam_set(q, 11, q->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 /* Set the height and width. These refer to the actual
265 CCD area *before* applying the selected decimation. */
266 qcam_set(q, 17, q->ccd_height);
267 qcam_set(q, 19, q->ccd_width / 2);
268
269 /* Set top and left. */
270 qcam_set(q, 0xd, q->top);
271 qcam_set(q, 0xf, q->left);
272
273 /* Set contrast and white balance. */
274 qcam_set(q, 0x19, q->contrast);
275 qcam_set(q, 0x1f, q->whitebal);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 /* Set the speed. */
278 qcam_set(q, 45, 2);
279}
280
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300281/* Read some bytes from the camera and put them in the buffer.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 nbytes should be a multiple of 3, because bidirectional mode gives
283 us three bytes at a time. */
284
285static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes)
286{
287 unsigned int bytes = 0;
288
289 qcam_set_ack(q, 0);
290 if (q->bidirectional)
291 {
292 /* It's a bidirectional port */
293 while (bytes < nbytes)
294 {
295 unsigned int lo1, hi1, lo2, hi2;
296 unsigned char r, g, b;
297
298 if (qcam_await_ready2(q, 1)) return bytes;
299 lo1 = parport_read_data(q->pport) >> 1;
300 hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
301 qcam_set_ack(q, 1);
302 if (qcam_await_ready2(q, 0)) return bytes;
303 lo2 = parport_read_data(q->pport) >> 1;
304 hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
305 qcam_set_ack(q, 0);
306 r = (lo1 | ((hi1 & 1)<<7));
307 g = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1);
308 b = (lo2 | ((hi2 & 1)<<7));
309 if (force_rgb) {
310 buf[bytes++] = r;
311 buf[bytes++] = g;
312 buf[bytes++] = b;
313 } else {
314 buf[bytes++] = b;
315 buf[bytes++] = g;
316 buf[bytes++] = r;
317 }
318 }
319 }
320 else
321 {
322 /* It's a unidirectional port */
323 int i = 0, n = bytes;
324 unsigned char rgb[3];
325
326 while (bytes < nbytes)
327 {
328 unsigned int hi, lo;
329
330 if (qcam_await_ready1(q, 1)) return bytes;
331 hi = (parport_read_status(q->pport) & 0xf0);
332 qcam_set_ack(q, 1);
333 if (qcam_await_ready1(q, 0)) return bytes;
334 lo = (parport_read_status(q->pport) & 0xf0);
335 qcam_set_ack(q, 0);
336 /* flip some bits */
337 rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
338 if (i >= 2) {
339get_fragment:
340 if (force_rgb) {
341 buf[n++] = rgb[0];
342 buf[n++] = rgb[1];
343 buf[n++] = rgb[2];
344 } else {
345 buf[n++] = rgb[2];
346 buf[n++] = rgb[1];
347 buf[n++] = rgb[0];
348 }
349 }
350 }
351 if (i) {
352 i = 0;
353 goto get_fragment;
354 }
355 }
356 return bytes;
357}
358
359#define BUFSZ 150
360
361static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len)
362{
363 unsigned lines, pixelsperline, bitsperxfer;
364 unsigned int is_bi_dir = q->bidirectional;
365 size_t wantlen, outptr = 0;
366 char tmpbuf[BUFSZ];
367
368 if (!access_ok(VERIFY_WRITE, buf, len))
369 return -EFAULT;
370
371 /* Wait for camera to become ready */
372 for (;;)
373 {
374 int i = qcam_get(q, 41);
375 if (i == -1) {
376 qc_setup(q);
377 return -EIO;
378 }
379 if ((i & 0x80) == 0)
380 break;
381 else
382 schedule();
383 }
384
385 if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1))
386 return -EIO;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300387
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 lines = q->height;
389 pixelsperline = q->width;
390 bitsperxfer = (is_bi_dir) ? 24 : 8;
391
392 if (is_bi_dir)
393 {
394 /* Turn the port around */
395 parport_data_reverse(q->pport);
396 mdelay(3);
397 qcam_set_ack(q, 0);
398 if (qcam_await_ready1(q, 1)) {
399 qc_setup(q);
400 return -EIO;
401 }
402 qcam_set_ack(q, 1);
403 if (qcam_await_ready1(q, 0)) {
404 qc_setup(q);
405 return -EIO;
406 }
407 }
408
409 wantlen = lines * pixelsperline * 24 / 8;
410
411 while (wantlen)
412 {
413 size_t t, s;
414 s = (wantlen > BUFSZ)?BUFSZ:wantlen;
415 t = qcam_read_bytes(q, tmpbuf, s);
416 if (outptr < len)
417 {
418 size_t sz = len - outptr;
419 if (sz > t) sz = t;
420 if (__copy_to_user(buf+outptr, tmpbuf, sz))
421 break;
422 outptr += sz;
423 }
424 wantlen -= t;
425 if (t < s)
426 break;
427 cond_resched();
428 }
429
430 len = outptr;
431
432 if (wantlen)
433 {
434 printk("qcam: short read.\n");
435 if (is_bi_dir)
436 parport_data_forward(q->pport);
437 qc_setup(q);
438 return len;
439 }
440
441 if (is_bi_dir)
442 {
443 int l;
444 do {
445 l = qcam_read_bytes(q, tmpbuf, 3);
446 cond_resched();
447 } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
448 if (force_rgb) {
449 if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
450 printk("qcam: bad EOF\n");
451 } else {
452 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
453 printk("qcam: bad EOF\n");
454 }
455 qcam_set_ack(q, 0);
456 if (qcam_await_ready1(q, 1))
457 {
458 printk("qcam: no ack after EOF\n");
459 parport_data_forward(q->pport);
460 qc_setup(q);
461 return len;
462 }
463 parport_data_forward(q->pport);
464 mdelay(3);
465 qcam_set_ack(q, 1);
466 if (qcam_await_ready1(q, 0))
467 {
468 printk("qcam: no ack to port turnaround\n");
469 qc_setup(q);
470 return len;
471 }
472 }
473 else
474 {
475 int l;
476 do {
477 l = qcam_read_bytes(q, tmpbuf, 1);
478 cond_resched();
479 } while (l && tmpbuf[0] == 0x7e);
480 l = qcam_read_bytes(q, tmpbuf+1, 2);
481 if (force_rgb) {
482 if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
483 printk("qcam: bad EOF\n");
484 } else {
485 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
486 printk("qcam: bad EOF\n");
487 }
488 }
489
490 qcam_write_data(q, 0);
491 return len;
492}
493
494/*
495 * Video4linux interfacing
496 */
497
498static int qcam_do_ioctl(struct inode *inode, struct file *file,
499 unsigned int cmd, void *arg)
500{
501 struct video_device *dev = video_devdata(file);
502 struct qcam_device *qcam=(struct qcam_device *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 switch(cmd)
505 {
506 case VIDIOCGCAP:
507 {
508 struct video_capability *b = arg;
509 strcpy(b->name, "Quickcam");
510 b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
511 b->channels = 1;
512 b->audios = 0;
513 b->maxwidth = 320;
514 b->maxheight = 240;
515 b->minwidth = 80;
516 b->minheight = 60;
517 return 0;
518 }
519 case VIDIOCGCHAN:
520 {
521 struct video_channel *v = arg;
522 if(v->channel!=0)
523 return -EINVAL;
524 v->flags=0;
525 v->tuners=0;
526 /* Good question.. its composite or SVHS so.. */
527 v->type = VIDEO_TYPE_CAMERA;
528 strcpy(v->name, "Camera");
529 return 0;
530 }
531 case VIDIOCSCHAN:
532 {
533 struct video_channel *v = arg;
534 if(v->channel!=0)
535 return -EINVAL;
536 return 0;
537 }
538 case VIDIOCGTUNER:
539 {
540 struct video_tuner *v = arg;
541 if(v->tuner)
542 return -EINVAL;
543 memset(v,0,sizeof(*v));
544 strcpy(v->name, "Format");
545 v->mode = VIDEO_MODE_AUTO;
546 return 0;
547 }
548 case VIDIOCSTUNER:
549 {
550 struct video_tuner *v = arg;
551 if(v->tuner)
552 return -EINVAL;
553 if(v->mode!=VIDEO_MODE_AUTO)
554 return -EINVAL;
555 return 0;
556 }
557 case VIDIOCGPICT:
558 {
559 struct video_picture *p = arg;
560 p->colour=0x8000;
561 p->hue=0x8000;
562 p->brightness=qcam->brightness<<8;
563 p->contrast=qcam->contrast<<8;
564 p->whiteness=qcam->whitebal<<8;
565 p->depth=24;
566 p->palette=VIDEO_PALETTE_RGB24;
567 return 0;
568 }
569 case VIDIOCSPICT:
570 {
571 struct video_picture *p = arg;
572
573 /*
574 * Sanity check args
575 */
576 if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24)
577 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /*
580 * Now load the camera.
581 */
582 qcam->brightness = p->brightness>>8;
583 qcam->contrast = p->contrast>>8;
584 qcam->whitebal = p->whiteness>>8;
585
Ingo Molnar3593cab2006-02-07 06:49:14 -0200586 mutex_lock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 parport_claim_or_block(qcam->pdev);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300588 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 parport_release(qcam->pdev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200590 mutex_unlock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 return 0;
592 }
593 case VIDIOCSWIN:
594 {
595 struct video_window *vw = arg;
596
597 if(vw->flags)
598 return -EINVAL;
599 if(vw->clipcount)
600 return -EINVAL;
601 if(vw->height<60||vw->height>240)
602 return -EINVAL;
603 if(vw->width<80||vw->width>320)
604 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 qcam->width = 80;
607 qcam->height = 60;
608 qcam->mode = QC_DECIMATION_4;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 if(vw->width>=160 && vw->height>=120)
611 {
612 qcam->width = 160;
613 qcam->height = 120;
614 qcam->mode = QC_DECIMATION_2;
615 }
616 if(vw->width>=320 && vw->height>=240)
617 {
618 qcam->width = 320;
619 qcam->height = 240;
620 qcam->mode = QC_DECIMATION_1;
621 }
622 qcam->mode |= QC_MILLIONS;
623#if 0
624 if(vw->width>=640 && vw->height>=480)
625 {
626 qcam->width = 640;
627 qcam->height = 480;
628 qcam->mode = QC_BILLIONS | QC_DECIMATION_1;
629 }
630#endif
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300631 /* Ok we figured out what to use from our
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 wide choice */
Ingo Molnar3593cab2006-02-07 06:49:14 -0200633 mutex_lock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 parport_claim_or_block(qcam->pdev);
635 qc_setup(qcam);
636 parport_release(qcam->pdev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200637 mutex_unlock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 return 0;
639 }
640 case VIDIOCGWIN:
641 {
642 struct video_window *vw = arg;
643 memset(vw, 0, sizeof(*vw));
644 vw->width=qcam->width;
645 vw->height=qcam->height;
646 return 0;
647 }
648 case VIDIOCKEY:
649 return 0;
650 case VIDIOCCAPTURE:
651 case VIDIOCGFBUF:
652 case VIDIOCSFBUF:
653 case VIDIOCGFREQ:
654 case VIDIOCSFREQ:
655 case VIDIOCGAUDIO:
656 case VIDIOCSAUDIO:
657 return -EINVAL;
658 default:
659 return -ENOIOCTLCMD;
660 }
661 return 0;
662}
663
664static int qcam_ioctl(struct inode *inode, struct file *file,
665 unsigned int cmd, unsigned long arg)
666{
667 return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
668}
669
670static ssize_t qcam_read(struct file *file, char __user *buf,
671 size_t count, loff_t *ppos)
672{
673 struct video_device *v = video_devdata(file);
674 struct qcam_device *qcam=(struct qcam_device *)v;
675 int len;
676
Ingo Molnar3593cab2006-02-07 06:49:14 -0200677 mutex_lock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 parport_claim_or_block(qcam->pdev);
679 /* Probably should have a semaphore against multiple users */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300680 len = qc_capture(qcam, buf,count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 parport_release(qcam->pdev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200682 mutex_unlock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return len;
684}
685
686/* video device template */
687static struct file_operations qcam_fops = {
688 .owner = THIS_MODULE,
689 .open = video_exclusive_open,
690 .release = video_exclusive_release,
691 .ioctl = qcam_ioctl,
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -0200692 .compat_ioctl = v4l_compat_ioctl32,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 .read = qcam_read,
694 .llseek = no_llseek,
695};
696
697static struct video_device qcam_template=
698{
699 .owner = THIS_MODULE,
700 .name = "Colour QuickCam",
701 .type = VID_TYPE_CAPTURE,
702 .hardware = VID_HARDWARE_QCAM_C,
703 .fops = &qcam_fops,
704};
705
706/* Initialize the QuickCam driver control structure. */
707
708static struct qcam_device *qcam_init(struct parport *port)
709{
710 struct qcam_device *q;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
713 if(q==NULL)
714 return NULL;
715
716 q->pport = port;
717 q->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
718 NULL, 0, NULL);
719
720 q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0;
721
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300722 if (q->pdev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 {
724 printk(KERN_ERR "c-qcam: couldn't register for %s.\n",
725 port->name);
726 kfree(q);
727 return NULL;
728 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
731
Ingo Molnar3593cab2006-02-07 06:49:14 -0200732 mutex_init(&q->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 q->width = q->ccd_width = 320;
734 q->height = q->ccd_height = 240;
735 q->mode = QC_MILLIONS | QC_DECIMATION_1;
736 q->contrast = 192;
737 q->brightness = 240;
738 q->whitebal = 128;
739 q->top = 1;
740 q->left = 14;
741 return q;
742}
743
744static struct qcam_device *qcams[MAX_CAMS];
745static unsigned int num_cams = 0;
746
747static int init_cqcam(struct parport *port)
748{
749 struct qcam_device *qcam;
750
751 if (parport[0] != -1)
752 {
753 /* The user gave specific instructions */
754 int i, found = 0;
755 for (i = 0; i < MAX_CAMS && parport[i] != -1; i++)
756 {
757 if (parport[0] == port->number)
758 found = 1;
759 }
760 if (!found)
761 return -ENODEV;
762 }
763
764 if (num_cams == MAX_CAMS)
765 return -ENOSPC;
766
767 qcam = qcam_init(port);
768 if (qcam==NULL)
769 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 parport_claim_or_block(qcam->pdev);
772
773 qc_reset(qcam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if (probe && qc_detect(qcam)==0)
776 {
777 parport_release(qcam->pdev);
778 parport_unregister_device(qcam->pdev);
779 kfree(qcam);
780 return -ENODEV;
781 }
782
783 qc_setup(qcam);
784
785 parport_release(qcam->pdev);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300786
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1)
788 {
789 printk(KERN_ERR "Unable to register Colour QuickCam on %s\n",
790 qcam->pport->name);
791 parport_unregister_device(qcam->pdev);
792 kfree(qcam);
793 return -ENODEV;
794 }
795
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300796 printk(KERN_INFO "video%d: Colour QuickCam found on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 qcam->vdev.minor, qcam->pport->name);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 qcams[num_cams++] = qcam;
800
801 return 0;
802}
803
804static void close_cqcam(struct qcam_device *qcam)
805{
806 video_unregister_device(&qcam->vdev);
807 parport_unregister_device(qcam->pdev);
808 kfree(qcam);
809}
810
811static void cq_attach(struct parport *port)
812{
813 init_cqcam(port);
814}
815
816static void cq_detach(struct parport *port)
817{
818 /* Write this some day. */
819}
820
821static struct parport_driver cqcam_driver = {
822 .name = "cqcam",
823 .attach = cq_attach,
824 .detach = cq_detach,
825};
826
827static int __init cqcam_init (void)
828{
829 printk(BANNER "\n");
830
831 return parport_register_driver(&cqcam_driver);
832}
833
834static void __exit cqcam_cleanup (void)
835{
836 unsigned int i;
837
838 for (i = 0; i < num_cams; i++)
839 close_cqcam(qcams[i]);
840
841 parport_unregister_driver(&cqcam_driver);
842}
843
844MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
845MODULE_DESCRIPTION(BANNER);
846MODULE_LICENSE("GPL");
847
848/* FIXME: parport=auto would never have worked, surely? --RR */
849MODULE_PARM_DESC(parport ,"parport=<auto|n[,n]...> for port detection method\n\
850probe=<0|1|2> for camera detection method\n\
851force_rgb=<0|1> for RGB data format (default BGR)");
852module_param_array(parport, int, NULL, 0);
853module_param(probe, int, 0);
854module_param(force_rgb, bool, 0);
855module_param(video_nr, int, 0);
856
857module_init(cqcam_init);
858module_exit(cqcam_cleanup);