blob: b9010bd3ed3e842de488d05a64cbb71e37fcbe2a [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>
Ingo Molnar3593cab2006-02-07 06:49:14 -020036#include <linux/mutex.h>
Julia Lawall168c6262008-04-16 16:13:15 -030037#include <linux/jiffies.h>
Hans Verkuild71964f2010-05-10 03:55:25 -030038#include <linux/videodev2.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/uaccess.h>
Hans Verkuild71964f2010-05-10 03:55:25 -030040#include <media/v4l2-device.h>
41#include <media/v4l2-common.h>
42#include <media/v4l2-ioctl.h>
Hans Verkuil34caed82012-05-08 15:20:21 -030043#include <media/v4l2-fh.h>
44#include <media/v4l2-ctrls.h>
45#include <media/v4l2-event.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Hans Verkuild71964f2010-05-10 03:55:25 -030047struct qcam {
48 struct v4l2_device v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 struct video_device vdev;
Hans Verkuil34caed82012-05-08 15:20:21 -030050 struct v4l2_ctrl_handler hdl;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 struct pardevice *pdev;
52 struct parport *pport;
53 int width, height;
54 int ccd_width, ccd_height;
55 int mode;
56 int contrast, brightness, whitebal;
57 int top, left;
58 unsigned int bidirectional;
Ingo Molnar3593cab2006-02-07 06:49:14 -020059 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060};
61
62/* cameras maximum */
63#define MAX_CAMS 4
64
65/* The three possible QuickCam modes */
66#define QC_MILLIONS 0x18
67#define QC_BILLIONS 0x10
68#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
69
70/* The three possible decimations */
71#define QC_DECIMATION_1 0
72#define QC_DECIMATION_2 2
73#define QC_DECIMATION_4 4
74
Hans Verkuild71964f2010-05-10 03:55:25 -030075#define BANNER "Colour QuickCam for Video4Linux v0.06"
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
78static int probe = 2;
Rusty Russell90ab5ee2012-01-13 09:32:20 +103079static bool force_rgb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080static int video_nr = -1;
81
Hans Verkuild71964f2010-05-10 03:55:25 -030082/* FIXME: parport=auto would never have worked, surely? --RR */
83MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
84 "probe=<0|1|2> for camera detection method\n"
85 "force_rgb=<0|1> for RGB data format (default BGR)");
86module_param_array(parport, int, NULL, 0);
87module_param(probe, int, 0);
88module_param(force_rgb, bool, 0);
89module_param(video_nr, int, 0);
90
91static struct qcam *qcams[MAX_CAMS];
92static unsigned int num_cams;
93
94static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
96 /* note: the QC specs refer to the PCAck pin by voltage, not
97 software level. PC ports have builtin inverters. */
Hans Verkuil51224aa2010-03-22 04:33:56 -030098 parport_frob_control(qcam->pport, 8, i ? 8 : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099}
100
Hans Verkuild71964f2010-05-10 03:55:25 -0300101static inline unsigned int qcam_ready1(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102{
Hans Verkuil51224aa2010-03-22 04:33:56 -0300103 return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105
Hans Verkuild71964f2010-05-10 03:55:25 -0300106static inline unsigned int qcam_ready2(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
Hans Verkuil51224aa2010-03-22 04:33:56 -0300108 return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109}
110
Hans Verkuild71964f2010-05-10 03:55:25 -0300111static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
Hans Verkuild71964f2010-05-10 03:55:25 -0300113 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 unsigned long oldjiffies = jiffies;
115 unsigned int i;
116
Julia Lawall168c6262008-04-16 16:13:15 -0300117 for (oldjiffies = jiffies;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300118 time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 if (qcam_ready1(qcam) == value)
120 return 0;
121
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300122 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 for a while. */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300124 for (i = 0; i < 50; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 if (qcam_ready1(qcam) == value)
126 return 0;
127 msleep_interruptible(100);
128 }
129
130 /* Probably somebody pulled the plug out. Not much we can do. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300131 v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 parport_read_status(qcam->pport),
133 parport_read_control(qcam->pport));
134 return 1;
135}
136
Hans Verkuild71964f2010-05-10 03:55:25 -0300137static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Hans Verkuild71964f2010-05-10 03:55:25 -0300139 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 unsigned long oldjiffies = jiffies;
141 unsigned int i;
142
Julia Lawall168c6262008-04-16 16:13:15 -0300143 for (oldjiffies = jiffies;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300144 time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 if (qcam_ready2(qcam) == value)
146 return 0;
147
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300148 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 for a while. */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300150 for (i = 0; i < 50; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 if (qcam_ready2(qcam) == value)
152 return 0;
153 msleep_interruptible(100);
154 }
155
156 /* Probably somebody pulled the plug out. Not much we can do. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300157 v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 parport_read_status(qcam->pport),
159 parport_read_control(qcam->pport),
160 parport_read_data(qcam->pport));
161 return 1;
162}
163
Hans Verkuild71964f2010-05-10 03:55:25 -0300164static int qcam_read_data(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
166 unsigned int idata;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 qcam_set_ack(qcam, 0);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300169 if (qcam_await_ready1(qcam, 1))
170 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 idata = parport_read_status(qcam->pport) & 0xf0;
172 qcam_set_ack(qcam, 1);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300173 if (qcam_await_ready1(qcam, 0))
174 return -1;
175 idata |= parport_read_status(qcam->pport) >> 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 return idata;
177}
178
Hans Verkuild71964f2010-05-10 03:55:25 -0300179static int qcam_write_data(struct qcam *qcam, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180{
Hans Verkuild71964f2010-05-10 03:55:25 -0300181 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 unsigned int idata;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 parport_write_data(qcam->pport, data);
185 idata = qcam_read_data(qcam);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300186 if (data != idata) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300187 v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 idata);
189 return 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300190 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 return 0;
192}
193
Hans Verkuild71964f2010-05-10 03:55:25 -0300194static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195{
196 if (qcam_write_data(qcam, cmd))
197 return -1;
198 if (qcam_write_data(qcam, data))
199 return -1;
200 return 0;
201}
202
Hans Verkuild71964f2010-05-10 03:55:25 -0300203static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
205 if (qcam_write_data(qcam, cmd))
206 return -1;
207 return qcam_read_data(qcam);
208}
209
Hans Verkuild71964f2010-05-10 03:55:25 -0300210static int qc_detect(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 unsigned int stat, ostat, i, count = 0;
213
214 /* The probe routine below is not very reliable. The IEEE-1284
215 probe takes precedence. */
216 /* XXX Currently parport provides no way to distinguish between
217 "the IEEE probe was not done" and "the probe was done, but
218 no device was found". Fix this one day. */
219 if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
220 && qcam->pport->probe_info[0].model
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300221 && !strcmp(qcam->pdev->port->probe_info[0].model,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 "Color QuickCam 2.0")) {
223 printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
224 return 1;
225 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 if (probe < 2)
228 return 0;
229
230 parport_write_control(qcam->pport, 0xc);
231
232 /* look for a heartbeat */
233 ostat = stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300234 for (i = 0; i < 250; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 mdelay(1);
236 stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300237 if (ostat != stat) {
238 if (++count >= 3)
239 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 ostat = stat;
241 }
242 }
243
244 /* Reset the camera and try again */
245 parport_write_control(qcam->pport, 0xc);
246 parport_write_control(qcam->pport, 0x8);
247 mdelay(1);
248 parport_write_control(qcam->pport, 0xc);
249 mdelay(1);
250 count = 0;
251
252 ostat = stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300253 for (i = 0; i < 250; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 mdelay(1);
255 stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300256 if (ostat != stat) {
257 if (++count >= 3)
258 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 ostat = stat;
260 }
261 }
262
263 /* no (or flatline) camera, give up */
264 return 0;
265}
266
Hans Verkuild71964f2010-05-10 03:55:25 -0300267static void qc_reset(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 parport_write_control(qcam->pport, 0xc);
270 parport_write_control(qcam->pport, 0x8);
271 mdelay(1);
272 parport_write_control(qcam->pport, 0xc);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300273 mdelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
276/* Reset the QuickCam and program for brightness, contrast,
277 * white-balance, and resolution. */
278
Hans Verkuild71964f2010-05-10 03:55:25 -0300279static void qc_setup(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
Hans Verkuild71964f2010-05-10 03:55:25 -0300281 qc_reset(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Hans Verkuil51224aa2010-03-22 04:33:56 -0300283 /* Set the brightness. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300284 qcam_set(qcam, 11, qcam->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286 /* Set the height and width. These refer to the actual
287 CCD area *before* applying the selected decimation. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300288 qcam_set(qcam, 17, qcam->ccd_height);
289 qcam_set(qcam, 19, qcam->ccd_width / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 /* Set top and left. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300292 qcam_set(qcam, 0xd, qcam->top);
293 qcam_set(qcam, 0xf, qcam->left);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295 /* Set contrast and white balance. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300296 qcam_set(qcam, 0x19, qcam->contrast);
297 qcam_set(qcam, 0x1f, qcam->whitebal);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 /* Set the speed. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300300 qcam_set(qcam, 45, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}
302
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300303/* Read some bytes from the camera and put them in the buffer.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 nbytes should be a multiple of 3, because bidirectional mode gives
305 us three bytes at a time. */
306
Hans Verkuild71964f2010-05-10 03:55:25 -0300307static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308{
309 unsigned int bytes = 0;
310
Hans Verkuild71964f2010-05-10 03:55:25 -0300311 qcam_set_ack(qcam, 0);
312 if (qcam->bidirectional) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 /* It's a bidirectional port */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300314 while (bytes < nbytes) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 unsigned int lo1, hi1, lo2, hi2;
316 unsigned char r, g, b;
317
Hans Verkuild71964f2010-05-10 03:55:25 -0300318 if (qcam_await_ready2(qcam, 1))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300319 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300320 lo1 = parport_read_data(qcam->pport) >> 1;
321 hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
322 qcam_set_ack(qcam, 1);
323 if (qcam_await_ready2(qcam, 0))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300324 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300325 lo2 = parport_read_data(qcam->pport) >> 1;
326 hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
327 qcam_set_ack(qcam, 0);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300328 r = lo1 | ((hi1 & 1) << 7);
329 g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
330 b = lo2 | ((hi2 & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 if (force_rgb) {
332 buf[bytes++] = r;
333 buf[bytes++] = g;
334 buf[bytes++] = b;
335 } else {
336 buf[bytes++] = b;
337 buf[bytes++] = g;
338 buf[bytes++] = r;
339 }
340 }
Hans Verkuil51224aa2010-03-22 04:33:56 -0300341 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 /* It's a unidirectional port */
343 int i = 0, n = bytes;
344 unsigned char rgb[3];
345
Hans Verkuil51224aa2010-03-22 04:33:56 -0300346 while (bytes < nbytes) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 unsigned int hi, lo;
348
Hans Verkuild71964f2010-05-10 03:55:25 -0300349 if (qcam_await_ready1(qcam, 1))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300350 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300351 hi = (parport_read_status(qcam->pport) & 0xf0);
352 qcam_set_ack(qcam, 1);
353 if (qcam_await_ready1(qcam, 0))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300354 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300355 lo = (parport_read_status(qcam->pport) & 0xf0);
356 qcam_set_ack(qcam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 /* flip some bits */
358 rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
359 if (i >= 2) {
360get_fragment:
361 if (force_rgb) {
362 buf[n++] = rgb[0];
363 buf[n++] = rgb[1];
364 buf[n++] = rgb[2];
365 } else {
366 buf[n++] = rgb[2];
367 buf[n++] = rgb[1];
368 buf[n++] = rgb[0];
369 }
370 }
371 }
372 if (i) {
373 i = 0;
374 goto get_fragment;
375 }
376 }
377 return bytes;
378}
379
380#define BUFSZ 150
381
Hans Verkuild71964f2010-05-10 03:55:25 -0300382static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
Hans Verkuild71964f2010-05-10 03:55:25 -0300384 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Hans Verkuil81730902012-04-20 07:30:48 -0300385 unsigned lines, pixelsperline;
Hans Verkuild71964f2010-05-10 03:55:25 -0300386 unsigned int is_bi_dir = qcam->bidirectional;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 size_t wantlen, outptr = 0;
388 char tmpbuf[BUFSZ];
389
390 if (!access_ok(VERIFY_WRITE, buf, len))
391 return -EFAULT;
392
393 /* Wait for camera to become ready */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300394 for (;;) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300395 int i = qcam_get(qcam, 41);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300396
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if (i == -1) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300398 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 return -EIO;
400 }
401 if ((i & 0x80) == 0)
402 break;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300403 schedule();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405
Hans Verkuild71964f2010-05-10 03:55:25 -0300406 if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 return -EIO;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300408
Hans Verkuild71964f2010-05-10 03:55:25 -0300409 lines = qcam->height;
410 pixelsperline = qcam->width;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Hans Verkuil51224aa2010-03-22 04:33:56 -0300412 if (is_bi_dir) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 /* Turn the port around */
Hans Verkuild71964f2010-05-10 03:55:25 -0300414 parport_data_reverse(qcam->pport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 mdelay(3);
Hans Verkuild71964f2010-05-10 03:55:25 -0300416 qcam_set_ack(qcam, 0);
417 if (qcam_await_ready1(qcam, 1)) {
418 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 return -EIO;
420 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300421 qcam_set_ack(qcam, 1);
422 if (qcam_await_ready1(qcam, 0)) {
423 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return -EIO;
425 }
426 }
427
428 wantlen = lines * pixelsperline * 24 / 8;
429
Hans Verkuil51224aa2010-03-22 04:33:56 -0300430 while (wantlen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 size_t t, s;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300432
433 s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
Hans Verkuild71964f2010-05-10 03:55:25 -0300434 t = qcam_read_bytes(qcam, tmpbuf, s);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300435 if (outptr < len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 size_t sz = len - outptr;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300437
438 if (sz > t)
439 sz = t;
440 if (__copy_to_user(buf + outptr, tmpbuf, sz))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 break;
442 outptr += sz;
443 }
444 wantlen -= t;
445 if (t < s)
446 break;
447 cond_resched();
448 }
449
450 len = outptr;
451
Hans Verkuil51224aa2010-03-22 04:33:56 -0300452 if (wantlen) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300453 v4l2_err(v4l2_dev, "short read.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (is_bi_dir)
Hans Verkuild71964f2010-05-10 03:55:25 -0300455 parport_data_forward(qcam->pport);
456 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 return len;
458 }
459
Hans Verkuil51224aa2010-03-22 04:33:56 -0300460 if (is_bi_dir) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 int l;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 do {
Hans Verkuild71964f2010-05-10 03:55:25 -0300464 l = qcam_read_bytes(qcam, tmpbuf, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 cond_resched();
466 } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
467 if (force_rgb) {
468 if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
Hans Verkuild71964f2010-05-10 03:55:25 -0300469 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 } else {
471 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
Hans Verkuild71964f2010-05-10 03:55:25 -0300472 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300474 qcam_set_ack(qcam, 0);
475 if (qcam_await_ready1(qcam, 1)) {
476 v4l2_err(v4l2_dev, "no ack after EOF\n");
477 parport_data_forward(qcam->pport);
478 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 return len;
480 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300481 parport_data_forward(qcam->pport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 mdelay(3);
Hans Verkuild71964f2010-05-10 03:55:25 -0300483 qcam_set_ack(qcam, 1);
484 if (qcam_await_ready1(qcam, 0)) {
485 v4l2_err(v4l2_dev, "no ack to port turnaround\n");
486 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 return len;
488 }
Hans Verkuil51224aa2010-03-22 04:33:56 -0300489 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 int l;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 do {
Hans Verkuild71964f2010-05-10 03:55:25 -0300493 l = qcam_read_bytes(qcam, tmpbuf, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 cond_resched();
495 } while (l && tmpbuf[0] == 0x7e);
Hans Verkuild71964f2010-05-10 03:55:25 -0300496 l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 if (force_rgb) {
498 if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
Hans Verkuild71964f2010-05-10 03:55:25 -0300499 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 } else {
501 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
Hans Verkuild71964f2010-05-10 03:55:25 -0300502 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 }
504 }
505
Hans Verkuild71964f2010-05-10 03:55:25 -0300506 qcam_write_data(qcam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 return len;
508}
509
510/*
511 * Video4linux interfacing
512 */
513
Hans Verkuild71964f2010-05-10 03:55:25 -0300514static int qcam_querycap(struct file *file, void *priv,
515 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516{
Hans Verkuild71964f2010-05-10 03:55:25 -0300517 struct qcam *qcam = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300518
Hans Verkuild71964f2010-05-10 03:55:25 -0300519 strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
520 strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
521 strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
Hans Verkuil34caed82012-05-08 15:20:21 -0300522 vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
523 vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 return 0;
525}
526
Hans Verkuild71964f2010-05-10 03:55:25 -0300527static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528{
Hans Verkuild71964f2010-05-10 03:55:25 -0300529 if (vin->index > 0)
530 return -EINVAL;
531 strlcpy(vin->name, "Camera", sizeof(vin->name));
532 vin->type = V4L2_INPUT_TYPE_CAMERA;
533 vin->audioset = 0;
534 vin->tuner = 0;
535 vin->std = 0;
536 vin->status = 0;
537 return 0;
538}
539
540static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
541{
542 *inp = 0;
543 return 0;
544}
545
546static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
547{
548 return (inp > 0) ? -EINVAL : 0;
549}
550
Hans Verkuild71964f2010-05-10 03:55:25 -0300551static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
552{
553 struct qcam *qcam = video_drvdata(file);
554 struct v4l2_pix_format *pix = &fmt->fmt.pix;
555
556 pix->width = qcam->width;
557 pix->height = qcam->height;
558 pix->pixelformat = V4L2_PIX_FMT_RGB24;
559 pix->field = V4L2_FIELD_NONE;
560 pix->bytesperline = 3 * qcam->width;
561 pix->sizeimage = 3 * qcam->width * qcam->height;
562 /* Just a guess */
563 pix->colorspace = V4L2_COLORSPACE_SRGB;
564 return 0;
565}
566
567static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
568{
569 struct v4l2_pix_format *pix = &fmt->fmt.pix;
570
571 if (pix->height < 60 || pix->width < 80) {
572 pix->height = 60;
573 pix->width = 80;
574 } else if (pix->height < 120 || pix->width < 160) {
575 pix->height = 120;
576 pix->width = 160;
577 } else {
578 pix->height = 240;
579 pix->width = 320;
580 }
581 pix->pixelformat = V4L2_PIX_FMT_RGB24;
582 pix->field = V4L2_FIELD_NONE;
583 pix->bytesperline = 3 * pix->width;
584 pix->sizeimage = 3 * pix->width * pix->height;
585 /* Just a guess */
586 pix->colorspace = V4L2_COLORSPACE_SRGB;
587 return 0;
588}
589
590static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
591{
592 struct qcam *qcam = video_drvdata(file);
593 struct v4l2_pix_format *pix = &fmt->fmt.pix;
594 int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
595
596 if (ret)
597 return ret;
598 switch (pix->height) {
599 case 60:
600 qcam->mode = QC_DECIMATION_4;
601 break;
602 case 120:
603 qcam->mode = QC_DECIMATION_2;
604 break;
605 default:
606 qcam->mode = QC_DECIMATION_1;
607 break;
608 }
609
610 mutex_lock(&qcam->lock);
611 qcam->mode |= QC_MILLIONS;
612 qcam->height = pix->height;
613 qcam->width = pix->width;
614 parport_claim_or_block(qcam->pdev);
615 qc_setup(qcam);
616 parport_release(qcam->pdev);
617 mutex_unlock(&qcam->lock);
618 return 0;
619}
620
621static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
622{
623 static struct v4l2_fmtdesc formats[] = {
624 { 0, 0, 0,
625 "RGB 8:8:8", V4L2_PIX_FMT_RGB24,
626 { 0, 0, 0, 0 }
627 },
628 };
629 enum v4l2_buf_type type = fmt->type;
630
631 if (fmt->index > 0)
632 return -EINVAL;
633
634 *fmt = formats[fmt->index];
635 fmt->type = type;
636 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637}
638
639static ssize_t qcam_read(struct file *file, char __user *buf,
640 size_t count, loff_t *ppos)
641{
Hans Verkuild71964f2010-05-10 03:55:25 -0300642 struct qcam *qcam = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 int len;
644
Ingo Molnar3593cab2006-02-07 06:49:14 -0200645 mutex_lock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 parport_claim_or_block(qcam->pdev);
647 /* Probably should have a semaphore against multiple users */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300648 len = qc_capture(qcam, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 parport_release(qcam->pdev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200650 mutex_unlock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 return len;
652}
653
Hans Verkuil34caed82012-05-08 15:20:21 -0300654static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
655{
656 struct qcam *qcam =
657 container_of(ctrl->handler, struct qcam, hdl);
658 int ret = 0;
659
660 mutex_lock(&qcam->lock);
661 switch (ctrl->id) {
662 case V4L2_CID_BRIGHTNESS:
663 qcam->brightness = ctrl->val;
664 break;
665 case V4L2_CID_CONTRAST:
666 qcam->contrast = ctrl->val;
667 break;
668 case V4L2_CID_GAMMA:
669 qcam->whitebal = ctrl->val;
670 break;
671 default:
672 ret = -EINVAL;
673 break;
674 }
675 if (ret == 0) {
676 parport_claim_or_block(qcam->pdev);
677 qc_setup(qcam);
678 parport_release(qcam->pdev);
679 }
680 mutex_unlock(&qcam->lock);
681 return ret;
682}
683
Hans Verkuilbec43662008-12-30 06:58:20 -0300684static const struct v4l2_file_operations qcam_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 .owner = THIS_MODULE,
Hans Verkuil34caed82012-05-08 15:20:21 -0300686 .open = v4l2_fh_open,
687 .release = v4l2_fh_release,
688 .poll = v4l2_ctrl_poll,
Hans Verkuil61df3c92010-11-14 10:09:38 -0300689 .unlocked_ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 .read = qcam_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691};
692
Hans Verkuild71964f2010-05-10 03:55:25 -0300693static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
694 .vidioc_querycap = qcam_querycap,
695 .vidioc_g_input = qcam_g_input,
696 .vidioc_s_input = qcam_s_input,
697 .vidioc_enum_input = qcam_enum_input,
Hans Verkuil34caed82012-05-08 15:20:21 -0300698 .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
Hans Verkuild71964f2010-05-10 03:55:25 -0300699 .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
700 .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
701 .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
Hans Verkuil34caed82012-05-08 15:20:21 -0300702 .vidioc_log_status = v4l2_ctrl_log_status,
703 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
704 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
705};
706
707static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
708 .s_ctrl = qcam_s_ctrl,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709};
710
711/* Initialize the QuickCam driver control structure. */
712
Hans Verkuild71964f2010-05-10 03:55:25 -0300713static struct qcam *qcam_init(struct parport *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714{
Hans Verkuild71964f2010-05-10 03:55:25 -0300715 struct qcam *qcam;
716 struct v4l2_device *v4l2_dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300717
Hans Verkuild71964f2010-05-10 03:55:25 -0300718 qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
719 if (qcam == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 return NULL;
721
Hans Verkuild71964f2010-05-10 03:55:25 -0300722 v4l2_dev = &qcam->v4l2_dev;
723 strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Hans Verkuild71964f2010-05-10 03:55:25 -0300725 if (v4l2_device_register(NULL, v4l2_dev) < 0) {
726 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
Julia Lawallee893e92011-07-04 11:11:42 -0300727 kfree(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return NULL;
729 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300730
Hans Verkuil34caed82012-05-08 15:20:21 -0300731 v4l2_ctrl_handler_init(&qcam->hdl, 3);
732 v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
733 V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
734 v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
735 V4L2_CID_CONTRAST, 0, 255, 1, 192);
736 v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
737 V4L2_CID_GAMMA, 0, 255, 1, 128);
738 if (qcam->hdl.error) {
739 v4l2_err(v4l2_dev, "couldn't register controls\n");
740 v4l2_ctrl_handler_free(&qcam->hdl);
741 kfree(qcam);
742 return NULL;
743 }
744
Hans Verkuild71964f2010-05-10 03:55:25 -0300745 qcam->pport = port;
746 qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
747 NULL, 0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Hans Verkuild71964f2010-05-10 03:55:25 -0300749 qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
750
751 if (qcam->pdev == NULL) {
752 v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
Hans Verkuil34caed82012-05-08 15:20:21 -0300753 v4l2_ctrl_handler_free(&qcam->hdl);
Hans Verkuild71964f2010-05-10 03:55:25 -0300754 kfree(qcam);
755 return NULL;
756 }
757
758 strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
759 qcam->vdev.v4l2_dev = v4l2_dev;
760 qcam->vdev.fops = &qcam_fops;
761 qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
762 qcam->vdev.release = video_device_release_empty;
Hans Verkuil34caed82012-05-08 15:20:21 -0300763 qcam->vdev.ctrl_handler = &qcam->hdl;
Hans Verkuild71964f2010-05-10 03:55:25 -0300764 video_set_drvdata(&qcam->vdev, qcam);
765
766 mutex_init(&qcam->lock);
767 qcam->width = qcam->ccd_width = 320;
768 qcam->height = qcam->ccd_height = 240;
769 qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
770 qcam->contrast = 192;
771 qcam->brightness = 240;
772 qcam->whitebal = 128;
773 qcam->top = 1;
774 qcam->left = 14;
775 return qcam;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776}
777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778static int init_cqcam(struct parport *port)
779{
Hans Verkuild71964f2010-05-10 03:55:25 -0300780 struct qcam *qcam;
781 struct v4l2_device *v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Hans Verkuil51224aa2010-03-22 04:33:56 -0300783 if (parport[0] != -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 /* The user gave specific instructions */
785 int i, found = 0;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300786
787 for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 if (parport[0] == port->number)
789 found = 1;
790 }
791 if (!found)
792 return -ENODEV;
793 }
794
795 if (num_cams == MAX_CAMS)
796 return -ENOSPC;
797
798 qcam = qcam_init(port);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300799 if (qcam == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300801
Hans Verkuild71964f2010-05-10 03:55:25 -0300802 v4l2_dev = &qcam->v4l2_dev;
803
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 parport_claim_or_block(qcam->pdev);
805
806 qc_reset(qcam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300807
Hans Verkuil51224aa2010-03-22 04:33:56 -0300808 if (probe && qc_detect(qcam) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 parport_release(qcam->pdev);
810 parport_unregister_device(qcam->pdev);
811 kfree(qcam);
812 return -ENODEV;
813 }
814
815 qc_setup(qcam);
816
817 parport_release(qcam->pdev);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300818
Hans Verkuildc60de32008-09-03 17:11:58 -0300819 if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300820 v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 qcam->pport->name);
822 parport_unregister_device(qcam->pdev);
823 kfree(qcam);
824 return -ENODEV;
825 }
826
Hans Verkuild71964f2010-05-10 03:55:25 -0300827 v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
Laurent Pinchart38c7c032009-11-27 13:57:15 -0300828 video_device_node_name(&qcam->vdev), qcam->pport->name);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300829
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 qcams[num_cams++] = qcam;
831
832 return 0;
833}
834
Hans Verkuild71964f2010-05-10 03:55:25 -0300835static void close_cqcam(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836{
837 video_unregister_device(&qcam->vdev);
Hans Verkuil34caed82012-05-08 15:20:21 -0300838 v4l2_ctrl_handler_free(&qcam->hdl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 parport_unregister_device(qcam->pdev);
840 kfree(qcam);
841}
842
843static void cq_attach(struct parport *port)
844{
845 init_cqcam(port);
846}
847
848static void cq_detach(struct parport *port)
849{
850 /* Write this some day. */
851}
852
853static struct parport_driver cqcam_driver = {
854 .name = "cqcam",
855 .attach = cq_attach,
856 .detach = cq_detach,
857};
858
Hans Verkuil51224aa2010-03-22 04:33:56 -0300859static int __init cqcam_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860{
Hans Verkuild71964f2010-05-10 03:55:25 -0300861 printk(KERN_INFO BANNER "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
863 return parport_register_driver(&cqcam_driver);
864}
865
Hans Verkuil51224aa2010-03-22 04:33:56 -0300866static void __exit cqcam_cleanup(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
868 unsigned int i;
869
870 for (i = 0; i < num_cams; i++)
871 close_cqcam(qcams[i]);
872
873 parport_unregister_driver(&cqcam_driver);
874}
875
876MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
877MODULE_DESCRIPTION(BANNER);
878MODULE_LICENSE("GPL");
Mauro Carvalho Chehab1990d502011-06-24 14:45:49 -0300879MODULE_VERSION("0.0.4");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881module_init(cqcam_init);
882module_exit(cqcam_cleanup);