summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Traina <pst@FreeBSD.org>1996-03-02 03:48:19 +0000
committerPaul Traina <pst@FreeBSD.org>1996-03-02 03:48:19 +0000
commit9d2baf5cdfbe28a3c134288f596f5c1be809c8fb (patch)
tree5f49d7e80dfec56f20372707ab823a774bf0a962
parent91477adc6ec872a3c870d63f78651868a5d8185c (diff)
Notes
-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 5c9cc20cc691..bef328a46106 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 5c9cc20cc691..bef328a46106 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 5d50516dd976..b65d7279edb0 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 000000000000..73f3e9c12391
--- /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 000000000000..d866687bdead
--- /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 400f81c74b89..2531a423ada5 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 */