📄 st.c
字号:
#ident "@@(#)st.c 1.1 92/07/30 SMI"#include "st.h"#if NST > 0/* * * SCSI Tape Driver * *//* * Copyright (C) 1988, 1989, 1990, 1991 by Sun Microsystems, Inc. *//* * the order of these defines is important */#include <scsi/scsi.h>#include <sys/file.h>#include <sys/mtio.h>#include <scsi/targets/stdef.h>#ifndef MT_DENSITY#define MT_DENSITY(dev) ((minor(dev) & MT_DENSITY_MASK) >> 3)#endif#ifndef STAT_BUS_RESET#define STAT_BUS_RESET 0x8 /* Command experienced a bus reset */#define STAT_DEV_RESET 0x10 /* Command experienced a device reset */#define STAT_ABORTED 0x20 /* Command was aborted */#define STAT_TIMEOUT 0x40 /* Command experienced a timeout */#endif#ifdef OPENPROMS#define DRIVER st_ops#define DNAME devp->sd_dev->devi_name#define DUNIT devp->sd_dev->devi_unit#define CNAME devp->sd_dev->devi_parent->devi_name#define CUNIT devp->sd_dev->devi_parent->devi_unit#else OPENPROMS#define DRIVER stdriver#define DNAME devp->sd_dev->md_driver->mdr_dname#define DUNIT devp->sd_dev->md_unit#define CNAME devp->sd_dev->md_driver->mdr_cname#define CUNIT devp->sd_dev->md_mc->mc_ctlr#endif OPENPROMS#define UPTR ((struct scsi_tape *)(devp)->sd_private)#define SCBP(pkt) ((struct scsi_status *)(pkt)->pkt_scbp)#define CDBP(pkt) ((union scsi_cdb *)(pkt)->pkt_cdbp)#define ROUTE (&devp->sd_address)#define BP_PKT(bp) ((struct scsi_pkt *)bp->av_back)#define Tgt(devp) (devp->sd_address.a_target)#define Lun(devp) (devp->sd_address.a_lun)#define IS_CLOSING(un) ((un)->un_state == ST_STATE_CLOSING || \ ((un)->un_state == ST_STATE_SENSING && \ (un)->un_laststate == ST_STATE_CLOSING))#define ASYNC_CMD 0#define SYNC_CMD 1/* * Macros for internal coding of count for SPACE command: * * Isfmk is 1 when spacing filemarks; 0 when spacing records: * bit 24 set indicates a space filemark command. * Fmk sets the filemark bit (24) and changes a backspace * count into a positive number with the sign bit set. * Blk changes a backspace count into a positive number with * the sign bit set. * space_cnt converts backwards counts to negative numbers. */#define Isfmk(x) (((int)x & (1<<24)) != 0)#define Fmk(x) ((1<<24)|(((int)x & (1<<31)) ? ((-(int)x) | (1<<31)):\ (int)x))#define Blk(x) (((int)x & (1<<31))? ((-(int)x) | (1<<31)): (int)x)#define space_cnt(x) ((((int)x)&(1<<31))? (-(((int)x)&((1<<24)-1))):\ ((int)x)&((1<<24)-1))/* * Debugging macros */#define DEBUGGING ((scsi_options & SCSI_DEBUG_TGT) || stdebug > 1)#define DEBUGGING_ALL ((scsi_options & SCSI_DEBUG_TGT) || stdebug)#define DPRINTF if (DEBUGGING) stprintf#define DPRINTF_IOCTL if (DEBUGGING || stdebug > 0) stprintf#define DPRINTF_ALL DPRINTF_IOCTL/* * Debugging turned on via conditional compilation switch -DST_DEBUG */#ifdef ST_DEBUGint st_debug = 0;#define ST_DEBUG_CMDS 0x01#endif ST_DEBUG/* * * Global Data Definitions * */extern struct st_drivetype st_drivetypes[];extern int st_ndrivetypes, st_retry_count, st_io_time, st_space_time;extern st_error_level;/* * * Local Static Data * */static int stdebug = 0;static int stpri = 0;struct scsi_device *stunits[ST_MAXUNIT];struct scsi_address *stsvdaddr[ST_MAXUNIT];static char *wrongtape = "%s%d: wrong tape for writing- use DC6150 tape (or equivalent)\n";/* * Configuration Data */#ifdef OPENPROMS/* * Device driver ops vector */int stslave(), stattach(), stopen(), stclose(), stread(), stwrite();int ststrategy(), stioctl();extern int nulldev(), nodev();struct dev_ops st_ops = { 1, stslave, stattach, stopen, stclose, stread, stwrite, ststrategy, nodev, nulldev, stioctl};#else OPENPROMS/* * stdriver is used to call slave, attach routines from scsi_make_device */int stslave(), stattach(), stopen(), stclose(), stread(), stwrite();int ststrategy(), stioctl();extern int nulldev(), nodev();struct mb_driver stdriver = { nulldev, stslave, stattach, nodev, nodev, nulldev, 0, "st", 0, 0, 0, 0};#endif OPENPROMS/* * * Local Function Declarations * */static void ststart(), stinit(), make_st_cmd(), stintr();static void st_set_state(), sterrmsg(), stdone(), st_test_append();static int strunout(), st_determine_generic();static int stcmd();static void stprintf();/* * * Autoconfiguration Routines * *//* * stslave is called by scsi_make_device at boot time. * Returns 1 on success; -1 on failure; 0 if kernel resources unavailable. */intstslave(devp)struct scsi_device *devp;{ int r; /* * fill in our local array */ if (DUNIT >= ST_MAXUNIT) return (0); /* Save pointer to scsi_device struct allocated by scsi_make_device */ stunits[DUNIT] = devp; /* * Make sure priority is the maximum of all host adapters with * tape devices so that spls properly block out other devices. */#ifdef OPENPROMS stpri = MAX(stpri, ipltospl(devp->sd_dev->devi_intr->int_pri));#else stpri = MAX(stpri, pritospl(devp->sd_dev->md_intpri));#endif OPENPROMS r = st_findslave(devp, 0); if (r < 0) { /* * Save the address so we can scsi_add_device later * (in stopen) if necessary. */ stsvdaddr[DUNIT] = (struct scsi_address *) kmem_zalloc(sizeof (struct scsi_address)); if (stsvdaddr[DUNIT]) { stunits[DUNIT] = 0; *stsvdaddr[DUNIT] = devp->sd_address; } else { r = 0; } } return (r);}/* * st_findslave is normally called by stslave, but may be called by stopen. * Returns 1 on success; -1 on failure; 0 if kernel resources unavailable. * Allocate buffers and initialize data structures. */static intst_findslave(devp, canwait)register struct scsi_device *devp;int canwait;{ struct scsi_pkt *rqpkt; struct scsi_tape *un; auto int (*f)() = (canwait == 0)? NULL_FUNC: SLEEP_FUNC; int rval = -1; /* * Call the routine scsi_slave to do some of the dirty work. * All scsi_slave does is do a TEST UNIT READY (and possibly * a non-extended REQUEST SENSE or two), and an INQUIRY command. * If the INQUIRY command succeeds, the field sd_inq in the * device structure will be filled in. * */ switch (scsi_slave(devp, canwait)) { default: case SCSIPROBE_NONCCS: /* * If it returns as NON-CCS- we may have a tape drive * that is so busy it cannot return INQUIRY data, hence * we'll hang on to the driver node so that a later * MUNIX-style autoconfiguration will spot it and attempt * to open the drive again... */ rval = 0; /* FALLTHROUGH */ case SCSIPROBE_NOMEM: case SCSIPROBE_NORESP: case SCSIPROBE_FAILURE: if (devp->sd_inq) { IOPBFREE (devp->sd_inq, SUN_INQSIZE); devp->sd_inq = (struct scsi_inquiry *) 0; } return (rval); case SCSIPROBE_EXISTS: if (devp->sd_inq->inq_dtype != DTYPE_SEQUENTIAL) { IOPBFREE (devp->sd_inq, SUN_INQSIZE); devp->sd_inq = (struct scsi_inquiry *) 0; return (rval); } break; } rval = 0; /* Allocate request sense buffer. */ rqpkt = get_pktiopb(ROUTE, (caddr_t *)&devp->sd_sense, CDB_GROUP0, 1, SENSE_LENGTH, B_READ, f); if (!rqpkt) { return (rval); } makecom_g0(rqpkt, devp, FLAG_NOPARITY, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH); rqpkt->pkt_pmon = -1; /* * The actual unit is present. * Now is the time to fill in the rest of our info.. */ un = UPTR = (struct scsi_tape *) kmem_zalloc(sizeof (struct scsi_tape)); if (!un) { printf("%s%d: no memory for tape structure\n", DNAME, DUNIT); free_pktiopb(rqpkt, (caddr_t)devp->sd_sense, SENSE_LENGTH); return (rval); } un->un_rbufp = (struct buf *) kmem_zalloc(sizeof (struct buf)); un->un_sbufp = (struct buf *) kmem_zalloc(sizeof (struct buf)); /* IOPBALLOC guarantees longword aligned allocation from iopbmap */ un->un_mspl = (struct seq_mode *) IOPBALLOC(MSIZE); if (!un->un_rbufp || !un->un_sbufp || !un->un_mspl) { printf("%s%d: no memory for raw/special buffer\n", DNAME, DUNIT); if (un->un_rbufp) (void) kmem_free((caddr_t)un->un_rbufp, sizeof (struct buf)); if (un->un_sbufp) (void) kmem_free((caddr_t)un->un_rbufp, sizeof (struct buf)); if (un->un_mspl) { IOPBFREE(un->un_mspl, MSIZE); } (void) kmem_free((caddr_t) un, sizeof (struct scsi_tape)); free_pktiopb(rqpkt, (caddr_t)devp->sd_sense, SENSE_LENGTH); return (rval); } else { rval = 1; } devp->sd_present = 1; un->un_fileno = -1; rqpkt->pkt_time = st_io_time; rqpkt->pkt_comp = stintr; un->un_rqs = rqpkt; un->un_sd = devp; un->un_dev = DUNIT;#ifdef OPENPROMS devp->sd_dev->devi_driver = &st_ops;#else devp->sd_dev->md_dk = (short) -1;#endif OPENPROMS return (rval);}/* * Attach tape. The controller is there. * Called by scsi_make_device. * Attach device (boot time or load time, if loadable module). * Also called by stopen. */intstattach(devp)struct scsi_device *devp;{ struct st_drivetype *dp; struct scsi_tape *un = UPTR; auto int found = 0; /* * Determine type of tape controller. Type is determined by * checking the resulta of the earlier inquiry command and * comparing vendor ids with strings in a table declared in stdef.h. */ for (dp = st_drivetypes; dp < &st_drivetypes[st_ndrivetypes]; dp++) { if (dp->length == 0) continue; if (bcmp(devp->sd_inq->inq_vid, dp->vid, dp->length) == 0) { found = 1; break; } } if (!found) { found = (sizeof (struct st_drivetype)) + 44; dp = (struct st_drivetype *) kmem_zalloc((unsigned) found); if (!dp) { return; } dp->name = (caddr_t) ((u_long) dp + sizeof (*dp)); /* * Make up a name */ bcopy("Vendor '", dp->name, 8); bcopy((caddr_t) devp->sd_inq->inq_vid, &dp->name[8], 8); bcopy("' Product '", &dp->name[16], 11); bcopy((caddr_t) devp->sd_inq->inq_pid, &dp->name[27], 16); dp->name[43] = '\''; /* * 'clean' vendor and product strings of non-printing chars */ for (found = 0; found < 43; found++) { if (dp->name[found] < 040 || dp->name[found] > 0176) dp->name[found] = '.'; } } /* Store tape drive characteristics. */ un->un_dp = dp; un->un_status = 0; un->un_attached = 1; if ((dp->options & ST_NOPARITY) == 0) un->un_rqs->pkt_flags &= ~FLAG_NOPARITY; printf("%s%d: <%s>\n", DNAME, DUNIT, dp->name);}/* * * * Regular Unix Entry points * * */intstopen(dev, flag)dev_t dev;int flag;{ int s, unit, err; u_int resid; register struct scsi_device *devp; register struct scsi_tape *un; /* * validate that we are addressing a sensible unit */ unit = MTUNIT(dev); if (DEBUGGING_ALL) { printf("st%d:\tstopen dev 0x%x flag 0x%x\n", unit, dev, flag); } if (unit >= ST_MAXUNIT) { return (ENXIO); } if (!(devp = stunits[unit])) { struct scsi_address *sa = stsvdaddr[unit]; if (sa) { if (scsi_add_device(sa, &DRIVER, "st", unit) == 0) { return (ENXIO); } /* stslave will have stuffed stunits */ devp = stunits[unit]; (void) kmem_free((caddr_t) sa, sizeof (*sa)); stsvdaddr[unit] = (struct scsi_address *) 0; } else { return (ENXIO); } } if (!devp->sd_present || (!(un = UPTR))) { /* * If findslave succeeds, we'll get a valid UPTR... */ s = splr(stpri); if (st_findslave(devp, 1) <= 0) { (void) splx(s); return (ENXIO); } (void) splx(s); un = UPTR;#ifdef OPENPROMS printf("%s%d at %s%d target %d lun %d\n", DNAME, DUNIT, CNAME, CUNIT, Tgt(devp), Lun(devp));#else OPENPROMS printf("%s%d at %s%d slave %d\n", DNAME, DUNIT, CNAME, CUNIT, Tgt(devp)<<3|Lun(devp));#endif OPENPROMS } if (!un->un_attached) { s = splr(stpri); stattach(devp); (void) splx(s); if (!un->un_attached) return (ENXIO); } /* * Check for the case of the tape in the middle of closing. * This isn't simply a check of the current state, because * we could be in state of sensing with the previous state * that of closing. */ s = splr(stpri); if (IS_CLOSING(un)) { while (IS_CLOSING(un)) { if (sleep((caddr_t) &lbolt, (PZERO+1)|PCATCH)) { (void) splx(s); return (EINTR); } } } else if (un->un_state != ST_STATE_CLOSED) { (void) splx(s); return (EBUSY); } (void) splx(s); /* * record current dev */ un->un_dev = dev; /* * See whether this is a generic device that we haven't figured * anything out about yet. */ if (un->un_dp->type == ST_TYPE_INVALID) { if (st_determine_generic(devp)) return (EIO); } /* * Clean up after any errors left by 'last' close. * This also handles the case of the initial open. */ un->un_laststate = un->un_state; un->un_state = ST_STATE_OPENING; resid = un->un_err_resid; /* save resid for status reporting */ err = stcmd(devp, SCMD_TEST_UNIT_READY, 0, SYNC_CMD); un->un_err_resid = resid; /* restore resid */ if (err != 0) { if (err == EINTR) { un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; return (err); } /* * Make sure the tape is ready */ un->un_fileno = -1; if (un->un_status != KEY_UNIT_ATTENTION) { un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; return (EIO); } } resid = un->un_err_resid; /* save resid for status reporting */ if (un->un_fileno < 0 && st_loadtape(devp)) { un->un_err_resid = resid; /* restore resid */ un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; return (EIO); } /* * do a mode sense to pick up state of current write-protect, */ (void) stcmd(devp, SCMD_MODE_SENSE, MSIZE, SYNC_CMD);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -