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