summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/asc.c
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>1995-09-08 03:14:00 +0000
committerjulian <julian@FreeBSD.org>1995-09-08 03:14:00 +0000
commit2c78caeddfb62f1c47dae9e29d3cb682adb87962 (patch)
tree46b0ce74c2c4d325b8e33cec7de46fc6e7618ec3 /sys/i386/isa/asc.c
parent23a00f08e3fe32447d2415068dda46462ae16f0d (diff)
downloadFreeBSD-src-2c78caeddfb62f1c47dae9e29d3cb682adb87962.zip
FreeBSD-src-2c78caeddfb62f1c47dae9e29d3cb682adb87962.tar.gz
Submitted by: Luigi Rizzo (luigi@iet.unipi.it)
Obtained from: Luigi Rizzo and Gunther Schadow Kernel support for the asc scanner driver
Diffstat (limited to 'sys/i386/isa/asc.c')
-rw-r--r--sys/i386/isa/asc.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/sys/i386/isa/asc.c b/sys/i386/isa/asc.c
new file mode 100644
index 0000000..ba497b1
--- /dev/null
+++ b/sys/i386/isa/asc.c
@@ -0,0 +1,850 @@
+/* asc.c - device driver for hand scanners
+ *
+ * Current version supports:
+ *
+ * - Trust AmiScan BW (GI1904 chipset)
+ *
+ * Copyright (c) 1995 Gunther Schadow. All rights reserved.
+ * Copyright (c) 1995 Luigi Rizzo. All rights reserved.
+ *
+ * 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.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gunther Schadow
+ * and Luigi Rizzo.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without 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 "asc.h"
+#if NASC > 0
+#ifdef FREEBSD_1_X
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "malloc.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/ascreg.h"
+
+#include "machine/asc_ioctl.h"
+#else
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+
+#include <machine/asc_ioctl.h>
+
+#include <sys/devconf.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/ascreg.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+
+int ascopen(dev_t dev, int flag);
+#endif
+
+#endif /* FREEBSD_1_X */
+
+/***
+ *** CONSTANTS & DEFINES
+ ***
+ ***/
+
+#define PROBE_FAIL 0
+#define PROBE_SUCCESS 5 /* number of io ports... */
+#define SUCCESS 0
+#define FAIL -1
+#define INVALID FAIL
+
+#define DMA1_READY 0x08
+#define ASCDEBUG
+#ifdef ASCDEBUG
+# define lprintf if(scu->flags & DEBUG) printf
+#else
+# define lprintf (void)
+#endif
+
+#define TIMEOUT (hz*15) /* timeout while reading a buffer - default value */
+#define ASCPRI PRIBIO /* priority while reading a buffer */
+
+/***
+ *** LAYOUT OF THE MINOR NUMBER
+ ***/
+
+#define UNIT_MASK 0xc0 /* unit asc0 .. asc3 */
+#define UNIT(x) (x >> 6)
+#define DBUG_MASK 0x20
+#define FRMT_MASK 0x18 /* output format */
+#define FRMT_RAW 0x00 /* output bits as read from scanner */
+#define FRMT_GRAY 0x10 /* output graymap (not implemented yet) */
+#define FRMT_PBM 0x08 /* output pbm format */
+#define FRMT_PGM 0x18
+
+/***
+ *** THE GEMOMETRY TABLE
+ ***/
+
+const struct asc_geom {
+ int dpi; /* dots per inch */
+ int dpl; /* dots per line */
+ int bpl; /* bytes per line */
+ int g_res; /* get resolution value (ASC_STAT) */
+} geomtab[] = {
+ { 800, 3312, 414, ASC_RES_800},
+ { 700, 2896, 362, ASC_RES_700},
+ { 600, 2480, 310, ASC_RES_600},
+ { 500, 1656, 258, ASC_RES_500},
+ { 400, 1656, 207, ASC_RES_400},
+ { 300, 1240, 155, ASC_RES_300},
+ { 200, 832, 104, ASC_RES_200},
+ { 100, 416, 52, ASC_RES_100},
+ { INVALID, 416, 52, INVALID } /* terminator */
+};
+
+/***
+ *** THE TABLE OF UNITS
+ ***/
+
+struct _sbuf {
+ size_t size;
+ size_t rptr;
+ size_t wptr; /* only changed in ascintr */
+ size_t count;
+ char *base;
+};
+
+struct asc_unit {
+ int base; /* base address */
+ int dma_num; /* dma number */
+ char dma_byte; /* mask of byte for setting DMA value */
+ char int_byte; /* mask of byte for setting int value */
+ char cfg_byte; /* mirror of byte written to config reg (ASC_CFG). */
+ char cmd_byte; /* mirror of byte written to cmd port (ASC_CMD)*/
+ int flags;
+#define ATTACHED 0x01
+#define OPEN 0x02
+#define READING 0x04
+#define DMA_ACTIVE 0x08
+#define SLEEPING 0x10
+#define SEL_COLL 0x20
+#define PBM_MODE 0x40
+#define DEBUG 0x80
+ int geometry; /* resolution as geomtab index */
+ int linesize; /* length of one scan line (from geom.table) */
+ int blen; /* length of buffer in lines */
+ int btime; /* timeout of buffer in seconds/hz */
+ struct _sbuf sbuf;
+ long icnt; /* interrupt count XXX for debugging */
+#ifdef FREEBSD_1_X
+ pid_t selp; /* select pointer... */
+#else
+ struct selinfo selp;
+#endif
+ int height; /* height, for pnm modes */
+ size_t bcount; /* bytes to read, for pnm modes */
+} unittab[NASC];
+
+/*** I could not find a reasonable buffer size limit other than by
+ *** experiments. MAXPHYS is obviously too much, while DEV_BSIZE and
+ *** PAGE_SIZE are really too small. There must be something wrong
+ *** with isa_dmastart/isa_dmarangecheck HELP!!!
+ ***/
+#define MAX_BUFSIZE 0x3000
+#define DEFAULT_BLEN 20
+
+/***
+ *** THE PER-DRIVER RECORD FOR ISA.C
+ ***/
+int ascprobe (struct isa_device *isdp);
+int ascattach(struct isa_device *isdp);
+struct isa_driver ascdriver = { ascprobe, ascattach, "asc" };
+
+#ifndef FREEBSD_1_X
+struct asc_softc {
+ struct isa_device *dev;
+} asc_softc[NASC];
+
+static struct kern_devconf kdc_asc[NASC] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+ "asc", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "GI1904 Hand scanner",
+ DC_CLS_MISC
+} };
+
+static inline void
+asc_registerdev(struct isa_device *id)
+{
+ if(id->id_unit)
+ kdc_asc[id->id_unit] = kdc_asc[0];
+ kdc_asc[id->id_unit].kdc_unit = id->id_unit;
+ kdc_asc[id->id_unit].kdc_isa = id;
+ dev_attach(&kdc_asc[id->id_unit]);
+}
+#endif /* ! FREEBSD_1_X */
+
+/***
+ *** LOCALLY USED SUBROUTINES
+ ***
+ ***/
+
+/***
+ *** get_resolution
+ *** read resolution from the scanner
+ ***/
+static void
+get_resolution(struct asc_unit *scu)
+{
+ int res, i, delay;
+
+ res=0;
+ scu->cmd_byte = ASC_STANDBY;
+ outb(ASC_CMD, scu->cmd_byte);
+ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres", hz/10);
+ for(delay= 100; (res=inb(ASC_STAT)) & ASC_RDY_FLAG; delay--)
+ {
+ i = tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres0", 1);
+ if ( ( i == 0 ) || ( i == EWOULDBLOCK ) )
+ i = SUCCESS;
+ else
+ break;
+ }
+ if (delay==0) {
+ lprintf("asc.get_resolution: timeout completing command\n");
+ return /* -1 */;
+ }
+ /* ... actual read resolution... */
+ res &= ASC_RES_MASK;
+ for (i=0; geomtab[i].dpi != INVALID; i++) {
+ if (geomtab[i].g_res == res) break;
+ }
+ if (geomtab[i].dpi==INVALID) {
+ scu->geometry= i; /* INVALID; */
+ lprintf("asc.get_resolution: wrong resolution\n");
+ } else {
+ lprintf("asc.get_resolution: %d dpi\n",geomtab[i].dpi);
+ scu->geometry = i;
+ }
+ scu->linesize = geomtab[scu->geometry].bpl;
+ scu->height = geomtab[scu->geometry].dpl; /* default... */
+}
+
+/***
+ *** buffer_allocate
+ *** allocate/reallocate a buffer
+ ***/
+
+static int
+buffer_allocate(struct asc_unit *scu)
+{
+ size_t size, size1;
+
+ size = scu->blen * scu->linesize;
+
+ lprintf("asc.buffer_allocate: need 0x%x bytes\n", size);
+
+ if ( size > MAX_BUFSIZE ) {
+ size1=size;
+ size= ( (MAX_BUFSIZE+scu->linesize-1) / scu->linesize)*scu->linesize;
+ lprintf("asc.buffer_allocate: 0x%x bytes are too much, try 0x%x\n",
+ size1, size);
+ /* return ENOMEM; */
+ }
+
+ if ( scu->sbuf.base != NULL )
+ if ( scu->sbuf.size == size ) {
+ lprintf("asc.buffer_allocate: keep old buffer\n");
+ return SUCCESS;
+ } else {
+ lprintf("asc.buffer_allocate: release old buffer\n");
+ free( scu->sbuf.base, M_DEVBUF );
+ }
+
+ scu->sbuf.base = (char *)malloc(size, M_DEVBUF, M_WAITOK);
+
+ if ( scu->sbuf.base == NULL ) {
+ lprintf("asc.buffer_allocate: "
+ "buffer allocatation failed for size = 0x%x\n",
+ scu->sbuf.size);
+ return ENOMEM;
+ }
+
+ scu->sbuf.size = size;
+ scu->sbuf.rptr = 0;
+ scu->sbuf.wptr = 0;
+ scu->sbuf.count = 0; /* available data for reading */
+
+ lprintf("asc.buffer_allocate: ok\n");
+
+ return SUCCESS;
+}
+
+/*** dma_restart
+ *** invoked locally to start dma. Must run in a critical section
+ ***/
+static void
+dma_restart(struct asc_unit *scu)
+{
+ unsigned char al=scu->cmd_byte;
+ isa_dmastart(B_READ, scu->sbuf.base+scu->sbuf.wptr,
+ scu->linesize, scu->dma_num);
+ /*** this is done in sub_20, after dmastart ? ***/
+#if 0
+ outb( ASC_CMD, al |= 4 );
+ outb( ASC_CMD, al |= 8 ); /* ??? seems useless */
+ outb( ASC_CMD, al &= 0xfb );
+ scu->cmd_byte = al;
+#else
+ outb( ASC_CMD, ASC_OPERATE);
+#endif
+ scu->flags |= DMA_ACTIVE;
+}
+
+/***
+ *** the main functions
+ ***/
+
+/*** asc_reset
+ *** resets the scanner and the config bytes...
+ ***/
+void
+asc_reset(struct asc_unit *scu)
+{
+ scu->cfg_byte = 0 ; /* clear... */
+ scu->cmd_byte = 0 ; /* clear... */
+
+ outb(ASC_CFG,scu->cfg_byte); /* for safety, do this here */
+ outb(ASC_CMD,scu->cmd_byte); /* probably not needed */
+ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres", hz/10); /* sleep .1 sec */
+
+ scu->blen = DEFAULT_BLEN;
+ scu->btime = TIMEOUT;
+ scu->height = 0 ; /* don't know better... */
+}
+/**************************************************************************
+ ***
+ *** ascprobe
+ *** read status port and check for proper configuration:
+ *** - if address group matches (status byte has reasonable value)
+ *** cannot check interrupt/dma, only clear the config byte.
+ ***/
+int
+ascprobe (struct isa_device *isdp)
+{
+ int unit = isdp->id_unit;
+ struct asc_unit *scu = unittab + unit;
+ int stb;
+
+ scu->base = isdp->id_iobase; /*** needed by the following macros ***/
+ scu->flags = DEBUG;
+
+ if ( isdp->id_iobase < 0 ) {
+ lprintf("asc%d.probe: no iobase given\n", unit);
+ return PROBE_FAIL;
+ }
+
+ if ((stb=inb(ASC_PROBE)) != ASC_PROBE_VALUE) {
+ lprintf("asc%d.probe: failed, got 0x%02x instead of 0x%02x\n",
+ unit, stb, ASC_PROBE_VALUE);
+ return PROBE_FAIL;
+ }
+
+ switch(ffs(isdp->id_irq) - 1) {
+ case 3:
+ scu->int_byte = ASC_CNF_IRQ3;
+ break;
+ case 5:
+ scu->int_byte = ASC_CNF_IRQ5;
+ break;
+ case 10:
+ scu->int_byte = ASC_CNF_IRQ10;
+ break;
+#if 0
+ case -1:
+ scu->int_byte = 0;
+ lprintf("asc%d.probe: warning - going interruptless\n", unit);
+ break;
+#endif
+ default:
+ lprintf("asc%d.probe: unsupported INT %d (only 3, 5, 10)\n",
+ unit, isdp->id_irq);
+ return PROBE_FAIL;
+ }
+ scu->dma_num = isdp->id_drq;
+ switch(scu->dma_num) {
+ case 1:
+ scu->dma_byte = ASC_CNF_DMA1;
+ break;
+ case 3:
+ scu->dma_byte = ASC_CNF_DMA3;
+ break;
+ default:
+ lprintf("asc%d.probe: unsupported DMA %d (only 1 or 3)\n", scu->dma_num);
+ return PROBE_FAIL;
+ }
+ asc_reset(scu);
+/* lprintf("asc%d.probe: ok\n", unit); */
+
+ scu->flags &= ~DEBUG;
+ scu->icnt = 0;
+
+ return PROBE_SUCCESS;
+}
+
+/**************************************************************************
+ ***
+ *** ascattach
+ *** finish initialization of unit structure, get geometry value (?)
+ ***/
+
+int
+ascattach(struct isa_device *isdp)
+{
+ int unit = isdp->id_unit;
+ struct asc_unit *scu = unittab + unit;
+#ifdef DEVFS
+ char buf[32];
+ void *x;
+#endif
+
+ scu->flags |= DEBUG;
+ printf("asc%d: [GI1904/Trust Ami-Scan Grey, type S2]\n", unit);
+
+ /* initialize buffer structure */
+ scu->sbuf.base = NULL;
+ scu->sbuf.size = INVALID;
+ scu->sbuf.rptr = INVALID;
+
+ scu->flags |= ATTACHED;
+/* lprintf("asc%d.attach: ok\n", unit); */
+ scu->flags &= ~DEBUG;
+
+#ifdef FREEBSD_1_X
+ scu->selp = (pid_t)0;
+#else
+ scu->selp.si_flags=0;
+ scu->selp.si_pid=(pid_t)0;
+ kdc_asc[isdp->id_unit].kdc_state = DC_BUSY; /* XXX don't know better */
+ asc_registerdev(isdp);
+#endif
+#ifdef DEVFS
+ sprintf(buf,"asc%d",unit);
+/* path name devsw minor type uid gid perm*/
+ x=dev_add("/misc", buf, ascopen, unit<<6, DV_CHR, 0, 0, 0666);
+#endif /*DEVFS*/
+ return 1; /* attach must not fail */
+}
+
+/**************************************************************************
+ ***
+ *** ascintr
+ *** the interrupt routine, at the end of DMA...
+ ***/
+void
+ascintr(int unit)
+{
+ struct asc_unit *scu = unittab + unit;
+ int chan_bit = 0x01 << scu->dma_num;
+
+ scu->icnt++;
+ /* ignore stray interrupts... */
+ if ( scu->flags & (OPEN |READING) != (OPEN | READING) ) {
+ /* must be after closing... */
+ scu->flags &= ~(OPEN | READING | DMA_ACTIVE | SLEEPING | SEL_COLL);
+ return;
+ }
+ if ( (scu->flags & DMA_ACTIVE) && (inb(DMA1_READY) & chan_bit) != 0) {
+ outb( ASC_CMD, ASC_STANDBY);
+ scu->flags &= ~DMA_ACTIVE;
+ /* bounce buffers... */
+ isa_dmadone(B_READ, scu->sbuf.base+scu->sbuf.wptr,
+ scu->linesize, scu->dma_num);
+ scu->sbuf.wptr += scu->linesize;
+ if (scu->sbuf.wptr >= scu->sbuf.size) scu->sbuf.wptr=0;
+ scu->sbuf.count += scu->linesize;
+ if (scu->flags & SLEEPING) {
+ scu->flags &= ~SLEEPING;
+ wakeup((caddr_t)scu);
+ }
+ if (scu->sbuf.size - scu->sbuf.count >= scu->linesize) {
+ dma_restart(scu);
+ }
+#ifdef FREEBSD_1_X
+ if (scu->selp) {
+ selwakeup(&scu->selp, scu->flags & SEL_COLL );
+ scu->selp=(pid_t)0;
+ scu->flags &= ~SEL_COLL;
+ }
+#else
+ if (scu->selp.si_pid) {
+ selwakeup(&scu->selp);
+ scu->selp.si_pid=(pid_t)0;
+ scu->selp.si_flags = 0;
+ }
+#endif
+ }
+}
+
+/**************************************************************************
+ ***
+ *** ascopen
+ *** set open flag, set modes according to minor number
+ *** FOR RELEASE:
+ *** don't switch scanner on, wait until first read or ioctls go before
+ ***/
+
+int
+ascopen(dev_t dev, int flag)
+{
+ int unit = UNIT(minor(dev)) & UNIT_MASK;
+ struct asc_unit *scu = unittab + unit;
+
+ lprintf("asc%d.open: minor %d icnt %d\n", unit, minor(dev), scu->icnt);
+
+ if ( unit >= NASC || !( scu->flags & ATTACHED ) ) {
+ lprintf("asc%d.open: unit was not attached successfully, flags 0x%04x\n",
+ unit, scu->flags);
+ return ENXIO;
+ }
+
+ if ( scu->flags & OPEN ) {
+ lprintf("asc%d.open: already open", unit);
+ return EBUSY;
+ }
+ scu->flags = ATTACHED | OPEN;
+
+ if ( minor(dev) & DBUG_MASK ) scu->flags |= DEBUG;
+
+ switch(minor(dev) & FRMT_MASK) {
+ case FRMT_PBM:
+ scu->flags |= PBM_MODE;
+ lprintf("asc%d.open: pbm mode\n", unit);
+ break;
+ case FRMT_RAW:
+ lprintf("asc%d.open: raw mode\n", unit);
+ scu->flags &= ~PBM_MODE;
+ break;
+ default:
+ lprintf("asc%d.open: gray maps are not yet supported", unit);
+ return ENXIO;
+ }
+
+ asc_reset(scu);
+ get_resolution(scu);
+ return SUCCESS;
+}
+
+int
+asc_startread(struct asc_unit *scu)
+{
+ /*** from here on, things can be delayed to the first read/ioctl ***/
+ /*** this was done in sub_12... ***/
+ scu->cfg_byte= scu->cmd_byte=0; /* init scanner */
+ outb(ASC_CMD, scu->cmd_byte);
+ /*** this was done in sub_16, set scan len... ***/
+ outb(ASC_BOH, 0 );
+ scu->cmd_byte = 0x90 ;
+ outb(ASC_CMD, scu->cmd_byte);
+ outb(ASC_LEN_L, scu->linesize & 0xff /* len_low */);
+ outb(ASC_LEN_H, (scu->linesize >>8) & 0xff /* len_high */);
+ /*** this was done in sub_21, config DMA ... ***/
+ scu->cfg_byte |= scu->dma_byte;
+ outb(ASC_CFG, scu->cfg_byte);
+ /*** sub_22: enable int on the scanner ***/
+ scu->cfg_byte |= scu->int_byte;
+ outb(ASC_CFG, scu->cfg_byte);
+ /*** sub_28: light on etc...***/
+ scu->cmd_byte = ASC_STANDBY;
+ outb(ASC_CMD, scu->cmd_byte);
+ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascstrd", hz/10); /* sleep .1 sec */
+ return SUCCESS;
+}
+
+/**************************************************************************
+ ***
+ *** ascclose
+ *** turn off scanner, release the buffer
+ *** should probably terminate dma ops, release int and dma. lr 12mar95
+ ***/
+
+int
+ascclose(dev_t dev, int flag)
+{
+ int unit = UNIT(minor(dev));
+ struct asc_unit *scu = unittab + unit;
+
+ lprintf("asc%d.close: minor %d\n",
+ unit, minor(dev));
+
+ if ( unit >= NASC || !( scu->flags & ATTACHED ) ) {
+ lprintf("asc%d.close: unit was not attached successfully 0x%04x\n",
+ unit, scu->flags);
+ return ENXIO;
+ }
+ /* all this is in sub_29... */
+ /* cli(); */
+ outb(ASC_CFG, 0 ); /* don't save in CFG byte!!! */
+ scu->cmd_byte &= ~ASC_LIGHT_ON;
+ outb(ASC_CMD, scu->cmd_byte);/* light off */
+ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascclo", hz/2); /* sleep 1/2 sec */
+ scu->cfg_byte &= ~ scu->dma_byte ; /* disable scanner dma */
+ scu->cfg_byte &= ~ scu->int_byte ; /* disable scanner int */
+ outb(ASC_CFG, scu->cfg_byte);
+ /* --- disable dma controller ? --- */
+ /* --- disable interrupts on the controller (sub_24) --- */
+
+ if ( scu->sbuf.base != NULL ) free( scu->sbuf.base, M_DEVBUF );
+
+ scu->sbuf.base = NULL;
+ scu->sbuf.size = INVALID;
+ scu->sbuf.rptr = INVALID;
+
+ scu->flags &= ~(DEBUG | OPEN | READING);
+
+ return SUCCESS;
+}
+
+static void
+pbm_init(struct asc_unit *scu)
+{
+ int width = geomtab[scu->geometry].dpl;
+ int l= sprintf(scu->sbuf.base,"P4 %d %d\n", width, scu->height);
+ char *p;
+
+ scu->bcount = scu->height * width / 8 + l;
+
+ /* move header to end of sbuf */
+ scu->sbuf.rptr=scu->sbuf.size-l;
+ bcopy(scu->sbuf.base, scu->sbuf.base+scu->sbuf.rptr,l);
+ scu->sbuf.count = l;
+ for(p = scu->sbuf.base + scu->sbuf.rptr; l; p++, l--)
+ *p = ~*p;
+}
+/**************************************************************************
+ ***
+ *** ascread
+ ***/
+
+int
+ascread(dev_t dev, struct uio *uio)
+{
+ int unit = UNIT(minor(dev));
+ struct asc_unit *scu = unittab + unit;
+ size_t nbytes;
+ int sps, res;
+ unsigned char *p;
+
+ lprintf("asc%d.read: minor %d icnt %d\n", unit, minor(dev), scu->icnt);
+
+ if ( unit >= NASC || !( scu->flags & ATTACHED ) ) {
+ lprintf("asc%d.read: unit was not attached successfully 0x%04x\n",
+ unit, scu->flags);
+ return ENXIO;
+ }
+
+ if ( !(scu->flags & READING) ) { /*** first read... ***/
+ /* allocate a buffer for reading data and init things */
+ if ( (res = buffer_allocate(scu)) == SUCCESS ) scu->flags |= READING;
+ else return res;
+ asc_startread(scu);
+ if ( scu->flags & PBM_MODE ) { /* initialize for pbm mode */
+ pbm_init(scu);
+ }
+ }
+
+ lprintf("asc%d.read(before): "
+ "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %d\n",
+ unit, scu->sbuf.size, scu->sbuf.rptr,
+ scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags,
+ scu->icnt);
+
+ sps=spltty();
+ if ( scu->sbuf.count == 0 ) { /* no data avail., must wait */
+ if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu);
+ scu->flags |= SLEEPING;
+ res = tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascread", 0);
+ scu->flags &= ~SLEEPING;
+ if ( res == 0 ) res = SUCCESS;
+ }
+ splx(sps); /* lower priority... */
+ if (scu->flags & DEBUG)
+ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascdly",hz);
+ lprintf("asc%d.read(after): "
+ "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %d\n",
+ unit, scu->sbuf.size, scu->sbuf.rptr,
+ scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags,scu->icnt);
+
+ /* first, not more than available... */
+ nbytes = min( uio->uio_resid, scu->sbuf.count );
+ /* second, contiguous data... */
+ nbytes = min( nbytes, (scu->sbuf.size - scu->sbuf.rptr) );
+ /* third, one line (will remove this later, XXX) */
+ nbytes = min( nbytes, scu->linesize );
+ if ( (scu->flags & PBM_MODE) )
+ nbytes = min( nbytes, scu->bcount );
+ lprintf("asc%d.read: transferring 0x%x bytes\n", unit, nbytes);
+
+ lprintf("asc%d.read: invert buffer\n",unit);
+ for(p = scu->sbuf.base + scu->sbuf.rptr, res=nbytes; res; p++, res--)
+ *p = ~*p;
+ res = uiomove(scu->sbuf.base + scu->sbuf.rptr, nbytes, uio);
+ if ( res != SUCCESS ) {
+ lprintf("asc%d.read: uiomove failed %d", unit, res);
+ return res;
+ }
+
+ sps=spltty();
+ scu->sbuf.rptr += nbytes;
+ if (scu->sbuf.rptr >= scu->sbuf.size) scu->sbuf.rptr=0;
+ scu->sbuf.count -= nbytes;
+ /* having moved some data, can read mode */
+ if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu);
+ splx(sps); /* lower priority... */
+ if ( scu->flags & PBM_MODE ) scu->bcount -= nbytes;
+
+ lprintf("asc%d.read: size 0x%x, pointer 0x%x, bcount 0x%x, ok\n",
+ unit, scu->sbuf.size, scu->sbuf.rptr, scu->bcount);
+
+ return SUCCESS;
+}
+
+/**************************************************************************
+ ***
+ *** ascioctl
+ ***/
+
+int
+ascioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int unit = UNIT(minor(dev));
+ struct asc_unit *scu = unittab + unit;
+
+ lprintf("asc%d.ioctl: minor %d\n",
+ unit, minor(dev));
+
+ if ( unit >= NASC || !( scu->flags & ATTACHED ) ) {
+ lprintf("asc%d.ioctl: unit was not attached successfully 0x04x\n",
+ unit, scu->flags);
+ return ENXIO;
+ }
+ switch(cmd) {
+ case ASC_GRES:
+ asc_reset(scu);
+ get_resolution(scu);
+ *(int *)data=geomtab[scu->geometry].dpi;
+ lprintf("asc%d.ioctl:ASC_GRES %ddpi\n", unit, *(int *)data);
+ return SUCCESS;
+ case ASC_GWIDTH:
+ *(int *)data=geomtab[scu->geometry].dpl;
+ lprintf("asc%d.ioctl:ASC_GWIDTH %d\n", unit, *(int *)data);
+ return SUCCESS;
+ case ASC_GHEIGHT:
+ *(int *)data=scu->height;
+ lprintf("asc%d.ioctl:ASC_GHEIGHT %d\n", unit, *(int *)data);
+ return SUCCESS;
+ case ASC_SHEIGHT:
+ lprintf("asc%d.ioctl:ASC_SHEIGHT %d\n", unit, *(int *)data);
+ if ( scu->flags & READING ) {
+ lprintf("asc%d:ioctl on already reading unit\n", unit);
+ return EBUSY;
+ }
+ scu->height=*(int *)data;
+ return SUCCESS;
+#if 0
+ case ASC_GBLEN:
+ *(int *)data=scu->blen;
+ lprintf("asc%d.ioctl:ASC_GBLEN %d\n", unit, *(int *)data);
+ return SUCCESS;
+ case ASC_SBLEN:
+ lprintf("asc%d.ioctl:ASC_SBLEN %d\n", unit, *(int *)data);
+ if (*(int *)data * geomtab[scu->geometry].dpl / 8 > MAX_BUFSIZE)
+ {
+ lprintf("asc%d:ioctl buffer size too high\n", unit);
+ return ENOMEM;
+ }
+ scu->blen=*(int *)data;
+ return SUCCESS;
+ case ASC_GBTIME:
+ *(int *)data = scu->btime / hz;
+ lprintf("asc%d.ioctl:ASC_GBTIME %d\n", unit, *(int *)data);
+ return SUCCESS;
+ case ASC_SBTIME:
+ scu->btime = *(int *)data * hz;
+ lprintf("asc%d.ioctl:ASC_SBTIME %d\n", unit, *(int *)data);
+ return SUCCESS;
+#endif
+ default: return ENOTTY;
+ }
+ return SUCCESS;
+}
+
+int
+ascselect(dev_t dev, int rw, struct proc *p)
+{
+ int unit = UNIT(minor(dev));
+ struct asc_unit *scu = unittab + unit;
+ int sps=spltty();
+ struct proc *p1;
+
+ if (scu->sbuf.count >0) {
+ splx(sps);
+ return 1;
+ }
+ if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu);
+#ifdef FREEBSD_1_X
+ if (scu->selp== (pid_t)0) {
+ scu->selp= p->p_pid;
+ } else {
+ scu->flags |= SEL_COLL;
+ }
+#else
+
+ if (scu->selp.si_pid && (p1=pfind(scu->selp.si_pid))
+ && p1->p_wchan == (caddr_t)&selwait)
+ scu->selp.si_flags = SI_COLL;
+ else
+ scu->selp.si_pid = p->p_pid;
+#endif
+ splx(sps);
+ return 0;
+}
+#endif /* NASC > 0 */
OpenPOWER on IntegriCloud