8deebb0160
Submitted by: Hiten Pandya <hiten@unixdaemons.com>
835 lines
21 KiB
C
835 lines
21 KiB
C
/* gsc.c - device driver for handy scanners
|
|
*
|
|
* Current version supports:
|
|
*
|
|
* - Genius GS-4500
|
|
*
|
|
* Copyright (c) 1995 Gunther Schadow. 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.
|
|
* 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.
|
|
*
|
|
* $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
#include "gsc.h"
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/bus.h>
|
|
|
|
#include <machine/gsc.h>
|
|
|
|
#include <i386/isa/isa.h>
|
|
#include <i386/isa/isa_device.h>
|
|
#include <i386/isa/gscreg.h>
|
|
|
|
#ifndef COMPAT_OLDISA
|
|
#error "The gsc device requires the old isa compatibility shims"
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
*
|
|
* CONSTANTS & DEFINES
|
|
*
|
|
***********************************************************************/
|
|
|
|
#define PROBE_FAIL 0
|
|
#define PROBE_SUCCESS IO_GSCSIZE
|
|
#define ATTACH_FAIL 0
|
|
#define ATTACH_SUCCESS 1
|
|
#define SUCCESS 0
|
|
#define FAIL -1
|
|
#define INVALID FAIL
|
|
|
|
#define DMA1_READY 0x08
|
|
|
|
#ifdef GSCDEBUG
|
|
#define lprintf(args) \
|
|
do { \
|
|
if (scu->flags & FLAG_DEBUG) \
|
|
printf args; \
|
|
} while (0)
|
|
#else
|
|
#define lprintf(args)
|
|
#endif
|
|
|
|
#define TIMEOUT (hz*15) /* timeout while reading a buffer - default value */
|
|
#define LONG (hz/60) /* timesteps while reading a buffer */
|
|
#define GSCPRI PRIBIO /* priority while reading a buffer */
|
|
|
|
/***********************************************************************
|
|
*
|
|
* LAYOUT OF THE MINOR NUMBER
|
|
*
|
|
***********************************************************************/
|
|
|
|
#define UNIT_MASK 0xc0 /* unit gsc0 .. gsc3 */
|
|
#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
|
|
*
|
|
***********************************************************************/
|
|
|
|
#define GEOMTAB_SIZE 7
|
|
|
|
static const struct gsc_geom {
|
|
int dpi; /* dots per inch */
|
|
int dpl; /* dots per line */
|
|
int g_res; /* get resolution value (status flag) */
|
|
int s_res; /* set resolution value (control register) */
|
|
} geomtab[GEOMTAB_SIZE] = {
|
|
{ 100, 424, GSC_RES_100, GSC_CNT_424},
|
|
{ 200, 840, GSC_RES_200, GSC_CNT_840},
|
|
{ 300, 1264, GSC_RES_300, GSC_CNT_1264},
|
|
{ 400, 1648, GSC_RES_400, GSC_CNT_1648},
|
|
{ -1, 1696, -1, GSC_CNT_1696},
|
|
{ -2, 2644, -2, GSC_CNT_2544},
|
|
{ -3, 3648, -3, GSC_CNT_3648},
|
|
};
|
|
|
|
#define NEW_GEOM { INVALID, INVALID, INVALID, INVALID }
|
|
|
|
/***********************************************************************
|
|
*
|
|
* THE TABLE OF UNITS
|
|
*
|
|
***********************************************************************/
|
|
|
|
struct _sbuf {
|
|
size_t size;
|
|
size_t poi;
|
|
char *base;
|
|
};
|
|
|
|
struct gsc_unit {
|
|
int channel; /* DMA channel */
|
|
int data; /* - video port */
|
|
int stat; /* - status port */
|
|
int ctrl; /* - control port */
|
|
int clrp; /* - clear port */
|
|
int flags;
|
|
#define ATTACHED 0x01
|
|
#define OPEN 0x02
|
|
#define READING 0x04
|
|
#define EOF 0x08
|
|
#define FLAG_DEBUG 0x10
|
|
#define PBM_MODE 0x20
|
|
int geometry; /* resolution as geomtab index */
|
|
int blen; /* length of buffer in lines */
|
|
int btime; /* timeout of buffer in seconds/hz */
|
|
struct _sbuf sbuf;
|
|
char ctrl_byte; /* the byte actually written to ctrl port */
|
|
int height; /* height, for pnm modes */
|
|
size_t bcount; /* bytes to read, for pnm modes */
|
|
struct _sbuf hbuf; /* buffer for pnm header data */
|
|
};
|
|
|
|
static struct gsc_unit unittab[NGSC];
|
|
|
|
/* 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 59
|
|
|
|
/***********************************************************************
|
|
*
|
|
* THE PER-DRIVER RECORD FOR ISA.C
|
|
*
|
|
***********************************************************************/
|
|
|
|
static int gscprobe (struct isa_device *isdp);
|
|
static int gscattach(struct isa_device *isdp);
|
|
|
|
struct isa_driver gscdriver = {
|
|
INTR_TYPE_TTY,
|
|
gscprobe,
|
|
gscattach,
|
|
"gsc"
|
|
};
|
|
COMPAT_ISA_DRIVER(gsc, gscdriver);
|
|
|
|
static d_open_t gscopen;
|
|
static d_close_t gscclose;
|
|
static d_read_t gscread;
|
|
static d_ioctl_t gscioctl;
|
|
|
|
#define CDEV_MAJOR 47
|
|
static struct cdevsw gsc_cdevsw = {
|
|
/* open */ gscopen,
|
|
/* close */ gscclose,
|
|
/* read */ gscread,
|
|
/* write */ nowrite,
|
|
/* ioctl */ gscioctl,
|
|
/* poll */ nopoll,
|
|
/* mmap */ nommap,
|
|
/* strategy */ nostrategy,
|
|
/* name */ "gsc",
|
|
/* maj */ CDEV_MAJOR,
|
|
/* dump */ nodump,
|
|
/* psize */ nopsize,
|
|
/* flags */ 0,
|
|
};
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* LOCALLY USED SUBROUTINES
|
|
*
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
*
|
|
* lookup_geometry -- lookup a record in the geometry table by pattern
|
|
*
|
|
* The caller supplies a geometry record pattern, where INVALID
|
|
* matches anything. Returns the index in the table or INVALID if
|
|
* lookup fails.
|
|
*/
|
|
|
|
static int
|
|
lookup_geometry(struct gsc_geom geom, const struct gsc_unit *scu)
|
|
{
|
|
struct gsc_geom tab;
|
|
int i;
|
|
|
|
for(i=0; i<GEOMTAB_SIZE; i++)
|
|
{
|
|
tab = geomtab[i];
|
|
|
|
if ( ( ( geom.dpi != INVALID ) && ( tab.dpi == geom.dpi ) ) ||
|
|
( ( geom.dpl != INVALID ) && ( tab.dpl == geom.dpl ) ) ||
|
|
( ( geom.g_res != INVALID ) && ( tab.g_res == geom.g_res ) ) ||
|
|
( ( geom.s_res != INVALID ) && ( tab.s_res == geom.s_res ) ) )
|
|
{
|
|
lprintf(("gsc.lookup_geometry: "
|
|
"geometry lookup found: %ddpi, %ddpl\n",
|
|
tab.dpi, tab.dpl));
|
|
return i;
|
|
}
|
|
}
|
|
|
|
lprintf(("gsc.lookup_geometry: "
|
|
"geometry lookup failed on {%d, %d, 0x%02x, 0x%02x}\n",
|
|
geom.dpi, geom.dpl, geom.g_res, geom.s_res));
|
|
|
|
return INVALID;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* get_geometry -- read geometry from status port
|
|
*
|
|
* Returns the index into geometry table or INVALID if it fails to
|
|
* either read the status byte or lookup the record.
|
|
*/
|
|
|
|
static int
|
|
get_geometry(const struct gsc_unit *scu)
|
|
{
|
|
struct gsc_geom geom = NEW_GEOM;
|
|
|
|
lprintf(("gsc.get_geometry: get geometry at 0x%03x\n", scu->stat));
|
|
|
|
if ( ( geom.g_res = inb(scu->stat) ) == FAIL )
|
|
return INVALID;
|
|
|
|
geom.g_res &= GSC_RES_MASK;
|
|
|
|
return lookup_geometry(geom, scu);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* buffer_allocate -- allocate/reallocate a buffer
|
|
* Now just checks that the preallocated buffer is large enough.
|
|
*/
|
|
|
|
static int
|
|
buffer_allocate(struct gsc_unit *scu)
|
|
{
|
|
size_t size;
|
|
|
|
size = scu->blen * geomtab[scu->geometry].dpl / 8;
|
|
|
|
lprintf(("gsc.buffer_allocate: need 0x%x bytes\n", size));
|
|
|
|
if ( size > MAX_BUFSIZE )
|
|
{
|
|
lprintf(("gsc.buffer_allocate: 0x%x bytes are too much\n", size));
|
|
return ENOMEM;
|
|
}
|
|
|
|
scu->sbuf.size = size;
|
|
scu->sbuf.poi = size;
|
|
|
|
lprintf(("gsc.buffer_allocate: ok\n"));
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* buffer_read -- scan a buffer
|
|
*/
|
|
|
|
static int
|
|
buffer_read(struct gsc_unit *scu)
|
|
{
|
|
int stb;
|
|
int res = SUCCESS;
|
|
int chan_bit;
|
|
char *p;
|
|
int sps;
|
|
int delay;
|
|
|
|
lprintf(("gsc.buffer_read: begin\n"));
|
|
|
|
if (scu->ctrl_byte == INVALID)
|
|
{
|
|
lprintf(("gsc.buffer_read: invalid ctrl_byte\n"));
|
|
return EIO;
|
|
}
|
|
|
|
sps=splbio();
|
|
|
|
outb( scu->ctrl, scu->ctrl_byte | GSC_POWER_ON );
|
|
outb( scu->clrp, 0 );
|
|
stb = inb( scu->stat );
|
|
|
|
isa_dmastart(ISADMA_READ, scu->sbuf.base, scu->sbuf.size, scu->channel);
|
|
|
|
chan_bit = 0x01 << scu->channel;
|
|
|
|
for(delay=0; !(inb(DMA1_READY) & 0x01 << scu->channel); delay += LONG)
|
|
{
|
|
if(delay >= scu->btime)
|
|
{
|
|
splx(sps);
|
|
lprintf(("gsc.buffer_read: timeout\n"));
|
|
res = EWOULDBLOCK;
|
|
break;
|
|
}
|
|
res = tsleep((caddr_t)scu, GSCPRI | PCATCH, "gscread", LONG);
|
|
if ( ( res == 0 ) || ( res == EWOULDBLOCK ) )
|
|
res = SUCCESS;
|
|
else
|
|
break;
|
|
}
|
|
splx(sps);
|
|
isa_dmadone(ISADMA_READ, scu->sbuf.base, scu->sbuf.size, scu->channel);
|
|
outb( scu->clrp, 0 );
|
|
|
|
if(res != SUCCESS)
|
|
{
|
|
lprintf(("gsc.buffer_read: aborted with %d\n", res));
|
|
return res;
|
|
}
|
|
|
|
lprintf(("gsc.buffer_read: invert buffer\n"));
|
|
for(p = scu->sbuf.base + scu->sbuf.size - 1; p >= scu->sbuf.base; p--)
|
|
*p = ~*p;
|
|
|
|
scu->sbuf.poi = 0;
|
|
lprintf(("gsc.buffer_read: ok\n"));
|
|
return SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* the main functions
|
|
*
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscprobe
|
|
*
|
|
* read status port and check for proper configuration:
|
|
* - if address group matches (status byte has reasonable value)
|
|
* - if DMA channel matches (status byte has correct value)
|
|
*/
|
|
|
|
static int
|
|
gscprobe (struct isa_device *isdp)
|
|
{
|
|
int unit = isdp->id_unit;
|
|
struct gsc_unit *scu = unittab + unit;
|
|
int stb;
|
|
struct gsc_geom geom = NEW_GEOM;
|
|
|
|
scu->flags = FLAG_DEBUG;
|
|
|
|
lprintf(("gsc%d.probe "
|
|
"on iobase 0x%03x, irq %d, drq %d, addr %p, size %d\n",
|
|
unit,
|
|
isdp->id_iobase,
|
|
isdp->id_irq,
|
|
isdp->id_drq,
|
|
isdp->id_maddr,
|
|
isdp->id_msize));
|
|
|
|
if ( isdp->id_iobase < 0 )
|
|
{
|
|
lprintf(("gsc%d.probe: no iobase given\n", unit));
|
|
return PROBE_FAIL;
|
|
}
|
|
|
|
stb = inb( GSC_STAT(isdp->id_iobase) );
|
|
if (stb == FAIL)
|
|
{
|
|
lprintf(("gsc%d.probe: get status byte failed\n", unit));
|
|
return PROBE_FAIL;
|
|
}
|
|
|
|
scu->data = GSC_DATA(isdp->id_iobase);
|
|
scu->stat = GSC_STAT(isdp->id_iobase);
|
|
scu->ctrl = GSC_CTRL(isdp->id_iobase);
|
|
scu->clrp = GSC_CLRP(isdp->id_iobase);
|
|
|
|
outb(scu->clrp,stb);
|
|
stb = inb(scu->stat);
|
|
|
|
switch(stb & GSC_CNF_MASK) {
|
|
case GSC_CNF_DMA1:
|
|
lprintf(("gsc%d.probe: DMA 1\n", unit));
|
|
scu->channel = 1;
|
|
break;
|
|
|
|
case GSC_CNF_DMA3:
|
|
lprintf(("gsc%d.probe: DMA 3\n", unit));
|
|
scu->channel = 3;
|
|
break;
|
|
|
|
case GSC_CNF_IRQ3:
|
|
lprintf(("gsc%d.probe: IRQ 3\n", unit));
|
|
goto probe_noirq;
|
|
case GSC_CNF_IRQ5:
|
|
lprintf(("gsc%d.probe: IRQ 5\n", unit));
|
|
probe_noirq:
|
|
lprintf(("gsc%d.probe: sorry, can't use IRQ yet\n", unit));
|
|
return PROBE_FAIL;
|
|
default:
|
|
lprintf(("gsc%d.probe: invalid status byte 0x%02x\n", unit, (u_char) stb));
|
|
return PROBE_FAIL;
|
|
}
|
|
|
|
if (isdp->id_drq < 0)
|
|
isdp->id_drq = scu->channel;
|
|
if (scu->channel != isdp->id_drq)
|
|
{
|
|
lprintf(("gsc%d.probe: drq mismatch: config: %d; hardware: %d\n",
|
|
unit, isdp->id_drq, scu->channel));
|
|
return PROBE_FAIL;
|
|
}
|
|
|
|
geom.g_res = stb & GSC_RES_MASK;
|
|
scu->geometry = lookup_geometry(geom, scu);
|
|
if (scu->geometry == INVALID)
|
|
{
|
|
lprintf(("gsc%d.probe: geometry lookup failed\n", unit));
|
|
return PROBE_FAIL;
|
|
}
|
|
else
|
|
{
|
|
scu->ctrl_byte = geomtab[scu->geometry].s_res;
|
|
outb(scu->ctrl, scu->ctrl_byte | GSC_POWER_ON);
|
|
|
|
lprintf(("gsc%d.probe: status 0x%02x, %ddpi\n",
|
|
unit, stb, geomtab[scu->geometry].dpi));
|
|
|
|
outb(scu->ctrl, scu->ctrl_byte & ~GSC_POWER_ON);
|
|
}
|
|
|
|
lprintf(("gsc%d.probe: ok\n", unit));
|
|
|
|
scu->flags &= ~FLAG_DEBUG;
|
|
|
|
return PROBE_SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscattach
|
|
*
|
|
* finish initialization of unit structure
|
|
* get geometry value
|
|
*/
|
|
|
|
static int
|
|
gscattach(struct isa_device *isdp)
|
|
{
|
|
int unit = isdp->id_unit;
|
|
struct gsc_unit *scu = unittab + unit;
|
|
|
|
scu->flags |= FLAG_DEBUG;
|
|
|
|
lprintf(("gsc%d.attach: "
|
|
"iobase 0x%03x, irq %d, drq %d, addr %p, size %d\n",
|
|
unit,
|
|
isdp->id_iobase,
|
|
isdp->id_irq,
|
|
isdp->id_drq,
|
|
isdp->id_maddr,
|
|
isdp->id_msize));
|
|
|
|
printf("gsc%d: GeniScan GS-4500 at %ddpi\n",
|
|
unit, geomtab[scu->geometry].dpi);
|
|
|
|
/*
|
|
* Initialize buffer structure.
|
|
* XXX this must be done early to give a good chance of getting a
|
|
* contiguous buffer. This wastes memory.
|
|
*/
|
|
scu->sbuf.base = contigmalloc((unsigned long)MAX_BUFSIZE, M_DEVBUF, M_NOWAIT,
|
|
0ul, 0xfffffful, 1ul, 0x10000ul);
|
|
if ( scu->sbuf.base == NULL )
|
|
{
|
|
lprintf(("gsc%d.attach: buffer allocation failed\n", unit));
|
|
return ATTACH_FAIL; /* XXX attach must not fail */
|
|
}
|
|
scu->sbuf.size = INVALID;
|
|
scu->sbuf.poi = INVALID;
|
|
|
|
scu->blen = DEFAULT_BLEN;
|
|
scu->btime = TIMEOUT;
|
|
|
|
scu->flags |= ATTACHED;
|
|
lprintf(("gsc%d.attach: ok\n", unit));
|
|
scu->flags &= ~FLAG_DEBUG;
|
|
#define GSC_UID 0
|
|
#define GSC_GID 13
|
|
make_dev(&gsc_cdevsw, unit<<6, GSC_UID, GSC_GID, 0666, "gsc%d", unit);
|
|
make_dev(&gsc_cdevsw, ((unit<<6) + FRMT_PBM),
|
|
GSC_UID, GSC_GID, 0666, "gsc%dp", unit);
|
|
make_dev(&gsc_cdevsw, ((unit<<6) + DBUG_MASK),
|
|
GSC_UID, GSC_GID, 0666, "gsc%dd", unit);
|
|
make_dev(&gsc_cdevsw, ((unit<<6) + DBUG_MASK+FRMT_PBM),
|
|
GSC_UID, GSC_GID, 0666, "gsc%dpd", unit);
|
|
|
|
return ATTACH_SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscopen
|
|
*
|
|
* set open flag
|
|
* set modes according to minor number
|
|
* don't switch scanner on, wait until first read ioctls go before
|
|
*/
|
|
|
|
static int
|
|
gscopen (dev_t dev, int flags, int fmt, struct thread *td)
|
|
{
|
|
struct gsc_unit *scu;
|
|
int unit;
|
|
|
|
unit = UNIT(minor(dev)) & UNIT_MASK;
|
|
if ( unit >= NGSC )
|
|
{
|
|
#ifdef GSCDEBUG
|
|
/* XXX lprintf isn't valid here since there is no scu. */
|
|
printf("gsc%d.open: unconfigured unit number (max %d)\n", unit, NGSC);
|
|
#endif
|
|
return ENXIO;
|
|
}
|
|
scu = unittab + unit;
|
|
if ( !( scu->flags & ATTACHED ) )
|
|
{
|
|
lprintf(("gsc%d.open: unit was not attached successfully 0x%04x\n",
|
|
unit, scu->flags));
|
|
return ENXIO;
|
|
}
|
|
|
|
if ( minor(dev) & DBUG_MASK )
|
|
scu->flags |= FLAG_DEBUG;
|
|
else
|
|
scu->flags &= ~FLAG_DEBUG;
|
|
|
|
switch(minor(dev) & FRMT_MASK) {
|
|
case FRMT_PBM:
|
|
scu->flags |= PBM_MODE;
|
|
lprintf(("gsc%d.open: pbm mode\n", unit));
|
|
break;
|
|
case FRMT_RAW:
|
|
lprintf(("gsc%d.open: raw mode\n", unit));
|
|
scu->flags &= ~PBM_MODE;
|
|
break;
|
|
default:
|
|
lprintf(("gsc%d.open: gray maps are not yet supported", unit));
|
|
return ENXIO;
|
|
}
|
|
|
|
lprintf(("gsc%d.open: minor %d\n",
|
|
unit, minor(dev)));
|
|
|
|
if ( scu->flags & OPEN )
|
|
{
|
|
lprintf(("gsc%d.open: already open", unit));
|
|
return EBUSY;
|
|
}
|
|
|
|
if (isa_dma_acquire(scu->channel))
|
|
return(EBUSY);
|
|
|
|
scu->flags |= OPEN;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscclose
|
|
*
|
|
* turn off scanner
|
|
* release the buffer
|
|
*/
|
|
|
|
static int
|
|
gscclose (dev_t dev, int flags, int fmt, struct thread *td)
|
|
{
|
|
int unit = UNIT(minor(dev));
|
|
struct gsc_unit *scu = unittab + unit;
|
|
|
|
lprintf(("gsc%d.close: minor %d\n",
|
|
unit, minor(dev)));
|
|
|
|
if ( unit >= NGSC || !( scu->flags & ATTACHED ) )
|
|
{
|
|
lprintf(("gsc%d.read: unit was not attached successfully 0x%04x\n",
|
|
unit, scu->flags));
|
|
return ENXIO;
|
|
}
|
|
|
|
outb(scu->ctrl, scu->ctrl_byte & ~GSC_POWER_ON);
|
|
|
|
scu->sbuf.size = INVALID;
|
|
scu->sbuf.poi = INVALID;
|
|
|
|
isa_dma_release(scu->channel);
|
|
|
|
scu->flags &= ~(FLAG_DEBUG | OPEN | READING);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscread
|
|
*/
|
|
|
|
static int
|
|
gscread (dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
int unit = UNIT(minor(dev));
|
|
struct gsc_unit *scu = unittab + unit;
|
|
size_t nbytes;
|
|
int res;
|
|
|
|
lprintf(("gsc%d.read: minor %d\n", unit, minor(dev)));
|
|
|
|
if ( unit >= NGSC || !( scu->flags & ATTACHED ) )
|
|
{
|
|
lprintf(("gsc%d.read: unit was not attached successfully 0x%04x\n",
|
|
unit, scu->flags));
|
|
return ENXIO;
|
|
}
|
|
|
|
if ( !(scu->flags & READING) )
|
|
{
|
|
res = buffer_allocate(scu);
|
|
if ( res == SUCCESS )
|
|
scu->flags |= READING;
|
|
else
|
|
return res;
|
|
|
|
scu->ctrl_byte = geomtab[scu->geometry].s_res;
|
|
|
|
/* initialize for pbm mode */
|
|
if ( scu->flags & PBM_MODE )
|
|
{
|
|
char *p;
|
|
int width = geomtab[scu->geometry].dpl;
|
|
|
|
sprintf(scu->sbuf.base,"P4 %d %d\n", width, scu->height);
|
|
scu->bcount = scu->height * width / 8;
|
|
|
|
lprintf(("gsc%d.read: initializing pbm mode: `%s', bcount: 0x%x\n",
|
|
unit, scu->sbuf.base, scu->bcount));
|
|
|
|
/* move header to end of sbuf */
|
|
for(p=scu->sbuf.base; *p; p++);
|
|
while(--p >= scu->sbuf.base)
|
|
{
|
|
*(char *)(scu->sbuf.base + --scu->sbuf.poi) = *p;
|
|
scu->bcount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
lprintf(("gsc%d.read(before buffer_read): "
|
|
"size 0x%x, pointer 0x%x, bcount 0x%x, ok\n",
|
|
unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount));
|
|
|
|
if ( scu->sbuf.poi == scu->sbuf.size )
|
|
if ( (res = buffer_read(scu)) != SUCCESS )
|
|
return res;
|
|
|
|
lprintf(("gsc%d.read(after buffer_read): "
|
|
"size 0x%x, pointer 0x%x, bcount 0x%x, ok\n",
|
|
unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount));
|
|
|
|
nbytes = MIN( uio->uio_resid, scu->sbuf.size - scu->sbuf.poi );
|
|
|
|
if ( (scu->flags & PBM_MODE) )
|
|
nbytes = MIN( nbytes, scu->bcount );
|
|
|
|
lprintf(("gsc%d.read: transferring 0x%x bytes", unit, nbytes));
|
|
|
|
res = uiomove(scu->sbuf.base + scu->sbuf.poi, nbytes, uio);
|
|
if ( res != SUCCESS )
|
|
{
|
|
lprintf(("gsc%d.read: uiomove failed %d", unit, res));
|
|
return res;
|
|
}
|
|
|
|
scu->sbuf.poi += nbytes;
|
|
if ( scu->flags & PBM_MODE ) scu->bcount -= nbytes;
|
|
|
|
lprintf(("gsc%d.read: size 0x%x, pointer 0x%x, bcount 0x%x, ok\n",
|
|
unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount));
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* gscioctl
|
|
*
|
|
*/
|
|
|
|
static int
|
|
gscioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
|
|
{
|
|
int unit = UNIT(minor(dev));
|
|
struct gsc_unit *scu = unittab + unit;
|
|
|
|
lprintf(("gsc%d.ioctl: minor %d\n",
|
|
unit, minor(dev)));
|
|
|
|
if ( unit >= NGSC || !( scu->flags & ATTACHED ) )
|
|
{
|
|
lprintf(("gsc%d.ioctl: unit was not attached successfully 0x%04x\n",
|
|
unit, scu->flags));
|
|
return ENXIO;
|
|
}
|
|
|
|
switch(cmd) {
|
|
case GSC_SRESSW:
|
|
lprintf(("gsc%d.ioctl:GSC_SRESSW\n", unit));
|
|
if ( scu->flags & READING )
|
|
{
|
|
lprintf(("gsc%d:ioctl on already reading unit\n", unit));
|
|
return EBUSY;
|
|
}
|
|
scu->geometry = get_geometry(scu);
|
|
return SUCCESS;
|
|
case GSC_GRES:
|
|
*(int *)data=geomtab[scu->geometry].dpi;
|
|
lprintf(("gsc%d.ioctl:GSC_GRES %ddpi\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
case GSC_GWIDTH:
|
|
*(int *)data=geomtab[scu->geometry].dpl;
|
|
lprintf(("gsc%d.ioctl:GSC_GWIDTH %d\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
case GSC_SRES:
|
|
case GSC_SWIDTH:
|
|
lprintf(("gsc%d.ioctl:GSC_SRES or GSC_SWIDTH %d\n",
|
|
unit, *(int *)data));
|
|
{ int g;
|
|
struct gsc_geom geom = NEW_GEOM;
|
|
if ( cmd == GSC_SRES )
|
|
geom.dpi = *(int *)data;
|
|
else
|
|
geom.dpl = *(int *)data;
|
|
if ( ( g = lookup_geometry(geom, scu) ) == INVALID )
|
|
return EINVAL;
|
|
scu->geometry = g;
|
|
return SUCCESS;
|
|
}
|
|
case GSC_GHEIGHT:
|
|
*(int *)data=scu->height;
|
|
lprintf(("gsc%d.ioctl:GSC_GHEIGHT %d\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
case GSC_SHEIGHT:
|
|
lprintf(("gsc%d.ioctl:GSC_SHEIGHT %d\n", unit, *(int *)data));
|
|
if ( scu->flags & READING )
|
|
{
|
|
lprintf(("gsc%d:ioctl on already reading unit\n", unit));
|
|
return EBUSY;
|
|
}
|
|
scu->height=*(int *)data;
|
|
return SUCCESS;
|
|
case GSC_GBLEN:
|
|
*(int *)data=scu->blen;
|
|
lprintf(("gsc%d.ioctl:GSC_GBLEN %d\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
case GSC_SBLEN:
|
|
lprintf(("gsc%d.ioctl:GSC_SBLEN %d\n", unit, *(int *)data));
|
|
if (*(int *)data * geomtab[scu->geometry].dpl / 8 > MAX_BUFSIZE)
|
|
{
|
|
lprintf(("gsc%d:ioctl buffer size too high\n", unit));
|
|
return ENOMEM;
|
|
}
|
|
scu->blen=*(int *)data;
|
|
return SUCCESS;
|
|
case GSC_GBTIME:
|
|
*(int *)data = scu->btime / hz;
|
|
lprintf(("gsc%d.ioctl:GSC_GBTIME %d\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
case GSC_SBTIME:
|
|
scu->btime = *(int *)data * hz;
|
|
lprintf(("gsc%d.ioctl:GSC_SBTIME %d\n", unit, *(int *)data));
|
|
return SUCCESS;
|
|
default: return ENOTTY;
|
|
}
|
|
}
|