FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/qcamio.c
1 /*
2 * Connectix QuickCam parallel-port camera video capture driver.
3 * Copyright (c) 1996, Paul Traina.
4 *
5 * This driver is based in part on work
6 * Copyright (c) 1996, Thomas Davis.
7 *
8 * Additional ideas from code written by Michael Chinn and Nelson Minar.
9 *
10 * QuickCam(TM) is a registered trademark of Connectix Inc.
11 * Use this driver at your own risk, it is not warranted by
12 * Connectix or the authors.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer
19 * in this position and unchanged.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. The name of the author may not be used to endorse or promote products
24 * derived from this software withough specific prior written permission
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #ifndef NQCAM
39 #include "qcam.h" /* this file defines NQCAM _only_ */
40 #endif
41
42 #if NQCAM > 0
43
44 #if defined(__FreeBSD__) || defined(__NetBSD__)
45 #include <sys/param.h>
46 #include <machine/cpufunc.h>
47 #ifdef KERNEL
48 #include <sys/systm.h>
49 #include <machine/clock.h>
50 #include <machine/qcam.h>
51 #else /* user mode version of driver */
52 #include <unistd.h>
53 #include <stdio.h>
54 #include "qcam.h"
55 #endif /* KERNEL */
56 #endif /* FreeBSD or NetBSD */
57
58 #ifdef bsdi
59 #include <sys/param.h>
60 #include <sys/conf.h>
61 #include <sys/device.h>
62 #include "qcam.h"
63 #endif /* bsdi */
64
65 #ifdef __linux__
66 #include <linux/kernel.h>
67 #include <linux/sched.h>
68 #include <linux/string.h>
69 #include <linux/delay.h>
70 #include <asm/io.h>
71 #include "qcam-linux.h"
72 #include "qcam.h"
73 #endif /* __linux__ */
74
75 #ifdef _SCO_DS
76 #include <limits.h>
77 #include <errno.h>
78 #include <sys/types.h>
79 #include "qcam-sco.h"
80 #include "qcam.h"
81 #endif
82
83 #ifdef __FreeBSD__
84 #include <i386/isa/qcamreg.h>
85 #include <i386/isa/qcamdefs.h>
86 #else
87 #include "qcamreg.h"
88 #include "qcamdefs.h"
89 #endif
90
91 /*
92 * There should be _NO_ operating system dependant code or definitions
93 * past this point.
94 */
95
96 static const u_char qcam_zoommode[3][3] = {
97 { QC_XFER_WIDE, QC_XFER_WIDE, QC_XFER_WIDE },
98 { QC_XFER_NARROW, QC_XFER_WIDE, QC_XFER_WIDE },
99 { QC_XFER_TIGHT, QC_XFER_NARROW, QC_XFER_WIDE }
100 };
101
102 static int qcam_timeouts;
103
104 #ifdef QCAM_GRAB_STATS
105
106 #define STATBUFSIZE (QC_MAXFRAMEBUFSIZE*2+50)
107 static u_short qcam_rsbhigh[STATBUFSIZE];
108 static u_short qcam_rsblow[STATBUFSIZE];
109 static u_short *qcam_rsbhigh_p = qcam_rsbhigh;
110 static u_short *qcam_rsblow_p = qcam_rsblow;
111 static u_short *qcam_rsbhigh_end = &qcam_rsbhigh[STATBUFSIZE];
112 static u_short *qcam_rsblow_end = &qcam_rsblow[STATBUFSIZE];
113
114 #define STATHIGH(T) \
115 if (qcam_rsbhigh_p < qcam_rsbhigh_end) \
116 *qcam_rsbhigh_p++ = ((T) - timeout); \
117 if (!timeout) qcam_timeouts++;
118
119 #define STATLOW(T) \
120 if (qcam_rsblow_p < qcam_rsblow_end) \
121 *qcam_rsblow_p++ = ((T) - timeout); \
122 if (!timeout) qcam_timeouts++;
123
124 #else
125
126 #define STATHIGH(T) if (!timeout) qcam_timeouts++;
127 #define STATLOW(T) if (!timeout) qcam_timeouts++;
128
129 #endif /* QCAM_GRAB_STATS */
130
131 #define READ_STATUS_BYTE_HIGH(P, V, T) { \
132 u_short timeout = (T); \
133 do { (V) = read_status((P)); \
134 } while (!(((V) & 0x08)) && --timeout); STATHIGH(T) \
135 }
136
137 #define READ_STATUS_BYTE_LOW(P, V, T) { \
138 u_short timeout = (T); \
139 do { (V) = read_status((P)); \
140 } while (((V) & 0x08) && --timeout); STATLOW(T) \
141 }
142
143 #define READ_DATA_WORD_HIGH(P, V, T) { \
144 u_int timeout = (T); \
145 do { (V) = read_data_word((P)); \
146 } while (!((V) & 0x01) && --timeout); STATHIGH(T) \
147 }
148
149 #define READ_DATA_WORD_LOW(P, V, T) { \
150 u_int timeout = (T); \
151 do { (V) = read_data_word((P)); \
152 } while (((V) & 0x01) && --timeout); STATLOW(T) \
153 }
154
155 inline static int
156 sendbyte (u_int port, int value, int sdelay)
157 {
158 u_char s1, s2;
159
160 write_data(port, value);
161 if (sdelay) {
162 DELAY(sdelay);
163 write_data(port, value);
164 }
165
166 write_control(port, QC_CTL_HIGHNIB);
167 READ_STATUS_BYTE_HIGH(port, s1, QC_TIMEOUT_CMD);
168
169 write_control(port, QC_CTL_LOWNIB);
170 READ_STATUS_BYTE_LOW(port, s2, QC_TIMEOUT_CMD);
171
172 return (s1 & 0xf0) | (s2 >> 4);
173 }
174
175 static int
176 send_command (struct qcam_softc *qs, int cmd, int value)
177 {
178 if (sendbyte(qs->iobase, cmd, qs->exposure) != cmd)
179 return 1;
180
181 if (sendbyte(qs->iobase, value, qs->exposure) != value)
182 return 1;
183
184 return 0; /* success */
185 }
186
187 static int
188 send_xfermode (struct qcam_softc *qs, int value)
189 {
190 if (sendbyte(qs->iobase, QC_XFERMODE, qs->exposure) != QC_XFERMODE)
191 return 1;
192
193 if (sendbyte(qs->iobase, value, qs->exposure) != value)
194 return 1;
195
196 return 0;
197 }
198
199 void
200 qcam_reset (struct qcam_softc *qs)
201 {
202 register u_int iobase = qs->iobase;
203 register u_char result;
204
205 write_control(iobase, 0x20);
206 write_data (iobase, 0x75);
207
208 result = read_data(iobase);
209
210 if ((result != 0x75) && !(qs->flags & QC_FORCEUNI))
211 qs->flags |= QC_BIDIR_HW; /* bidirectional parallel port */
212 else
213 qs->flags &= ~QC_BIDIR_HW;
214
215 write_control(iobase, 0x0b);
216 DELAY(250);
217 write_control(iobase, QC_CTL_LOWNIB);
218 DELAY(250);
219 }
220
221 static int
222 qcam_waitfor_bi (u_int port)
223 {
224 u_char s1, s2;
225
226 write_control(port, QC_CTL_HIGHWORD);
227 READ_STATUS_BYTE_HIGH(port, s1, QC_TIMEOUT_INIT);
228
229 write_control(port, QC_CTL_LOWWORD);
230 READ_STATUS_BYTE_LOW(port, s2, QC_TIMEOUT);
231
232 return (s1 & 0xf0) | (s2 >> 4);
233 }
234
235 /*
236 * The pixels are read in 16 bits at a time, and we get 3 valid pixels per
237 * 16-bit read. The encoding format looks like this:
238 *
239 * |---- status reg -----| |----- data reg ------|
240 * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
241 * 3 3 3 3 2 x x x 2 2 2 1 1 1 1 R
242 *
243 * 1 = left pixel R = camera ready
244 * 2 = middle pixel x = unknown/unused?
245 * 3 = right pixel
246 *
247 * XXX do not use this routine yet! It does not work.
248 * Nelson believes that even though 6 pixels are read in per 2 words,
249 * only the 1 & 2 pixels from the first word are correct. This seems
250 * bizzare, more study is needed here.
251 */
252
253 #define DECODE_WORD_BI4BPP(P, W) \
254 *(P)++ = 15 - (((W) >> 12) & 0x0f); \
255 *(P)++ = 15 - ((((W) >> 8) & 0x08) | (((W) >> 5) & 0x07)); \
256 *(P)++ = 15 - (((W) >> 1) & 0x0f);
257
258 static void
259 qcam_bi_4bit (struct qcam_softc *qs)
260 {
261 u_char *p;
262 u_int port;
263 u_short word;
264
265 port = qs->iobase; /* for speed */
266
267 qcam_waitfor_bi(port);
268
269 /*
270 * Unlike the other routines, this routine has NOT be interleaved
271 * yet because we don't have the algorythm for 4bbp down tight yet,
272 * so why add to the confusion?
273 */
274 for (p = qs->buffer; p < qs->buffer_end; ) {
275 write_control(port, QC_CTL_HIGHWORD);
276 READ_DATA_WORD_HIGH(port, word, QC_TIMEOUT);
277 DECODE_WORD_BI4BPP(p, word);
278
279 write_control(port, QC_CTL_LOWWORD);
280 READ_DATA_WORD_HIGH(port, word, QC_TIMEOUT);
281 DECODE_WORD_BI4BPP(p, word);
282 }
283 }
284
285 /*
286 * The pixels are read in 16 bits at a time, 12 of those bits contain
287 * pixel information, the format looks like this:
288 *
289 * |---- status reg -----| |----- data reg ------|
290 * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
291 * 2 2 2 2 2 x x x 2 1 1 1 1 1 1 R
292 *
293 * 1 = left pixel R = camera ready
294 * 2 = right pixel x = unknown/unused?
295 */
296
297 #define DECODE_WORD_BI6BPP(P, W) \
298 *(P)++ = 63 - (((W) >> 1) & 0x3f); \
299 *(P)++ = 63 - ((((W) >> 10) & 0x3e) | (((W) >> 7) & 0x01));
300
301 static void
302 qcam_bi_6bit (struct qcam_softc *qs)
303 {
304 u_char *p;
305 u_short hi, low;
306 u_int port;
307
308 port = qs->iobase; /* for speed */
309
310 qcam_waitfor_bi(port);
311
312 /*
313 * This was interleaved before, but I cut it back to the simple
314 * mode so that it's easier for people to play with it. A quick
315 * unrolling of the loop coupled with interleaved decoding and I/O
316 * should get us a slight CPU bonus later.
317 */
318 for (p = qs->buffer; p < qs->buffer_end; ) {
319 write_control(port, QC_CTL_HIGHWORD);
320 READ_DATA_WORD_HIGH(port, hi, QC_TIMEOUT);
321 DECODE_WORD_BI6BPP(p, hi);
322
323 write_control(port, QC_CTL_LOWWORD);
324 READ_DATA_WORD_LOW(port, low, QC_TIMEOUT);
325 DECODE_WORD_BI6BPP(p, low);
326 }
327 }
328
329 /*
330 * We're doing something tricky here that makes this routine a little
331 * more complex than you would expect. We're interleaving the high
332 * and low nibble reads with the math required for nibble munging.
333 * This should allow us to use the "free" time while we're waiting for
334 * the next nibble to come ready to do any data conversion operations.
335 */
336 #define DECODE_WORD_UNI4BPP(P, W) \
337 *(P)++ = 15 - ((W) >> 4);
338
339 static void
340 qcam_uni_4bit (struct qcam_softc *qs)
341 {
342 u_char *p, *end, hi, low;
343 u_int port;
344
345 port = qs->iobase;
346 p = qs->buffer;
347 end = qs->buffer_end - 1;
348
349 /* request and wait for first nibble */
350
351 write_control(port, QC_CTL_HIGHNIB);
352 READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT_INIT);
353
354 /* request second nibble, munge first nibble while waiting, read 2nd */
355
356 write_control(port, QC_CTL_LOWNIB);
357 DECODE_WORD_UNI4BPP(p, hi);
358 READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
359
360 while (p < end) {
361 write_control(port, QC_CTL_HIGHNIB);
362 DECODE_WORD_UNI4BPP(p, low);
363 READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
364
365 write_control(port, QC_CTL_LOWNIB);
366 DECODE_WORD_UNI4BPP(p, hi);
367 READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
368 }
369 DECODE_WORD_UNI4BPP(p, low);
370 }
371
372 /*
373 * If you treat each pair of nibble operations as pulling in a byte, you
374 * end up with a byte stream that looks like this:
375 *
376 * msb lsb
377 * 2 2 1 1 1 1 1 1
378 * 2 2 2 2 3 3 3 3
379 * 3 3 4 4 4 4 4 4
380 */
381
382 static void
383 qcam_uni_6bit (struct qcam_softc *qs)
384 {
385 u_char *p;
386 u_int port;
387 u_char word1, word2, word3, hi, low;
388
389 port = qs->iobase;
390
391 /*
392 * This routine has been partially interleaved... we can do a better
393 * job, but for right now, I've deliberately kept it less efficient
394 * so we can play with decoding without hurting peoples brains.
395 */
396 for (p = qs->buffer; p < qs->buffer_end; ) {
397 write_control(port, QC_CTL_HIGHNIB);
398 READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT_INIT);
399 write_control(port, QC_CTL_LOWNIB);
400 READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
401 write_control(port, QC_CTL_HIGHNIB);
402 word1 = (hi & 0xf0) | (low >>4);
403 READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
404 write_control(port, QC_CTL_LOWNIB);
405 *p++ = 63 - (word1 >> 2);
406 READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
407 write_control(port, QC_CTL_HIGHNIB);
408 word2 = (hi & 0xf0) | (low >> 4);
409 READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
410 write_control(port, QC_CTL_LOWNIB);
411 *p++ = 63 - (((word1 & 0x03) << 4) | (word2 >> 4));
412 READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
413 word3 = (hi & 0xf0) | (low >> 4);
414 *p++ = 63 - (((word2 & 0x0f) << 2) | (word3 >> 6));
415 *p++ = 63 - (word3 & 0x3f);
416 }
417
418 /* XXX this is something xfqcam does, doesn't make sense to me,
419 but we don't see timeoutes here... ? */
420 write_control(port, QC_CTL_LOWNIB);
421 READ_STATUS_BYTE_LOW(port, word1, QC_TIMEOUT);
422 write_control(port, QC_CTL_HIGHNIB);
423 READ_STATUS_BYTE_LOW(port, word1, QC_TIMEOUT);
424 }
425
426 static void
427 qcam_xferparms (struct qcam_softc *qs)
428 {
429 int bidir;
430
431 qs->xferparms = 0;
432
433 bidir = (qs->flags & QC_BIDIR_HW);
434 if (bidir)
435 qs->xferparms |= QC_XFER_BIDIR;
436
437 if (qcam_debug)
438 printf("qcam%d: %dbpp %sdirectional scan mode selected\n",
439 qs->unit, qs->bpp, bidir ? "bi" : "uni");
440
441 if (qs->bpp == 6) {
442 qs->xferparms |= QC_XFER_6BPP;
443 qs->scanner = bidir ? qcam_bi_6bit : qcam_uni_6bit;
444 } else {
445 qs->scanner = bidir ? qcam_bi_4bit : qcam_uni_4bit;
446 }
447
448 if (qs->x_size > 160 || qs->y_size > 120) {
449 qs->xferparms |= qcam_zoommode[0][qs->zoom];
450 } else if (qs->x_size > 80 || qs->y_size > 60) {
451 qs->xferparms |= qcam_zoommode[1][qs->zoom];
452 } else
453 qs->xferparms |= qcam_zoommode[2][qs->zoom];
454 }
455
456 static void
457 qcam_init (struct qcam_softc *qs)
458 {
459 int x_size = (qs->bpp == 4) ? qs->x_size / 2 : qs->x_size / 4;
460
461 qcam_xferparms(qs);
462
463 send_command(qs, QC_BRIGHTNESS, qs->brightness);
464 send_command(qs, QC_BRIGHTNESS, 1);
465 send_command(qs, QC_BRIGHTNESS, 1);
466 send_command(qs, QC_BRIGHTNESS, qs->brightness);
467 send_command(qs, QC_BRIGHTNESS, qs->brightness);
468 send_command(qs, QC_BRIGHTNESS, qs->brightness);
469 send_command(qs, QC_YSIZE, qs->y_size);
470 send_command(qs, QC_XSIZE, x_size);
471 send_command(qs, QC_YORG, qs->y_origin);
472 send_command(qs, QC_XORG, qs->x_origin);
473 send_command(qs, QC_CONTRAST, qs->contrast);
474 send_command(qs, QC_WHITEBALANCE, qs->whitebalance);
475
476 if (qs->buffer)
477 qs->buffer_end = qs->buffer +
478 min((qs->x_size*qs->y_size), QC_MAXFRAMEBUFSIZE);
479
480 qs->init_req = 0;
481 }
482
483 int
484 qcam_scan (struct qcam_softc *qs)
485 {
486 int timeouts;
487
488 #ifdef QCAM_GRAB_STATS
489 bzero(qcam_rsbhigh, sizeof(qcam_rsbhigh));
490 bzero(qcam_rsblow, sizeof(qcam_rsblow));
491 qcam_rsbhigh_p = qcam_rsbhigh;
492 qcam_rsblow_p = qcam_rsblow;
493 #endif
494
495 timeouts = qcam_timeouts;
496
497 if (qs->init_req)
498 qcam_init(qs);
499
500 if (send_xfermode(qs, qs->xferparms))
501 return 1;
502
503 if (qcam_debug && (timeouts != qcam_timeouts))
504 printf("qcam%d: %d timeouts during init\n", qs->unit,
505 qcam_timeouts - timeouts);
506
507 timeouts = qcam_timeouts;
508
509 if (qs->scanner)
510 (*qs->scanner)(qs);
511 else
512 return 1;
513
514 if (qcam_debug && (timeouts != qcam_timeouts))
515 printf("qcam%d: %d timeouts during scan\n", qs->unit,
516 qcam_timeouts - timeouts);
517
518 write_control(qs->iobase, 0x0f);
519
520 return 0; /* success */
521 }
522
523 void
524 qcam_default (struct qcam_softc *qs)
525 {
526 qs->contrast = QC_DEF_CONTRAST;
527 qs->brightness = QC_DEF_BRIGHTNESS;
528 qs->whitebalance = QC_DEF_WHITEBALANCE;
529 qs->x_size = QC_DEF_XSIZE;
530 qs->y_size = QC_DEF_YSIZE;
531 qs->x_origin = QC_DEF_XORG;
532 qs->y_origin = QC_DEF_YORG;
533 qs->bpp = QC_DEF_BPP;
534 qs->zoom = QC_DEF_ZOOM;
535 qs->exposure = QC_DEF_EXPOSURE;
536 }
537
538 int
539 qcam_ioctl_get (struct qcam_softc *qs, struct qcam *info)
540 {
541 info->qc_version = QC_IOCTL_VERSION;
542 info->qc_xsize = qs->x_size;
543 info->qc_ysize = qs->y_size;
544 info->qc_xorigin = qs->x_origin;
545 info->qc_yorigin = qs->y_origin;
546 info->qc_bpp = qs->bpp;
547 info->qc_zoom = qs->zoom;
548 info->qc_exposure = qs->exposure;
549 info->qc_brightness = qs->brightness;
550 info->qc_whitebalance = qs->whitebalance;
551 info->qc_contrast = qs->contrast;
552
553 return 0; /* success */
554 }
555
556 int
557 qcam_ioctl_set (struct qcam_softc *qs, struct qcam *info)
558 {
559 /*
560 * sanity check parameters passed in by user
561 * we're extra paranoid right now because the API
562 * is in flux
563 */
564 if (info->qc_xsize > QC_MAX_XSIZE ||
565 info->qc_ysize > QC_MAX_YSIZE ||
566 info->qc_xorigin > QC_MAX_XSIZE ||
567 info->qc_yorigin > QC_MAX_YSIZE ||
568 (info->qc_bpp != 4 && info->qc_bpp != 6) ||
569 info->qc_zoom > QC_ZOOM_200 ||
570 info->qc_brightness > UCHAR_MAX ||
571 info->qc_whitebalance > UCHAR_MAX ||
572 info->qc_contrast > UCHAR_MAX)
573 return 1; /* failure */
574
575 /* version check */
576 if (info->qc_version != QC_IOCTL_VERSION)
577 return 1; /* failure */
578
579 qs->x_size = info->qc_xsize;
580 qs->y_size = info->qc_ysize;
581 qs->x_origin = info->qc_xorigin;
582 qs->y_origin = info->qc_yorigin;
583 qs->bpp = info->qc_bpp;
584 qs->zoom = info->qc_zoom;
585 qs->exposure = info->qc_exposure;
586 qs->brightness = info->qc_brightness;
587 qs->whitebalance = info->qc_whitebalance;
588 qs->contrast = info->qc_contrast;
589
590 /* request initialization before next scan pass */
591 qs->init_req = 1;
592
593 return 0; /* success */
594 }
595
596 #ifndef QCAM_INVASIVE_SCAN
597 /*
598 * Attempt a non-destructive probe for the QuickCam.
599 * Current models appear to toggle the upper 4 bits of
600 * the status register at approximately 5-10 Hz.
601 *
602 * Be aware that this isn't the way that Connectix detects the
603 * camera (they send a reset and try to handshake), but this
604 * way is safe.
605 */
606 int
607 qcam_detect (u_int port)
608 {
609 int i, transitions = 0;
610 u_char reg, last;
611
612 write_control(port, 0x20);
613 write_control(port, 0x0b);
614 write_control(port, 0x0e);
615
616 last = reg = read_status(port);
617
618 for (i = 0; i < QC_PROBELIMIT; i++) {
619 reg = read_status(port) & 0xf0;
620
621 if (reg != last) /* if we got a toggle, count it */
622 transitions++;
623
624 last = reg;
625 LONGDELAY(100000); /* 100ms */
626 }
627
628 return transitions >= QC_PROBECNTLOW &&
629 transitions <= QC_PROBECNTHI;
630 }
631 #else
632 /*
633 * This form of probing for the camera can cause garbage to show
634 * up on your printers if they're plugged in instead. However,
635 * some folks have a problem with the nondestructive scan when
636 * using EPP/ECP parallel ports.
637 *
638 * Try to send down a brightness command, if we succeed, we've
639 * got a camera on the remote side.
640 */
641 int
642 qcam_detect (u_int port)
643 {
644 write_control(port, 0x20);
645 write_data(port, 0x75);
646 read_data(port);
647 write_control(port, 0x0b);
648 DELAY(250);
649 write_control(port, 0x0e);
650 DELAY(250);
651
652 if (sendbyte(port, QC_BRIGHTNESS, QC_DEF_EXPOSURE) != QC_BRIGHTNESS)
653 return 0; /* failure */
654 return (sendbyte(port, 1, QC_DEF_EXPOSURE) == 1);
655 }
656 #endif
657
658 #endif /* NQCAM */
Cache object: 13a7deeb0e839b49774acf6612622ea2
|