Annotation of coherent/b/STREAMS/io.386/haiCt.c, revision 1.1

1.1     ! root        1: /***********************************************************************
        !             2:  *  Module: haict.c
        !             3:  *
        !             4:  *  Unix device driver functions for accessing SCSI tape drives as
        !             5:  *  character devices.  Conforms to Mark Williams Coherent definition
        !             6:  *  of the Unix Device Driver interface for Coherent v4.0.1.
        !             7:  *
        !             8:  *  The philosophy of this driver is to support basic functions on
        !             9:  *  the tape drive (read, write, retension, rewind, skip, etc). There
        !            10:  *  are more features out there for all the SCSI tape drives out there
        !            11:  *  than I know what to do with.  I leave custom support for these
        !            12:  *  drives to the people who have them.  To this end this drive will
        !            13:  *  blindly follow whatever information it can get using Mode Sense
        !            14:  *  and Read Block Limits CDB's.  These tests are done at open time.
        !            15:  *  An application can change the operation of the driver by applying
        !            16:  *  the mode select command through I/O Control mechanism.
        !            17:  *
        !            18:  *  Copyright (c) 1993, Christopher Sean Hilton, All Rights Reserved.
        !            19:  *
        !            20:  *  Last Modified: Mon Jul 26 17:18:59 1993 by [chris]
        !            21:  *
        !            22:  *  $Id: haict.c,v 2.4 93/08/09 13:45:06 bin Exp Locker: bin $
        !            23:  *
        !            24:  *  $Log:      haict.c,v $
        !            25:  * Revision 2.4  93/08/09  13:45:06  bin
        !            26:  * Kernel 82 changes
        !            27:  * 
        !            28:  */
        !            29: 
        !            30: #include <stddef.h>
        !            31: #include <sys/coherent.h>
        !            32: #include <sys/buf.h>
        !            33: #include <sys/inode.h>
        !            34: #include <sys/stat.h>
        !            35: #include <sys/sched.h>
        !            36: #include <errno.h>
        !            37: #include <sys/mtioctl.h>
        !            38: #include <sys/file.h>          /* Louis */
        !            39: 
        !            40: #include <sys/haiscsi.h>
        !            41: #include <sys/haiioctl.h>
        !            42: 
        !            43: #define REWINDTAPE      0x01
        !            44: #define     IMMEDIATE   0x0010
        !            45: #define REQSENSE        0x03
        !            46: #define READBLKLMT      0x05
        !            47: #define READ            0x08
        !            48: #define WRITE           0x0a
        !            49: #define WRITEFM         0x10
        !            50: #define SPACE           0x11
        !            51: #define     SKIP_BLK    0x00
        !            52: #define     SKIP_FM     0x01
        !            53: #define     SKIP_SEQFM  0x02
        !            54: #define     SKIP_EOT    0x03
        !            55: #define MODESELECT      0x15
        !            56: #define ERASE           0x19
        !            57: #define     ERASE_BLOCK 0x0000
        !            58: #define     ERASE_TAPE  0x0001
        !            59: #define MODESENSE       0x1a
        !            60: #define LOAD            0x1b
        !            61: #define     RETENSION   0x0020
        !            62: 
        !            63: /***********************************************************************
        !            64:  *  The Tandberg TDC3600 requires special handling because it doesn't
        !            65:  *  respond properly to a write when the tape is at the logical end
        !            66:  *  of tape.  Tape should be organized as follows:
        !            67:  *
        !            68:  *  File 1 --------------| File 2-m
        !            69:  *  DATA[1] | ...DATA[n] | FILEMARK | DATA[1-n] | FILEMARK | FILEMARK |
        !            70:  *
        !            71:  *  The double file marks signify the Logical End of Tape.  When a
        !            72:  *  tape opened in write mode is written to and then closed on the
        !            73:  *  no rewind device it is necessary to write a Logical End of tape
        !            74:  *  and then skip backwards over one of the filemarks.  This leaves
        !            75:  *  the tape positioned between the two filemarks. When this happens
        !            76:  *  on my Tandberg the drive will then lock up (nice huh?) and fail
        !            77:  *  to do anything until the tape is rewound or retensioned.
        !            78:  */
        !            79: 
        !            80: 
        !            81: #define CTDIRTY         0x0001
        !            82: #define CTCLOSING       0x0002
        !            83: 
        !            84: #define CTILI           0x0020      /* Sensekey's Illegal Length Indicator */
        !            85: #define CTEOM           0x0040      /* Sensekey's End OF Media bit */
        !            86: #define CTFILEMARK      0x0080      /* Sensekey's Filemark bit */
        !            87: #define     CTSKMASK    (CTILI | CTEOM | CTFILEMARK)
        !            88: #define CTRDMD          0x0100      /* we are reading from the tape */
        !            89: #define CTWRTMD         0x0200      /* we are writing to the tape */
        !            90: 
        !            91: #ifndef HAICACHESZ
        !            92: /*
        !            93:  *  There wasn't much of a difference in speed between 32 and 40 block
        !            94:  *  in my experiance so save as much kalloc memory as possible.
        !            95:  */
        !            96: 
        !            97: #define HAICACHESZ      (32 * BSIZE)/* 32 Block Cache for each device */
        !            98: #endif
        !            99: 
        !           100: #ifndef HAICTVERBOSE
        !           101: /*
        !           102:  *  HAICTVERBOSE is done as a define because I wanted to be able to use two
        !           103:  *  tape drives in my system at one time (why?!).  ld complains if it is a
        !           104:  *  patchable constant.  This problem must be solved but products must also
        !           105:  *  ship. [csh]
        !           106:  */
        !           107: 
        !           108: #define HAICTVERBOSE    0x0001      /* Switch console messages on/off */
        !           109: #endif
        !           110: 
        !           111: typedef enum {
        !           112:     CTIDLE = 0,
        !           113:     CTINIT,
        !           114:     CTFBRD,
        !           115:     CTVBRD,
        !           116:     CTFBWRT,
        !           117:     CTVBWRT,
        !           118:     CTLASTWRT,
        !           119:     CTSENSE,
        !           120:     CTWRITEFM,
        !           121:     CTSPACE,
        !           122:     CTREWIND,
        !           123:     CTERASE,
        !           124:     CTLOADRETEN,
        !           125:     CTIOCTL
        !           126: } ctstate_t;
        !           127: 
        !           128: /* Block Descriptors in the mode sense command. */
        !           129: 
        !           130: typedef struct blkdscr_s *blkdscr_p;
        !           131: 
        !           132: typedef struct blkdscr_s {
        !           133:     union {
        !           134:         unsigned char   mediatype;
        !           135:         unsigned long   totalblocks;
        !           136:     } mt;
        !           137:     union {
        !           138:         unsigned char reserved;
        !           139:         unsigned long blocksize;
        !           140:     } rb;
        !           141: } blkdscr_t;
        !           142: 
        !           143: typedef struct blklim_s *blklim_p;
        !           144: 
        !           145: typedef struct blklim_s {
        !           146:     unsigned        blmax;      /* Maximum size for Reads/Writes */
        !           147:     unsigned short  blmin;      /* Minimum size for Reads/Writes */
        !           148: } blklim_t;
        !           149: 
        !           150: typedef struct ctctrl_s *ctctrl_p;
        !           151: 
        !           152: typedef struct ctctrl_s {
        !           153:     char            *cache,     /* Transfer Cache */
        !           154:                     *start;     /* Start of data in cache */
        !           155:     size_t          cachesize,  /* Size of cache */
        !           156:                     avail;      /* bytes availaible in cache */
        !           157:     ctstate_t       state;
        !           158:     unsigned        block,      /* Block size of device */
        !           159:                     blmax;      /* Block limits maximum */
        !           160:     unsigned short  blmin,      /* Block Limits minimum */
        !           161:                     flags,      /* Flags from device */
        !           162:                     inuse;      /* In Use flag */
        !           163:     srb_t           srb;        /* SCSI Request block for transfers */
        !           164: } ctctrl_t;
        !           165: 
        !           166: static int ctinit();        /* Initialize a SCSI device at (id) */
        !           167: static void ctopen();       /* Open SCSI tape at (dev) */
        !           168: static void ctclose();      /* Close a SCSI tape at (dev) */
        !           169: static void ctread();       /* Read from SCSI tape at (dev) */
        !           170: static void ctwrite();      /* Write SCSI tape at (dev) */
        !           171: static void ctioctl();      /* I/O Control routine */
        !           172: static int fillcache();     /* Fill the tape cache */
        !           173: static int flushcache();    /* Flush the tape cache */
        !           174: 
        !           175: extern int nulldev();
        !           176: extern int nonedev();
        !           177: 
        !           178: #define min(a, b)           ((a) < (b) ? (a) : (b))
        !           179: 
        !           180: #ifdef TDC3600
        !           181: #define ctdca   haict3600
        !           182: #endif
        !           183: 
        !           184: dca_t ctdca = {
        !           185:     ctopen,         /* Open */
        !           186:     ctclose,        /* Close */
        !           187:     hainonblk,      /* No Block point here but don't just drop Buffers */
        !           188:     ctread,         /* Read */
        !           189:     ctwrite,        /* Write */
        !           190:     ctioctl,        /* Ioctl */
        !           191:     ctinit,         /* Load */
        !           192:     nulldev,        /* Unload */
        !           193:     nulldev         /* Poll */
        !           194: };
        !           195: 
        !           196: static ctctrl_p         ctdevs[MAXDEVS];
        !           197: 
        !           198: /***********************************************************************
        !           199:  *  Utility functions.                                                 *
        !           200:  ***********************************************************************/
        !           201: 
        !           202: #define ctvmsg(l, cmd) { if (HAICTVERBOSE & (l)) { (cmd); } }
        !           203: 
        !           204: /***********************************************************************
        !           205:  *  ctbusywait()
        !           206:  *
        !           207:  *  Wait for the tape drive state to return to idle.  This is easy
        !           208:  *  for two reasons: 1) With no block entry point its safe to sleep
        !           209:  *  at any time.  2) We shouldn't really need this anyhow.  This is
        !           210:  *  unneccessary because without a block routine and with only one
        !           211:  *  process able to open the tape drive at a time the state of the
        !           212:  *  tape drive driver is well defined.  So, why is it here you ask?
        !           213:  *  because one day some user might fork a process that owns the tape
        !           214:  *  drive.  This would cause 40 days and nights worth of rain etc.
        !           215:  *  Now all that will happen is both processes will be able to write/read
        !           216:  *  from the tape drive and the data that they get will be complete
        !           217:  *  garbage.  However, the kernel will not break.
        !           218:  */
        !           219: 
        !           220: static int ctbusywait(c, newstate)
        !           221: register ctctrl_p   c;
        !           222: register ctstate_t  newstate;
        !           223: {
        !           224:     register int    s;
        !           225:     int             retval;
        !           226: 
        !           227:     s = sphi();
        !           228:     retval = 1;
        !           229:     while (c->state != CTIDLE)
        !           230:         if (x_sleep(&(c->srb. status), pritape, slpriSigCatch, "ctbsywt")) {
        !           231:             u. u_error = EINTR;
        !           232:             retval = 0;
        !           233:             break;
        !           234:         }
        !           235:     c->state = newstate;
        !           236:     spl(s);
        !           237:     return retval;
        !           238: }   /* ctbusywait() */
        !           239: 
        !           240: /***********************************************************************
        !           241:  *  loadtape()
        !           242:  *
        !           243:  *  Move the tape to the load point.
        !           244:  */
        !           245: 
        !           246: static int loadtape(c, opt)
        !           247: register ctctrl_p c;
        !           248: int opt;
        !           249: {
        !           250:     register srb_p      r = &(c->srb);
        !           251:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           252: 
        !           253:     if (!ctbusywait(c, CTLOADRETEN))
        !           254:         return 0;
        !           255:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           256:     r->xferdir = 0;
        !           257:     r->timeout = 300;
        !           258:     memset(g0, 0, sizeof(cdb_t));
        !           259:     g0->opcode = LOAD;
        !           260:     g0->xfr_len = 1;        /* Move tape to load point. */
        !           261:     if (opt & IMMEDIATE)
        !           262:         g0->lun_lba |= 1;
        !           263:     if (opt & RETENSION)
        !           264:         g0->xfr_len |= 2;
        !           265:     doscsi(r, 4, pritape, slpriSigCatch, "loadtape");
        !           266:     if (r->status != ST_GOOD && printerror(r, "Load failed"))
        !           267:         u. u_error = EIO;
        !           268: 
        !           269:     c->state = CTIDLE;
        !           270:     c->flags &= ~(CTFILEMARK | CTEOM);
        !           271:     return (r->status == ST_GOOD);
        !           272: }   /* loadtape() */
        !           273: 
        !           274: /***********************************************************************
        !           275:  *  writefm()
        !           276:  *
        !           277:  *  Write Filemarks on the tape.
        !           278:  */
        !           279: 
        !           280: static void writefm(c, count)
        !           281: register ctctrl_p c;
        !           282: int count;
        !           283: {
        !           284:     register srb_p      r = &(c->srb);
        !           285:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           286: 
        !           287:     if (!ctbusywait(c, CTWRITEFM))
        !           288:         return;
        !           289:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           290:     r->xferdir = 0;
        !           291:     r->timeout = 40;
        !           292:     g0->opcode = WRITEFM;
        !           293:     g0->lun_lba = (r->lun << 5);
        !           294:     g0->lba_mid = ((unsigned char *) &count)[2];
        !           295:     g0->lba_low = ((unsigned char *) &count)[1];
        !           296:     g0->xfr_len = ((unsigned char *) &count)[0];
        !           297:     g0->control = 0;
        !           298:     doscsi(r, 4, pritape, slpriSigCatch, "writefm");
        !           299:     if (r->status != ST_GOOD && printerror(r, "Write filemarks failed"))
        !           300:         u. u_error = EIO;
        !           301:     c->state = CTIDLE;
        !           302: }   /* writefm() */
        !           303: 
        !           304: /***********************************************************************
        !           305:  *  space()
        !           306:  *
        !           307:  *  Space over blocks/filemarks/etc.
        !           308:  */
        !           309: 
        !           310: static void space(c, count, object)
        !           311: register ctctrl_p c;
        !           312: int count;
        !           313: int object;
        !           314: {
        !           315:     register srb_p      r = &(c->srb);
        !           316:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           317: 
        !           318:     if (!ctbusywait(c, CTSPACE))
        !           319:         return;
        !           320:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           321:     r->xferdir = 0;
        !           322:     r->timeout = 300;
        !           323:     g0->opcode = SPACE;
        !           324:     g0->lun_lba = (r->lun << 5) | (object & 3);
        !           325:     g0->lba_mid = ((unsigned char *) &count)[2];
        !           326:     g0->lba_low = ((unsigned char *) &count)[1];
        !           327:     g0->xfr_len = ((unsigned char *) &count)[0];
        !           328:     g0->control = 0;
        !           329:     doscsi(r, 2, pritape, slpriSigCatch, "space");
        !           330:     if (r->status != ST_GOOD && printerror(r, "Space failed"))
        !           331:         u. u_error = EIO;
        !           332:     c->state = CTIDLE;
        !           333: }   /* space() */
        !           334: 
        !           335: /***********************************************************************
        !           336:  *  rewind()
        !           337:  *
        !           338:  *  Rewind the tape drive back to the load point.
        !           339:  */
        !           340: 
        !           341: static void rewind(c, wait)
        !           342: register ctctrl_p c;
        !           343: int wait;
        !           344: {
        !           345:     register srb_p      r = &(c->srb);
        !           346:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           347: 
        !           348:     if (!ctbusywait(c, CTREWIND))
        !           349:         return;
        !           350:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           351:     r->timeout = 300;
        !           352:     r->xferdir = 0;
        !           353:     memset(g0, 0, sizeof(cdb_t));
        !           354:     g0->opcode = REWINDTAPE;
        !           355:     if (!wait)
        !           356:         g0->lun_lba = (r->lun << 5) | 1;
        !           357:     doscsi(r, 2, pritape, slpriSigCatch, "rewind");
        !           358:     if (r->status != ST_GOOD && printerror(r, "Rewind failed"))
        !           359:         u. u_error = EIO;
        !           360:     c->flags = 0;
        !           361:     c->state = CTIDLE;
        !           362: }   /* rewind() */
        !           363: 
        !           364: static void erase(c, to_eot)
        !           365: register ctctrl_p c;
        !           366: int to_eot;
        !           367: {
        !           368:     register srb_p      r = &(c->srb);
        !           369:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           370: 
        !           371:     if (!ctbusywait(c, CTERASE))
        !           372:         return;
        !           373:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           374:     r->timeout = 300;
        !           375:     r->xferdir = 0;
        !           376:     memset(g0, 0, sizeof(cdb_t));
        !           377:     g0->opcode = ERASE;
        !           378:     g0->lun_lba = (r->lun << 5);
        !           379:     if (to_eot)
        !           380:         g0->lun_lba |= 1;
        !           381:     doscsi(r, 2, pritape, slpriSigCatch, "erase");
        !           382:     if (r->status != ST_GOOD && printerror(r, "Erase failed"))
        !           383:         u. u_error = EIO;
        !           384:     if (to_eot)
        !           385:         c->flags &= ~(CTFILEMARK | CTEOM | CTILI | CTDIRTY);
        !           386:     c->state = CTIDLE;
        !           387: }   /* erase() */
        !           388: 
        !           389: /***********************************************************************
        !           390:  *  Device Driver Entry Point routines.                                *
        !           391:  ***********************************************************************/
        !           392: 
        !           393: /***********************************************************************
        !           394:  *  ctinit()
        !           395:  *
        !           396:  *  Initialize the tape device at (id).  This doesn't do anything,
        !           397:  *  not even verify that the drive is there because it could be powered
        !           398:  *  off.
        !           399:  */
        !           400: 
        !           401: static int ctinit(id)
        !           402: register int id;
        !           403: {
        !           404:     register ctctrl_p c = kalloc(sizeof(ctctrl_t));
        !           405: 
        !           406:     if (!c) {
        !           407:         printf("\tTape Driver: Could not allocate control structure.\n");
        !           408:         return 0;
        !           409:     }
        !           410: 
        !           411:     printf("\tCoherent SCSI Tape driver v1.1\n");
        !           412:     memset(c, 0, sizeof(ctctrl_t));
        !           413:     c->inuse = 0;
        !           414:     c->cache = c->start = NULL;
        !           415:     c->cachesize = HAICACHESZ;
        !           416:     c->srb. target = id;
        !           417:     c->srb. lun = 0;
        !           418:     c->state = CTIDLE;
        !           419:     ctdevs[id] = c;
        !           420:     return 1;
        !           421: }
        !           422: 
        !           423: static void ctopen(dev, mode)
        !           424: dev_t   dev;
        !           425: int mode;
        !           426: {
        !           427:     register ctctrl_p   c = ctdevs[tid(dev)];
        !           428:     register srb_p      r = &(c->srb);
        !           429:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           430:     int                 rblerf;         /* read block limits error flag */
        !           431:     int                 s;
        !           432:     char                buf[64];
        !           433:     blkdscr_p           bd = (blkdscr_p) (buf + 4);
        !           434:     blklim_p            bl = (blklim_p) (buf);
        !           435: 
        !           436:     if (!c) {
        !           437:         u. u_error = ENXIO;
        !           438:         return;
        !           439:     }
        !           440:     if ((mode != IPR) && (mode != IPW)) {
        !           441:         u. u_error = EINVAL;
        !           442:         return;
        !           443:     }
        !           444: 
        !           445:     s = sphi();
        !           446: 
        !           447:     if (c->inuse) {
        !           448:         u. u_error = EDBUSY;
        !           449:         goto done;
        !           450:     }
        !           451: 
        !           452:     c->inuse = 1;
        !           453:     c->state = CTINIT;
        !           454:     r->dev = dev;      /* Save the rewind bit for close. */
        !           455: 
        !           456: /***********************************************************************
        !           457:  *  Repeat the test unit ready command to the tape drive.  This tells
        !           458:  *  the state of the tape drive.  Repeating the command also clears out
        !           459:  *  the request sense condition which comes after each time the user
        !           460:  *  changes tapes.
        !           461:  */
        !           462: 
        !           463:     r->timeout = 2;
        !           464:     r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           465:     r->xferdir = 0;
        !           466:     memset(g0, 0, sizeof(cdb_t));       /* Test Unit Ready */
        !           467:     memset(r->sensebuf, 0, sizeof(r->sensebuf));
        !           468:     doscsi(r, 4, pritape, slpriSigCatch, "ctopen");
        !           469: 
        !           470: /***********************************************************************
        !           471:  *  If the command fails there probably wasn't a tape in the drive.
        !           472:  */
        !           473: 
        !           474:     if (r->status != ST_GOOD) {     /* Is there a tape in the drive? */
        !           475:         if (r->status != ST_USRABRT) {
        !           476:             /* Otherwise assume no tape. */
        !           477:             u. u_error = ENXIO;
        !           478:             devmsg(r->dev, "Tape drive not ready.");
        !           479:             goto openfailed;
        !           480:         }
        !           481:     }
        !           482: #ifdef TDC3600
        !           483: 
        !           484: /***********************************************************************
        !           485:  *  TDC3600's do a retension after all bus device resets and tape changes
        !           486:  *  so I block in the open routine waiting for that operation to complete.
        !           487:  *  This is a kludge to fix the author's system.  I anticipate that
        !           488:  *  other host adapters may not be so kind about retrying commands
        !           489:  *  until a device reports that it is no longer busy.  Hence the error
        !           490:  *  message when this command fails. All this really does is allows
        !           491:  *  me to safely start applications while the initial retension is still
        !           492:  *  happening [csh].
        !           493:  */
        !           494: 
        !           495:     else {
        !           496:         if (r->sensebuf[0] == 0x70 && r->sensebuf[2] == 0x06) {
        !           497: 
        !           498:             /* Yep, The user just put it there */
        !           499: 
        !           500:             r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
        !           501:             r->xferdir = 0;
        !           502:             r->timeout = 300;
        !           503:             memset(g0, 0, sizeof(cdb_t));
        !           504:             g0->opcode = LOAD;
        !           505:             g0->xfr_len = 1;        /* Move tape to load point. */
        !           506:             doscsi(r, 4, pritape, slpriSigCatch, "ctopen");
        !           507:             if (r->status != ST_GOOD &&
        !           508:                 printerror(r, "Load failed - TDC3600 not ready")) {
        !           509:                 u. u_error = ENXIO;
        !           510:                 goto openfailed;
        !           511:             }
        !           512:         }
        !           513:     }
        !           514: #endif
        !           515: 
        !           516:     ctvmsg(0x0100, (devmsg(r->dev, "Read block limits.")));
        !           517: #ifdef TDC3600
        !           518:     c->blmin = c->blmax = 512;
        !           519:     ctvmsg(0x0100, (devmsg(r->dev, "Tandberg TDC36XX - Block limits set")));
        !           520:     rblerf = 0;
        !           521: #else
        !           522:     r->buf. space = KRNL_ADDR;
        !           523:     r->buf. addr. caddr = (caddr_t) bl;
        !           524:     r->buf. size = sizeof(blklim_t);
        !           525:     r->xferdir = DMAREAD;
        !           526:     r->timeout = 2;
        !           527:     memset(g0, 0, sizeof(cdb_t));
        !           528:     g0->opcode = READBLKLMT;
        !           529:     g0->xfr_len = 6;
        !           530:     doscsi(r, 3, pritape, slpriSigCatch, "ctopen");
        !           531: 
        !           532: /***********************************************************************
        !           533:  *  This is a bit oblique:  The Read Block limits is used to determine
        !           534:  *  what the tape drive that we have can do.  I have yet to run into
        !           535:  *  a tape drive that can do variable mode yet so I'm just winging
        !           536:  *  this.  In any case all I did was make it okay for the Archive Tape
        !           537:  *  drives to say "I don't support that command".
        !           538:  */
        !           539: 
        !           540:     if (rblerf = (r->status != ST_GOOD)) {
        !           541:         ctvmsg(0x0010, (printerror(r, "Read Block LImits")));
        !           542:         c->blmax = c->blmin = 0;
        !           543:     }
        !           544:     else {
        !           545:         flip(bl->blmax);    /* SCSI to INTEL order */
        !           546:         flip(bl->blmin);    /* Ditto */
        !           547:         c->blmax = (bl->blmax & 0x00ffffff);
        !           548:         c->blmin = bl->blmin;
        !           549:     }
        !           550: #endif
        !           551: 
        !           552: /***********************************************************************
        !           553:  *  Use mode sense to find out if the tape is write protected and also
        !           554:  *  to find out what the current tape blocksize is.
        !           555:  */
        !           556: 
        !           557:     r->buf. space = KRNL_ADDR;
        !           558:     r->buf. addr. caddr = (caddr_t) buf;
        !           559:     r->buf. size = sizeof(buf);
        !           560:     r->xferdir = DMAREAD;
        !           561:     r->timeout = 2;
        !           562:     memset(g0, 0, sizeof(cdb_t));
        !           563:     g0->opcode = MODESENSE;
        !           564:     g0->xfr_len = sizeof(buf);
        !           565:     doscsi(r, 3, pritape, slpriSigCatch, "ctopen");
        !           566:     if (r->status != ST_GOOD) {
        !           567:         if (printerror(r, "Mode sense failed"))
        !           568:             u. u_error = EIO;
        !           569:         goto openfailed;
        !           570:     }
        !           571: 
        !           572: /***********************************************************************
        !           573:  *  If tape drive opened in write mode make sure the tape is not write
        !           574:  *  protected now.
        !           575:  */
        !           576: 
        !           577:     if (mode == IPW && (buf[2] & 0x80) != 0) {
        !           578:         devmsg(dev, "Tape is write protected");
        !           579:         u. u_error = ENXIO;
        !           580:         goto openfailed;
        !           581:     }
        !           582: 
        !           583: /***********************************************************************
        !           584:  *  If mode sense returned any media descriptors take the first one,
        !           585:  *  it's the default, and use it.
        !           586:  *
        !           587:  *  There are two possible pitfalls here:
        !           588:  *
        !           589:  *      1) According to SCSI-1 it not an error to return zero block
        !           590:  *  descriptors.  If you're favorite drive returns zero block descriptors
        !           591:  *  this driver will fail here.
        !           592:  *
        !           593:  *      2)  I am assuming that the first media descriptor returned is
        !           594:  *  the "current" media type which the drive expects to use.  This
        !           595:  *  works with SCSI-2 but SCSI-1 says that the first descriptor is
        !           596:  *  the "default" media type.  SCSI-1 is lacking in the definition
        !           597:  *  of "default" for my conservative taste (when programming is
        !           598:  *  concerned).
        !           599:  *
        !           600:  *  I would like to put the issue of supporting real oddball tape drives
        !           601:  *  like my Tandberg to rest and only have one source source file for
        !           602:  *  scsi tape drives but I don't think that will work.  In the future
        !           603:  *  there will probably be two or maybe three if DAT proves to be as
        !           604:  *  much fun as this is.
        !           605:  */
        !           606: 
        !           607:     if (buf[3]) {   /* If mode sense returned any media descriptors */
        !           608:         bd->rb. blocksize &= 0xffffff00;
        !           609:         flip(bd->rb. blocksize);
        !           610:         c->block = bd->rb. blocksize;
        !           611:         if (c->block && c->cachesize % c->block)
        !           612:             c->cachesize -= (c->cachesize % c->block);
        !           613:     }
        !           614:     else {
        !           615:         devmsg(r->dev, "No media descriptors: Contact Mark Williams Tech support");
        !           616:         u. u_error = ENXIO;
        !           617:         goto openfailed;
        !           618:     }
        !           619:     ctvmsg(0x0001, devmsg(dev, "Blocksize: %d bytes.", c->block));
        !           620: 
        !           621: /***********************************************************************
        !           622:  *  One last check:  If we aren't using block mode (!c->block)
        !           623:  *  and we didn't get any block limits then we cannot support this
        !           624:  *  drive.
        !           625:  */
        !           626: 
        !           627:     if (!c->block && rblerf) {
        !           628:         devmsg(r->dev, "<No block limits on variable mode tape drive>");
        !           629:         devmsg(r->dev, "<Contact Mark Williams Tech Support>");
        !           630:         u. u_error = ENXIO;
        !           631:         goto openfailed;
        !           632:     }
        !           633: 
        !           634:     c->flags = (mode == IPR) ? CTRDMD : CTWRTMD;
        !           635:     if (c->block) {
        !           636:         c->cache = kalloc(HAICACHESZ);
        !           637:         if (!c->cache) {
        !           638:             devmsg(dev, "Could not allocate tape cache");
        !           639:             u. u_error = ENOMEM;
        !           640:             goto openfailed;
        !           641:         }
        !           642:         c->avail = (c->flags & CTRDMD) ? 0 : HAICACHESZ;
        !           643:         c->start = c->cache;
        !           644:     }
        !           645:     c->state = CTIDLE;
        !           646:     goto done;
        !           647: 
        !           648: openfailed:
        !           649:     c->state = CTIDLE;
        !           650:     c->inuse = 0;
        !           651: 
        !           652: done:
        !           653:     spl(s);
        !           654: }   /* ctopen() */
        !           655: 
        !           656: /***********************************************************************
        !           657:  *  ctclose()
        !           658:  *
        !           659:  *  Close the SCSI Device at (dev).
        !           660:  */
        !           661: 
        !           662: static void ctclose(dev)
        !           663: register dev_t  dev;
        !           664: {
        !           665:     register ctctrl_p   c = ctdevs[tid(dev)];
        !           666:     register srb_p      r = &(c->srb);
        !           667:     int                 s;
        !           668: 
        !           669:     if (!c) {
        !           670:         u. u_error = ENXIO;
        !           671:         return;
        !           672:     }
        !           673: 
        !           674:     s = sphi();
        !           675:     if (c->block && (c->flags & CTWRTMD)) {
        !           676:         if (ctbusywait(c, CTLASTWRT))
        !           677:             flushcache(c);
        !           678:         c->state = CTIDLE;
        !           679:     }
        !           680:     spl(s);
        !           681:     if (c->cache) {
        !           682:         kfree(c->cache);
        !           683:         c->cache = c->start = NULL;
        !           684:         c->avail = 0;
        !           685:     }
        !           686: 
        !           687: #ifndef TDC3600
        !           688: /*
        !           689:  *  Write two filemarks (Logical End of Tape) and then skip backwards
        !           690:  *  over one of them to ready for the another write operation on the
        !           691:  *  no rewind device.  This code guarantees properly formed tapes according
        !           692:  *  to the wisened old nine-track hacker that I work with.  The problem
        !           693:  *  is that...
        !           694:  */
        !           695:     if (c->flags & CTDIRTY) {
        !           696:         writefm(c, 1);         /* Write a file mark */
        !           697:         writefm(c, 1);         /* Write another filemark */
        !           698:         space(c, -1, SKIP_FM); /* Go back to the first filemark */
        !           699:     }
        !           700: #else
        !           701: /*
        !           702:  *  ... problem is that my Tandberg considers it an error to do anything
        !           703:  *  after it has skipped past a filemark. So all commands except load and
        !           704:  *  rewind fail after the previous code.  The following will work in
        !           705:  *  all situations but there is a risk that a user's tapes will only
        !           706:  *  have one filemark at Logical End-of-Tape if the user isn't careful
        !           707:  *  to use the rewind device the last time he uses tape drive.  This is
        !           708:  *  only a problem with drives which insist upon doing a Retension each
        !           709:  *  time the tape is changed or the drive gets reset.
        !           710:  */
        !           711:     if (c->flags & CTDIRTY) {
        !           712:         writefm(c, 1);
        !           713:         if (r->dev & REWIND)
        !           714:             writefm(c, 1);
        !           715:     }
        !           716: #endif
        !           717: 
        !           718:     if (r->dev & REWIND)
        !           719:         rewind(c, 0);
        !           720: 
        !           721:     c->inuse = 0;
        !           722:     return;
        !           723: }   /* ctclose() */
        !           724: 
        !           725: /***********************************************************************
        !           726:  *  fillcache() --  Read from the tape into the cache (really?)
        !           727:  *
        !           728:  *  return 0 and set u. u_error on any errors.
        !           729:  */
        !           730: 
        !           731: static int fillcache(c)
        !           732: register ctctrl_p   c;
        !           733: {
        !           734:     register srb_p      r = (&c->srb);
        !           735:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           736:     size_t              blocks;
        !           737:     extsense_p          e;
        !           738:     int                 info;
        !           739:     int                 retval = 0;
        !           740: 
        !           741:     r->buf. space = KRNL_ADDR;
        !           742:     r->buf. addr. caddr = (caddr_t) c->cache;
        !           743:     r->buf. size = HAICACHESZ;
        !           744:     r->xferdir = DMAREAD;
        !           745:     r->timeout = 30;
        !           746:     r->tries = 0;
        !           747:     g0->opcode = READ;
        !           748:     g0->lun_lba = (r->lun << 5) | 1;
        !           749:     blocks = HAICACHESZ / c->block;
        !           750:     g0->lba_mid = ((unsigned char *) &blocks)[2];
        !           751:     g0->lba_low = ((unsigned char *) &blocks)[1];
        !           752:     g0->xfr_len = ((unsigned char *) &blocks)[0];
        !           753:     g0->control = 0;
        !           754:     doscsi(r, 1, pritape, slpriSigCatch, "ctblkrd");
        !           755:     switch (r->status) {
        !           756:     case ST_GOOD:
        !           757:         c->start = c->cache;
        !           758:         c->avail = r->buf. size;
        !           759:         retval = 1;
        !           760:         break;
        !           761:     case ST_CHKCOND:
        !           762:         e = r->sensebuf;
        !           763:         if ((e->errorcode & 0x70) == 0x70) {
        !           764:             info = 0;
        !           765:             if (e->errorcode & 0x80) {
        !           766:                 info = e->info;
        !           767:                 flip(info);
        !           768:             }
        !           769:             if (e->sensekey & (CTFILEMARK | CTEOM)) {
        !           770:                 c->flags |= (e->sensekey & (CTFILEMARK | CTEOM));
        !           771:                 c->start = c->cache;
        !           772:                 c->avail = HAICACHESZ - (info * c->block);
        !           773:                 retval = 1;
        !           774:                 break;
        !           775:             }
        !           776:         }
        !           777:         printsense(r->dev, "Read failed", r->sensebuf);
        !           778:         u. u_error = EIO;
        !           779:         retval = 0;
        !           780:         break;
        !           781:     case ST_USRABRT:
        !           782:         u. u_error = EINTR;
        !           783:         c->start = c->cache;
        !           784:         c->avail = 0;
        !           785:         retval = 0;
        !           786:         break;
        !           787:     default:
        !           788:         devmsg(r->dev, "Read failed: status (0x%x)", r->status);
        !           789:         u. u_error = EIO;
        !           790:         retval = 0;
        !           791:         break;
        !           792:     }
        !           793:     return retval;
        !           794: }   /* fillcache() */
        !           795: 
        !           796: /***********************************************************************
        !           797:  *  ctfbrd()    --  Fixed block read handler.  Reads from the tape
        !           798:  *                  drive through the cache when the tape drive is
        !           799:  *                  in fixed block mode.
        !           800:  */
        !           801: 
        !           802: static void ctfbrd(c, iop)
        !           803: register ctctrl_p   c;
        !           804: register IO         *iop;
        !           805: {
        !           806:     register size_t reqcount,   /* Total bytes transfered toward request */
        !           807:                     xfrsize;    /* Current transfer size */
        !           808:     size_t          total,      /* System global memory total transfer size */
        !           809:                     size;       /* System global memory current transfer size */
        !           810: 
        !           811:     if (!ctbusywait(c, CTFBRD))
        !           812:         return;
        !           813:     reqcount = 0;
        !           814:     while (iop->io_ioc) {
        !           815:         xfrsize = min(c->avail, iop->io_ioc);
        !           816:         if (xfrsize > 0) {
        !           817:             switch (iop->io_seg) {
        !           818:             case IOSYS:
        !           819:                 memcpy(iop->io. vbase + reqcount, c->start, xfrsize);
        !           820:                 break;
        !           821:             case IOUSR:
        !           822:                 kucopy(c->start, iop->io. vbase + reqcount, xfrsize);
        !           823:                 break;
        !           824:             case IOPHY:
        !           825:                 total = 0;
        !           826:                 while (total < xfrsize) {
        !           827:                     size = min(xfrsize - total, NBPC);
        !           828:                     xpcopy(c->start + total,
        !           829:                            iop->io. pbase + reqcount + total,
        !           830:                            size,
        !           831:                            SEG_386_KD | SEG_VIRT);
        !           832:                     total += size;
        !           833:                 }
        !           834:                 break;
        !           835:             }
        !           836:             c->start += xfrsize;
        !           837:             c->avail -= xfrsize;
        !           838:             reqcount += xfrsize;
        !           839:             iop->io_ioc -= xfrsize;
        !           840:         }
        !           841:         if (iop->io_ioc) {
        !           842:             if (c->flags & CTFILEMARK) {
        !           843:                 c->flags &= ~CTFILEMARK;
        !           844:                 break;
        !           845:             }
        !           846: 
        !           847:             if (c->flags & CTEOM) {
        !           848:                 u. u_error = EIO;
        !           849:                 break;
        !           850:             }
        !           851: 
        !           852:             if (!fillcache(c))
        !           853:                 break;
        !           854:         }
        !           855:     }   /* while */
        !           856:     c->state = CTIDLE;
        !           857: }   /* ctfbrd() */
        !           858: 
        !           859: /***********************************************************************
        !           860:  *  ctvbrd()    --  Variable block read entry point.
        !           861:  */
        !           862: 
        !           863: static void ctvbrd(c, iop)
        !           864: register ctctrl_p   c;
        !           865: IO                  *iop;
        !           866: {
        !           867:     register srb_p      r = &(c->srb);
        !           868:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           869:     size_t              xfrsize;
        !           870:     extsense_p          e;
        !           871:     int                 info;
        !           872: 
        !           873:     if (!ctbusywait(c, CTVBRD))
        !           874:         return;
        !           875: 
        !           876:     if (c->flags & CTEOM) {
        !           877:         u. u_error = EIO;
        !           878:         return;
        !           879:     }
        !           880:     if (c->flags & CTFILEMARK) {
        !           881:         c->flags &= ~CTFILEMARK;
        !           882:         return;
        !           883:     }
        !           884:     switch (iop->io_seg) {
        !           885:     case IOSYS:
        !           886:         r->buf. space = KRNL_ADDR;
        !           887:         r->buf. addr. caddr = iop->io. vbase;
        !           888:         break;
        !           889:     case IOUSR:
        !           890:         r->buf. space = USER_ADDR;
        !           891:         r->buf. addr. caddr = iop->io. vbase;
        !           892:         break;
        !           893:     case IOPHY:
        !           894:         r->buf. space = PHYS_ADDR;
        !           895:         r->buf. addr. paddr = iop->io. pbase;
        !           896:         break;
        !           897:     }
        !           898:     r->buf. size = xfrsize = iop->io_ioc;
        !           899:     r->xferdir = DMAREAD;
        !           900:     r->timeout = 30;
        !           901:     g0->opcode = READ;
        !           902:     g0->lun_lba = (r->lun << 5);
        !           903:     g0->lba_mid = ((unsigned char *) &xfrsize)[2];
        !           904:     g0->lba_low = ((unsigned char *) &xfrsize)[1];
        !           905:     g0->xfr_len = ((unsigned char *) &xfrsize)[0];
        !           906:     g0->control = 0;
        !           907:     doscsi(r, 1, pritape, slpriSigCatch, "ctvbrd");
        !           908:     switch (r->status) {
        !           909:     case ST_GOOD:
        !           910:         iop->io_ioc -= r->buf. size;
        !           911:         break;
        !           912:     case ST_CHKCOND:
        !           913:         e = r->sensebuf;
        !           914:         if ((e->errorcode & 0x70) == 0x70) {
        !           915:             info = 0;
        !           916:             if (e->errorcode & 0x80) {
        !           917:                 info = (long) e->info;
        !           918:                 flip(info);
        !           919:             }
        !           920:             if (e->sensekey & (CTFILEMARK | CTEOM)) {
        !           921:                 c->flags |= (e->sensekey & (CTFILEMARK | CTEOM));
        !           922:                 break;
        !           923:             }
        !           924:             else if (e->sensekey & CTILI) {
        !           925:                 devmsg(r->dev,
        !           926:                        "Read failed buffer size %d blocksize %d",
        !           927:                        xfrsize,
        !           928:                        xfrsize - info);
        !           929:                 if (info > 0)
        !           930:                     iop->io_ioc -= (xfrsize - info);
        !           931:                 else
        !           932:                     u. u_error = EIO;
        !           933:                 break;
        !           934:             }
        !           935:         }
        !           936:         printsense(r->dev, "Read failed", r->sensebuf);
        !           937:         u. u_error = EIO;
        !           938:         break;
        !           939:     case ST_USRABRT:
        !           940:         break;
        !           941:     default:
        !           942:         devmsg(r->dev, "Read failed: status (0x%x)", r->status);
        !           943:         u. u_error = EIO;
        !           944:         break;
        !           945:     }
        !           946:     c->state = CTIDLE;
        !           947: }   /* ctvbrd() */
        !           948: 
        !           949: /***********************************************************************
        !           950:  *  ctread()    --  OS Read entry point.
        !           951:  */
        !           952: 
        !           953: static void ctread(dev, iop)
        !           954: dev_t       dev;
        !           955: register IO *iop;
        !           956: {
        !           957:     register ctctrl_p   c = ctdevs[tid(dev)];
        !           958: 
        !           959:     if (!c) {
        !           960:         u. u_error = EINVAL;
        !           961:         return;
        !           962:     }
        !           963: 
        !           964:     if (c->block)
        !           965:         ctfbrd(c, iop);
        !           966:     else
        !           967:         ctvbrd(c, iop);
        !           968: }   /* ctread() */
        !           969: 
        !           970: /***********************************************************************
        !           971:  *  flushcache()    --  flush the data in the cache to the tape.
        !           972:  *
        !           973:  *  returns 0 and sets u. u_error on failure else returns 1.
        !           974:  */
        !           975: 
        !           976: static int flushcache(c)
        !           977: register ctctrl_p   c;
        !           978: {
        !           979:     register srb_p      r = (&c->srb);
        !           980:     register g0cmd_p    g0 = &(r->cdb. g0);
        !           981:     size_t              xfrsize;
        !           982:     extsense_p          e;
        !           983:     int                 info;
        !           984:     int                 retval = 0;
        !           985: 
        !           986:     if (c->avail >= HAICACHESZ)
        !           987:         return 1;
        !           988: 
        !           989:     r->buf. space = KRNL_ADDR;
        !           990:     r->buf. addr. caddr = (caddr_t) c->cache;
        !           991:     r->buf. size = xfrsize = HAICACHESZ - c->avail;
        !           992:     r->xferdir = DMAWRITE;
        !           993:     r->timeout = 30;
        !           994:     r->tries = 0;
        !           995:     g0->opcode = WRITE;
        !           996:     g0->lun_lba = (r->lun << 5);
        !           997:     if (c->block) {
        !           998:         g0->lun_lba |= 1;
        !           999:         xfrsize = (xfrsize + c->block - 1) / c->block;
        !          1000:     }
        !          1001:     g0->lba_mid = ((unsigned char *) &xfrsize)[2];
        !          1002:     g0->lba_low = ((unsigned char *) &xfrsize)[1];
        !          1003:     g0->xfr_len = ((unsigned char *) &xfrsize)[0];
        !          1004:     g0->control = 0;
        !          1005:     doscsi(r, 1, pritape, slpriSigCatch, "ctblkwrt");
        !          1006:     switch (r->status) {
        !          1007:     case ST_GOOD:
        !          1008:         c->start = c->cache;
        !          1009:         c->avail = HAICACHESZ;
        !          1010:         retval = 1;
        !          1011:         break;
        !          1012:     case ST_CHKCOND:
        !          1013:         e = r->sensebuf;
        !          1014:         if ((e->errorcode & 0x70) == 0x70) {
        !          1015:             info = 0;
        !          1016:             if (e->errorcode & 0x80) {
        !          1017:                 info = e->info;
        !          1018:                 flip(info);
        !          1019:             }
        !          1020:             if (e->sensekey & CTEOM) {
        !          1021:                 c->flags |= CTEOM;
        !          1022:                 devmsg(r->dev, "End of tape on block write");
        !          1023:             }
        !          1024:         }
        !          1025:         printsense(r->dev, "Read failed", r->sensebuf);
        !          1026:         u. u_error = EIO;
        !          1027:         retval = 0;
        !          1028:         break;
        !          1029:     case ST_USRABRT:
        !          1030:         retval = 0;
        !          1031:         break;
        !          1032:     default:
        !          1033:         devmsg(r->dev, "Read failed: status (0x%x)", r->status);
        !          1034:         u. u_error = EIO;
        !          1035:         retval = 0;
        !          1036:         break;
        !          1037:     }
        !          1038:     return retval;
        !          1039: }   /* flushcache() */
        !          1040: 
        !          1041: /***********************************************************************
        !          1042:  *  ctfbwrt()   --  Fixed block write.  This should be fast because
        !          1043:  *                  it uses the tapes drives optimum setting and it
        !          1044:  *                  goes through a cache.
        !          1045:  */
        !          1046: 
        !          1047: static void ctfbwrt(c, iop)
        !          1048: register ctctrl_p   c;
        !          1049: register IO         *iop;
        !          1050: {
        !          1051:     register size_t reqcount,   /* Total bytes transfered */
        !          1052:                     xfrsize;    /* Current transfer size */
        !          1053:     size_t          total,      /* System global memory total transfer size */
        !          1054:                     size;       /* System global memory current transfer size */
        !          1055: 
        !          1056:     if (!ctbusywait(c, CTFBWRT))
        !          1057:         return;
        !          1058: 
        !          1059:     reqcount = 0;
        !          1060:     while (iop->io_ioc) {
        !          1061:         xfrsize = min(c->avail, iop->io_ioc);
        !          1062:         if (xfrsize) {
        !          1063:             switch (iop->io_seg) {
        !          1064:             case IOSYS:
        !          1065:                 memcpy(c->start, iop->io. vbase + reqcount, xfrsize);
        !          1066:                 break;
        !          1067:             case IOUSR:
        !          1068:                 ukcopy(iop->io. vbase + reqcount, c->start, xfrsize);
        !          1069:                 break;
        !          1070:             case IOPHY:
        !          1071:                 total = 0;
        !          1072:                 while (total < xfrsize) {
        !          1073:                     size = min(xfrsize - total, NBPC);
        !          1074:                     pxcopy(iop->io. pbase + reqcount + total,
        !          1075:                            c->start + total,
        !          1076:                            size,
        !          1077:                            SEG_386_KD | SEG_VIRT);
        !          1078:                     total += size;
        !          1079:                 }
        !          1080:                 break;
        !          1081:             }
        !          1082:             c->start += xfrsize;
        !          1083:             c->avail -= xfrsize;
        !          1084:             reqcount += xfrsize;
        !          1085:             iop->io_ioc -= xfrsize;
        !          1086:         }
        !          1087:         if (iop->io_ioc) {
        !          1088:             if (!flushcache(c))
        !          1089:                 break;
        !          1090:         }
        !          1091:     }   /* while */
        !          1092:     c->state = CTIDLE;
        !          1093: }   /* ctfbwrt() */
        !          1094: 
        !          1095: /***********************************************************************
        !          1096:  *  ctvbwrt()   --  Variable block writes.
        !          1097:  */
        !          1098: 
        !          1099: static void ctvbwrt(c, iop)
        !          1100: register ctctrl_p   c;
        !          1101: register IO         *iop;
        !          1102: {
        !          1103:     register srb_p      r = &(c->srb);
        !          1104:     register g0cmd_p    g0 = &(r->cdb. g0);
        !          1105:     size_t              xfrsize;
        !          1106:     extsense_p          e;
        !          1107:     int                 info;
        !          1108: 
        !          1109:     if (!ctbusywait(c, CTVBWRT))
        !          1110:         return;
        !          1111: 
        !          1112:     if (c->blmax && iop->io_ioc > c->blmax) {
        !          1113:         devmsg(r->dev, "Tape Error: maximum read/write size is %d bytes.", c->blmax);
        !          1114:         u. u_error = EIO;
        !          1115:         return;
        !          1116:     }
        !          1117:     switch (iop->io_seg) {
        !          1118:     case IOSYS:
        !          1119:         r->buf. space = KRNL_ADDR;
        !          1120:         r->buf. addr. caddr = iop->io. vbase;
        !          1121:         break;
        !          1122:     case IOUSR:
        !          1123:         r->buf. space = USER_ADDR;
        !          1124:         r->buf. addr. caddr = iop->io. vbase;
        !          1125:         break;
        !          1126:     case IOPHY:
        !          1127:         r->buf. space = PHYS_ADDR;
        !          1128:         r->buf. addr. paddr = iop->io. pbase;
        !          1129:         break;
        !          1130:     }
        !          1131:     xfrsize = min(iop->io_ioc, c->blmin);
        !          1132:     r->buf. size = xfrsize;
        !          1133:     r->xferdir = DMAWRITE;
        !          1134:     r->timeout = 30;
        !          1135:     g0->opcode = WRITE;
        !          1136:     g0->lun_lba = (r->lun << 5);
        !          1137:     g0->lba_mid = ((unsigned char *) &xfrsize)[2];
        !          1138:     g0->lba_low = ((unsigned char *) &xfrsize)[1];
        !          1139:     g0->xfr_len = ((unsigned char *) &xfrsize)[0];
        !          1140:     g0->control = 0;
        !          1141:     doscsi(r, 1, pritape, slpriSigCatch, "ctvbwrt");
        !          1142:     switch (r->status) {
        !          1143:     case ST_GOOD:
        !          1144:         iop->io_ioc -= r->buf. size;
        !          1145:         break;
        !          1146:     case ST_CHKCOND:
        !          1147:         e = r->sensebuf;
        !          1148:         if ((e->errorcode & 0x70) == 0x70) {
        !          1149:             info = 0;
        !          1150:             if (e->errorcode & 0x80) {
        !          1151:                 info = (long) e->info;
        !          1152:                 flip(info);
        !          1153:             }
        !          1154:             if (e->sensekey & CTEOM) {
        !          1155:                 c->flags |= CTEOM;
        !          1156:                 devmsg(r->dev, "End of tape");
        !          1157:             }
        !          1158:         }
        !          1159:         printsense(r->dev, "Write failed", r->sensebuf);
        !          1160:         u. u_error = EIO;
        !          1161:         break;
        !          1162:     case ST_USRABRT:
        !          1163:         break;
        !          1164:     default:
        !          1165:         devmsg(r->dev, "Read failed: status (0x%x)", r->status);
        !          1166:         u. u_error = EIO;
        !          1167:         break;
        !          1168:     }
        !          1169:     c->state = CTIDLE;
        !          1170: }   /* ctvbwrt() */
        !          1171: 
        !          1172: /***********************************************************************
        !          1173:  *  ctwrite()   -- Write entry point for tape drive.
        !          1174:  */
        !          1175: 
        !          1176: static void ctwrite(dev, iop)
        !          1177: register dev_t  dev;
        !          1178: register IO     *iop;
        !          1179: {
        !          1180:     register ctctrl_p c = ctdevs[tid(dev)];
        !          1181: 
        !          1182:     if (!c) {
        !          1183:         u. u_error = ENXIO;
        !          1184:         return;
        !          1185:     }
        !          1186: 
        !          1187:     c->flags |= CTDIRTY;
        !          1188:     if (c->block)
        !          1189:         ctfbwrt(c, iop);
        !          1190:     else
        !          1191:         ctvbwrt(c, iop);
        !          1192: }   /* ctwrite() */
        !          1193: 
        !          1194: /***********************************************************************
        !          1195:  *  ctioctl()
        !          1196:  *
        !          1197:  *  I/O Control Entry point for Cartridge tape devices.
        !          1198:  *
        !          1199:  *  This function had been modified to allow applications level programs
        !          1200:  *  to select modes and features for the tape drive. As stated above,
        !          1201:  *  the philosophy of this driver is to provide least common denominator
        !          1202:  *  support for all tape drives.  I know that you spend big bucks to
        !          1203:  *  get that (insert your favorite drive brand/model).  If I decide
        !          1204:  *  to support everything out there on the market then I won't be able
        !          1205:  *  to write network drivers, serial drivers, etc.  So if you need
        !          1206:  *  to do something to the tape drive to make it work (mode sense/select)
        !          1207:  *  you can do it through this ctioctl as an applications program.
        !          1208:  */
        !          1209: 
        !          1210: static void ctioctl(dev, cmd, vec)
        !          1211: dev_t           dev;
        !          1212: register int    cmd;
        !          1213: char            *vec;
        !          1214: {
        !          1215:     register ctctrl_p   c = ctdevs[tid(dev)];
        !          1216:     register srb_p      r = &(c->srb);
        !          1217:     int                 s;
        !          1218: 
        !          1219:     if (!c) {
        !          1220:         u. u_error = EINVAL;
        !          1221:         return;
        !          1222:     }
        !          1223: 
        !          1224:     switch (cmd) {
        !          1225:     case MTREWIND:      /* Rewind */
        !          1226:         rewind(c, 1);
        !          1227:         break;
        !          1228:     case MTWEOF:        /* Write end of file mark */
        !          1229:         writefm(c, 1);
        !          1230:         break;
        !          1231:     case MTRSKIP:       /* Record skip */
        !          1232:         space(c, 1, SKIP_BLK);
        !          1233:         break;
        !          1234:     case MTFSKIP:       /* File skip */
        !          1235:         space(c, 1, SKIP_FM);
        !          1236:         break;
        !          1237:     case MTTENSE:       /* Tension tape */
        !          1238:         loadtape(c, RETENSION);
        !          1239:         break;
        !          1240:     case MTERASE:       /* Erase tape */
        !          1241:         erase(c, ERASE_TAPE);
        !          1242:         break;
        !          1243:     case MTDEC:         /* DEC mode */
        !          1244:     case MTIBM:         /* IBM mode */
        !          1245:     case MT800:         /* 800 bpi */
        !          1246:     case MT1600:        /* 1600 bpi */
        !          1247:     case MT6250:        /* 6250 bpi */
        !          1248:         return;
        !          1249:     default:
        !          1250:         if (!ctbusywait(c, CTIOCTL))
        !          1251:             return;
        !          1252:         s = sphi();
        !          1253:         haiioctl(r, cmd, vec);
        !          1254:         c->state = CTIDLE;
        !          1255:         spl(s);
        !          1256:         break;
        !          1257:     }
        !          1258: }   /* ctioctl() */
        !          1259: 
        !          1260: /* End of file */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.