/* * @(#)st.c 1.1 94/10/31 Copyright (c) 1988 by Sun Microsystems, Inc. * * Driver for SCSI tape drives. */ /*#define STDEBUG /* Allow compiling of debug code */ #define REL4 /* Enable release 4 mods */ #ifdef REL4 #include #include #include #include #include #include #include #include #include #else REL4 #include "saio.h" #include "param.h" #include "../h/buf.h" #include "../sun/dklabel.h" #include "../sun/dkio.h" #include "../mon/idprom.h" #include "../mon/cpu.addrs.h" #include "scsi.h" #include "streg.h" #endif REL4 #ifdef STDEBUG int st_debug = 1; /* 0 = normal operation * 1 = extended error info only * 2 = debug and error info * 3 = all status info */ /* Handy debugging 0, 1, and 2 argument printfs */ #define DPRINTF(str) \ if (st_debug > 1) printf(str) #define DPRINTF1(str, arg1) \ if (st_debug > 1) printf(str,arg1) #define DPRINTF2(str, arg1, arg2) \ if (st_debug > 1) printf(str,arg1,arg2) /* Handy extended error reporting 0, 1, and 2 argument printfs */ #define EPRINTF(str) \ if (st_debug) printf(str) #define EPRINTF1(str, arg1) \ if (st_debug) printf(str,arg1) #define EPRINTF2(str, arg1, arg2) \ if (st_debug) printf(str,arg1,arg2) static char *st_key_error_str[] = SENSE_KEY_INFO; #define MAX_KEY_ERROR_STR \ (sizeof(st_key_error_str)/sizeof(st_key_error_str[0])) #else STDEBUG #define DPRINTF(str) #define DPRINTF1(str, arg2) #define DPRINTF2(str, arg1, arg2) #define EPRINTF(str) #define EPRINTF1(str, arg2) #define EPRINTF2(str, arg1, arg2) #endif STDEBUG #ifdef BOOTBLOCK #define SC_ERROR(str) #define SC_ERROR1(str, arg1) #else BOOTBLOCK #define SC_ERROR printf #define SC_ERROR1 printf #endif BOOTBLOCK extern int sc_reset; extern int xxprobe(), xxboot(); int stopen(), stclose(), ststrategy(); /* * This table defines the auto-probe disk list. Currently only st0 and st1 * are probed automatically. Others can be added by simply adding them * to the following table. */ u_char st_index[] = { 0x20, 0x28, 0x18, 0x10 }; struct stparam { int st_target; int st_unit; int st_ctype; int st_dev_bsize; int st_eof; int st_lastcmd; struct host_saioreq h_sip; /* sip for host adapter */ }; /* * DMA-able buffers */ /* Max tape record length allowed for various devices */ #define ST_MAXXTREC (10*1024) /* 1/2-inch reel */ /* Define virtual addresses space. Note, sun-2 is limited. */ #ifdef sun2 #define ST_MAXSIZE (30 * 1024) /*#define ST_MAXSIZE (20 * 1024) */ #else sun2 #define ST_MAXSIZE (126 * 512) #endif sun2 struct stdma { char databuffer[ST_MAXSIZE]; char sbuffer[SC_SENSE_LENGTH]; }; #define BUF (((struct stdma *)sip->si_dmaaddr)->databuffer) #define STSBUF (((struct stdma *)sip->si_dmaaddr)->sbuffer) /* * What resources we need to run */ struct devinfo stinfo = { 0, /* No device to map in */ sizeof (struct stdma), sizeof (struct stparam), 0, /* Dummy devices */ 0, /* Dummy devices */ MAP_MAINMEM, ST_MAXSIZE, /* transfer size */ }; struct boottab stdriver = { "st", xxprobe, xxboot, stopen, stclose, ststrategy, "st: SCSI tape", &stinfo }; #define TAPE_TARGET 4 /* default SCSI target # for tape */ #define SENSELOC 4 /* sysgen returns sense at this offset */ #define ROUNDUP(x) ((x + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1)) #define min(a,b) ((a)<(b)? (a): (b)) #define ISASYSGEN(stp) (stp->st_ctype == ST_TYPE_SYSGEN ? 1 : 0) #ifdef SDDEBUG sd_print_buffer(y, count) register u_char *y; register int count; { register int x; for (x = 0; x < count; x++) printf("%x ", *y++); printf("\n"); } #endif SDDEBUG /* * Open the SCSI Tape controller */ stopen(sip) register struct saioreq *sip; { register struct stparam *stp; register struct host_saioreq *h_sip; /* Host adapter block */ register struct st_ms_mspl *ms; register short r; register int skip; int i, index, ctlr; int do_reset = 1; DPRINTF("stopen:\n"); stp = (struct stparam *) sip->si_devdata; bzero( (char *)stp, (sizeof (struct stparam))); ctlr = sip->si_ctlr; /* Ctlr. number */ stp->st_ctype = ST_TYPE_SYSGEN; /* Always start with Sysgen! */ /* Set up host adapter and target info. */ ST_OPEN_PROBE: if (sip->si_unit == 0) { index = 0; stp->st_unit = st_index[index] & 0x07; /* Logical unit */ stp->st_target = st_index[index++] >> 3; /* Target number */ } else { stp->st_unit = sip->si_unit & 0x07; /* Logical unit */ stp->st_target = sip->si_unit >> 3; /* Target number */ } ST_RETRY_OPEN: EPRINTF2("stopen: ctlr= 0x%x unit= 0x%x ", ctlr, stp->st_unit); EPRINTF1("target= 0x%x\n", stp->st_target); h_sip = &stp->h_sip; h_sip->ctlr = ctlr; /* Ctlr. number */ h_sip->unit = stp->st_target; /* Target number */ /* Probe for host adapter. */ if (scsi_probe(h_sip) == 0) { /* If an error occurred previously, reset the SCSI bus. */ if (sc_reset && do_reset) { EPRINTF("stopen: reset\n"); (void) (*h_sip->reset)(h_sip, 1); } /* * Test for the controller being ready. First test will fail * if the SCSI bus is permanently busy or if a previous op * was interrupted in mid-transfer. Second one should work. */ for (i = 0; i < 4; i++) { if ((r=stcmd(SC_TEST_UNIT_READY, sip, 0)) > 0) goto ST_OPEN; else if (r < -1) break; /*DELAY(2200); /* XXX: Extra timing margin for HP */ } /* If current tape device not ready, try the next one. */ if (sip->si_unit == 0 && index < sizeof(st_index)) { stp->st_unit = st_index[index] & 0x07; stp->st_target = st_index[index++] >> 3; do_reset = 0; goto ST_RETRY_OPEN; } } /* If current host adapter not ready, try the next one. */ if (sip->si_ctlr == 0 && ctlr < SC_NSC) { EPRINTF("stopen: trying next ha\n"); do_reset++; ctlr++; h_sip->devaddr = 0; goto ST_OPEN_PROBE; } EPRINTF("stopen: device offline\n"); return (-1); /* * Check for tape drive (that is..it's not a disk). Note, if * inquiry fails, we will presume the user knows what he's doing. */ ST_OPEN: sc_reset = 1; sip->si_ctlr = ctlr; sip->si_unit = (stp->st_target <<3) + stp->st_unit; sip->si_ma = BUF; if (stcmd(SC_INQUIRY, sip, 0) > 0 && ((struct scsi_inquiry_data *)sip->si_ma)->dtype != 1) { EPRINTF("stopen: not a tape\n"); return (-1); } /* * To figure out what type of tape controller is out there we send * a REQUEST_SENSE command and see how much sense data comes back. */ stp->st_ctype = ST_TYPE_EMULEX; sip->si_cc = ST_EMULEX_SENSE_LEN; /*sip->si_ma = BUF;*/ r = stcmd(SC_REQUEST_SENSE, sip, 0); DPRINTF1("stopen: sense bytes= %x\n", r); if (r == ST_SYSGEN_SENSE_BYTES) { EPRINTF("stopen: Sysgen found\n"); stp->st_ctype = ST_TYPE_SYSGEN; stp->st_dev_bsize = DEV_BSIZE; sip->si_cc = ST_SYSGEN_SENSE_LEN; if (stcmd(SC_REQUEST_SENSE, sip, 0) <= 0) { SC_ERROR("st: cannot get sense\n"); return (-1); } } else { /* sip->si_ma = BUF; */ ms = (struct st_ms_mspl *) sip->si_ma; r = stcmd(SC_MODE_SENSE, sip, 0); stp->st_dev_bsize = (ms->high_bl <<16) + (ms->mid_bl <<8) + (ms->low_bl); EPRINTF1("stopen: density= 0x%x\n", ms->density); EPRINTF1("stopen: block size= %d\n", stp->st_dev_bsize); } /* Check for 1/2" tape */ (void) stcmd(SC_REWIND, sip, 0); if (stp->st_dev_bsize == 0) { if (stcmd(SC_QIC24, sip, 0) <= 0) { EPRINTF("stopen: Fixed CCS found\n"); stp->st_dev_bsize = DEV_BSIZE; goto STOPEN_TAPE; } sip->si_cc = 0; /* sip->si_ma = BUF; */ if (stcmd(SC_READ, sip, 0) <= 0) { EPRINTF("stopen: Emulex found\n"); stp->st_dev_bsize = DEV_BSIZE; (void) stcmd(SC_REWIND, sip, 0); goto STOPEN_TAPE; } else { EPRINTF("stopen: Variable CCS found\n"); stinfo.d_maxiobytes = ST_MAXXTREC; (void) stcmd(SC_REWIND, sip, 0); goto STOPEN_SKIP; } } /* * Default format mode for emulex is Qic24 for Sun-3 * or later. For Sun-2 use Qic-11. */ STOPEN_TAPE: #ifdef sun2 (void) stcmd(SC_QIC11, sip, 1); #else sun2 (void) stcmd(SC_QIC24, sip, 1); #endif sun2 /* * If read fails, try the other format. For Sun-3 or later * this means Qic-11. For Sun-2, try Qic-24. */ sip->si_cc = 512; sip->si_ma = BUF; if (stcmd(SC_READ, sip, 0) > 0) { (void) stcmd(SC_REWIND, sip, 0); } else { (void) stcmd(SC_REWIND, sip, 0); #ifdef sun2 if (stcmd(SC_QIC24, sip, 1) <= 0) SC_ERROR("st: mode select failed\n"); #else sun2 if (stcmd(SC_QIC11, sip, 1) <= 0) SC_ERROR("st: mode select failed\n"); #endif sun2 } STOPEN_SKIP: skip = sip->si_boff; while (skip--) { sip->si_cc = 0; if (stcmd(SC_SPACE_FILE, sip, 1) <= 0) { SC_ERROR("st: space file failed\n"); return (-1); } } sc_reset = 0; stp->st_eof = 0; stp->st_lastcmd = 0; return (0); } /* * Close the tape drive and don't rewind it. */ stclose(sip) register struct saioreq *sip; { register struct stparam *stp; EPRINTF("stclose:\n"); stp = (struct stparam *) sip->si_devdata; if (stp->st_lastcmd == SC_WRITE) (void) stcmd(SC_WRITE_FILE_MARK, sip, 0); sc_reset = 0; return (0); } /* * Perform a read or write of the SCSI tape. */ ststrategy(sip, rw) register struct saioreq *sip; int rw; { register struct stparam *stp; DPRINTF("ststrategy:\n"); sc_reset = 1; stp = (struct stparam *) sip->si_devdata; if (stp->st_eof) { stp->st_eof = 0; return (0); } DPRINTF2("ststrategy: addr= 0x%x size= 0x%x\n", (int)(sip->si_ma), sip->si_cc); rw = stcmd((rw == WRITE ? SC_WRITE : SC_READ), sip, 1); return (rw); } /* * Execute a scsi tape command */ static int stcmd(cmd, sip, errprint) short cmd; register struct saioreq *sip; int errprint; { register struct stparam *stp; register struct host_saioreq *h_sip; /* Sip for host adapter */ register struct st_ms_mspl *mode; register struct st_sense *ems; register struct sysgen_sense *scs; struct scsi_cdb6 cdb, scdb; struct scsi_scb scb, sscb; char *buf; int density; int r, cc; DPRINTF("stcmd:\n"); AGAIN: stp = (struct stparam *)sip->si_devdata; h_sip = &stp->h_sip; /* host adapter */ h_sip->unit = stp->st_target; buf = sip->si_ma; /* Set up cdb */ bzero((char *) &scb, sizeof scb); bzero((char *) &cdb, CDB_GROUP0); stp->st_lastcmd = cmd; cdb.cmd = cmd; cdb.lun = stp->st_unit; h_sip->cc = 0; h_sip->ma = 0; DPRINTF2("stcmd: cmd= 0x%x lun= 0x%x\n", cdb.cmd, cdb.lun); /* Some fields in the cdb are command specific */ switch (cmd) { case SC_READ: h_sip->dma_dir = SC_RECV_DATA; if (stp->st_dev_bsize == 0) { DPRINTF1("stcmd: variable read (0x%x)\n", sip->si_cc); cc = h_sip->cc = sip->si_cc; } else { DPRINTF1("stcmd: read (0x%x)\n", sip->si_cc); cc = h_sip->cc = ROUNDUP(sip->si_cc); cc = cc / stp->st_dev_bsize; cdb.t_code = 1; } STCMD_RD: cdb.mid_count = (cc >> 8) & 0xFF; cdb.low_count = cc & 0xFF; if (buf >= DVMA_BASE) { h_sip->ma = buf; DPRINTF1("stcmd: DVMA addr= 0x%x\n", h_sip->ma); } else { h_sip->ma = BUF; DPRINTF("stcmd: BUF addr\n"); } if (cc == 0) h_sip->dma_dir = SC_NO_DATA; break; case SC_WRITE: h_sip->dma_dir = SC_SEND_DATA; if (buf < DVMA_BASE) { DPRINTF("stcmd: write bcopy\n"); bcopy(buf, BUF, sip->si_cc); } if (stp->st_dev_bsize == 0) { DPRINTF1("stcmd: variable write (0x%x)\n", sip->si_cc); cc = h_sip->cc = sip->si_cc; } else { DPRINTF1("stcmd: write (0x%x)\n", sip->si_cc); cc = h_sip->cc = ROUNDUP(sip->si_cc); cc = cc / stp->st_dev_bsize; cdb.t_code = 1; } goto STCMD_RD; /* break; */ case SC_MODE_SENSE: DPRINTF("stcmd: mode sense\n"); h_sip->dma_dir = SC_RECV_DATA; h_sip->cc = sip->si_cc = sizeof (struct st_ms_mspl); cdb.low_count = h_sip->cc & 0xFF; h_sip->ma = BUF; bzero((char *)h_sip->ma, sizeof (struct st_ms_mspl)); break; case SC_QIC11: EPRINTF("stcmd: low density (qic-11)\n"); sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; if (ISASYSGEN(stp)) { cdb.cmd = SC_QIC02; cdb.high_count = ST_SYSGEN_QIC11; } else { density = ST_EMULEX_QIC11; goto MODE; } break; case SC_QIC24: EPRINTF("stcmd: high density (qic-24)\n"); sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; if (ISASYSGEN(stp)) { cdb.cmd = SC_QIC02; cdb.high_count = ST_SYSGEN_QIC24; } else { density = ST_EMULEX_QIC24; goto MODE; } break; case SC_MODE_SELECT: MODE: cdb.cmd = SC_MODE_SELECT; mode = (struct st_ms_mspl *)STSBUF; bzero((char *)mode, sizeof(*mode)); mode->bufm = 1; mode->bd_len = MS_BD_LEN; mode->density = density; mode->mid_bl = (stp->st_dev_bsize >> 8) & 0xff; mode->low_bl = stp->st_dev_bsize & 0xff; h_sip->ma = (char *)mode; h_sip->dma_dir = SC_SEND_DATA; sip->si_cc = h_sip->cc = cdb.low_count = sizeof (struct st_ms_mspl); break; case SC_SPACE_FILE: EPRINTF("stcmd: space file\n"); cdb.cmd = SC_SPACE_REC; /* Real SCSI cmd */ cdb.t_code = 1; /* Space file, not rec */ cdb.low_count = 1; /* Space 1 file */ sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; break; case SC_WRITE_FILE_MARK: DPRINTF("stcmd: write file mark\n"); cdb.low_count = 1; sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; break; case SC_REQUEST_SENSE: DPRINTF("stcmd: request sense\n"); cdb.low_count = h_sip->cc = sip->si_cc; h_sip->ma = buf; h_sip->dma_dir = SC_RECV_DATA; /* bzero((char *)buf, sizeof (struct st_ms_mspl)); */ break; case SC_INQUIRY: EPRINTF("stcmd: inquiry\n"); h_sip->cc = sip->si_cc = sizeof (struct scsi_inquiry_data); cdb.low_count = h_sip->cc; h_sip->dma_dir = SC_RECV_DATA; h_sip->ma = buf; bzero((char *)buf, h_sip->cc); break; case SC_TEST_UNIT_READY: #ifdef STDEBUG DPRINTF("stcmd: test unit\n"); sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; break; #endif STDEBUG case SC_REWIND: DPRINTF("stcmd: rewind\n"); sip->si_cc = 0; h_sip->dma_dir = SC_NO_DATA; break; default: SC_ERROR1("st: unknown command (0x%x)\n", cdb.cmd); return (0); } /* Execute the command */ r = (*h_sip->doit)(&cdb, &scb, h_sip); DPRINTF2("stcmd: r = 0x%x (0x%x)\n", r, sip->si_cc); /* Host adapter error */ if (r < 0) { EPRINTF("stcmd: SCSI bus error\n"); return (r); } /* Allow for tape blocking */ if (r > sip->si_cc) { EPRINTF1("stcmd: round to 0x%x\n", sip->si_cc); r = sip->si_cc; } /* Device busy error */ if(scb.busy) { EPRINTF("stcmd: busy\n"); DELAY(2000000); goto AGAIN; } if (cmd == SC_READ && buf < DVMA_BASE) { DPRINTF("stcmd: read bcopy\n"); if (min(sip->si_cc, r)) bcopy(BUF, buf, (min(sip->si_cc, r))); } /* We may need to get sense data */ if (scb.chk) { EPRINTF("stcmd: check condition\n"); bzero((char *) &sscb, sizeof(sscb)); bzero((char *) &scdb, CDB_GROUP0); scdb.cmd = SC_REQUEST_SENSE; scdb.lun = stp->st_unit; if (ISASYSGEN(stp)) { h_sip->cc = scdb.low_count = ST_SYSGEN_SENSE_LEN; } else { h_sip->cc = scdb.low_count = ST_EMULEX_SENSE_LEN; } h_sip->ma = STSBUF; h_sip->dma_dir = SC_RECV_DATA; (void) (*h_sip->doit)(&scdb, &sscb, h_sip); /* Process Sysgen tape drive errors */ if (ISASYSGEN(stp)) { scs = (struct sysgen_sense *)STSBUF; stp->st_eof = 1; /* No tape */ if (scs->no_cart) { EPRINTF("stcmd: not ready\n"); return (-2); } /* QIC-24 */ if (scs->no_data != 0) { EPRINTF("stcmd: blank check\n"); return (-2); } /* EOF */ #ifdef STDEBUG if (scs->file_mark) EPRINTF("stcmd: eof\n"); #endif STDEBUG /* Other errors */ if ((scs->file_mark == 0) && errprint) { SC_ERROR1("st: error %x\n", *(u_short *)&STSBUF[SENSELOC]); } return (r); /* Process MT-02 and CCS tape drive errors */ } else { ems = (struct st_sense *)STSBUF; /* Blank Check error */ if (ems->key == SC_BLANK_CHECK) { EPRINTF("stcmd: blank check\n"); stp->st_eof = 1; return (-2); /* Soft error */ } else if (ems->key == SC_RECOVERABLE_ERROR) { EPRINTF("stcmd: soft error\n"); return (r ? r : 1); /* EOF error */ }else if (ems->fil_mk) { EPRINTF("stcmd: eof\n"); stp->st_eof = 1; return (r); /* Illegal length error */ } else if (ems->ili) { EPRINTF("stcmd: illegal length\n"); return (r ? r : 1); /* Other errors */ } else if(errprint) { SC_ERROR1("st: sense key = %x", ems->key); #ifdef STDEBUG SC_ERROR1(" (%s)", st_key_error_str[ems->key]); #endif STDEBUG SC_ERROR1(" error = %x\n", ems->error); stp->st_eof = 1; return (-1); } } } if (r >= sip->si_cc) { /* Normal completion exit */ return (sip->si_cc ? sip->si_cc : 1); } else { /* Error exit */ if (errprint) { EPRINTF1("stcmd: short transfer (0x%x)\n", r); } return (r); } }