blob: fda32f52554a75d63cb34652b4b7bd61b49c3890 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Hans Verkuild71964f2010-05-10 03:55:25 -030044struct qcam {
45 struct v4l2_device v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 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;
Ingo Molnar3593cab2006-02-07 06:49:14 -020055 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056};
57
58/* cameras maximum */
59#define MAX_CAMS 4
60
61/* The three possible QuickCam modes */
62#define QC_MILLIONS 0x18
63#define QC_BILLIONS 0x10
64#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
65
66/* The three possible decimations */
67#define QC_DECIMATION_1 0
68#define QC_DECIMATION_2 2
69#define QC_DECIMATION_4 4
70
Hans Verkuild71964f2010-05-10 03:55:25 -030071#define BANNER "Colour QuickCam for Video4Linux v0.06"
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
74static int probe = 2;
Rusty Russell90ab5ee2012-01-13 09:32:20 +103075static bool force_rgb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static int video_nr = -1;
77
Hans Verkuild71964f2010-05-10 03:55:25 -030078/* FIXME: parport=auto would never have worked, surely? --RR */
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)
Linus Torvalds1da177e2005-04-16 15:20:36 -070091{
92 /* note: the QC specs refer to the PCAck pin by voltage, not
93 software level. PC ports have builtin inverters. */
Hans Verkuil51224aa2010-03-22 04:33:56 -030094 parport_frob_control(qcam->pport, 8, i ? 8 : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96
Hans Verkuild71964f2010-05-10 03:55:25 -030097static inline unsigned int qcam_ready1(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
Hans Verkuil51224aa2010-03-22 04:33:56 -030099 return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
Hans Verkuild71964f2010-05-10 03:55:25 -0300102static inline unsigned int qcam_ready2(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
Hans Verkuil51224aa2010-03-22 04:33:56 -0300104 return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105}
106
Hans Verkuild71964f2010-05-10 03:55:25 -0300107static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108{
Hans Verkuild71964f2010-05-10 03:55:25 -0300109 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 unsigned long oldjiffies = jiffies;
111 unsigned int i;
112
Julia Lawall168c6262008-04-16 16:13:15 -0300113 for (oldjiffies = jiffies;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300114 time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 if (qcam_ready1(qcam) == value)
116 return 0;
117
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300118 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 for a while. */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300120 for (i = 0; i < 50; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 if (qcam_ready1(qcam) == value)
122 return 0;
123 msleep_interruptible(100);
124 }
125
126 /* Probably somebody pulled the plug out. Not much we can do. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300127 v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 parport_read_status(qcam->pport),
129 parport_read_control(qcam->pport));
130 return 1;
131}
132
Hans Verkuild71964f2010-05-10 03:55:25 -0300133static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Hans Verkuild71964f2010-05-10 03:55:25 -0300135 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 unsigned long oldjiffies = jiffies;
137 unsigned int i;
138
Julia Lawall168c6262008-04-16 16:13:15 -0300139 for (oldjiffies = jiffies;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300140 time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 if (qcam_ready2(qcam) == value)
142 return 0;
143
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300144 /* If the camera didn't respond within 1/25 second, poll slowly
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 for a while. */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300146 for (i = 0; i < 50; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 if (qcam_ready2(qcam) == value)
148 return 0;
149 msleep_interruptible(100);
150 }
151
152 /* Probably somebody pulled the plug out. Not much we can do. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300153 v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 parport_read_status(qcam->pport),
155 parport_read_control(qcam->pport),
156 parport_read_data(qcam->pport));
157 return 1;
158}
159
Hans Verkuild71964f2010-05-10 03:55:25 -0300160static int qcam_read_data(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
162 unsigned int idata;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 qcam_set_ack(qcam, 0);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300165 if (qcam_await_ready1(qcam, 1))
166 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 idata = parport_read_status(qcam->pport) & 0xf0;
168 qcam_set_ack(qcam, 1);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300169 if (qcam_await_ready1(qcam, 0))
170 return -1;
171 idata |= parport_read_status(qcam->pport) >> 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 return idata;
173}
174
Hans Verkuild71964f2010-05-10 03:55:25 -0300175static int qcam_write_data(struct qcam *qcam, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176{
Hans Verkuild71964f2010-05-10 03:55:25 -0300177 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 unsigned int idata;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 parport_write_data(qcam->pport, data);
181 idata = qcam_read_data(qcam);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300182 if (data != idata) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300183 v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 idata);
185 return 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300186 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 return 0;
188}
189
Hans Verkuild71964f2010-05-10 03:55:25 -0300190static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
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
Hans Verkuild71964f2010-05-10 03:55:25 -0300199static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
201 if (qcam_write_data(qcam, cmd))
202 return -1;
203 return qcam_read_data(qcam);
204}
205
Hans Verkuild71964f2010-05-10 03:55:25 -0300206static int qc_detect(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
208 unsigned int stat, ostat, i, count = 0;
209
210 /* The probe routine below is not very reliable. The IEEE-1284
211 probe takes precedence. */
212 /* XXX Currently parport provides no way to distinguish between
213 "the IEEE probe was not done" and "the probe was done, but
214 no device was found". Fix this one day. */
215 if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
216 && qcam->pport->probe_info[0].model
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300217 && !strcmp(qcam->pdev->port->probe_info[0].model,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 "Color QuickCam 2.0")) {
219 printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
220 return 1;
221 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 if (probe < 2)
224 return 0;
225
226 parport_write_control(qcam->pport, 0xc);
227
228 /* look for a heartbeat */
229 ostat = stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300230 for (i = 0; i < 250; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 mdelay(1);
232 stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300233 if (ostat != stat) {
234 if (++count >= 3)
235 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 ostat = stat;
237 }
238 }
239
240 /* Reset the camera and try again */
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);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300249 for (i = 0; i < 250; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 mdelay(1);
251 stat = parport_read_status(qcam->pport);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300252 if (ostat != stat) {
253 if (++count >= 3)
254 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 ostat = stat;
256 }
257 }
258
259 /* no (or flatline) camera, give up */
260 return 0;
261}
262
Hans Verkuild71964f2010-05-10 03:55:25 -0300263static void qc_reset(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
265 parport_write_control(qcam->pport, 0xc);
266 parport_write_control(qcam->pport, 0x8);
267 mdelay(1);
268 parport_write_control(qcam->pport, 0xc);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300269 mdelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
272/* Reset the QuickCam and program for brightness, contrast,
273 * white-balance, and resolution. */
274
Hans Verkuild71964f2010-05-10 03:55:25 -0300275static void qc_setup(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
Hans Verkuild71964f2010-05-10 03:55:25 -0300277 qc_reset(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Hans Verkuil51224aa2010-03-22 04:33:56 -0300279 /* Set the brightness. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300280 qcam_set(qcam, 11, qcam->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282 /* Set the height and width. These refer to the actual
283 CCD area *before* applying the selected decimation. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300284 qcam_set(qcam, 17, qcam->ccd_height);
285 qcam_set(qcam, 19, qcam->ccd_width / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287 /* Set top and left. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300288 qcam_set(qcam, 0xd, qcam->top);
289 qcam_set(qcam, 0xf, qcam->left);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 /* Set contrast and white balance. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300292 qcam_set(qcam, 0x19, qcam->contrast);
293 qcam_set(qcam, 0x1f, qcam->whitebal);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 /* Set the speed. */
Hans Verkuild71964f2010-05-10 03:55:25 -0300296 qcam_set(qcam, 45, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300299/* Read some bytes from the camera and put them in the buffer.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 nbytes should be a multiple of 3, because bidirectional mode gives
301 us three bytes at a time. */
302
Hans Verkuild71964f2010-05-10 03:55:25 -0300303static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 unsigned int bytes = 0;
306
Hans Verkuild71964f2010-05-10 03:55:25 -0300307 qcam_set_ack(qcam, 0);
308 if (qcam->bidirectional) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 /* It's a bidirectional port */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300310 while (bytes < nbytes) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 unsigned int lo1, hi1, lo2, hi2;
312 unsigned char r, g, b;
313
Hans Verkuild71964f2010-05-10 03:55:25 -0300314 if (qcam_await_ready2(qcam, 1))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300315 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300316 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))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300320 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300321 lo2 = parport_read_data(qcam->pport) >> 1;
322 hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
323 qcam_set_ack(qcam, 0);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300324 r = lo1 | ((hi1 & 1) << 7);
325 g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
326 b = lo2 | ((hi2 & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 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 }
Hans Verkuil51224aa2010-03-22 04:33:56 -0300337 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 /* It's a unidirectional port */
339 int i = 0, n = bytes;
340 unsigned char rgb[3];
341
Hans Verkuil51224aa2010-03-22 04:33:56 -0300342 while (bytes < nbytes) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 unsigned int hi, lo;
344
Hans Verkuild71964f2010-05-10 03:55:25 -0300345 if (qcam_await_ready1(qcam, 1))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300346 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300347 hi = (parport_read_status(qcam->pport) & 0xf0);
348 qcam_set_ack(qcam, 1);
349 if (qcam_await_ready1(qcam, 0))
Hans Verkuil51224aa2010-03-22 04:33:56 -0300350 return bytes;
Hans Verkuild71964f2010-05-10 03:55:25 -0300351 lo = (parport_read_status(qcam->pport) & 0xf0);
352 qcam_set_ack(qcam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 /* flip some bits */
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
Hans Verkuild71964f2010-05-10 03:55:25 -0300378static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379{
Hans Verkuild71964f2010-05-10 03:55:25 -0300380 struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 unsigned lines, pixelsperline, bitsperxfer;
Hans Verkuild71964f2010-05-10 03:55:25 -0300382 unsigned int is_bi_dir = qcam->bidirectional;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 size_t wantlen, outptr = 0;
384 char tmpbuf[BUFSZ];
385
386 if (!access_ok(VERIFY_WRITE, buf, len))
387 return -EFAULT;
388
389 /* Wait for camera to become ready */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300390 for (;;) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300391 int i = qcam_get(qcam, 41);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300392
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if (i == -1) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300394 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 return -EIO;
396 }
397 if ((i & 0x80) == 0)
398 break;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300399 schedule();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 }
401
Hans Verkuild71964f2010-05-10 03:55:25 -0300402 if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 return -EIO;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300404
Hans Verkuild71964f2010-05-10 03:55:25 -0300405 lines = qcam->height;
406 pixelsperline = qcam->width;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 bitsperxfer = (is_bi_dir) ? 24 : 8;
408
Hans Verkuil51224aa2010-03-22 04:33:56 -0300409 if (is_bi_dir) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 /* Turn the port around */
Hans Verkuild71964f2010-05-10 03:55:25 -0300411 parport_data_reverse(qcam->pport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 mdelay(3);
Hans Verkuild71964f2010-05-10 03:55:25 -0300413 qcam_set_ack(qcam, 0);
414 if (qcam_await_ready1(qcam, 1)) {
415 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return -EIO;
417 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300418 qcam_set_ack(qcam, 1);
419 if (qcam_await_ready1(qcam, 0)) {
420 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 return -EIO;
422 }
423 }
424
425 wantlen = lines * pixelsperline * 24 / 8;
426
Hans Verkuil51224aa2010-03-22 04:33:56 -0300427 while (wantlen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 size_t t, s;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300429
430 s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
Hans Verkuild71964f2010-05-10 03:55:25 -0300431 t = qcam_read_bytes(qcam, tmpbuf, s);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300432 if (outptr < len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 size_t sz = len - outptr;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300434
435 if (sz > t)
436 sz = t;
437 if (__copy_to_user(buf + outptr, tmpbuf, sz))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 break;
439 outptr += sz;
440 }
441 wantlen -= t;
442 if (t < s)
443 break;
444 cond_resched();
445 }
446
447 len = outptr;
448
Hans Verkuil51224aa2010-03-22 04:33:56 -0300449 if (wantlen) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300450 v4l2_err(v4l2_dev, "short read.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if (is_bi_dir)
Hans Verkuild71964f2010-05-10 03:55:25 -0300452 parport_data_forward(qcam->pport);
453 qc_setup(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 return len;
455 }
456
Hans Verkuil51224aa2010-03-22 04:33:56 -0300457 if (is_bi_dir) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 int l;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300459
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 do {
Hans Verkuild71964f2010-05-10 03:55:25 -0300461 l = qcam_read_bytes(qcam, tmpbuf, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 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)
Hans Verkuild71964f2010-05-10 03:55:25 -0300466 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 } else {
468 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
Hans Verkuild71964f2010-05-10 03:55:25 -0300469 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300471 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 return len;
477 }
Hans Verkuild71964f2010-05-10 03:55:25 -0300478 parport_data_forward(qcam->pport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 mdelay(3);
Hans Verkuild71964f2010-05-10 03:55:25 -0300480 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 return len;
485 }
Hans Verkuil51224aa2010-03-22 04:33:56 -0300486 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 int l;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300488
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 do {
Hans Verkuild71964f2010-05-10 03:55:25 -0300490 l = qcam_read_bytes(qcam, tmpbuf, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 cond_resched();
492 } while (l && tmpbuf[0] == 0x7e);
Hans Verkuild71964f2010-05-10 03:55:25 -0300493 l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 if (force_rgb) {
495 if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
Hans Verkuild71964f2010-05-10 03:55:25 -0300496 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 } else {
498 if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
Hans Verkuild71964f2010-05-10 03:55:25 -0300499 v4l2_err(v4l2_dev, "bad EOF\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 }
501 }
502
Hans Verkuild71964f2010-05-10 03:55:25 -0300503 qcam_write_data(qcam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return len;
505}
506
507/*
508 * Video4linux interfacing
509 */
510
Hans Verkuild71964f2010-05-10 03:55:25 -0300511static int qcam_querycap(struct file *file, void *priv,
512 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
Hans Verkuild71964f2010-05-10 03:55:25 -0300514 struct qcam *qcam = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300515
Hans Verkuild71964f2010-05-10 03:55:25 -0300516 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));
Hans Verkuild71964f2010-05-10 03:55:25 -0300519 vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 return 0;
521}
522
Hans Verkuild71964f2010-05-10 03:55:25 -0300523static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
Hans Verkuild71964f2010-05-10 03:55:25 -0300525 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 /* Just a guess */
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 /* Just a guess */
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700}
701
702static ssize_t qcam_read(struct file *file, char __user *buf,
703 size_t count, loff_t *ppos)
704{
Hans Verkuild71964f2010-05-10 03:55:25 -0300705 struct qcam *qcam = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 int len;
707
Ingo Molnar3593cab2006-02-07 06:49:14 -0200708 mutex_lock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 parport_claim_or_block(qcam->pdev);
710 /* Probably should have a semaphore against multiple users */
Hans Verkuil51224aa2010-03-22 04:33:56 -0300711 len = qc_capture(qcam, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 parport_release(qcam->pdev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200713 mutex_unlock(&qcam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return len;
715}
716
Hans Verkuilbec43662008-12-30 06:58:20 -0300717static const struct v4l2_file_operations qcam_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 .owner = THIS_MODULE,
Hans Verkuil61df3c92010-11-14 10:09:38 -0300719 .unlocked_ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 .read = qcam_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721};
722
Hans Verkuild71964f2010-05-10 03:55:25 -0300723static 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,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735};
736
737/* Initialize the QuickCam driver control structure. */
738
Hans Verkuild71964f2010-05-10 03:55:25 -0300739static struct qcam *qcam_init(struct parport *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
Hans Verkuild71964f2010-05-10 03:55:25 -0300741 struct qcam *qcam;
742 struct v4l2_device *v4l2_dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300743
Hans Verkuild71964f2010-05-10 03:55:25 -0300744 qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
745 if (qcam == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 return NULL;
747
Hans Verkuild71964f2010-05-10 03:55:25 -0300748 v4l2_dev = &qcam->v4l2_dev;
749 strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Hans Verkuild71964f2010-05-10 03:55:25 -0300751 if (v4l2_device_register(NULL, v4l2_dev) < 0) {
752 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
Julia Lawallee893e92011-07-04 11:11:42 -0300753 kfree(qcam);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 return NULL;
755 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300756
Hans Verkuild71964f2010-05-10 03:55:25 -0300757 qcam->pport = port;
758 qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
759 NULL, 0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Hans Verkuild71964f2010-05-10 03:55:25 -0300761 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786}
787
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788static int init_cqcam(struct parport *port)
789{
Hans Verkuild71964f2010-05-10 03:55:25 -0300790 struct qcam *qcam;
791 struct v4l2_device *v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Hans Verkuil51224aa2010-03-22 04:33:56 -0300793 if (parport[0] != -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 /* The user gave specific instructions */
795 int i, found = 0;
Hans Verkuil51224aa2010-03-22 04:33:56 -0300796
797 for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 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);
Hans Verkuil51224aa2010-03-22 04:33:56 -0300809 if (qcam == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300811
Hans Verkuild71964f2010-05-10 03:55:25 -0300812 v4l2_dev = &qcam->v4l2_dev;
813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 parport_claim_or_block(qcam->pdev);
815
816 qc_reset(qcam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300817
Hans Verkuil51224aa2010-03-22 04:33:56 -0300818 if (probe && qc_detect(qcam) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 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);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300828
Hans Verkuildc60de32008-09-03 17:11:58 -0300829 if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
Hans Verkuild71964f2010-05-10 03:55:25 -0300830 v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 qcam->pport->name);
832 parport_unregister_device(qcam->pdev);
833 kfree(qcam);
834 return -ENODEV;
835 }
836
Hans Verkuild71964f2010-05-10 03:55:25 -0300837 v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
Laurent Pinchart38c7c032009-11-27 13:57:15 -0300838 video_device_node_name(&qcam->vdev), qcam->pport->name);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 qcams[num_cams++] = qcam;
841
842 return 0;
843}
844
Hans Verkuild71964f2010-05-10 03:55:25 -0300845static void close_cqcam(struct qcam *qcam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
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 /* Write this some day. */
860}
861
862static struct parport_driver cqcam_driver = {
863 .name = "cqcam",
864 .attach = cq_attach,
865 .detach = cq_detach,
866};
867
Hans Verkuil51224aa2010-03-22 04:33:56 -0300868static int __init cqcam_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
Hans Verkuild71964f2010-05-10 03:55:25 -0300870 printk(KERN_INFO BANNER "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
872 return parport_register_driver(&cqcam_driver);
873}
874
Hans Verkuil51224aa2010-03-22 04:33:56 -0300875static void __exit cqcam_cleanup(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876{
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");
Mauro Carvalho Chehab1990d502011-06-24 14:45:49 -0300888MODULE_VERSION("0.0.4");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890module_init(cqcam_init);
891module_exit(cqcam_cleanup);