diff options
| author | Paul Traina <pst@FreeBSD.org> | 1996-03-02 03:48:19 +0000 |
|---|---|---|
| committer | Paul Traina <pst@FreeBSD.org> | 1996-03-02 03:48:19 +0000 |
| commit | 9d2baf5cdfbe28a3c134288f596f5c1be809c8fb (patch) | |
| tree | 5f49d7e80dfec56f20372707ab823a774bf0a962 | |
| parent | 91477adc6ec872a3c870d63f78651868a5d8185c (diff) | |
Notes
| -rw-r--r-- | sys/conf/files.i386 | 3 | ||||
| -rw-r--r-- | sys/i386/conf/files.i386 | 3 | ||||
| -rw-r--r-- | sys/i386/isa/qcam.c | 475 | ||||
| -rw-r--r-- | sys/i386/isa/qcamdefs.h | 112 | ||||
| -rw-r--r-- | sys/i386/isa/qcamio.c | 521 | ||||
| -rw-r--r-- | sys/i386/isa/qcamreg.h | 8 |
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 */ |
