summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorPaul Traina <pst@FreeBSD.org>1996-02-02 19:27:57 +0000
committerPaul Traina <pst@FreeBSD.org>1996-02-02 19:27:57 +0000
commitb0f4bf00635ceaac9f3b6f95db19249f163821c8 (patch)
tree8cbfba24d6a0f0968e8b1e3cdbb67256efabe9b9 /sys
parent0b59977032efaf8ff499c157f9bf51d95449ab06 (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/i386/isa/qcam.c778
-rw-r--r--sys/i386/isa/qcamreg.h83
2 files changed, 861 insertions, 0 deletions
diff --git a/sys/i386/isa/qcam.c b/sys/i386/isa/qcam.c
new file mode 100644
index 000000000000..8cd4daf06f94
--- /dev/null
+++ b/sys/i386/isa/qcam.c
@@ -0,0 +1,778 @@
+/*
+ * 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.
+ */
+
+#include "qcam.h"
+#if NQCAM > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/devconf.h>
+#include <sys/errno.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /* DEVFS */
+
+#include <machine/cpu.h>
+#include <machine/clock.h>
+#include <machine/qcam.h>
+
+#include <i386/isa/qcamreg.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 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];
+
+static struct kern_devconf kdc_qcam_template = {
+ 0, 0, 0, /* filled in by dev_attach() */
+ "qcam", /* kdc_name */
+ 0, /* kdc_unit */
+ { /* kdc_md */
+ MDDT_ISA, /* mddc_devtype */
+ 0, /* mddc_flags */
+ "tty" /* mddc_imask[4] */
+ },
+ isa_generic_externalize, /* kdc_externalize */
+ 0, /* kdc_internalize */
+ 0, /* kdc_goaway */
+ ISA_EXTERNALLEN, /* kdc_datalen */
+ &kdc_isa0, /* kdc_parent */
+ 0, /* kdc_parentdata */
+ DC_UNCONFIGURED, /* kdc_state */
+ "", /* kdc_description */
+ DC_CLS_MISC /* class */
+};
+
+/* 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_BIDIR_REQ 0x08 /* bidir xfer requested */
+
+#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 }
+};
+
+#define UNIT(dev) minor(dev)
+
+static int qcam_probe(struct isa_device *id);
+static int qcam_attach(struct isa_device *id);
+
+struct isa_driver qcamdriver =
+ {qcam_probe, qcam_attach, "qcam"};
+
+static d_open_t qcam_open;
+static d_close_t qcam_close;
+static d_read_t qcam_read;
+static d_ioctl_t qcam_ioctl;
+
+#define CDEV_MAJOR 73 /* XXX change this! */
+
+static struct cdevsw qcam_cdevsw =
+ { qcam_open, qcam_close, qcam_read, nowrite,
+ qcam_ioctl, nostop, nullreset, nodevtotty,
+ 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->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, 0x28);
+ 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
+ * ...soon, my son.
+ */
+
+ bidir = (((qs->flags & (QC_BIDIR_HW|QC_BIDIR_REQ)) ==
+ (QC_BIDIR_HW|QC_BIDIR_REQ)) &&
+ (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, const char *descr)
+{
+ struct kern_devconf *kdc = &qcam_softc[id->id_unit].kdc;
+
+ *kdc = kdc_qcam_template; /* byte-copy template */
+
+ kdc->kdc_unit = id->id_unit;
+ kdc->kdc_parentdata = id;
+ kdc->kdc_description = descr;
+
+ dev_attach(kdc);
+}
+
+static int
+qcam_probe (struct isa_device *devp)
+{
+ u_char reg, last;
+ int i, transitions = 0;
+
+ switch (devp->id_iobase) { /* don't probe weird ports */
+ case IO_LPT1:
+ case IO_LPT2:
+ case IO_LPT3:
+ break;
+ default:
+ printf("qcam%d: ignoring non-standard port 0x%x\n",
+ devp->id_unit, devp->id_iobase);
+ return 0;
+ }
+
+ /* write 0's to control and data ports */
+ 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;
+ }
+
+ qcam_registerdev(devp, "QuickCam video input");
+ 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)
+{
+ struct qcam_softc *qs = &qcam_softc[devp->id_unit];
+
+ qs->iobase = devp->id_iobase;
+ qs->unit = devp->id_unit;
+ qs->kdc.kdc_state = DC_IDLE;
+ qs->flags |= QC_ALIVE;
+
+ qcam_default(qs);
+ qcam_xferparms(qs);
+
+#ifdef DEVFS
+{
+ char name[32];
+ sprintf(name,"qcam%d", qs->unit);
+ qs->devfs_token = devfs_add_devsw("/", name, &qcam_cdevsw, qs->unit,
+ DV_CHR, 0, 0, 0600);
+}
+#endif
+ return 1;
+}
+
+static int
+qcam_open (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct qcam_softc *qs = (struct qcam_softc *)&qcam_softc[UNIT(dev)];
+
+ if (!(qs->flags & QC_ALIVE))
+ return ENXIO;
+
+ if (qs->flags & QC_OPEN)
+ return EBUSY;
+
+ qs->buffer_end = qs->buffer = malloc(QC_MAXFRAMEBUFSIZE, M_DEVBUF,
+ M_WAITOK);
+ if (!qs->buffer)
+ return ENOMEM;
+
+ qcam_reset(qs);
+ qcam_default(qs);
+ qcam_init(qs);
+
+ qs->flags |= QC_OPEN;
+ qs->kdc.kdc_state = DC_BUSY;
+ return 0;
+}
+
+static int
+qcam_close (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct qcam_softc *qs = (struct qcam_softc *)&qcam_softc[UNIT(dev)];
+
+ if (qs->buffer) {
+ free(qs->buffer, M_DEVBUF);
+ qs->buffer = NULL;
+ qs->buffer_end = NULL;
+ }
+
+ qs->flags &= ~QC_OPEN;
+ qs->kdc.kdc_state = DC_IDLE;
+ return 0;
+}
+
+static int
+qcam_read (dev_t dev, struct uio *uio, int ioflag)
+{
+ struct qcam_softc *qs = (struct qcam_softc *)&qcam_softc[UNIT(dev)];
+ int bytes, bufsize;
+ int error;
+
+ /* if we've seeked back to 0, that's our signal to scan */
+ if (uio->uio_offset == 0)
+ qcam_scan(qs);
+
+ bufsize = qs->buffer_end - qs->buffer;
+ if (uio->uio_offset > bufsize)
+ return EIO;
+
+ bytes = min(uio->uio_resid, (bufsize - uio->uio_offset));
+ error = uiomove(qs->buffer + uio->uio_offset, bytes, uio);
+ if (error)
+ return error;
+
+ return 0; /* success */
+}
+
+static int
+qcam_ioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ struct qcam_softc *qs = (struct qcam_softc *)&qcam_softc[UNIT(dev)];
+ struct qcam *info= (struct qcam *)data;
+
+ if (!data)
+ return(EINVAL);
+
+ switch (cmd) {
+
+ case QC_GET:
+ info->qc_version = QC_IOCTL_VERSION;
+ info->qc_xsize = qs->x_size;
+ info->qc_ysize = qs->y_size;
+ info->qc_xorigin = qs->x_origin;
+ info->qc_yorigin = qs->y_origin;
+ info->qc_bpp = qs->bpp;
+ info->qc_zoom = qs->zoom;
+ info->qc_brightness = qs->brightness;
+ info->qc_whitebalance = qs->whitebalance;
+ info->qc_contrast = qs->contrast;
+
+ if (qs->flags & QC_BIDIR_REQ)
+ info->qc_mode = QC_MODE_BIDIR;
+ else
+ info->qc_mode = QC_MODE_UNIDIR;
+ break;
+
+ case QC_SET:
+ /*
+ * sanity check parameters passed in by user
+ * we're extra paranoid right now because the API
+ * is in flux
+ */
+ if (info->qc_xsize > QC_MAX_XSIZE ||
+ info->qc_ysize > QC_MAX_YSIZE ||
+ info->qc_xorigin > QC_MAX_XSIZE ||
+ info->qc_yorigin > QC_MAX_YSIZE ||
+ (info->qc_bpp != 4 && info->qc_bpp != 6) ||
+ info->qc_zoom > QC_ZOOM_200 ||
+ info->qc_brightness > UCHAR_MAX ||
+ info->qc_whitebalance > UCHAR_MAX ||
+ info->qc_contrast > UCHAR_MAX)
+ return EINVAL;
+
+ if (info->qc_mode & ~QC_MODE_BIDIR)
+ return EINVAL;
+
+ /* version check */
+ if (info->qc_version != QC_IOCTL_VERSION)
+ return EINVAL;
+
+ qs->x_size = info->qc_xsize;
+ qs->y_size = info->qc_ysize;
+ qs->x_origin = info->qc_xorigin;
+ qs->y_origin = info->qc_yorigin;
+ qs->bpp = info->qc_bpp;
+ qs->zoom = info->qc_zoom;
+ qs->brightness = info->qc_brightness;
+ qs->whitebalance = info->qc_whitebalance;
+ qs->contrast = info->qc_contrast;
+
+ if (info->qc_mode & QC_MODE_BIDIR)
+ qs->flags |= QC_BIDIR_REQ;
+ else
+ qs->flags &= ~QC_BIDIR_REQ;
+
+ /* request initialization before next scan pass */
+ qs->init_req = 1;
+ break;
+
+ default:
+ return ENOTTY;
+ }
+
+ return 0;
+}
+
+/*
+ *-------------------------------------------------------------------
+ */
+
+#ifdef ACTUALLY_LKM_NOT_KERNEL
+/*
+ * Loadable QuickCam driver stubs
+ * XXX 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
+qcam_drvinit (void *unused)
+{
+ static int qcam_devsw_installed = 0;
+ dev_t dev;
+
+ if (!qcam_devsw_installed) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&qcam_cdevsw, NULL);
+ qcam_devsw_installed++;
+ }
+}
+
+SYSINIT(qcamdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,qcam_drvinit,NULL)
+
+#endif /* NQCAM */
diff --git a/sys/i386/isa/qcamreg.h b/sys/i386/isa/qcamreg.h
new file mode 100644
index 000000000000..400f81c74b89
--- /dev/null
+++ b/sys/i386/isa/qcamreg.h
@@ -0,0 +1,83 @@
+/*
+ * 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 following information is hardware dependant. It should not be used
+ * by user applications, see machine/qcam.h for the applications interface.
+ */
+
+#ifndef _QCAMREG_H
+#define _QCAMREG_H
+
+/*
+ * Camera autodetection parameters
+ */
+#define QC_PROBELIMIT 30 /* number of times to probe */
+#define QC_PROBECNTLOW 5 /* minimum transitions */
+#define QC_PROBECNTHI 25 /* maximum transitions */
+
+/*
+ * QuickCam camera commands
+ */
+#define QC_BRIGHTNESS 0x0b
+#define QC_CONTRAST 0x19
+#define QC_WHITEBALANCE 0x1f
+#define QC_XFERMODE 0x07
+#define QC_XSIZE 0x13
+#define QC_YSIZE 0x11
+#define QC_YORG 0x0d
+#define QC_XORG 0x0f
+
+/*
+ * XFERmode register flags
+ */
+#define QC_XFER_BIDIR 0x01 /* bidirectional transfer */
+#define QC_XFER_6BPP 0x02 /* 6 bits per pixel */
+#define QC_XFER_WIDE 0x00 /* wide angle */
+#define QC_XFER_NARROW 0x04 /* narrow */
+#define QC_XFER_TIGHT 0x08 /* very narrow */
+
+/*
+ * QuickCam default values (don't depend on these staying the same)
+ */
+#define QC_DEF_XSIZE 160
+#define QC_DEF_YSIZE 120
+#define QC_DEF_XORG 7
+#define QC_DEF_YORG 1
+#define QC_DEF_BPP 6
+#define QC_DEF_CONTRAST 180
+#define QC_DEF_BRIGHTNESS 180
+#define QC_DEF_WHITEBALANCE 150
+#define QC_DEF_ZOOM QC_ZOOM_100
+
+#endif /* _QCAMREG_H */