summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpst <pst@FreeBSD.org>1996-03-02 03:48:19 +0000
committerpst <pst@FreeBSD.org>1996-03-02 03:48:19 +0000
commit762729293978fb9b9aa6dc7d9dd4c493d3d5e1b2 (patch)
tree5f49d7e80dfec56f20372707ab823a774bf0a962
parentb7119381ddf8aa189b8d1e37080426b23971412d (diff)
downloadFreeBSD-src-762729293978fb9b9aa6dc7d9dd4c493d3d5e1b2.zip
FreeBSD-src-762729293978fb9b9aa6dc7d9dd4c493d3d5e1b2.tar.gz
Update the Connectix QuickCam driver to match my current work.
- split driver into FreeBSD specific and camera specific portions (qcamio.c can run in user mode, with a Linux "driver top" etc, and qcam.c should be trivial to port to NetBSD and BSDI.) - support for 4bppand bidirectional transfers working better - start of interleaved data-transfers byte-stream decodes (some of this stuff has been pulled out for the moment to make it easier to debug) At this point, anyone who wants to port it to other platforms should feel free to do so. Please feed changes directly back to me so that I can produce a unified distribution.
-rw-r--r--sys/conf/files.i3863
-rw-r--r--sys/i386/conf/files.i3863
-rw-r--r--sys/i386/isa/qcam.c475
-rw-r--r--sys/i386/isa/qcamdefs.h112
-rw-r--r--sys/i386/isa/qcamio.c521
-rw-r--r--sys/i386/isa/qcamreg.h8
6 files changed, 659 insertions, 463 deletions
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 5c9cc20..bef328a 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.127 1996/02/02 06:55:35 pst Exp $
+# $Id: files.i386,v 1.128 1996/02/26 00:58:38 gibbs Exp $
#
aic7xxx_asm optional ahc device-driver \
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
@@ -120,6 +120,7 @@ i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
i386/isa/prof_machdep.c optional profiling-routine
i386/isa/psm.c optional psm device-driver
i386/isa/qcam.c optional qcam device-driver
+i386/isa/qcamio.c optional qcam device-driver
i386/isa/random_machdep.c standard
i386/isa/rc.c optional rc device-driver
i386/isa/scd.c optional scd device-driver
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index 5c9cc20..bef328a 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.127 1996/02/02 06:55:35 pst Exp $
+# $Id: files.i386,v 1.128 1996/02/26 00:58:38 gibbs Exp $
#
aic7xxx_asm optional ahc device-driver \
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
@@ -120,6 +120,7 @@ i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
i386/isa/prof_machdep.c optional profiling-routine
i386/isa/psm.c optional psm device-driver
i386/isa/qcam.c optional qcam device-driver
+i386/isa/qcamio.c optional qcam device-driver
i386/isa/random_machdep.c standard
i386/isa/rc.c optional rc device-driver
i386/isa/scd.c optional scd device-driver
diff --git a/sys/i386/isa/qcam.c b/sys/i386/isa/qcam.c
index 5d50516..b65d727 100644
--- a/sys/i386/isa/qcam.c
+++ b/sys/i386/isa/qcam.c
@@ -56,56 +56,17 @@
#include <machine/qcam.h>
#include <i386/isa/qcamreg.h>
+#include <i386/isa/qcamdefs.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
int qcam_debug = 1;
-static struct qcam_softc {
- u_char *buffer; /* frame buffer */
- u_char *buffer_end; /* end of frame buffer */
- u_int flags;
- u_int conf_flags; /* config file flags */
- u_int iobase;
- int unit; /* device */
- void (*scanner)(struct qcam_softc *);
-
- int init_req; /* initialization required */
- int x_size; /* pixels */
- int y_size; /* pixels */
- int x_origin; /* ?? units */
- int y_origin; /* ?? units */
- int zoom; /* 0=none, 1=1.5x, 2=2x */
- int bpp; /* 4 or 6 */
- u_char xferparms; /* calcualted transfer params */
- u_char contrast;
- u_char brightness;
- u_char whitebalance;
-
- struct kern_devconf kdc; /* kernel config database */
-
-#ifdef DEVFS
- void *devfs_token;
-#endif
-} qcam_softc[NQCAM];
-
-/* flags in softc */
-#define QC_OPEN 0x01 /* device open */
-#define QC_ALIVE 0x02 /* probed and attached */
-#define QC_BIDIR_HW 0x04 /* bidir parallel port */
+static struct qcam_softc qcam_softc[NQCAM];
-/* flags from kernel configuration - these are temporary only! */
#define QC_CONF_NODETECT 0x01 /* always assume camera is present */
#define QC_CONF_FORCEUNI 0x02 /* force unidirectional transfers */
-#define QC_MAXFRAMEBUFSIZE (QC_MAX_XSIZE*QC_MAX_YSIZE)
-
-static const u_char qcam_zoommode[3][3] = {
- { QC_XFER_WIDE, QC_XFER_WIDE, QC_XFER_WIDE },
- { QC_XFER_NARROW, QC_XFER_WIDE, QC_XFER_WIDE },
- { QC_XFER_TIGHT, QC_XFER_NARROW, QC_XFER_WIDE }
-};
-
static struct kern_devconf kdc_qcam_template = {
0, 0, 0, /* filled in by dev_attach() */
"qcam", /* kdc_name */
@@ -147,301 +108,6 @@ static struct cdevsw qcam_cdevsw =
noselect, nommap, nostrategy, "qcam",
NULL, -1 };
-#define read_data(P) inb((P))
-#define read_data_word(P) inw((P))
-#define read_status(P) inb((P)+1)
-#define write_data(P, V) outb((P)+0, (V))
-#define write_status(P, V) outb((P)+1, (V))
-#define write_control(P, V) outb((P)+2, (V))
-
-#define QC_TIMEOUT 10000 /* timeout on reads */
-
-#define READ_STATUS_BYTE_HIGH(P, V) { \
- u_int timeout = QC_TIMEOUT; \
- do { (V) = read_status((P)); \
- } while (!(((V) & 0x08)) && timeout-- > 0); \
-}
-
-#define READ_STATUS_BYTE_LOW(P, V) { \
- u_int timeout = QC_TIMEOUT; \
- do { (V) = read_status((P)); \
- } while (((V) & 0x08) && timeout-- > 0); \
-}
-
-#define READ_DATA_WORD_HIGH(P, V) { \
- u_int timeout = QC_TIMEOUT; \
- do { (V) = read_data_word((P)); \
- } while (!(((V) & 0x01)) && timeout-- > 0); \
-}
-
-#define READ_DATA_WORD_LOW(P, V) { \
- u_int timeout = QC_TIMEOUT; \
- do { (V) = read_data_word((P)); \
- } while (((V) & 0x01) && timeout-- > 0); \
-}
-
-inline static int
-sendbyte (u_int port, int value)
-{
- u_char s1, s2;
-
- write_data(port, value);
- DELAY(100);
- write_data(port, value);
- DELAY(100);
- write_data(port, value);
-
- write_control(port, 0x06);
- READ_STATUS_BYTE_HIGH(port, s1);
-
- write_control(port, 0x0e);
- READ_STATUS_BYTE_LOW(port, s2);
-
- return (s1 & 0xf0) | (s2 >> 4);
-}
-
-static int
-send_command (u_int port, int cmd, int value)
-{
- if (sendbyte(port, cmd) != cmd)
- return 1;
-
- if (sendbyte(port, value) != value)
- return 1;
-
- return 0; /* success */
-}
-
-static void
-qcam_reset (struct qcam_softc *qs)
-{
- register u_int iobase = qs->iobase;
-
- write_control(iobase, 0x20);
- write_data (iobase, 0x75);
-
- if ((read_data(iobase) != 0x75) &&
- !(qs->conf_flags & QC_CONF_FORCEUNI))
- qs->flags |= QC_BIDIR_HW; /* bidirectional parallel port */
- else
- qs->flags &= ~QC_BIDIR_HW;
-
- write_control(iobase, 0x0b);
- DELAY(250);
- write_control(iobase, 0x0e);
- DELAY(250);
-}
-
-static int
-qcam_waitfor_bi (u_int port)
-{
- u_char s1, s2;
-
- write_control(port, 0x26);
- READ_STATUS_BYTE_HIGH(port, s1);
-
- write_control(port, 0x2f);
- READ_STATUS_BYTE_LOW(port, s2);
-
- return (s1 & 0xf0) | (s2 >> 4);
-}
-
-static void
-qcam_bi_4bit (struct qcam_softc *qs)
-{
- static int first_time = 1;
-
- if (first_time) {
- first_time = 0;
- printf("qcam%d: bidirectional 4bpp transfers not implemented\n",
- qs->unit);
- }
-}
-
-static void
-qcam_bi_6bit (struct qcam_softc *qs)
-{
- u_char *p;
- u_int word, port;
-
- port = qs->iobase; /* for speed */
-
- qcam_waitfor_bi(port); /* wait for ready */
-
- for (p = qs->buffer; p < qs->buffer_end; ) {
- write_control(port, 0x26);
- READ_DATA_WORD_HIGH(port, word);
-
- /* this can be optimized _significantly_ */
- word = (((word & 0xff00) >> 3) | (word & 0x00ff)) << 1;
- word = ((word & 0x00ff) >> 2) | (word & 0xff00);
- *p++ = 63 - (word & 0xff);
- *p++ = 63 - ((word >> 8) & 0xff);
-
- write_control(port, 0x2f);
- READ_DATA_WORD_LOW(port, word);
-
- /* this can be optimized _significantly_ */
- word = (((word & 0xff00) >> 3) | (word & 0x00ff)) << 1;
- word = ((word & 0x00ff) >> 2) | (word & 0xff00);
-
- *p++ = 63 - (word & 0xff);
- *p++ = 63 - ((word >> 8) & 0xff);
- }
-}
-
-static void
-qcam_uni_4bit (struct qcam_softc *qs)
-{
- u_char *p;
- u_int word, port;
-
- port = qs->iobase;
-
- for (p = qs->buffer; p < qs->buffer_end; ) {
- write_control(port, 0x06);
- READ_STATUS_BYTE_HIGH(port, word);
-
- *p++ = 16 - ((word & 0xf0) >> 4);
-
- write_control(port, 0x0e);
- READ_STATUS_BYTE_LOW(port, word);
-
- *p++ = 16 - ((word & 0xf0) >> 4);
- }
-}
-
-static void
-qcam_uni_6bit (struct qcam_softc *qs)
-{
- u_char *p;
- u_int word1, word2, word3, port;
-
- port = qs->iobase;
-
- for (p = qs->buffer; p < qs->buffer_end; ) {
- write_control(port, 0x06);
- READ_STATUS_BYTE_HIGH(port, word1);
-
- word2 = word1 & 0xf0;
-
- write_control(port, 0x0e);
- READ_STATUS_BYTE_LOW(port, word1);
-
- word2 |= (word1 & 0xf0) >> 4;
- *p++ = 63 - ((word2 >> 2) & 0x3f);
-
- write_control(port, 0x06);
- READ_STATUS_BYTE_HIGH(port, word1);
-
- word3 = word2;
- word2 = word1 & 0xf0;
-
- write_control(port, 0x0e);
- READ_STATUS_BYTE_LOW(port, word1);
-
- word2 |= (word1 & 0xf0) >> 4;
- *p++ = 63 - (((word3 & 0x03) << 4) | (word2 >> 4));
-
- write_control(port, 0x06);
- READ_STATUS_BYTE_HIGH(port, word1);
-
- word3 = word2;
- word2 = word1 & 0xf0;
-
- write_control(port, 0x0e);
- READ_STATUS_BYTE_LOW(port, word1);
-
- word2 |= (word1 & 0xf0) >> 4;
- *p++ = 63 - (((word3 & 0x0f) << 2) | (word2 >> 6));
- *p++ = 63 - (word2 & 0x3f);
- }
-}
-
-static void
-qcam_xferparms (struct qcam_softc *qs)
-{
- int bidir;
-
- qs->xferparms = 0;
-
- /*
- * XXX the && qs->bpp==6 is a temporary hack because we don't
- * have code for doing 4bpp bidirectional transfers yet.
- */
- bidir = (qs->flags & QC_BIDIR_HW) && (qs->bpp == 6);
-
- if (bidir)
- qs->xferparms |= QC_XFER_BIDIR;
-
- if (qs->bpp == 6) {
- qs->xferparms |= QC_XFER_6BPP;
- qs->scanner = bidir ? qcam_bi_6bit : qcam_uni_6bit;
- } else {
- qs->scanner = bidir ? qcam_bi_4bit : qcam_uni_4bit;
- }
-
- if (qs->x_size > 160 || qs->y_size > 120) {
- qs->xferparms |= qcam_zoommode[0][qs->zoom];
- } else if (qs->x_size > 80 || qs->y_size > 60) {
- qs->xferparms |= qcam_zoommode[1][qs->zoom];
- } else
- qs->xferparms |= qcam_zoommode[2][qs->zoom];
-
-#ifdef DEBUG
- if (qcam_debug)
- printf("qcam%d: [(%d,%d), %sdir, %dbpp, %d zoom] = 0x%x\n",
- qs->unit, qs->x_size, qs->y_size,
- bidir ? "bi" : "uni", qs->bpp, qs->zoom,
- qs->xferparms);
-#endif
-}
-
-static void
-qcam_init (struct qcam_softc *qs)
-{
- int x_size = (qs->bpp == 4) ? qs->x_size / 2 : qs->x_size / 4;
-
- qcam_xferparms(qs);
-
- send_command(qs->iobase, QC_BRIGHTNESS, qs->brightness);
- send_command(qs->iobase, QC_BRIGHTNESS, 1);
- send_command(qs->iobase, QC_BRIGHTNESS, 1);
- send_command(qs->iobase, QC_BRIGHTNESS, qs->brightness);
- send_command(qs->iobase, QC_BRIGHTNESS, qs->brightness);
- send_command(qs->iobase, QC_BRIGHTNESS, qs->brightness);
- send_command(qs->iobase, QC_XSIZE, x_size);
- send_command(qs->iobase, QC_YSIZE, qs->y_size);
- send_command(qs->iobase, QC_YORG, qs->y_origin);
- send_command(qs->iobase, QC_XORG, qs->x_origin);
- send_command(qs->iobase, QC_CONTRAST, qs->contrast);
- send_command(qs->iobase, QC_WHITEBALANCE, qs->whitebalance);
-
- if (qs->buffer)
- qs->buffer_end = qs->buffer +
- min((qs->x_size*qs->y_size), QC_MAXFRAMEBUFSIZE);
-
- qs->init_req = 0;
-}
-
-static void
-qcam_scan (struct qcam_softc *qs)
-{
- register u_int iobase = qs->iobase;
-
- if (qs->init_req)
- qcam_init(qs);
-
- send_command(iobase, QC_BRIGHTNESS, qs->brightness);
- send_command(iobase, QC_XFERMODE, qs->xferparms);
-
- if (qs->scanner)
- (*qs->scanner)(qs);
-
- write_control(iobase, 0x0f);
- write_control(iobase, 0x0b);
-}
-
static void
qcam_registerdev (struct isa_device *id)
{
@@ -473,65 +139,20 @@ qcam_probe (struct isa_device *devp)
}
/*
- * XXX The probe code is reported to be flakey on parallel port
- * cards set up for bidirectional transfers.
+ * XXX The probe code is reported to be flakey.
* We need to work on this some more, so temporarily,
* allow bit one of the "flags" parameter to bypass this
* check.
*/
- if (!(devp->id_flags & QC_CONF_NODETECT)) {
- write_control(devp->id_iobase, 0x20);
- write_control(devp->id_iobase, 0x0b);
- write_control(devp->id_iobase, 0x0e);
-
- /*
- * Attempt a non-destructive probe for the QuickCam.
- * Current models appear to toggle the upper 4 bits of
- * the status register at approximately 5-10 Hz.
- *
- * Be aware that this isn't the way that Connectix detects the
- * camera (they send a reset and try to handshake), but this
- * way is safe.
- */
- last = reg = read_status(devp->id_iobase);
-
- for (i = 0; i < QC_PROBELIMIT; i++) {
-
- reg = read_status(devp->id_iobase) & 0xf0;
-
- if (reg != last) /* if we got a toggle, count it */
- transitions++;
-
- last = reg;
- DELAY(100000); /* 100ms */
- }
-
- if (transitions <= QC_PROBECNTLOW || transitions >= QC_PROBECNTHI) {
- if (bootverbose)
- printf("qcam%d: not found, probed %d, got %d transitions\n",
- devp->id_unit, i, transitions);
- return 0;
- }
- }
+ if (!(devp->id_flags & QC_CONF_NODETECT))
+ if (!qcam_detect(devp->id_iobase))
+ return 0; /* failure */
qcam_registerdev(devp);
return 1; /* found */
}
-static void
-qcam_default (struct qcam_softc *qs) {
- qs->contrast = QC_DEF_CONTRAST;
- qs->brightness = QC_DEF_BRIGHTNESS;
- qs->whitebalance = QC_DEF_WHITEBALANCE;
- qs->x_size = QC_DEF_XSIZE;
- qs->y_size = QC_DEF_YSIZE;
- qs->x_origin = QC_DEF_XORG;
- qs->y_origin = QC_DEF_YORG;
- qs->bpp = QC_DEF_BPP;
- qs->zoom = QC_DEF_ZOOM;
-}
-
static int
qcam_attach (struct isa_device *devp)
{
@@ -539,13 +160,14 @@ qcam_attach (struct isa_device *devp)
qs->iobase = devp->id_iobase;
qs->unit = devp->id_unit;
- qs->conf_flags = devp->id_flags;
qs->kdc.kdc_state = DC_IDLE;
qs->flags |= QC_ALIVE;
+ /* force unidirectional parallel port mode? */
+ if (devp->id_flags & QC_CONF_FORCEUNI)
+ qs->flags |= QC_FORCEUNI;
+
qcam_reset(qs);
- qcam_default(qs);
- qcam_xferparms(qs);
printf("qcam%d: %sdirectional parallel port\n",
qs->unit, qs->flags & QC_BIDIR_HW ? "bi" : "uni");
@@ -573,13 +195,13 @@ qcam_open (dev_t dev, int flags, int fmt, struct proc *p)
return EBUSY;
qs->buffer_end = qs->buffer = malloc(QC_MAXFRAMEBUFSIZE, M_DEVBUF,
- M_WAITOK);
+ M_NOWAIT);
if (!qs->buffer)
return ENOMEM;
qcam_reset(qs);
qcam_default(qs);
- qcam_init(qs);
+ qs->init_req = 1; /* request initialization before scan */
qs->flags |= QC_OPEN;
qs->kdc.kdc_state = DC_BUSY;
@@ -612,7 +234,8 @@ qcam_read (dev_t dev, struct uio *uio, int ioflag)
/* if we've seeked back to 0, that's our signal to scan */
if (uio->uio_offset == 0)
- qcam_scan(qs);
+ if (qcam_scan(qs))
+ return EIO;
bufsize = qs->buffer_end - qs->buffer;
if (uio->uio_offset > bufsize)
@@ -693,76 +316,6 @@ qcam_ioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
}
/*
- *-------------------------------------------------------------------
- */
-
-#ifdef ACTUALLY_LKM_NOT_KERNEL
-/*
- * Loadable QuickCam driver stubs
- * This isn't quite working yet, but the template work is done.
- *
- * XXX Do not attempt to use this driver as a LKM (yet).
- */
-#include <sys/exec.h>
-#include <sys/sysent.h>
-#include <sys/lkm.h>
-
-/*
- * Construct lkm_dev structures (see lkm.h)
- */
-
-MOD_DEV(qcam, LM_DT_CHAR, CDEV_MAJOR, &qcam_cdevsw);
-
-static int
-qcam_load (struct lkm_table *lkmtp, int cmd)
-{
- /* we need to call attach here with sane parameters */
- return 0; /* nothing need be done */
-}
-
-static int
-qcam_unload (struct lkm_table *lkmtp, int cmd)
-{
- int i;
-
- for (i = 0; i < NQCAM; i++)
- if (qcam_softc[i].flags & QC_OPEN)
- return EBUSY;
-
- return 0;
-}
-
-int
-qcam_mod (struct lkm_table *lkmtp, int cmd, int ver)
-{
- int err = 0;
-
- if (ver != LKM_VERSION)
- return EINVAL;
-
- switch (cmd) {
- case LKM_E_LOAD:
- err = qcam_load(lkmtp, cmd);
- break;
- case LKM_E_UNLOAD:
- err = qcam_unload(lkmtp, cmd);
- break;
- }
-
- if (err)
- return err;
-
- /* register the cdevsw entry */
- lkmtp->private.lkm_dev = &qcam_module;
- return lkmdispatch(lkmtp, cmd);
-}
-#endif /* LKM */
-
-/*
- *-------------------------------------------------------------------
- */
-
-/*
* Initialize the dynamic cdevsw hooks.
*/
static void
diff --git a/sys/i386/isa/qcamdefs.h b/sys/i386/isa/qcamdefs.h
new file mode 100644
index 0000000..73f3e9c
--- /dev/null
+++ b/sys/i386/isa/qcamdefs.h
@@ -0,0 +1,112 @@
+/*
+ * FreeBSD Connectix QuickCam parallel-port camera video capture driver.
+ * Copyright (c) 1996, Paul Traina.
+ *
+ * This driver is based in part on the Linux QuickCam driver which is
+ * Copyright (c) 1996, Thomas Davis.
+ *
+ * QuickCam(TM) is a registered trademark of Connectix Inc.
+ * Use this driver at your own risk, it is not warranted by
+ * Connectix or the authors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The information in this file is private and shared between various
+ * parts of the QuickCam(TM) driver.
+ */
+
+#ifndef _QCAM_DEFS_H
+#define _QCAM_DEFS_H 1
+
+extern int qcam_debug;
+
+struct qcam_softc {
+ u_char *buffer; /* frame buffer */
+ u_char *buffer_end; /* end of frame buffer */
+ u_int flags;
+ u_int iobase;
+ int unit; /* device */
+ void (*scanner)(struct qcam_softc *);
+
+ int init_req; /* initialization required */
+ int x_size; /* pixels */
+ int y_size; /* pixels */
+ int x_origin; /* ?? units */
+ int y_origin; /* ?? units */
+ int zoom; /* 0=none, 1=1.5x, 2=2x */
+ int bpp; /* 4 or 6 */
+ u_char xferparms; /* calcualted transfer params */
+ u_char contrast;
+ u_char brightness;
+ u_char whitebalance;
+
+#ifdef KERNEL
+#ifdef __FreeBSD__
+ struct kern_devconf kdc; /* kernel config database */
+#ifdef DEVFS
+ void *devfs_token;
+#endif /* DEVFS */
+#endif /* __FreeBSD__ */
+#endif /* KERNEL */
+};
+
+/* flags in softc */
+#define QC_OPEN 0x01 /* device open */
+#define QC_ALIVE 0x02 /* probed and attached */
+#define QC_BIDIR_HW 0x04 /* bidir parallel port */
+#define QC_FORCEUNI 0x08 /* ...but force unidir mode */
+
+#define QC_MAXFRAMEBUFSIZE (QC_MAX_XSIZE*QC_MAX_YSIZE)
+
+#ifdef LINUX /* Linux is backwards from *BSD */
+
+#define read_data(P) inb((P))
+#define read_data_word(P) inw((P))
+#define read_status(P) inb((P)+1)
+#define write_data(P, V) outb((V), (P)+0)
+#define write_status(P, V) outb((V), (P)+1)
+#define write_control(P, V) outb((V), (P)+2)
+
+#else /* FreeBSD/NetBSD/BSDI */
+
+#define read_data(P) inb((P))
+#define read_data_word(P) inw((P))
+#define read_status(P) inb((P)+1)
+#define write_data(P, V) outb((P)+0, (V))
+#define write_status(P, V) outb((P)+1, (V))
+#define write_control(P, V) outb((P)+2, (V))
+
+#endif
+
+#define QC_TIMEOUT_INIT 60000 /* timeout for first
+ read of scan */
+#define QC_TIMEOUT_CMD 5000 /* timeout for control cmds */
+#define QC_TIMEOUT 400 /* timeout on scan reads */
+
+extern int qcam_detect __P((u_int port));
+extern void qcam_reset __P((struct qcam_softc *qs));
+extern int qcam_scan __P((struct qcam_softc *qs));
+extern void qcam_default __P((struct qcam_softc *qs));
+
+#endif /* _QCAM_DEFS_H */
diff --git a/sys/i386/isa/qcamio.c b/sys/i386/isa/qcamio.c
new file mode 100644
index 0000000..d866687
--- /dev/null
+++ b/sys/i386/isa/qcamio.c
@@ -0,0 +1,521 @@
+/*
+ * FreeBSD Connectix QuickCam parallel-port camera video capture driver.
+ * Copyright (c) 1996, Paul Traina.
+ *
+ * This driver is based in part on the Linux QuickCam driver which is
+ * Copyright (c) 1996, Thomas Davis.
+ *
+ * Additional ideas from code written by Michael Chinn and Nelson Minar.
+ *
+ * QuickCam(TM) is a registered trademark of Connectix Inc.
+ * Use this driver at your own risk, it is not warranted by
+ * Connectix or the authors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qcam.h"
+#if NQCAM > 0
+
+#include <sys/param.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/clock.h>
+#ifdef KERNEL
+#include <sys/systm.h>
+#include <sys/devconf.h>
+#endif /* KERNEL */
+
+#include <machine/qcam.h>
+#include "qcamreg.h"
+#include "qcamdefs.h"
+
+/*
+ * There should be _NO_ operating system dependant code or definitions
+ * past this point.
+ */
+
+static const u_char qcam_zoommode[3][3] = {
+ { QC_XFER_WIDE, QC_XFER_WIDE, QC_XFER_WIDE },
+ { QC_XFER_NARROW, QC_XFER_WIDE, QC_XFER_WIDE },
+ { QC_XFER_TIGHT, QC_XFER_NARROW, QC_XFER_WIDE }
+};
+
+static int qcam_timeouts;
+
+#ifdef QCAM_GRAB_STATS
+
+#define STATBUFSIZE (QC_MAXFRAMEBUFSIZE*2+50)
+static u_short qcam_rsbhigh[STATBUFSIZE];
+static u_short qcam_rsblow[STATBUFSIZE];
+static u_short *qcam_rsbhigh_p = qcam_rsbhigh;
+static u_short *qcam_rsblow_p = qcam_rsblow;
+static u_short *qcam_rsbhigh_end = &qcam_rsbhigh[STATBUFSIZE];
+static u_short *qcam_rsblow_end = &qcam_rsblow[STATBUFSIZE];
+
+#define STATHIGH(T) \
+ if (qcam_rsbhigh_p < qcam_rsbhigh_end) \
+ *qcam_rsbhigh_p++ = ((T) - timeout); \
+ if (!timeout) qcam_timeouts++;
+
+#define STATLOW(T) \
+ if (qcam_rsblow_p < qcam_rsblow_end) \
+ *qcam_rsblow_p++ = ((T) - timeout); \
+ if (!timeout) qcam_timeouts++;
+
+#else
+
+#define STATHIGH(T) if (!timeout) qcam_timeouts++;
+#define STATLOW(T) if (!timeout) qcam_timeouts++;
+
+#endif /* QCAM_GRAB_STATS */
+
+#define READ_STATUS_BYTE_HIGH(P, V, T) { \
+ u_short timeout = (T); \
+ do { (V) = read_status((P)); \
+ } while (!(((V) & 0x08)) && --timeout); STATHIGH(T) \
+}
+
+#define READ_STATUS_BYTE_LOW(P, V, T) { \
+ u_short timeout = (T); \
+ do { (V) = read_status((P)); \
+ } while (((V) & 0x08) && --timeout); STATLOW(T) \
+}
+
+#define READ_DATA_WORD_HIGH(P, V, T) { \
+ u_int timeout = (T); \
+ do { (V) = read_data_word((P)); \
+ } while (!((V) & 0x01) && --timeout); STATHIGH(T) \
+}
+
+#define READ_DATA_WORD_LOW(P, V, T) { \
+ u_int timeout = (T); \
+ do { (V) = read_data_word((P)); \
+ } while (((V) & 0x01) && --timeout); STATLOW(T) \
+}
+
+static int
+sendbyte (u_int port, int value)
+{
+ u_char s1, s2;
+
+ write_data(port, value);
+ DELAY(100);
+ write_data(port, value);
+ DELAY(100);
+ write_data(port, value);
+
+ write_control(port, QC_CTL_HIGHNIB);
+ READ_STATUS_BYTE_HIGH(port, s1, QC_TIMEOUT_CMD);
+
+ write_control(port, QC_CTL_LOWNIB);
+ READ_STATUS_BYTE_LOW(port, s2, QC_TIMEOUT_CMD);
+
+ return (s1 & 0xf0) | (s2 >> 4);
+}
+
+static int
+send_command (struct qcam_softc *qs, int cmd, int value)
+{
+ if (sendbyte(qs->iobase, cmd) != cmd)
+ return 1;
+
+ if (sendbyte(qs->iobase, value) != value)
+ return 1;
+
+ return 0; /* success */
+}
+
+void
+qcam_reset (struct qcam_softc *qs)
+{
+ register u_int iobase = qs->iobase;
+ register u_char result;
+
+ write_control(iobase, 0x20);
+ write_data (iobase, 0x75);
+
+ result = read_data(iobase);
+
+ if ((result != 0x75) && !(qs->flags & QC_FORCEUNI))
+ qs->flags |= QC_BIDIR_HW; /* bidirectional parallel port */
+ else
+ qs->flags &= ~QC_BIDIR_HW;
+
+ write_control(iobase, 0x0b);
+ DELAY(250);
+ write_control(iobase, QC_CTL_LOWNIB);
+ DELAY(250);
+}
+
+static int
+qcam_waitfor_bi (u_int port)
+{
+ u_char s1, s2;
+
+ write_control(port, QC_CTL_HIGHWORD);
+ READ_STATUS_BYTE_HIGH(port, s1, QC_TIMEOUT_INIT);
+
+ write_control(port, QC_CTL_LOWWORD);
+ READ_STATUS_BYTE_LOW(port, s2, QC_TIMEOUT);
+
+ return (s1 & 0xf0) | (s2 >> 4);
+}
+
+/*
+ * The pixels are read in 16 bits at a time, and we get 3 valid pixels per
+ * 16-bit read. The encoding format looks like this:
+ *
+ * |---- status reg -----| |----- data reg ------|
+ * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
+ * 3 3 3 3 2 x x x 2 2 2 1 1 1 1 R
+ *
+ * 1 = left pixel R = camera ready
+ * 2 = middle pixel x = unknown/unused?
+ * 3 = right pixel
+ *
+ * XXX do not use this routine yet! It does not work.
+ * Nelson believes that even though 6 pixels are read in per 2 words,
+ * only the 1 & 2 pixels from the first word are correct. This seems
+ * bizzare, more study is needed here.
+ */
+
+#define DECODE_WORD_BI4BPP(P, W) \
+ *(P)++ = 16 - (((W) >> 12) & 0x0f); \
+ *(P)++ = 16 - ((((W) >> 8) & 0x08) | (((W) >> 5) & 0x07)); \
+ *(P)++ = 16 - (((W) >> 1) & 0x0f);
+
+static void
+qcam_bi_4bit (struct qcam_softc *qs)
+{
+ u_char *p;
+ u_int port;
+ u_short word;
+
+ port = qs->iobase; /* for speed */
+
+ qcam_waitfor_bi(port);
+
+ /*
+ * Unlike the other routines, this routine has NOT be interleaved
+ * yet because we don't have the algorythm for 4bbp down tight yet,
+ * so why add to the confusion?
+ */
+ for (p = qs->buffer; p < qs->buffer_end; ) {
+ write_control(port, QC_CTL_HIGHWORD);
+ READ_DATA_WORD_HIGH(port, word, QC_TIMEOUT);
+ DECODE_WORD_BI4BPP(p, word);
+
+ write_control(port, QC_CTL_LOWWORD);
+ READ_DATA_WORD_HIGH(port, word, QC_TIMEOUT);
+ DECODE_WORD_BI4BPP(p, word);
+ }
+}
+
+/*
+ * The pixels are read in 16 bits at a time, 12 of those bits contain
+ * pixel information, the format looks like this:
+ *
+ * |---- status reg -----| |----- data reg ------|
+ * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
+ * 2 2 2 2 2 x x x 2 1 1 1 1 1 1 R
+ *
+ * 1 = left pixel R = camera ready
+ * 2 = right pixel x = unknown/unused?
+ */
+
+#define DECODE_WORD_BI6BPP(P, W) \
+ *(P)++ = 63 - (((W) >> 1) & 0x3f); \
+ *(P)++ = 63 - ((((W) >> 10) & 0x3e) | (((W) >> 7) & 0x01));
+
+static void
+qcam_bi_6bit (struct qcam_softc *qs)
+{
+ u_char *p, *end;
+ u_short hi, low, dummy;
+ u_int port;
+
+ port = qs->iobase; /* for speed */
+
+ qcam_waitfor_bi(port);
+
+ for (p = qs->buffer; p < qs->buffer_end; ) {
+ write_control(port, QC_CTL_HIGHWORD);
+ READ_DATA_WORD_HIGH(port, hi, QC_TIMEOUT);
+ DECODE_WORD_BI6BPP(p, hi);
+
+ write_control(port, QC_CTL_LOWWORD);
+ READ_DATA_WORD_LOW(port, low, QC_TIMEOUT);
+ DECODE_WORD_BI6BPP(p, low);
+ }
+
+#ifdef notdef
+ /* XXX xfqcam does this, seems stupid, the read times out */
+ write_control(port, QC_CTL_HIGHWORD);
+ READ_DATA_WORD_HIGH(port, dummy, QC_TIMEOUT);
+#endif
+}
+
+/*
+ * We're doing something tricky here that makes this routine a little
+ * more complex than you would expect. We're interleaving the high
+ * and low nibble reads with the math required for nibble munging.
+ * This should allow us to use the "free" time while we're waiting for
+ * the next nibble to come ready to do any data conversion operations.
+ */
+#define DECODE_WORD_UNI4BPP(P, W) \
+ *(P)++ = 16 - ((W) >> 4);
+
+static void
+qcam_uni_4bit (struct qcam_softc *qs)
+{
+ u_char *p, *end, hi, low;
+ u_int port;
+
+ port = qs->iobase;
+ p = qs->buffer;
+ end = qs->buffer_end - 1;
+
+ /* request and wait for first nibble */
+
+ write_control(port, QC_CTL_HIGHNIB);
+ READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT_INIT);
+
+ /* request second nibble, munge first nibble while waiting, read 2nd */
+
+ write_control(port, QC_CTL_LOWNIB);
+ DECODE_WORD_UNI4BPP(p, hi);
+ READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
+
+ while (p < end) {
+ write_control(port, QC_CTL_HIGHNIB);
+ DECODE_WORD_UNI4BPP(p, low);
+ READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
+
+ write_control(port, QC_CTL_LOWNIB);
+ DECODE_WORD_UNI4BPP(p, hi);
+ READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
+ }
+ DECODE_WORD_UNI4BPP(p, low);
+}
+
+/*
+ * If you treat each pair of nibble operations as pulling in a byte, you
+ * end up with a byte stream that looks like this:
+ *
+ * msb lsb
+ * 2 2 1 1 1 1 1 1
+ * 2 2 2 2 3 3 3 3
+ * 3 3 4 4 4 4 4 4
+ */
+
+static void
+qcam_uni_6bit (struct qcam_softc *qs)
+{
+ u_char *p;
+ u_int port;
+ u_char word1, word2, word3, hi, low;
+
+ port = qs->iobase;
+
+ /*
+ * This routine has been partially interleaved... we can do a better
+ * job, but for right now, keep it simple.
+ */
+ for (p = qs->buffer; p < qs->buffer_end; ) {
+ write_control(port, QC_CTL_HIGHNIB);
+ READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT_INIT);
+ write_control(port, QC_CTL_LOWNIB);
+ READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
+ write_control(port, QC_CTL_HIGHNIB);
+ word1 = (hi & 0xf0) | (low >>4);
+ READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
+ write_control(port, QC_CTL_LOWNIB);
+ *p++ = 63 - (word1 >> 2);
+ READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
+ write_control(port, QC_CTL_HIGHNIB);
+ word2 = (hi & 0xf0) | (low >> 4);
+ READ_STATUS_BYTE_HIGH(port, hi, QC_TIMEOUT);
+ write_control(port, QC_CTL_LOWNIB);
+ *p++ = 63 - (((word1 & 0x03) << 4) | (word2 >> 4));
+ READ_STATUS_BYTE_LOW(port, low, QC_TIMEOUT);
+ word3 = (hi & 0xf0) | (low >> 4);
+ *p++ = 63 - (((word2 & 0x0f) << 2) | (word3 >> 6));
+ *p++ = 63 - (word3 & 0x3f);
+ }
+
+ /* XXX this is something xfqcam does, doesn't make sense to me,
+ but we don't see timeoutes here... ? */
+ write_control(port, QC_CTL_LOWNIB);
+ READ_STATUS_BYTE_LOW(port, word1, QC_TIMEOUT);
+ write_control(port, QC_CTL_HIGHNIB);
+ READ_STATUS_BYTE_LOW(port, word1, QC_TIMEOUT);
+}
+
+static void
+qcam_xferparms (struct qcam_softc *qs)
+{
+ int bidir;
+
+ qs->xferparms = 0;
+
+ bidir = (qs->flags & QC_BIDIR_HW);
+ if (bidir)
+ qs->xferparms |= QC_XFER_BIDIR;
+
+ if (qcam_debug)
+ printf("qcam%d: %dbpp %sdirectional scan mode selected\n",
+ qs->unit, qs->bpp, bidir ? "bi" : "uni");
+
+ if (qs->bpp == 6) {
+ qs->xferparms |= QC_XFER_6BPP;
+ qs->scanner = bidir ? qcam_bi_6bit : qcam_uni_6bit;
+ } else {
+ qs->scanner = bidir ? qcam_bi_4bit : qcam_uni_4bit;
+ }
+
+ if (qs->x_size > 160 || qs->y_size > 120) {
+ qs->xferparms |= qcam_zoommode[0][qs->zoom];
+ } else if (qs->x_size > 80 || qs->y_size > 60) {
+ qs->xferparms |= qcam_zoommode[1][qs->zoom];
+ } else
+ qs->xferparms |= qcam_zoommode[2][qs->zoom];
+}
+
+static void
+qcam_init (struct qcam_softc *qs)
+{
+ int x_size = (qs->bpp == 4) ? qs->x_size / 2 : qs->x_size / 4;
+
+ qcam_xferparms(qs);
+
+ send_command(qs, QC_BRIGHTNESS, qs->brightness);
+ send_command(qs, QC_BRIGHTNESS, 1);
+ send_command(qs, QC_BRIGHTNESS, 1);
+ send_command(qs, QC_BRIGHTNESS, qs->brightness);
+ send_command(qs, QC_BRIGHTNESS, qs->brightness);
+ send_command(qs, QC_BRIGHTNESS, qs->brightness);
+ send_command(qs, QC_YSIZE, qs->y_size);
+ send_command(qs, QC_XSIZE, x_size);
+ send_command(qs, QC_YORG, qs->y_origin);
+ send_command(qs, QC_XORG, qs->x_origin);
+ send_command(qs, QC_CONTRAST, qs->contrast);
+ send_command(qs, QC_WHITEBALANCE, qs->whitebalance);
+
+ if (qs->buffer)
+ qs->buffer_end = qs->buffer +
+ min((qs->x_size*qs->y_size), QC_MAXFRAMEBUFSIZE);
+
+ qs->init_req = 0;
+}
+
+int
+qcam_scan (struct qcam_softc *qs)
+{
+ int timeouts;
+
+#ifdef QCAM_GRAB_STATS
+ bzero(qcam_rsbhigh, sizeof(qcam_rsbhigh));
+ bzero(qcam_rsblow, sizeof(qcam_rsblow));
+ qcam_rsbhigh_p = qcam_rsbhigh;
+ qcam_rsblow_p = qcam_rsblow;
+#endif
+
+ timeouts = qcam_timeouts;
+
+ if (qs->init_req)
+ qcam_init(qs);
+
+ if (send_command(qs, QC_XFERMODE, qs->xferparms))
+ return 1;
+
+ if (qcam_debug && (timeouts != qcam_timeouts))
+ printf("qcam%d: %d timeouts during init\n", qs->unit,
+ qcam_timeouts - timeouts);
+
+ timeouts = qcam_timeouts;
+
+ if (qs->scanner)
+ (*qs->scanner)(qs);
+ else
+ return 1;
+
+ if (qcam_debug && (timeouts != qcam_timeouts))
+ printf("qcam%d: %d timeouts during scan\n", qs->unit,
+ qcam_timeouts - timeouts);
+
+ write_control(qs->iobase, 0x0f);
+
+ return 0; /* success */
+}
+
+void
+qcam_default (struct qcam_softc *qs) {
+ qs->contrast = QC_DEF_CONTRAST;
+ qs->brightness = QC_DEF_BRIGHTNESS;
+ qs->whitebalance = QC_DEF_WHITEBALANCE;
+ qs->x_size = QC_DEF_XSIZE;
+ qs->y_size = QC_DEF_YSIZE;
+ qs->x_origin = QC_DEF_XORG;
+ qs->y_origin = QC_DEF_YORG;
+ qs->bpp = QC_DEF_BPP;
+ qs->zoom = QC_DEF_ZOOM;
+}
+
+int
+qcam_detect (u_int port) {
+ int i, transitions;
+ u_char reg, last;
+
+ write_control(port, 0x20);
+ write_control(port, 0x0b);
+ write_control(port, 0x0e);
+
+ /*
+ * Attempt a non-destructive probe for the QuickCam.
+ * Current models appear to toggle the upper 4 bits of
+ * the status register at approximately 5-10 Hz.
+ *
+ * Be aware that this isn't the way that Connectix detects the
+ * camera (they send a reset and try to handshake), but this
+ * way is safe.
+ */
+ transitions = 0;
+
+ last = reg = read_status(port);
+
+ for (i = 0; i < QC_PROBELIMIT; i++) {
+ reg = read_status(port) & 0xf0;
+
+ if (reg != last) /* if we got a toggle, count it */
+ transitions++;
+
+ last = reg;
+ DELAY(100000); /* 100ms */
+ }
+
+ return transitions >= QC_PROBECNTLOW && transitions <= QC_PROBECNTHI;
+}
+
+#endif /* NQCAM */
diff --git a/sys/i386/isa/qcamreg.h b/sys/i386/isa/qcamreg.h
index 400f81c..2531a42 100644
--- a/sys/i386/isa/qcamreg.h
+++ b/sys/i386/isa/qcamreg.h
@@ -80,4 +80,12 @@
#define QC_DEF_WHITEBALANCE 150
#define QC_DEF_ZOOM QC_ZOOM_100
+/*
+ * QuickCam parallel port handshake constants
+ */
+#define QC_CTL_HIGHNIB 0x06
+#define QC_CTL_LOWNIB 0x0e
+#define QC_CTL_HIGHWORD 0x26
+#define QC_CTL_LOWWORD 0x2f
+
#endif /* _QCAMREG_H */
OpenPOWER on IntegriCloud