/* * 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. * * 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 #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /* DEVFS */ #include #include #include #include #include #include int qcam_debug = 1; static struct qcam_softc qcam_softc[NQCAM]; #define QC_CONF_NODETECT 0x01 /* always assume camera is present */ #define QC_CONF_FORCEUNI 0x02 /* force unidirectional transfers */ 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 */ "QuickCam video input", /* kdc_description */ DC_CLS_MISC /* class */ }; #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 static struct cdevsw qcam_cdevsw = { qcam_open, qcam_close, qcam_read, nowrite, qcam_ioctl, nostop, nullreset, nodevtotty, noselect, nommap, nostrategy, "qcam", NULL, -1 }; static void qcam_registerdev (struct isa_device *id) { 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; #ifndef ACTUALLY_LKM_NOT_KERNEL /* there's a bug in dev_attach when running from an LKM */ dev_attach(kdc); #endif } 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; } /* * 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)) if (!qcam_detect(devp->id_iobase)) return 0; /* failure */ qcam_registerdev(devp); return 1; /* found */ } 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; /* force unidirectional parallel port mode? */ if (devp->id_flags & QC_CONF_FORCEUNI) qs->flags |= QC_FORCEUNI; qcam_reset(qs); printf("qcam%d: %sdirectional parallel port\n", qs->unit, qs->flags & QC_BIDIR_HW ? "bi" : "uni"); #ifdef DEVFS qs->devfs_token = devfs_add_devswf(&qcam_cdevsw, qs->unit, DV_CHR, 0, 0, 0600, "qcam%d", qs->unit); #endif return 1; } static int qcam_open (dev_t dev, int flags, int fmt, struct proc *p) { struct qcam_softc *qs = &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); qs->init_req = 1; /* request initialization before scan */ 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 = &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 = &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) if (qcam_scan(qs)) return EIO; 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 = &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_exposure = qs->exposure; info->qc_brightness = qs->brightness; info->qc_whitebalance = qs->whitebalance; info->qc_contrast = qs->contrast; 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; /* 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->exposure = info->qc_exposure; qs->brightness = info->qc_brightness; qs->whitebalance = info->qc_whitebalance; qs->contrast = info->qc_contrast; /* request initialization before next scan pass */ qs->init_req = 1; break; default: return ENOTTY; } return 0; } /* * 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) #ifdef QCAM_MODULE #include #include #include #include static struct isa_device qcam_mod_dev = {0, &qcamdriver, IO_LPT1, 0, -1, (caddr_t) 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}; MOD_DEV(qcam, LM_DT_CHAR, CDEV_MAJOR, &qcam_cdevsw); static int qcam_load (struct lkm_table *lkmtp, int cmd) { if (qcam_probe(&qcam_mod_dev)) { qcam_attach(&qcam_mod_dev); qcam_drvinit(NULL); /* XXX this shouldn't NEED to be here * the LKM code should be doing this * for us! */ uprintf("qcam: driver loaded\n"); return 0; } else { uprintf("qcam: probe failed\n"); return 1; } } static int qcam_unload (struct lkm_table *lkmtp, int cmd) { struct qcam_softc *qs; int i; for (i = 0; i < NQCAM; i++) { qs = &qcam_softc[i]; if (qs->flags & QC_OPEN) { uprintf("qcam%d: cannot unload, device busy", qs->unit); return 1; } } uprintf("qcam: driver unloaded\n"); return 0; } static int qcam_stat (struct lkm_table *lkmtp, int cmd) { return 0; } int qcam_mod (struct lkm_table *lkmtp, int cmd, int ver) { #define _module qcam_module DISPATCH(lkmtp, cmd, ver, qcam_load, qcam_unload, qcam_stat); } #endif /* QCAM_MODULE */ #endif /* NQCAM */