[BACK]Return to ss.c CVS log [TXT][DIR] Up to [local] / sys / scsi

Annotation of sys/scsi/ss.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: ss.c,v 1.58 2006/12/21 02:05:46 krw Exp $     */
        !             2: /*     $NetBSD: ss.c,v 1.10 1996/05/05 19:52:55 christos Exp $ */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1995, 1997 Kenneth Stailey.  All rights reserved.
        !             6:  *   modified for configurable scanner support by Joachim Koenig
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            16:  * 3. All advertising materials mentioning features or use of this software
        !            17:  *    must display the following acknowledgement:
        !            18:  *     This product includes software developed by Kenneth Stailey.
        !            19:  * 4. The name of the author may not be used to endorse or promote products
        !            20:  *    derived from this software without specific prior written permission.
        !            21:  *
        !            22:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            23:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
        !            24:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
        !            25:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
        !            26:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
        !            27:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
        !            28:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
        !            29:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            30:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
        !            31:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            32:  */
        !            33:
        !            34: #include <sys/param.h>
        !            35: #include <sys/systm.h>
        !            36: #include <sys/fcntl.h>
        !            37: #include <sys/errno.h>
        !            38: #include <sys/ioctl.h>
        !            39: #include <sys/malloc.h>
        !            40: #include <sys/buf.h>
        !            41: #include <sys/proc.h>
        !            42: #include <sys/user.h>
        !            43: #include <sys/device.h>
        !            44: #include <sys/conf.h>
        !            45: #include <sys/scanio.h>
        !            46:
        !            47: #include <scsi/scsi_all.h>
        !            48: #include <scsi/scsi_scanner.h>
        !            49: #include <scsi/scsiconf.h>
        !            50: #include <scsi/ssvar.h>
        !            51:
        !            52: #include <scsi/ss_mustek.h>
        !            53:
        !            54: #define SSMODE(z)      ( minor(z)       & 0x03)
        !            55: #define SSUNIT(z)      ((minor(z) >> 4)       )
        !            56:
        !            57: /*
        !            58:  * If the mode is 3 (e.g. minor = 3,7,11,15)
        !            59:  * then the device has been openned to set defaults
        !            60:  * This mode does NOT ALLOW I/O, only ioctls
        !            61:  */
        !            62: #define MODE_REWIND    0
        !            63: #define MODE_NONREWIND 1
        !            64: #define MODE_CONTROL   3
        !            65:
        !            66: struct quirkdata {
        !            67:        char *name;
        !            68:        u_int quirks;
        !            69: #define SS_Q_WINDOW_DESC_LEN   0x0001 /* needs special WDL */
        !            70: #define SS_Q_BRIGHTNESS                0x0002 /* needs special value for brightness */
        !            71: #define SS_Q_REV_BRIGHTNESS    0x0004 /* reverse brightness control in s/w */
        !            72: #define SS_Q_THRESHOLD         0x0008 /* needs special value for threshold */
        !            73: #define SS_Q_MONO_THRESHOLD    0x0010 /* same as SS_Q_THRESHOLD but only
        !            74:                                        * for monochrome image data */
        !            75: #define SS_Q_CONTRAST          0x0020 /* needs special value for contrast */
        !            76: #define SS_Q_REV_CONTRAST      0x0040 /* reverse contrast control in s/w */
        !            77: #define SS_Q_HALFTONE          0x0080 /* uses non-zero halftone */
        !            78: #define SS_Q_SET_RIF           0x0100 /* set RIF bit */
        !            79: #define SS_Q_PADDING_TYPE      0x0200 /* does not truncate to byte boundary */
        !            80: #define SS_Q_BIT_ORDERING      0x0400 /* needs non-zero bit ordering */
        !            81:        long window_descriptor_length;
        !            82:        u_int8_t brightness;
        !            83:        u_int8_t threshold;
        !            84:        u_int8_t contrast;
        !            85:        u_int8_t halftone_pattern[2];
        !            86:        int pad_type;
        !            87:        long bit_ordering;
        !            88:        u_int8_t scanner_type;
        !            89:        /*
        !            90:         * To enable additional scanner options, point vendor_unique_sw
        !            91:         * at a function that adds more stuff to the SET_WINDOW parameters.
        !            92:         */
        !            93:        int     (*vendor_unique_sw)(struct ss_softc *, struct scan_io *,
        !            94:                                        struct scsi_set_window *, void *);
        !            95:        /*
        !            96:         * If the scanner requires use of GET_BUFFER_STATUS before READ
        !            97:         * it can be called from ss_minphys().
        !            98:         */
        !            99:        void    (*special_minphys)(struct ss_softc *, struct buf *);
        !           100:
        !           101:        /*
        !           102:         *
        !           103:         */
        !           104:        int     (*compute_sizes)(void);
        !           105: };
        !           106:
        !           107: struct ss_quirk_inquiry_pattern {
        !           108:        struct scsi_inquiry_pattern pattern;
        !           109:        struct quirkdata quirkdata;
        !           110: };
        !           111:
        !           112: struct  quirkdata ss_gen_quirks = {
        !           113:        "generic", 0, 0, 0, 0, 0,
        !           114:        {0, 0}, 0, 0, GENERIC_SCSI2,
        !           115:        NULL, NULL, NULL
        !           116: };
        !           117:
        !           118: void    ssstrategy(struct buf *);
        !           119: void    ssstart(void *);
        !           120: void   ssminphys(struct buf *);
        !           121:
        !           122: void   ss_identify_scanner(struct ss_softc *, struct scsi_inquiry_data*);
        !           123: int    ss_set_window(struct ss_softc *, struct scan_io *);
        !           124:
        !           125: int    ricoh_is410_sw(struct ss_softc *, struct scan_io *,
        !           126:                            struct scsi_set_window *, void *);
        !           127: int    umax_uc630_sw(struct ss_softc *, struct scan_io *,
        !           128:                           struct scsi_set_window *, void *);
        !           129: #ifdef NOTYET  /* for ADF support  */
        !           130: int    fujitsu_m3096g_sw(struct ss_softc *, struct scan_io *,
        !           131:                               struct scsi_set_window *, void *);
        !           132: #endif
        !           133:
        !           134: void   get_buffer_status(struct ss_softc *, struct buf *);
        !           135:
        !           136: /*
        !           137:  * WDL:
        !           138:  *
        !           139:  *  Ricoh IS-50 & IS-410 insist on 320 (even it transfer len is less.)
        !           140:  *  Ricoh FS-1 accepts 256 (I haven't tested other values.)
        !           141:  *  UMAX UC-630 accepts 46 (I haven't tested other values.)
        !           142:  *  Fujitsu M3096G wants 40 <= x <= 248 (tested OK at 40 & 64.)
        !           143:  */
        !           144:
        !           145: const struct ss_quirk_inquiry_pattern ss_quirk_patterns[] = {
        !           146:        {{T_SCANNER, T_FIXED,
        !           147:         "ULTIMA  ", "AT3     1.60    ", "    "}, {
        !           148:                 "Ultima AT3",
        !           149:                 SS_Q_HALFTONE |
        !           150:                 SS_Q_PADDING_TYPE,
        !           151:                 0, 0, 0, 0, { 3, 0 }, 0, 0,
        !           152:                 ULTIMA_AT3,
        !           153:                 NULL, NULL, NULL
        !           154:         }},
        !           155:        {{T_SCANNER, T_FIXED,
        !           156:         "ULTIMA  ", "A6000C PLUS     ", "    "}, {
        !           157:                 "Ultima A6000C",
        !           158:                 SS_Q_HALFTONE |
        !           159:                 SS_Q_PADDING_TYPE,
        !           160:                 0, 0, 0, 0, { 3, 0 }, 0, 0,
        !           161:                 ULTIMA_AC6000C,
        !           162:                 NULL, NULL, NULL
        !           163:         }},
        !           164:        {{T_SCANNER, T_FIXED,
        !           165:         "RICOH   ", "IS50            ", "    "}, {
        !           166:                 "Ricoh IS-50",
        !           167:                 SS_Q_WINDOW_DESC_LEN |
        !           168:                 SS_Q_REV_BRIGHTNESS |
        !           169:                 SS_Q_THRESHOLD |
        !           170:                 SS_Q_REV_CONTRAST |
        !           171:                 SS_Q_HALFTONE |
        !           172:                 SS_Q_BIT_ORDERING,
        !           173:                 320, 0, 0, 0, { 2, 0x0a }, 0, 7,
        !           174:                 RICOH_IS50,
        !           175:                 ricoh_is410_sw, get_buffer_status, NULL
        !           176:         }},
        !           177:        {{T_SCANNER, T_FIXED,
        !           178:         "RICOH   ", "IS410           ", "    "}, {
        !           179:                 "Ricoh IS-410",
        !           180:                 SS_Q_WINDOW_DESC_LEN |
        !           181:                 SS_Q_THRESHOLD |
        !           182:                 SS_Q_HALFTONE |
        !           183:                 SS_Q_BIT_ORDERING,
        !           184:                 320, 0, 0, 0, { 2, 0x0a }, 0, 7,
        !           185:                 RICOH_IS410,
        !           186:                 ricoh_is410_sw, get_buffer_status, NULL
        !           187:         }},
        !           188:        {{T_SCANNER, T_FIXED,          /* Ricoh IS-410 OEMed by IBM */
        !           189:         "IBM     ", "2456-001        ", "    "}, {
        !           190:                 "IBM 2456",
        !           191:                 SS_Q_WINDOW_DESC_LEN |
        !           192:                 SS_Q_THRESHOLD |
        !           193:                 SS_Q_HALFTONE |
        !           194:                 SS_Q_BIT_ORDERING,
        !           195:                 320, 0, 0, 0, { 2, 0x0a }, 0, 7,
        !           196:                 RICOH_IS410,
        !           197:                 ricoh_is410_sw, get_buffer_status, NULL
        !           198:         }},
        !           199:        {{T_SCANNER, T_FIXED,
        !           200:         "UMAX    ", "UC630           ", "    "}, {
        !           201:                 "UMAX UC-630",
        !           202:                 SS_Q_WINDOW_DESC_LEN |
        !           203:                 SS_Q_HALFTONE,
        !           204:                 0x2e, 0, 0, 0, { 0, 1 }, 0, 0,
        !           205:                 UMAX_UC630,
        !           206:                 umax_uc630_sw, NULL, NULL
        !           207:         }},
        !           208:        {{T_SCANNER, T_FIXED,
        !           209:         "UMAX    ", "UG630           ", "    "}, {
        !           210:                 "UMAX UG-630",
        !           211:                 SS_Q_WINDOW_DESC_LEN |
        !           212:                 SS_Q_HALFTONE,
        !           213:                 0x2e, 0, 0, 0, { 0, 1 }, 0, 0,
        !           214:                 UMAX_UG630,
        !           215:                 umax_uc630_sw, NULL, NULL
        !           216:         }},
        !           217: #ifdef NOTYET                  /* ADF version */
        !           218:        {{T_SCANNER, T_FIXED,
        !           219:         "FUJITSU ", "M3096Gm         ", "    "}, {
        !           220:                 "Fujitsu M3096G",
        !           221:                 SS_Q_WINDOW_DESC_LEN |
        !           222:                 SS_Q_BRIGHTNESS |
        !           223:                 SS_Q_MONO_THRESHOLD |
        !           224:                 SS_Q_HALFTONE |
        !           225:                 SS_Q_SET_RIF |
        !           226:                 SS_Q_PADDING_TYPE,
        !           227:                 64, 0, 0, 0, { 0, 1 }, 0, 0,
        !           228:                 FUJITSU_M3096G,
        !           229:                 fujistsu_m3096g_sw, NULL, NULL
        !           230:         }},
        !           231: #else                          /* flatbed-only version */
        !           232:        {{T_SCANNER, T_FIXED,
        !           233:         "FUJITSU ", "M3096Gm         ", "    "}, {
        !           234:                 "Fujitsu M3096G",
        !           235:                 SS_Q_BRIGHTNESS |
        !           236:                 SS_Q_MONO_THRESHOLD |
        !           237:                 SS_Q_CONTRAST |
        !           238:                 SS_Q_HALFTONE |
        !           239:                 SS_Q_PADDING_TYPE,
        !           240:                 0, 0, 0, 0, { 0, 1 }, 0, 0,
        !           241:                 FUJITSU_M3096G,
        !           242:                 NULL, NULL, NULL
        !           243:         }},
        !           244: #endif
        !           245: };
        !           246:
        !           247:
        !           248: int ssmatch(struct device *, void *, void *);
        !           249: void ssattach(struct device *, struct device *, void *);
        !           250:
        !           251: struct cfattach ss_ca = {
        !           252:        sizeof(struct ss_softc), ssmatch, ssattach
        !           253: };
        !           254:
        !           255: struct cfdriver ss_cd = {
        !           256:        NULL, "ss", DV_DULL
        !           257: };
        !           258:
        !           259: struct scsi_device ss_switch = {
        !           260:        NULL,
        !           261:        ssstart,
        !           262:        NULL,
        !           263:        NULL,
        !           264: };
        !           265:
        !           266: const struct scsi_inquiry_pattern ss_patterns[] = {
        !           267:        {T_SCANNER, T_FIXED,
        !           268:         "",         "",                 ""},
        !           269:        {T_SCANNER, T_REMOV,
        !           270:         "",         "",                 ""},
        !           271:        {T_PROCESSOR, T_FIXED,
        !           272:         "HP      ", "C1750A          ", ""},
        !           273:        {T_PROCESSOR, T_FIXED,
        !           274:         "HP      ", "C1790A          ", ""},
        !           275:        {T_PROCESSOR, T_FIXED,
        !           276:         "HP      ", "C2500A          ", ""},
        !           277:        {T_PROCESSOR, T_FIXED,
        !           278:         "HP      ", "C2570A          ", ""},
        !           279:        {T_PROCESSOR, T_FIXED,
        !           280:         "HP      ", "C2520A          ", ""},
        !           281:        {T_PROCESSOR, T_FIXED,
        !           282:         "HP      ", "C1130A          ", ""},
        !           283:        {T_PROCESSOR, T_FIXED,
        !           284:         "HP      ", "C5110A          ", ""},
        !           285:        {T_PROCESSOR, T_FIXED,
        !           286:         "HP      ", "C6290A          ", ""},
        !           287:        {T_PROCESSOR, T_FIXED,
        !           288:         "HP      ", "C5190A          ", ""},
        !           289:        {T_PROCESSOR, T_FIXED,
        !           290:         "HP      ", "C7190A          ", ""},
        !           291:        {T_PROCESSOR, T_FIXED,
        !           292:         "HP      ", "C6270A          ", ""},
        !           293:        {T_PROCESSOR, T_FIXED,
        !           294:         "HP      ", "C7670A          ", ""},
        !           295: };
        !           296:
        !           297: int
        !           298: ssmatch(parent, match, aux)
        !           299:        struct device *parent;
        !           300:        void *match, *aux;
        !           301: {
        !           302:        struct scsi_attach_args *sa = aux;
        !           303:        int priority;
        !           304:
        !           305:        (void)scsi_inqmatch(sa->sa_inqbuf,
        !           306:            ss_patterns, sizeof(ss_patterns)/sizeof(ss_patterns[0]),
        !           307:            sizeof(ss_patterns[0]), &priority);
        !           308:        return (priority);
        !           309: }
        !           310:
        !           311: /*
        !           312:  * The routine called by the low level scsi routine when it discovers
        !           313:  * A device suitable for this driver
        !           314:  * If it is a know special, call special attach routine to install
        !           315:  * special handlers into the ss_softc structure
        !           316:  */
        !           317: void
        !           318: ssattach(parent, self, aux)
        !           319:        struct device *parent, *self;
        !           320:        void *aux;
        !           321: {
        !           322:        struct ss_softc *ss = (void *)self;
        !           323:        struct scsi_attach_args *sa = aux;
        !           324:        struct scsi_link *sc_link = sa->sa_sc_link;
        !           325:
        !           326:        SC_DEBUG(sc_link, SDEV_DB2, ("ssattach:\n"));
        !           327:
        !           328:        /*
        !           329:         * Store information needed to contact our base driver
        !           330:         */
        !           331:        ss->sc_link = sc_link;
        !           332:        sc_link->device = &ss_switch;
        !           333:        sc_link->device_softc = ss;
        !           334:        sc_link->openings = 1;
        !           335:
        !           336:        if (!bcmp(sa->sa_inqbuf->vendor, "MUSTEK", 6))
        !           337:                mustek_attach(ss, sa);
        !           338:        else if (!bcmp(sa->sa_inqbuf->vendor, "HP      ", 8))
        !           339:                scanjet_attach(ss, sa);
        !           340:        else
        !           341:                ss_identify_scanner(ss, sa->sa_inqbuf);
        !           342:
        !           343:        /*
        !           344:         * populate the scanio struct with legal values
        !           345:         */
        !           346:        ss->sio.scan_width              = 1200;
        !           347:        ss->sio.scan_height             = 1200;
        !           348:        ss->sio.scan_x_resolution       = 100;
        !           349:        ss->sio.scan_y_resolution       = 100;
        !           350:        ss->sio.scan_x_origin           = 0;
        !           351:        ss->sio.scan_y_origin           = 0;
        !           352:        ss->sio.scan_brightness         = 128;
        !           353:        ss->sio.scan_contrast           = 128;
        !           354:        ss->sio.scan_quality            = 100;
        !           355:        ss->sio.scan_image_mode         = SIM_GRAYSCALE;
        !           356:
        !           357:        /* XXX fill in the rest of the scan_io struct by calling the
        !           358:           compute_sizes routine */
        !           359:
        !           360:        /*
        !           361:         * Set up the buf queue for this device
        !           362:         */
        !           363:        ss->buf_queue.b_active = 0;
        !           364:        ss->buf_queue.b_actf = 0;
        !           365:        ss->buf_queue.b_actb = &ss->buf_queue.b_actf;
        !           366: }
        !           367:
        !           368: void
        !           369: ss_identify_scanner(ss, inqbuf)
        !           370:        struct ss_softc *ss;
        !           371:        struct scsi_inquiry_data *inqbuf;
        !           372: {
        !           373:        const struct ss_quirk_inquiry_pattern *finger;
        !           374:        int priority;
        !           375:        /*
        !           376:         * look for non-standard scanners with help of the quirk table
        !           377:         * and install functions for special handling
        !           378:         */
        !           379:        finger = (const struct ss_quirk_inquiry_pattern *)scsi_inqmatch(inqbuf,
        !           380:            ss_quirk_patterns,
        !           381:            sizeof(ss_quirk_patterns)/sizeof(ss_quirk_patterns[0]),
        !           382:            sizeof(ss_quirk_patterns[0]), &priority);
        !           383:        if (priority != 0) {
        !           384:                ss->quirkdata = &finger->quirkdata;
        !           385:                if (ss->quirkdata->special_minphys != NULL) {
        !           386:                        ss->special.minphys = ss->quirkdata->special_minphys;
        !           387:                }
        !           388:                ss->sio.scan_scanner_type = ss->quirkdata->scanner_type;
        !           389:                printf("\n%s: %s\n", ss->sc_dev.dv_xname, ss->quirkdata->name);
        !           390:        } else {
        !           391:                printf("\n%s: generic scanner\n", ss->sc_dev.dv_xname);
        !           392:                bzero(&ss_gen_quirks, sizeof(ss_gen_quirks));
        !           393:                ss->quirkdata = &ss_gen_quirks;
        !           394:                ss->sio.scan_scanner_type = GENERIC_SCSI2;
        !           395:        }
        !           396: }
        !           397:
        !           398: /*
        !           399:  * open the device.
        !           400:  */
        !           401: int
        !           402: ssopen(dev, flag, mode, p)
        !           403:        dev_t dev;
        !           404:        int flag;
        !           405:        int mode;
        !           406:        struct proc *p;
        !           407: {
        !           408:        int unit;
        !           409:        u_int ssmode;
        !           410:        int error = 0;
        !           411:        struct ss_softc *ss;
        !           412:        struct scsi_link *sc_link;
        !           413:
        !           414:        unit = SSUNIT(dev);
        !           415:        if (unit >= ss_cd.cd_ndevs)
        !           416:                return (ENXIO);
        !           417:        ss = ss_cd.cd_devs[unit];
        !           418:        if (!ss)
        !           419:                return (ENXIO);
        !           420:
        !           421:        ssmode = SSMODE(dev);
        !           422:        sc_link = ss->sc_link;
        !           423:
        !           424:        SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev,
        !           425:            unit, ss_cd.cd_ndevs));
        !           426:
        !           427:        if (sc_link->flags & SDEV_OPEN) {
        !           428:                printf("%s: already open\n", ss->sc_dev.dv_xname);
        !           429:                return (EBUSY);
        !           430:        }
        !           431:
        !           432:        /*
        !           433:         * Catch any unit attention errors.
        !           434:         *
        !           435:         * SCSI_IGNORE_MEDIA_CHANGE: when you have an ADF, some scanners
        !           436:         * consider paper to be a changeable media
        !           437:         *
        !           438:         */
        !           439:        error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES,
        !           440:            SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_ILLEGAL_REQUEST |
        !           441:            (ssmode == MODE_CONTROL ? SCSI_IGNORE_NOT_READY : 0));
        !           442:        if (error)
        !           443:                goto bad;
        !           444:
        !           445:        sc_link->flags |= SDEV_OPEN;    /* unit attn are now errors */
        !           446:
        !           447:        /*
        !           448:         * If the mode is 3 (e.g. minor = 3,7,11,15)
        !           449:         * then the device has been opened to set defaults
        !           450:         * This mode does NOT ALLOW I/O, only ioctls
        !           451:         */
        !           452:        if (ssmode == MODE_CONTROL)
        !           453:                return (0);
        !           454:
        !           455:        SC_DEBUG(sc_link, SDEV_DB2, ("open complete\n"));
        !           456:        return (0);
        !           457:
        !           458: bad:
        !           459:        sc_link->flags &= ~SDEV_OPEN;
        !           460:        return (error);
        !           461: }
        !           462:
        !           463: /*
        !           464:  * close the device.. only called if we are the LAST
        !           465:  * occurence of an open device
        !           466:  */
        !           467: int
        !           468: ssclose(dev, flag, mode, p)
        !           469:        dev_t dev;
        !           470:        int flag;
        !           471:        int mode;
        !           472:        struct proc *p;
        !           473: {
        !           474:        struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
        !           475:        int error;
        !           476:
        !           477:        SC_DEBUG(ss->sc_link, SDEV_DB1, ("closing\n"));
        !           478:
        !           479:        if (SSMODE(dev) == MODE_REWIND) {
        !           480:                if (ss->special.rewind_scanner) {
        !           481:                        /* call special handler to rewind/abort scan */
        !           482:                        error = (ss->special.rewind_scanner)(ss);
        !           483:                        if (error)
        !           484:                                return (error);
        !           485:                } else {
        !           486:                        /* XXX add code to restart a SCSI2 scanner, if any */
        !           487:                }
        !           488:                ss->sio.scan_window_size = 0;
        !           489:                ss->flags &= ~SSF_TRIGGERED;
        !           490:        }
        !           491:        ss->sc_link->flags &= ~SDEV_OPEN;
        !           492:
        !           493:        return (0);
        !           494: }
        !           495:
        !           496: /*
        !           497:  * trim the size of the transfer if needed,
        !           498:  * called by physio
        !           499:  * basically the smaller of our min and the scsi driver's
        !           500:  * minphys
        !           501:  */
        !           502: void
        !           503: ssminphys(bp)
        !           504:        struct buf *bp;
        !           505: {
        !           506:        struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(bp->b_dev)];
        !           507:
        !           508:        (ss->sc_link->adapter->scsi_minphys)(bp);
        !           509:
        !           510:        /*
        !           511:         * trim the transfer further for special devices this is
        !           512:         * because some scanners only read multiples of a line at a
        !           513:         * time, also some cannot disconnect, so the read must be
        !           514:         * short enough to happen quickly
        !           515:         */
        !           516:        if (ss->special.minphys)
        !           517:                (ss->special.minphys)(ss, bp);
        !           518: }
        !           519:
        !           520: /*
        !           521:  * Do a read on a device for a user process.
        !           522:  * Prime scanner at start of read, check uio values, call ssstrategy
        !           523:  * via physio for the actual transfer.
        !           524:  */
        !           525: int
        !           526: ssread(dev, uio, flag)
        !           527:        dev_t dev;
        !           528:        struct uio *uio;
        !           529:        int flag;
        !           530: {
        !           531:        struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
        !           532:        int error;
        !           533:
        !           534:        /* if the scanner has not yet been started, do it now */
        !           535:        if (!(ss->flags & SSF_TRIGGERED)) {
        !           536:                if (ss->special.trigger_scanner) {
        !           537:                        error = (ss->special.trigger_scanner)(ss);
        !           538:                        if (error)
        !           539:                                return (error);
        !           540:                } else {
        !           541:                        struct scsi_start_stop trigger_cmd;
        !           542:                        bzero(&trigger_cmd, sizeof(trigger_cmd));
        !           543:                        trigger_cmd.opcode = START_STOP;
        !           544:                        trigger_cmd.how = SSS_START;
        !           545:                        scsi_scsi_cmd(ss->sc_link,
        !           546:                                (struct scsi_generic *)&trigger_cmd,
        !           547:                                sizeof(trigger_cmd), 0, 0, 4, 5000, NULL, 0);
        !           548:                }
        !           549:                ss->flags |= SSF_TRIGGERED;
        !           550:        }
        !           551:
        !           552:        return (physio(ssstrategy, NULL, dev, B_READ, ssminphys, uio));
        !           553: }
        !           554:
        !           555: /*
        !           556:  * Actually translate the requested transfer into one the physical
        !           557:  * driver can understand The transfer is described by a buf and will
        !           558:  * include only one physical transfer.
        !           559:  */
        !           560: void
        !           561: ssstrategy(bp)
        !           562:        struct buf *bp;
        !           563: {
        !           564:        struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(bp->b_dev)];
        !           565:        struct buf *dp;
        !           566:        int s;
        !           567:
        !           568:        SC_DEBUG(ss->sc_link, SDEV_DB2, ("ssstrategy: %ld bytes @ blk %d\n",
        !           569:            bp->b_bcount, bp->b_blkno));
        !           570:
        !           571:        if (bp->b_bcount > ss->sio.scan_window_size)
        !           572:                bp->b_bcount = ss->sio.scan_window_size;
        !           573:
        !           574:        /*
        !           575:         * If it's a null transfer, return immediately
        !           576:         */
        !           577:        if (bp->b_bcount == 0)
        !           578:                goto done;
        !           579:
        !           580:        s = splbio();
        !           581:
        !           582:        /*
        !           583:         * Place it in the queue of activities for this scanner
        !           584:         * at the end (a bit silly because we only have on user..)
        !           585:         * (but it could fork() or dup())
        !           586:         */
        !           587:        dp = &ss->buf_queue;
        !           588:        bp->b_actf = NULL;
        !           589:        bp->b_actb = dp->b_actb;
        !           590:        *dp->b_actb = bp;
        !           591:        dp->b_actb = &bp->b_actf;
        !           592:
        !           593:        /*
        !           594:         * Tell the device to get going on the transfer if it's
        !           595:         * not doing anything, otherwise just wait for completion
        !           596:         * (All a bit silly if we're only allowing 1 open but..)
        !           597:         */
        !           598:        ssstart(ss);
        !           599:
        !           600:        splx(s);
        !           601:        return;
        !           602:
        !           603: done:
        !           604:        /*
        !           605:         * Correctly set the buf to indicate a completed xfer
        !           606:         */
        !           607:        bp->b_resid = bp->b_bcount;
        !           608:        s = splbio();
        !           609:        biodone(bp);
        !           610:        splx(s);
        !           611: }
        !           612:
        !           613: /*
        !           614:  * ssstart looks to see if there is a buf waiting for the device
        !           615:  * and that the device is not already busy. If both are true,
        !           616:  * It dequeues the buf and creates a scsi command to perform the
        !           617:  * transfer required. The transfer request will call scsi_done
        !           618:  * on completion, which will in turn call this routine again
        !           619:  * so that the next queued transfer is performed.
        !           620:  * The bufs are queued by the strategy routine (ssstrategy)
        !           621:  *
        !           622:  * This routine is also called after other non-queued requests
        !           623:  * have been made of the scsi driver, to ensure that the queue
        !           624:  * continues to be drained.
        !           625:  * ssstart() is called at splbio
        !           626:  */
        !           627: void
        !           628: ssstart(v)
        !           629:        void *v;
        !           630: {
        !           631:        struct ss_softc *ss = v;
        !           632:        struct scsi_link *sc_link = ss->sc_link;
        !           633:        struct buf *bp, *dp;
        !           634:        struct scsi_r_scanner read_cmd;
        !           635:        int flags;
        !           636:
        !           637:        SC_DEBUG(sc_link, SDEV_DB2, ("ssstart\n"));
        !           638:        /*
        !           639:         * See if there is a buf to do and we are not already
        !           640:         * doing one
        !           641:         */
        !           642:        while (sc_link->openings > 0) {
        !           643:                /* if a special awaits, let it proceed first */
        !           644:                if (sc_link->flags & SDEV_WAITING) {
        !           645:                        sc_link->flags &= ~SDEV_WAITING;
        !           646:                        wakeup((caddr_t)sc_link);
        !           647:                        return;
        !           648:                }
        !           649:
        !           650:                /*
        !           651:                 * See if there is a buf with work for us to do..
        !           652:                 */
        !           653:                dp = &ss->buf_queue;
        !           654:                if ((bp = dp->b_actf) == NULL)
        !           655:                        return;
        !           656:                if ((dp = bp->b_actf) != NULL)
        !           657:                        dp->b_actb = bp->b_actb;
        !           658:                else
        !           659:                        ss->buf_queue.b_actb = bp->b_actb;
        !           660:                *bp->b_actb = dp;
        !           661:
        !           662:                if (ss->special.read) {
        !           663:                        (ss->special.read)(ss, bp);
        !           664:                } else {
        !           665:                        /* generic scsi2 scanner read */
        !           666:                        bzero(&read_cmd, sizeof(read_cmd));
        !           667:                        read_cmd.opcode = READ_BIG;
        !           668:                        _lto3b(bp->b_bcount, read_cmd.len);
        !           669:                        flags = SCSI_DATA_IN;
        !           670:                        /*
        !           671:                         * go ask the adapter to do all this for us
        !           672:                         */
        !           673:                        if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)
        !           674:                            &read_cmd, sizeof(read_cmd), (u_char *) bp->b_data,
        !           675:                            bp->b_bcount, 0, 100000, bp, flags | SCSI_NOSLEEP))
        !           676:                                printf("%s: not queued\n", ss->sc_dev.dv_xname);
        !           677:                }
        !           678:        }
        !           679: }
        !           680:
        !           681: /*
        !           682:  * Perform special action on behalf of the user;
        !           683:  * knows about the internals of this device
        !           684:  */
        !           685: int
        !           686: ssioctl(dev, cmd, addr, flag, p)
        !           687:        dev_t dev;
        !           688:        u_long cmd;
        !           689:        caddr_t addr;
        !           690:        int flag;
        !           691:        struct proc *p;
        !           692: {
        !           693:        struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(dev)];
        !           694:        int error = 0;
        !           695:        struct scan_io *sio;
        !           696:
        !           697:        switch (cmd) {
        !           698:        case SCIOCGET:
        !           699:                /* call special handler, if any */
        !           700:                if (ss->special.get_params) {
        !           701:                        error = (ss->special.get_params)(ss);
        !           702:                        if (error)
        !           703:                                return (error);
        !           704:                }
        !           705:                bcopy(&ss->sio, addr, sizeof(struct scan_io));
        !           706:                break;
        !           707:        case SCIOCSET:
        !           708:                sio = (struct scan_io *)addr;
        !           709:
        !           710:                /* call special handler, if any */
        !           711:                if (ss->special.set_params) {
        !           712:                        error = (ss->special.set_params)(ss, sio);
        !           713:                        if (error)
        !           714:                                return (error);
        !           715:                } else {
        !           716:                        /* XXX add routine to validate parameters */
        !           717:                        ss_set_window(ss, sio);
        !           718:                }
        !           719:                break;
        !           720:        case SCIOCRESTART:
        !           721:                /* call special handler, if any */
        !           722:                if (ss->special.rewind_scanner ) {
        !           723:                        error = (ss->special.rewind_scanner)(ss);
        !           724:                        if (error)
        !           725:                                return (error);
        !           726:                } else
        !           727:                        /* XXX add code for SCSI2 scanner, if any */
        !           728:                        return (EOPNOTSUPP);
        !           729:                ss->flags &= ~SSF_TRIGGERED;
        !           730:                break;
        !           731:        case SCIOC_USE_ADF:
        !           732:                /* XXX add Automatic Document Feeder Support */
        !           733:                return (EOPNOTSUPP);
        !           734:        default:
        !           735:                if (SSMODE(dev) != MODE_CONTROL)
        !           736:                        return (ENOTTY);
        !           737:                return (scsi_do_ioctl(ss->sc_link, dev, cmd, addr,
        !           738:                    flag, p));
        !           739:        }
        !           740:        return (error);
        !           741: }
        !           742:
        !           743: int
        !           744: ss_set_window(ss, sio)
        !           745:        struct ss_softc *ss;
        !           746:        struct scan_io *sio;
        !           747: {
        !           748:        struct scsi_set_window  window_cmd;
        !           749:        struct {
        !           750:                struct scsi_window_data window_data;
        !           751:                /* vendor_unique must provide enough space for worst case
        !           752:                 * (currently Ricoh IS-410.)  40 + 280 = 320 which is the size
        !           753:                 * of its window descriptor length
        !           754:                 */
        !           755:                u_int8_t vendor_unique[280];
        !           756:        } wd;
        !           757: #define window_data   wd.window_data
        !           758: #define vendor_unique wd.vendor_unique
        !           759:        struct scsi_link        *sc_link = ss->sc_link;
        !           760:
        !           761:        /*
        !           762:         * The CDB for SET WINDOW goes in here.
        !           763:         * The two structures that follow are sent via data out.
        !           764:         */
        !           765:        bzero(&window_cmd, sizeof(window_cmd));
        !           766:        window_cmd.opcode = SET_WINDOW;
        !           767:        _lto3l(sizeof(window_data), window_cmd.len);
        !           768:
        !           769:        bzero(&window_data, sizeof(window_data));
        !           770:        if (ss->quirkdata->quirks & SS_Q_WINDOW_DESC_LEN)
        !           771:                _lto2l(ss->quirkdata->window_descriptor_length,
        !           772:                    window_data.window_desc_len);
        !           773:        else
        !           774:                _lto2l(40L, window_data.window_desc_len);
        !           775:
        !           776:        /* start of SET_WINDOW parameter block */
        !           777:
        !           778:        /* leave window id at zero */
        !           779:        /* leave auto bit at zero */
        !           780:        _lto2l(sio->scan_x_resolution, window_data.x_res);
        !           781:        _lto2l(sio->scan_y_resolution, window_data.y_res);
        !           782:        _lto4l(sio->scan_x_origin, window_data.x_org);
        !           783:        _lto4l(sio->scan_y_origin, window_data.y_org);
        !           784:        _lto4l(sio->scan_width,  window_data.width);
        !           785:        _lto4l(sio->scan_height, window_data.length);
        !           786:
        !           787:        if (ss->quirkdata->quirks & SS_Q_REV_BRIGHTNESS)
        !           788:                window_data.brightness = 256 - sio->scan_brightness;
        !           789:        else if (ss->quirkdata->quirks & SS_Q_BRIGHTNESS)
        !           790:                window_data.brightness = ss->quirkdata->brightness;
        !           791:        else
        !           792:                window_data.brightness = sio->scan_brightness;
        !           793:
        !           794:        /*
        !           795:         * threshold: Default is to follow brightness.
        !           796:         * If SS_Q_MONO_THRESHOLD is set then the quirkdata contains a special
        !           797:         * value to be used instead of default when image data is monochrome.
        !           798:         * Otherwise if SS_Q_THRESHOLD is set then the quirkdata contains
        !           799:         * the threshold to always use.
        !           800:         * Both SS_Q_MONO_THRESHOLD and SS_Q_THRESHOLD should not be set at
        !           801:         * the same time.
        !           802:         */
        !           803:        if (ss->quirkdata->quirks & SS_Q_MONO_THRESHOLD) {
        !           804:                if (sio->scan_image_mode == SIM_BINARY_MONOCHROME ||
        !           805:                    sio->scan_image_mode == SIM_DITHERED_MONOCHROME)
        !           806:                        window_data.threshold = ss->quirkdata->threshold;
        !           807:                else
        !           808:                        window_data.threshold = sio->scan_brightness;
        !           809:        } else if (ss->quirkdata->quirks & SS_Q_THRESHOLD)
        !           810:                window_data.threshold = ss->quirkdata->threshold;
        !           811:        else
        !           812:                window_data.threshold = sio->scan_brightness;
        !           813:
        !           814:        if (ss->quirkdata->quirks & SS_Q_REV_CONTRAST)
        !           815:                window_data.contrast = 256 - sio->scan_contrast;
        !           816:        else if (ss->quirkdata->quirks & SS_Q_CONTRAST)
        !           817:                window_data.contrast = ss->quirkdata->contrast;
        !           818:        else
        !           819:                window_data.contrast = sio->scan_contrast;
        !           820:
        !           821:        switch (sio->scan_image_mode) {
        !           822:        case SIM_RED:
        !           823:        case SIM_GREEN:
        !           824:        case SIM_BLUE:
        !           825:                window_data.image_comp = SIM_GRAYSCALE;
        !           826:                break;
        !           827:        default:
        !           828:                window_data.image_comp = sio->scan_image_mode;
        !           829:        }
        !           830:
        !           831:        window_data.bits_per_pixel = sio->scan_bits_per_pixel;
        !           832:
        !           833:        if (ss->quirkdata->quirks & SS_Q_HALFTONE) {
        !           834:                window_data.halftone_pattern[0] =
        !           835:                        ss->quirkdata->halftone_pattern[0];
        !           836:                window_data.halftone_pattern[1] =
        !           837:                        ss->quirkdata->halftone_pattern[1];
        !           838:        } /* else leave halftone set to zero. */
        !           839:
        !           840:        if (ss->quirkdata->quirks & SS_Q_SET_RIF)
        !           841:                window_data.rif = 1;
        !           842:
        !           843:        if (ss->quirkdata->quirks & SS_Q_PADDING_TYPE)
        !           844:                window_data.pad_type = ss->quirkdata->pad_type;
        !           845:        else
        !           846:                window_data.pad_type = 3; /* 3 = truncate to byte boundary */
        !           847:
        !           848:        if (ss->quirkdata->quirks & SS_Q_BIT_ORDERING)
        !           849:                _lto2l(ss->quirkdata->bit_ordering, window_data.bit_ordering);
        !           850:        /* else leave bit_ordering set to zero. */
        !           851:
        !           852:        /* leave compression type & argument set to zero. */
        !           853:
        !           854: #undef window_data
        !           855:
        !           856:        if (ss->quirkdata->vendor_unique_sw != NULL)
        !           857:                return ((*ss->quirkdata->vendor_unique_sw)(ss, sio,
        !           858:                    &window_cmd, (void *)&wd));
        !           859:        else
        !           860:                /* send the command to the scanner */
        !           861:                return (scsi_scsi_cmd(sc_link,
        !           862:                    (struct scsi_generic *)&window_cmd,
        !           863:                    sizeof(window_cmd), (u_char *) &wd.window_data,
        !           864:                    (ss->quirkdata->quirks & SS_Q_WINDOW_DESC_LEN) ?
        !           865:                    ss->quirkdata->window_descriptor_length : 40,
        !           866:                    4, 5000, NULL, SCSI_DATA_OUT));
        !           867: }
        !           868:
        !           869: int
        !           870: ricoh_is410_sw(ss, sio, wcmd, vwd)
        !           871:        struct ss_softc *ss;
        !           872:        struct scan_io *sio;
        !           873:        struct scsi_set_window *wcmd;
        !           874:        void *vwd;
        !           875: {
        !           876:        struct ricoh_is410_window_data {
        !           877:                struct scsi_window_data window_data;
        !           878:                u_int8_t res1;
        !           879:                u_int8_t res2;
        !           880:                u_int    mrif:1; /* reverse image format (grayscale negative) */
        !           881:                u_int    filtering:3;
        !           882:                u_int    gamma_id:4;
        !           883:        } *rwd = (struct ricoh_is410_window_data*)vwd;
        !           884:        struct scsi_link *sc_link = ss->sc_link;
        !           885:
        !           886:        rwd->mrif = 1;          /* force grayscale to match PGM */
        !           887:
        !           888:        /* send the command to the scanner */
        !           889:        return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
        !           890:            sizeof(struct scsi_set_window), (u_char *)rwd,
        !           891:            sizeof(struct ricoh_is410_window_data), 4, 5000, NULL,
        !           892:            SCSI_DATA_OUT));
        !           893: }
        !           894:
        !           895: int
        !           896: umax_uc630_sw(ss, sio, wcmd, vwd)
        !           897:        struct ss_softc *ss;
        !           898:        struct scan_io *sio;
        !           899:        struct scsi_set_window *wcmd;
        !           900:        void *vwd;
        !           901: {
        !           902:        struct umax_uc630_window_data {
        !           903:                struct scsi_window_data window_data;
        !           904:                u_int8_t speed;
        !           905:                u_int8_t select_color;
        !           906:                u_int8_t highlight;
        !           907:                u_int8_t shadow;
        !           908:                u_int8_t paper_length[2];
        !           909:        } *uwd = (struct umax_uc630_window_data*)vwd;
        !           910:        struct scsi_link *sc_link = ss->sc_link;
        !           911:
        !           912:        uwd->speed = 1;         /* speed: fastest speed that doesn't smear */
        !           913:        switch (sio->scan_image_mode) { /* UMAX has three-pass color. */
        !           914:        case SIM_RED:                   /* This selects which filter to use. */
        !           915:                uwd->select_color = 0x80;
        !           916:                break;
        !           917:        case SIM_GREEN:
        !           918:                uwd->select_color = 0x40;
        !           919:                break;
        !           920:        case SIM_BLUE:
        !           921:                uwd->select_color = 0x20;
        !           922:                break;
        !           923:        }
        !           924:        uwd->highlight = 50;            /* 50 = highest; 0 = lowest */
        !           925:        /* leave shadow set to zero. */
        !           926:        /* XXX paper length is for ADF */
        !           927:
        !           928:        /* send the command to the scanner */
        !           929:        return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
        !           930:            sizeof(struct scsi_set_window), (u_char *)uwd,
        !           931:            sizeof(struct umax_uc630_window_data), 4, 5000, NULL,
        !           932:            SCSI_DATA_OUT));
        !           933: }
        !           934:
        !           935: #ifdef NOTYET /* for ADF support */
        !           936: int
        !           937: fujitsu_m3096g_sw(ss, sio, wcmd, vwd)
        !           938:        struct ss_softc *ss;
        !           939:        struct scan_io *sio;
        !           940:        struct scsi_set_window *wcmd;
        !           941:        void *vwd;
        !           942: {
        !           943:        struct fujitsu_m3096g_window_data {
        !           944:                struct scsi_window_data window_data;
        !           945:                u_int8_t id;
        !           946:                u_int8_t res1;
        !           947:                u_int8_t outline;
        !           948:                u_int8_t emphasis;
        !           949:                u_int8_t mixed;
        !           950:                u_int8_t mirroring;
        !           951:                u_int8_t res2[5];
        !           952:                u_int8_t subwindow_list[2];
        !           953:                u_int    paper_size_std:2;
        !           954:                u_int    res3:1;
        !           955:                u_int    paper_orientaton:1;
        !           956:                u_int    paper_size_type:4;
        !           957: /* defines for Paper Size Type: */
        !           958: #define FUJITSU_PST_A3                 0x03
        !           959: #define FUJITSU_PST_A4                 0x04
        !           960: #define FUJITSU_PST_A5                 0x05
        !           961: #define FUJITSU_PST_DOUBLE_LETTER      0x06
        !           962: #define FUJITSU_PST_LETTER             0x07
        !           963: #define FUJITSU_PST_B4                 0x0C
        !           964: #define FUJITSU_PST_B5                 0x0D
        !           965: #define FUJITSU_PST_LEGAL              0x0F
        !           966:                u_int8_t paper_width_x[4];
        !           967:                u_int8_t paper_width_y[4];
        !           968:                u_int8_t res4[2];
        !           969:        } *fwd = (struct fujitsu_m3096g_window_data*)vwd;
        !           970:        struct scsi_link *sc_link = ss->sc_link;
        !           971:
        !           972:        /* send the command to the scanner */
        !           973:        return (scsi_scsi_cmd(sc_link, (struct scsi_generic *)wcmd,
        !           974:            sizeof(struct scsi_set_window), (u_char *)fwd,
        !           975:            sizeof(struct fujitsu_m3096g_window_data), 4, 5000, NULL,
        !           976:            SCSI_DATA_OUT));
        !           977: }
        !           978: #endif
        !           979:
        !           980: void
        !           981: get_buffer_status(ss, bp)
        !           982:        struct ss_softc *ss;
        !           983:        struct buf *bp;
        !           984: {
        !           985:        struct scsi_get_buffer_status gbs_cmd;
        !           986:        struct scsi_link *sc_link = ss->sc_link;
        !           987:        struct {
        !           988:                u_int8_t stat_len[3];
        !           989:                u_int8_t res1;
        !           990:                u_int8_t window_id;
        !           991:                u_int8_t res2;
        !           992:                u_int8_t tgt_accept_buf_len[3];
        !           993:                u_int8_t tgt_send_buf_len[3];
        !           994:        } buf_sz_retn;
        !           995:        int flags;
        !           996:
        !           997:        bzero(&gbs_cmd, sizeof(gbs_cmd));
        !           998:        gbs_cmd.opcode = GET_BUFFER_STATUS;
        !           999:        _lto2b(12, gbs_cmd.len);
        !          1000:        flags = SCSI_DATA_IN;
        !          1001:
        !          1002:        if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &gbs_cmd,
        !          1003:            sizeof(gbs_cmd), (u_char *) &buf_sz_retn, sizeof(buf_sz_retn),
        !          1004:            0, 100000, bp, flags | SCSI_NOSLEEP)) {
        !          1005:                printf("%s: not queued\n", ss->sc_dev.dv_xname);
        !          1006:        }
        !          1007:        bp->b_bcount = MIN(_3btol(buf_sz_retn.tgt_send_buf_len), bp->b_bcount);
        !          1008: }
        !          1009:
        !          1010: #ifdef NOTYET
        !          1011: int
        !          1012: umax_compute_sizes(ss)
        !          1013:        struct ss_softc *ss;
        !          1014: {
        !          1015:        ss->sio.scan_lines = ;
        !          1016:        ss->sio.scan_window_size = ;
        !          1017: }
        !          1018:
        !          1019: int
        !          1020: calc_umax_row_len(dpi, ww)
        !          1021:        int dpi;
        !          1022:        int ww;
        !          1023: {
        !          1024:        int st[301];
        !          1025:        int i;
        !          1026:        int rowB = 0;
        !          1027:
        !          1028:        for (i = 1; i <= 300; i++)
        !          1029:                st[i] = 1;
        !          1030:
        !          1031:        for (i = 1; i <= 300 - dpi; i++)
        !          1032:                st[i * 300 / (300 - dpi)] = 0;
        !          1033:
        !          1034:        for (i = 1; i <= (ww % 1200) / 4; i++) {
        !          1035:                if (st[i])
        !          1036:                        rowB++;
        !          1037:        }
        !          1038:
        !          1039:        return ((ww / 1200) * dpi + rowB);
        !          1040: }
        !          1041: #endif

CVSweb