File: [local] / sys / scsi / ss.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:16:08 2008 UTC (16 years, 3 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: ss.c,v 1.58 2006/12/21 02:05:46 krw Exp $ */
/* $NetBSD: ss.c,v 1.10 1996/05/05 19:52:55 christos Exp $ */
/*
* Copyright (c) 1995, 1997 Kenneth Stailey. All rights reserved.
* modified for configurable scanner support by Joachim Koenig
*
* 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 Kenneth Stailey.
* 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/scanio.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_scanner.h>
#include <scsi/scsiconf.h>
#include <scsi/ssvar.h>
#include <scsi/ss_mustek.h>
#define SSMODE(z) ( minor(z) & 0x03)
#define SSUNIT(z) ((minor(z) >> 4) )
/*
* If the mode is 3 (e.g. minor = 3,7,11,15)
* then the device has been openned to set defaults
* This mode does NOT ALLOW I/O, only ioctls
*/
#define MODE_REWIND 0
#define MODE_NONREWIND 1
#define MODE_CONTROL 3
struct quirkdata {
char *name;
u_int quirks;
#define SS_Q_WINDOW_DESC_LEN 0x0001 /* needs special WDL */
#define SS_Q_BRIGHTNESS 0x0002 /* needs special value for brightness */
#define SS_Q_REV_BRIGHTNESS 0x0004 /* reverse brightness control in s/w */
#define SS_Q_THRESHOLD 0x0008 /* needs special value for threshold */
#define SS_Q_MONO_THRESHOLD 0x0010 /* same as SS_Q_THRESHOLD but only
* for monochrome image data */
#define SS_Q_CONTRAST 0x0020 /* needs special value for contrast */
#define SS_Q_REV_CONTRAST 0x0040 /* reverse contrast control in s/w */
#define SS_Q_HALFTONE 0x0080 /* uses non-zero halftone */
#define SS_Q_SET_RIF 0x0100 /* set RIF bit */
#define SS_Q_PADDING_TYPE 0x0200 /* does not truncate to byte boundary */
#define SS_Q_BIT_ORDERING 0x0400 /* needs non-zero bit ordering */
long window_descriptor_length;
u_int8_t brightness;
u_int8_t threshold;
u_int8_t contrast;
u_int8_t halftone_pattern[2];
int pad_type;
long bit_ordering;
u_int8_t scanner_type;
/*
* To enable additional scanner options, point vendor_unique_sw
* at a function that adds more stuff to the SET_WINDOW parameters.
*/
int (*vendor_unique_sw)(struct ss_softc *, struct scan_io *,
struct scsi_set_window *, void *);
/*
* If the scanner requires use of GET_BUFFER_STATUS before READ
* it can be called from ss_minphys().
*/
void (*special_minphys)(struct ss_softc *, struct buf *);
/*
*
*/
int (*compute_sizes)(void);
};
struct ss_quirk_inquiry_pattern {
struct scsi_inquiry_pattern pattern;
struct quirkdata quirkdata;
};
struct quirkdata ss_gen_quirks = {
"generic", 0, 0, 0, 0, 0,
{0, 0}, 0, 0, GENERIC_SCSI2,
NULL, NULL, NULL
};
void ssstrategy(struct buf *);
void ssstart(void *);
void ssminphys(struct buf *);
void ss_identify_scanner(struct ss_softc *, struct scsi_inquiry_data*);
int ss_set_window(struct ss_softc *, struct scan_io *);
int ricoh_is410_sw(struct ss_softc *, struct scan_io *,
struct scsi_set_window *, void *);
int umax_uc630_sw(struct ss_softc *, struct scan_io *,
struct scsi_set_window *, void *);
#ifdef NOTYET /* for ADF support */
int fujitsu_m3096g_sw(struct ss_softc *, struct scan_io *,
struct scsi_set_window *, void *);
#endif
void get_buffer_status(struct ss_softc *, struct buf *);
/*
* WDL:
*
* Ricoh IS-50 & IS-410 insist on 320 (even it transfer len is less.)
* Ricoh FS-1 accepts 256 (I haven't tested other values.)
* UMAX UC-630 accepts 46 (I haven't tested other values.)
* Fujitsu M3096G wants 40 <= x <= 248 (tested OK at 40 & 64.)
*/
const struct ss_quirk_inquiry_pattern ss_quirk_patterns[] = {
{{T_SCANNER, T_FIXED,
"ULTIMA ", "AT3 1.60 ", " "}, {
"Ultima AT3",
SS_Q_HALFTONE |
SS_Q_PADDING_TYPE,
0, 0, 0, 0, { 3, 0 }, 0, 0,
ULTIMA_AT3,
NULL, NULL, NULL
}},
{{T_SCANNER, T_FIXED,
"ULTIMA ", "A6000C PLUS ", " "}, {
"Ultima A6000C",
SS_Q_HALFTONE |
SS_Q_PADDING_TYPE,
0, 0, 0, 0, { 3, 0 }, 0, 0,
ULTIMA_AC6000C,
NULL, NULL, NULL
}},
{{T_SCANNER, T_FIXED,
"RICOH ", "IS50 ", " "}, {
"Ricoh IS-50",
SS_Q_WINDOW_DESC_LEN |
SS_Q_REV_BRIGHTNESS |
SS_Q_THRESHOLD |
SS_Q_REV_CONTRAST |
SS_Q_HALFTONE |
SS_Q_BIT_ORDERING,
320, 0, 0, 0, { 2, 0x0a }, 0, 7,
RICOH_IS50,
ricoh_is410_sw, get_buffer_status, NULL
}},
{{T_SCANNER, T_FIXED,
"RICOH ", "IS410 ", " "}, {
"Ricoh IS-410",
SS_Q_WINDOW_DESC_LEN |
SS_Q_THRESHOLD |
SS_Q_HALFTONE |
SS_Q_BIT_ORDERING,
320, 0, 0, 0, { 2, 0x0a }, 0, 7,
RICOH_IS410,
ricoh_is410_sw, get_buffer_status, NULL
}},
{{T_SCANNER, T_FIXED, /* Ricoh IS-410 OEMed by IBM */
"IBM ", "2456-001 ", " "}, {
"IBM 2456",
SS_Q_WINDOW_DESC_LEN |
SS_Q_THRESHOLD |
SS_Q_HALFTONE |
SS_Q_BIT_ORDERING,
320, 0, 0, 0, { 2, 0x0a }, 0, 7,
RICOH_IS410,
ricoh_is410_sw, get_buffer_status, NULL
}},
{{T_SCANNER, T_FIXED,
"UMAX ", "UC630 ", " "}, {
"UMAX UC-630",
SS_Q_WINDOW_DESC_LEN |
SS_Q_HALFTONE,
0x2e, 0, 0, 0, { 0, 1 }, 0, 0,
UMAX_UC630,
umax_uc630_sw, NULL, NULL
}},
{{T_SCANNER, T_FIXED,
"UMAX ", "UG630 ", " "}, {
"UMAX UG-630",
SS_Q_WINDOW_DESC_LEN |
SS_Q_HALFTONE,
0x2e, 0, 0, 0, { 0, 1 }, 0, 0,
UMAX_UG630,
umax_uc630_sw, NULL, NULL
}},
#ifdef NOTYET /* ADF version */
{{T_SCANNER, T_FIXED,
"FUJITSU ", "M3096Gm ", " "}, {
"Fujitsu M3096G",
SS_Q_WINDOW_DESC_LEN |
SS_Q_BRIGHTNESS |
SS_Q_MONO_THRESHOLD |
SS_Q_HALFTONE |
SS_Q_SET_RIF |
SS_Q_PADDING_TYPE,
64, 0, 0, 0, { 0, 1 }, 0, 0,
FUJITSU_M3096G,
fujistsu_m3096g_sw, NULL, NULL
}},
#else /* flatbed-only version */
{{T_SCANNER, T_FIXED,
"FUJITSU ", "M3096Gm ", " "}, {
"Fujitsu M3096G",
SS_Q_BRIGHTNESS |
SS_Q_MONO_THRESHOLD |
SS_Q_CONTRAST |
SS_Q_HALFTONE |
SS_Q_PADDING_TYPE,
0, 0, 0, 0, { 0, 1 }, 0, 0,
FUJITSU_M3096G,
NULL, NULL, NULL
}},
#endif
};
int ssmatch(struct device *, void *, void *);
void ssattach(struct device *, struct device *, void *);
struct cfattach ss_ca = {
sizeof(struct ss_softc), ssmatch, ssattach
};
struct cfdriver ss_cd = {
NULL, "ss", DV_DULL
};
struct scsi_device ss_switch = {
NULL,
ssstart,
NULL,
NULL,
};
const struct scsi_inquiry_pattern ss_patterns[] = {
{T_SCANNER, T_FIXED,
"", "", ""},
{T_SCANNER, T_REMOV,
"", "", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C1750A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C1790A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C2500A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C2570A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C2520A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C1130A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C5110A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C6290A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C5190A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C7190A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C6270A ", ""},
{T_PROCESSOR, T_FIXED,
"HP ", "C7670A ", ""},
};
int
ssmatch(parent, match, aux)
struct device *parent;
void *match, *aux;
{
struct scsi_attach_args *sa = aux;
int priority;
(void)scsi_inqmatch(sa->sa_inqbuf,
ss_patterns, sizeof(ss_patterns)/sizeof(ss_patterns[0]),
sizeof(ss_patterns[0]), &priority);
return (priority);
}
/*
* The routine called by the low level scsi routine when it discovers
* A device suitable for this driver
* If it is a know special, call special attach routine to install
* special handlers into the ss_softc structure
*/
void
ssattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct ss_softc *ss = (void *)self;
struct scsi_attach_args *sa = aux;
struct scsi_link *sc_link = sa->sa_sc_link;
SC_DEBUG(sc_link, SDEV_DB2, ("ssattach:\n"));
/*
* Store information needed to contact our base driver
*/
ss->sc_link = sc_link;
sc_link->device = &ss_switch;
sc_link->device_softc = ss;
sc_link->openings = 1;
if (!bcmp(sa->sa_inqbuf->vendor, "MUSTEK", 6))
mustek_attach(ss, sa);
else if (!bcmp(sa->sa_inqbuf->vendor, "HP ", 8))
scanjet_attach(ss, sa);
else
ss_identify_scanner(ss, sa->sa_inqbuf);
/*
* populate the scanio struct with legal values
*/
ss->sio.scan_width = 1200;
ss->sio.scan_height = 1200;
ss->sio.scan_x_resolution = 100;
ss->sio.scan_y_resolution = 100;
ss->sio.scan_x_origin = 0;
ss->sio.scan_y_origin = 0;
ss->sio.scan_brightness = 128;
ss->sio.scan_contrast = 128;
ss->sio.scan_quality = 100;
ss->sio.scan_image_mode = SIM_GRAYSCALE;
/* XXX fill in the rest of the scan_io struct by calling the
compute_sizes routine */
/*
* Set up the buf queue for this device
*/
ss->buf_queue.b_active = 0;
ss->buf_queue.b_actf = 0;
ss->buf_queue.b_actb = &ss->buf_queue.b_actf;
}
void
ss_identify_scanner(ss, inqbuf)
struct ss_softc *ss;
struct scsi_inquiry_data *inqbuf;
{
const struct ss_quirk_inquiry_pattern *finger;
int priority;
/*
* look for non-standard scanners with help of the quirk table
* and install functions for special handling
*/
finger = (const struct ss_quirk_inquiry_pattern *)scsi_inqmatch(inqbuf,
ss_quirk_patterns,
sizeof(ss_quirk_patterns)/sizeof(ss_quirk_patterns[0]),
sizeof(ss_quirk_patterns[0]), &priority);
if (priority != 0) {
ss->quirkdata = &finger->quirkdata;
if (ss->quirkdata->special_minphys != NULL) {
ss->special.minphys = ss->quirkdata->special_minphys;
}
ss->sio.scan_scanner_type = ss->quirkdata->scanner_type;
printf("\n%s: %s\n", ss->sc_dev.dv_xname, ss->quirkdata->name);
} else {
printf("\n%s: generic scanner\n", ss->sc_dev.dv_xname);
bzero(&ss_gen_quirks, sizeof(ss_gen_quirks));
ss->quirkdata = &ss_gen_quirks;
ss->sio.scan_scanner_type = GENERIC_SCSI2;
}
}
/*
* open the device.
*/
int
ssopen(dev, flag, mode, p)
dev_t dev;
int flag;
int mode;
struct proc *p;
{
int unit;
u_int ssmode;
int error = 0;
struct ss_softc *ss;
struct scsi_link *sc_link;
unit = SSUNIT(dev);
if (unit >= ss_cd.cd_ndevs)
return (ENXIO);
ss = ss_cd.cd_devs[unit];
if (!ss)
return (ENXIO);
ssmode = SSMODE(dev);
sc_link = ss->sc_link;
SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev,
unit, ss_cd.cd_ndevs));
if (sc_link->flags & SDEV_OPEN) {
printf("%s: already open\n", ss->sc_dev.dv_xname);
return (EBUSY);
}
/*
* Catch any unit attention errors.
*
* SCSI_IGNORE_MEDIA_CHANGE: when you have an ADF, some scanners
* consider paper to be a changeable media
*
*/
error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES,
SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_ILLEGAL_REQUEST |
(ssmode == MODE_CONTROL ? SCSI_IGNORE_NOT_READY : 0));
if (error)
goto bad;
sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */
/*
* If the mode is 3 (e.g. minor = 3,7,11,15)
* then the device has been opened to set defaults
* This mode does NOT ALLOW I/O, only ioctls
*/
if (ssmode == MODE_CONTROL)
return (0);
SC_DEBUG(sc_link, SDEV_DB2, ("open complete\n"));
return (0);
bad:
sc_link->flags &= ~SDEV_OPEN;
return (error);
}
/*
* close the device.. only called if we are the LAST
* occurence of an open device
*/
int
ssclose(dev, flag, mode, p)
dev_t dev;
int flag;
int mode;
struct proc *p;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
int error;
SC_DEBUG(ss->sc_link, SDEV_DB1, ("closing\n"));
if (SSMODE(dev) == MODE_REWIND) {
if (ss->special.rewind_scanner) {
/* call special handler to rewind/abort scan */
error = (ss->special.rewind_scanner)(ss);
if (error)
return (error);
} else {
/* XXX add code to restart a SCSI2 scanner, if any */
}
ss->sio.scan_window_size = 0;
ss->flags &= ~SSF_TRIGGERED;
}
ss->sc_link->flags &= ~SDEV_OPEN;
return (0);
}
/*
* trim the size of the transfer if needed,
* called by physio
* basically the smaller of our min and the scsi driver's
* minphys
*/
void
ssminphys(bp)
struct buf *bp;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(bp->b_dev)];
(ss->sc_link->adapter->scsi_minphys)(bp);
/*
* trim the transfer further for special devices this is
* because some scanners only read multiples of a line at a
* time, also some cannot disconnect, so the read must be
* short enough to happen quickly
*/
if (ss->special.minphys)
(ss->special.minphys)(ss, bp);
}
/*
* Do a read on a device for a user process.
* Prime scanner at start of read, check uio values, call ssstrategy
* via physio for the actual transfer.
*/
int
ssread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
int error;
/* if the scanner has not yet been started, do it now */
if (!(ss->flags & SSF_TRIGGERED)) {
if (ss->special.trigger_scanner) {
error = (ss->special.trigger_scanner)(ss);
if (error)
return (error);
} else {
struct scsi_start_stop trigger_cmd;
bzero(&trigger_cmd, sizeof(trigger_cmd));
trigger_cmd.opcode = START_STOP;
trigger_cmd.how = SSS_START;
scsi_scsi_cmd(ss->sc_link,
(struct scsi_generic *)&trigger_cmd,
sizeof(trigger_cmd), 0, 0, 4, 5000, NULL, 0);
}
ss->flags |= SSF_TRIGGERED;
}
return (physio(ssstrategy, NULL, dev, B_READ, ssminphys, uio));
}
/*
* Actually translate the requested transfer into one the physical
* driver can understand The transfer is described by a buf and will
* include only one physical transfer.
*/
void
ssstrategy(bp)
struct buf *bp;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(bp->b_dev)];
struct buf *dp;
int s;
SC_DEBUG(ss->sc_link, SDEV_DB2, ("ssstrategy: %ld bytes @ blk %d\n",
bp->b_bcount, bp->b_blkno));
if (bp->b_bcount > ss->sio.scan_window_size)
bp->b_bcount = ss->sio.scan_window_size;
/*
* If it's a null transfer, return immediately
*/
if (bp->b_bcount == 0)
goto done;
s = splbio();
/*
* Place it in the queue of activities for this scanner
* at the end (a bit silly because we only have on user..)
* (but it could fork() or dup())
*/
dp = &ss->buf_queue;
bp->b_actf = NULL;
bp->b_actb = dp->b_actb;
*dp->b_actb = bp;
dp->b_actb = &bp->b_actf;
/*
* Tell the device to get going on the transfer if it's
* not doing anything, otherwise just wait for completion
* (All a bit silly if we're only allowing 1 open but..)
*/
ssstart(ss);
splx(s);
return;
done:
/*
* Correctly set the buf to indicate a completed xfer
*/
bp->b_resid = bp->b_bcount;
s = splbio();
biodone(bp);
splx(s);
}
/*
* ssstart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
* It dequeues the buf and creates a scsi command to perform the
* transfer required. The transfer request will call scsi_done
* on completion, which will in turn call this routine again
* so that the next queued transfer is performed.
* The bufs are queued by the strategy routine (ssstrategy)
*
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
* ssstart() is called at splbio
*/
void
ssstart(v)
void *v;
{
struct ss_softc *ss = v;
struct scsi_link *sc_link = ss->sc_link;
struct buf *bp, *dp;
struct scsi_r_scanner read_cmd;
int flags;
SC_DEBUG(sc_link, SDEV_DB2, ("ssstart\n"));
/*
* See if there is a buf to do and we are not already
* doing one
*/
while (sc_link->openings > 0) {
/* if a special awaits, let it proceed first */
if (sc_link->flags & SDEV_WAITING) {
sc_link->flags &= ~SDEV_WAITING;
wakeup((caddr_t)sc_link);
return;
}
/*
* See if there is a buf with work for us to do..
*/
dp = &ss->buf_queue;
if ((bp = dp->b_actf) == NULL)
return;
if ((dp = bp->b_actf) != NULL)
dp->b_actb = bp->b_actb;
else
ss->buf_queue.b_actb = bp->b_actb;
*bp->b_actb = dp;
if (ss->special.read) {
(ss->special.read)(ss, bp);
} else {
/* generic scsi2 scanner read */
bzero(&read_cmd, sizeof(read_cmd));
read_cmd.opcode = READ_BIG;
_lto3b(bp->b_bcount, read_cmd.len);
flags = SCSI_DATA_IN;
/*
* go ask the adapter to do all this for us
*/
if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)
&read_cmd, sizeof(read_cmd), (u_char *) bp->b_data,
bp->b_bcount, 0, 100000, bp, flags | SCSI_NOSLEEP))
printf("%s: not queued\n", ss->sc_dev.dv_xname);
}
}
}
/*
* Perform special action on behalf of the user;
* knows about the internals of this device
*/
int
ssioctl(dev, cmd, addr, flag, p)
dev_t dev;
u_long cmd;
caddr_t addr;
int flag;
struct proc *p;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
int error = 0;
struct scan_io *sio;
switch (cmd) {
case SCIOCGET:
/* call special handler, if any */
if (ss->special.get_params) {
error = (ss->special.get_params)(ss);
if (error)
return (error);
}
bcopy(&ss->sio, addr, sizeof(struct scan_io));
break;
case SCIOCSET:
sio = (struct scan_io *)addr;
/* call special handler, if any */
if (ss->special.set_params) {
error = (ss->special.set_params)(ss, sio);
if (error)
return (error);
} else {
/* XXX add routine to validate parameters */
ss_set_window(ss, sio);
}
break;
case SCIOCRESTART:
/* call special handler, if any */
if (ss->special.rewind_scanner ) {
error = (ss->special.rewind_scanner)(ss);
if (error)
return (error);
} else
/* XXX add code for SCSI2 scanner, if any */
return (EOPNOTSUPP);
ss->flags &= ~SSF_TRIGGERED;
break;
case SCIOC_USE_ADF:
/* XXX add Automatic Document Feeder Support */
return (EOPNOTSUPP);
default:
if (SSMODE(dev) != MODE_CONTROL)
return (ENOTTY);
return (scsi_do_ioctl(ss->sc_link, dev, cmd, addr,
flag, p));
}
return (error);
}
int
ss_set_window(ss, sio)
struct ss_softc *ss;
struct scan_io *sio;
{
struct scsi_set_window window_cmd;
struct {
struct scsi_window_data window_data;
/* vendor_unique must provide enough space for worst case
* (currently Ricoh IS-410.) 40 + 280 = 320 which is the size
* of its window descriptor length
*/
u_int8_t vendor_unique[280];
} wd;
#define window_data wd.window_data
#define vendor_unique wd.vendor_unique
struct scsi_link *sc_link = ss->sc_link;
/*
* The CDB for SET WINDOW goes in here.
* The two structures that follow are sent via data out.
*/
bzero(&window_cmd, sizeof(window_cmd));
window_cmd.opcode = SET_WINDOW;
_lto3l(sizeof(window_data), window_cmd.len);
bzero(&window_data, sizeof(window_data));
if (ss->quirkdata->quirks & SS_Q_WINDOW_DESC_LEN)
_lto2l(ss->quirkdata->window_descriptor_length,
window_data.window_desc_len);
else
_lto2l(40L, window_data.window_desc_len);
/* start of SET_WINDOW parameter block */
/* leave window id at zero */
/* leave auto bit at zero */
_lto2l(sio->scan_x_resolution, window_data.x_res);
_lto2l(sio->scan_y_resolution, window_data.y_res);
_lto4l(sio->scan_x_origin, window_data.x_org);
_lto4l(sio->scan_y_origin, window_data.y_org);
_lto4l(sio->scan_width, window_data.width);
_lto4l(sio->scan_height, window_data.length);
if (ss->quirkdata->quirks & SS_Q_REV_BRIGHTNESS)
window_data.brightness = 256 - sio->scan_brightness;
else if (ss->quirkdata->quirks & SS_Q_BRIGHTNESS)
window_data.brightness = ss->quirkdata->brightness;
else
window_data.brightness = sio->scan_brightness;
/*
* threshold: Default is to follow brightness.
* If SS_Q_MONO_THRESHOLD is set then the quirkdata contains a special
* value to be used instead of default when image data is monochrome.
* Otherwise if SS_Q_THRESHOLD is set then the quirkdata contains
* the threshold to always use.
* Both SS_Q_MONO_THRESHOLD and SS_Q_THRESHOLD should not be set at
* the same time.
*/
if (ss->quirkdata->quirks & SS_Q_MONO_THRESHOLD) {
if (sio->scan_image_mode == SIM_BINARY_MONOCHROME ||
sio->scan_image_mode == SIM_DITHERED_MONOCHROME)
window_data.threshold = ss->quirkdata->threshold;
else
window_data.threshold = sio->scan_brightness;
} else if (ss->quirkdata->quirks & SS_Q_THRESHOLD)
window_data.threshold = ss->quirkdata->threshold;
else
window_data.threshold = sio->scan_brightness;
if (ss->quirkdata->quirks & SS_Q_REV_CONTRAST)
window_data.contrast = 256 - sio->scan_contrast;
else if (ss->quirkdata->quirks & SS_Q_CONTRAST)
window_data.contrast = ss->quirkdata->contrast;
else
window_data.contrast = sio->scan_contrast;
switch (sio->scan_image_mode) {
case SIM_RED:
case SIM_GREEN:
case SIM_BLUE:
window_data.image_comp = SIM_GRAYSCALE;
break;
default:
window_data.image_comp = sio->scan_image_mode;
}
window_data.bits_per_pixel = sio->scan_bits_per_pixel;
if (ss->quirkdata->quirks & SS_Q_HALFTONE) {
window_data.halftone_pattern[0] =
ss->quirkdata->halftone_pattern[0];
window_data.halftone_pattern[1] =
ss->quirkdata->halftone_pattern[1];
} /* else leave halftone set to zero. */
if (ss->quirkdata->quirks & SS_Q_SET_RIF)
window_data.rif = 1;
if (ss->quirkdata->quirks & SS_Q_PADDING_TYPE)
window_data.pad_type = ss->quirkdata->pad_type;
else
window_data.pad_type = 3; /* 3 = truncate to byte boundary */
if (ss->quirkdata->quirks & SS_Q_BIT_ORDERING)
_lto2l(ss->quirkdata->bit_ordering, window_data.bit_ordering);
/* else leave bit_ordering set to zero. */
/* leave compression type & argument set to zero. */
#undef window_data
if (ss->quirkdata->vendor_unique_sw != NULL)
return ((*ss->quirkdata->vendor_unique_sw)(ss, sio,
&window_cmd, (void *)&wd));
else
/* send the command to the scanner */
return (scsi_scsi_cmd(sc_link,
(struct scsi_generic *)&window_cmd,
sizeof(window_cmd), (u_char *) &wd.window_data,
(ss->quirkdata->quirks & SS_Q_WINDOW_DESC_LEN) ?
ss->quirkdata->window_descriptor_length : 40,
4, 5000, NULL, SCSI_DATA_OUT));
}
int
ricoh_is410_sw(ss, sio, wcmd, vwd)
struct ss_softc *ss;
struct scan_io *sio;
struct scsi_set_window *wcmd;
void *vwd;
{
struct ricoh_is410_window_data {
struct scsi_window_data window_data;
u_int8_t res1;
u_int8_t res2;
u_int mrif:1; /* reverse image format (grayscale negative) */
u_int filtering:3;
u_int gamma_id:4;
} *rwd = (struct ricoh_is410_window_data*)vwd;
struct scsi_link *sc_link = ss->sc_link;
rwd->mrif = 1; /* force grayscale to match PGM */
/* send the command to the scanner */
return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
sizeof(struct scsi_set_window), (u_char *)rwd,
sizeof(struct ricoh_is410_window_data), 4, 5000, NULL,
SCSI_DATA_OUT));
}
int
umax_uc630_sw(ss, sio, wcmd, vwd)
struct ss_softc *ss;
struct scan_io *sio;
struct scsi_set_window *wcmd;
void *vwd;
{
struct umax_uc630_window_data {
struct scsi_window_data window_data;
u_int8_t speed;
u_int8_t select_color;
u_int8_t highlight;
u_int8_t shadow;
u_int8_t paper_length[2];
} *uwd = (struct umax_uc630_window_data*)vwd;
struct scsi_link *sc_link = ss->sc_link;
uwd->speed = 1; /* speed: fastest speed that doesn't smear */
switch (sio->scan_image_mode) { /* UMAX has three-pass color. */
case SIM_RED: /* This selects which filter to use. */
uwd->select_color = 0x80;
break;
case SIM_GREEN:
uwd->select_color = 0x40;
break;
case SIM_BLUE:
uwd->select_color = 0x20;
break;
}
uwd->highlight = 50; /* 50 = highest; 0 = lowest */
/* leave shadow set to zero. */
/* XXX paper length is for ADF */
/* send the command to the scanner */
return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
sizeof(struct scsi_set_window), (u_char *)uwd,
sizeof(struct umax_uc630_window_data), 4, 5000, NULL,
SCSI_DATA_OUT));
}
#ifdef NOTYET /* for ADF support */
int
fujitsu_m3096g_sw(ss, sio, wcmd, vwd)
struct ss_softc *ss;
struct scan_io *sio;
struct scsi_set_window *wcmd;
void *vwd;
{
struct fujitsu_m3096g_window_data {
struct scsi_window_data window_data;
u_int8_t id;
u_int8_t res1;
u_int8_t outline;
u_int8_t emphasis;
u_int8_t mixed;
u_int8_t mirroring;
u_int8_t res2[5];
u_int8_t subwindow_list[2];
u_int paper_size_std:2;
u_int res3:1;
u_int paper_orientaton:1;
u_int paper_size_type:4;
/* defines for Paper Size Type: */
#define FUJITSU_PST_A3 0x03
#define FUJITSU_PST_A4 0x04
#define FUJITSU_PST_A5 0x05
#define FUJITSU_PST_DOUBLE_LETTER 0x06
#define FUJITSU_PST_LETTER 0x07
#define FUJITSU_PST_B4 0x0C
#define FUJITSU_PST_B5 0x0D
#define FUJITSU_PST_LEGAL 0x0F
u_int8_t paper_width_x[4];
u_int8_t paper_width_y[4];
u_int8_t res4[2];
} *fwd = (struct fujitsu_m3096g_window_data*)vwd;
struct scsi_link *sc_link = ss->sc_link;
/* send the command to the scanner */
return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
sizeof(struct scsi_set_window), (u_char *)fwd,
sizeof(struct fujitsu_m3096g_window_data), 4, 5000, NULL,
SCSI_DATA_OUT));
}
#endif
void
get_buffer_status(ss, bp)
struct ss_softc *ss;
struct buf *bp;
{
struct scsi_get_buffer_status gbs_cmd;
struct scsi_link *sc_link = ss->sc_link;
struct {
u_int8_t stat_len[3];
u_int8_t res1;
u_int8_t window_id;
u_int8_t res2;
u_int8_t tgt_accept_buf_len[3];
u_int8_t tgt_send_buf_len[3];
} buf_sz_retn;
int flags;
bzero(&gbs_cmd, sizeof(gbs_cmd));
gbs_cmd.opcode = GET_BUFFER_STATUS;
_lto2b(12, gbs_cmd.len);
flags = SCSI_DATA_IN;
if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &gbs_cmd,
sizeof(gbs_cmd), (u_char *) &buf_sz_retn, sizeof(buf_sz_retn),
0, 100000, bp, flags | SCSI_NOSLEEP)) {
printf("%s: not queued\n", ss->sc_dev.dv_xname);
}
bp->b_bcount = MIN(_3btol(buf_sz_retn.tgt_send_buf_len), bp->b_bcount);
}
#ifdef NOTYET
int
umax_compute_sizes(ss)
struct ss_softc *ss;
{
ss->sio.scan_lines = ;
ss->sio.scan_window_size = ;
}
int
calc_umax_row_len(dpi, ww)
int dpi;
int ww;
{
int st[301];
int i;
int rowB = 0;
for (i = 1; i <= 300; i++)
st[i] = 1;
for (i = 1; i <= 300 - dpi; i++)
st[i * 300 / (300 - dpi)] = 0;
for (i = 1; i <= (ww % 1200) / 4; i++) {
if (st[i])
rowB++;
}
return ((ww / 1200) * dpi + rowB);
}
#endif