Index: head/sys/scsi/scsi_tape.h =================================================================== --- head/sys/scsi/scsi_tape.h (revision 12702) +++ head/sys/scsi/scsi_tape.h (revision 12703) @@ -1,313 +1,313 @@ /* * SCSI tape interface description */ /* * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * */ /* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * - * $Id: scsi_tape.h,v 1.13 1995/05/30 08:13:43 rgrimes Exp $ + * $Id: scsi_tape.h,v 1.14 1995/11/30 07:43:46 pst Exp $ */ #ifndef SCSI_SCSI_TAPE_H #define SCSI_SCSI_TAPE_H 1 /* * SCSI command formats */ struct scsi_rw_tape { u_char op_code; u_char byte2; #define SRWT_FIXED 0x01 u_char len[3]; u_char control; -} rw_tape; +}; struct scsi_space { u_char op_code; u_char byte2; #define SS_CODE 0x03 u_char number[3]; u_char control; -} space; +}; #define SP_BLKS 0 #define SP_FILEMARKS 1 #define SP_SEQ_FILEMARKS 2 #define SP_EOM 3 struct scsi_write_filemarks { u_char op_code; u_char byte2; u_char number[3]; u_char control; -} write_filemarks; +}; struct scsi_rewind { u_char op_code; u_char byte2; #define SR_IMMED 0x01 u_char unused[3]; u_char control; -} rewind; +}; /* ** Tape erase - AKL: Andreas Klemm */ struct scsi_erase { u_char op_code; u_char byte2; #define SE_LONG 0x01 /* ** Archive Viper 2525 doesn't allow short ** erase, other tapes possibly don't allow ** that, too. */ #define SE_IMMED 0x02 u_char unused[3]; u_char control; -} erase; +}; struct scsi_load { u_char op_code; u_char byte2; #define SL_IMMED 0x01 u_char unused[2]; u_char how; u_char control; -} load; +}; #define LD_UNLOAD 0 #define LD_LOAD 1 #define LD_RETEN 2 struct scsi_blk_limits { u_char op_code; u_char byte2; u_char unused[3]; u_char control; -} blk_limits; +}; /* * Opcodes */ #define REWIND 0x01 #define READ_BLK_LIMITS 0x05 #define READ_COMMAND_TAPE 0x08 #define WRITE_COMMAND_TAPE 0x0a #define WRITE_FILEMARKS 0x10 #define SPACE 0x11 #define ERASE 0x19 #define LOAD_UNLOAD 0x1b struct scsi_blk_limits_data { u_char reserved; u_char max_length_2; /* Most significant */ u_char max_length_1; u_char max_length_0; /* Least significant */ u_char min_length_1; /* Most significant */ u_char min_length_0; /* Least significant */ }; /* defines for the device specific byte in the mode select/sense header */ #define SMH_DSP_SPEED 0x0F #define SMH_DSP_BUFF_MODE 0x70 #define SMH_DSP_BUFF_MODE_OFF 0x00 #define SMH_DSP_BUFF_MODE_ON 0x10 #define SMH_DSP_BUFF_MODE_MLTI 0x20 #define SMH_DSP_WRITE_PROT 0x80 /* A special for the CIPHER ST150S(old drive) */ struct blk_desc_cipher { u_char density; u_char nblocks[3]; u_char reserved; u_char blklen[3]; u_char other; #define ST150_SEC 0x01 /* soft error count */ #define SR150_AUI 0x02 /* autoload inhibit */ }; /* * This structure defines the various mode pages that tapes know about. */ #define PAGE_HEADERLEN 2 struct tape_pages { u_char pg_code; /* page code */ #define ST_PAGE_CONFIGURATION 0x10 #define ST_PAGE_MEDIUM_PART 0x11 #define ST_PAGE_MEDIUM_PART2 0x12 #define ST_PAGE_MEDIUM_PART3 0x13 #define ST_PAGE_MEDIUM_PART4 0x14 #define ST_P_CODE 0x3F /* page code */ #define ST_P_PS 0x80 /* page savable */ u_char pg_length; /* page length */ union { struct { u_char active_format; /* active format for density*/ #define ST_P_CAP 0x40 /* change active Partition */ #define ST_P_CAF 0x20 /* change active format */ #define ST_P_AF 0x1F /* active format */ u_char active_partition; /* */ u_char write_buffer_full_ratio; /* highwater writing*/ u_char read_buffer_empty_ratio; /* lowwater reading*/ u_char write_delay_high; /* # 100mSecs before flush*/ u_char write_delay_low; /* of buffer to the media */ u_char flags1; /* various single bit flags */ #define ST_P_DBR 0x80 /* supports data-buffer recovery */ #define ST_P_BIS 0x40 /* supports Block_ID */ #define ST_P_RSmk 0x20 /* Reports setmarks during reads and spaces */ #define ST_P_AVC 0x10 /* Supports Automatic Velocity Control */ #define ST_P_SOCF 0x0C /* Stop On Consecutive Filemarks, */ #define ST_P_RBO 0x02 /* Recoverd Buffered Data order, 1 = LIFO */ #define ST_P_REW 0x01 /* Report Early Warning (see SEW) */ u_char gap_size; /*I/B gap, 1=min 0=default */ u_char flags2; /* various single bit flags */ #define ST_P_EOD 0xE0 /* What is and EOD....*/ #define ST_P_EOD_DEF 0x00 /* Drive's default */ #define ST_P_EOD_FMT 0x20 /* define by format */ #define ST_P_EOD_SOCF 0x40 /* define by SOCF (above) */ #define ST_P_EEG 0x10 /* use EOD above */ #define ST_P_SEW 0x04 /* Synchronise at Early warning.. flush buffers*/ u_char early_warn_high;/* buf size at early warning */ u_char early_warn_med; /* after early warning, only */ u_char early_warn_low; /* buufer this much data */ u_char data_compress_alg; /* 0 = off, 1 = default */ u_char reserved; /* The standard says so */ } configuration; struct { #define ST_MAXPARTS 16 /*for now*/ u_char max_add_parts; /* that drive allows */ u_char parts_defined; /* max min(ST_MAXPARTS,max_add_parts) */ u_char flags; #define ST_P_FDP 0x80 #define ST_P_SDP 0x40 #define ST_P_IDP 0x20 #define ST_P_PSUM 0x18 /* units of part defs.. */ #define ST_P_PSUM_BYTES 0x0 /* units of part defs.. */ #define ST_P_PSUM_KBYTES 0x08 /* units of part defs.. */ #define ST_P_PSUM_MBYTES 0x10 /* units of part defs.. */ u_char medium_format_recog; #define ST_P_REC_NONE 0x00 #define ST_P_REC_FMT 0x01 /* can recognise format of new media */ #define ST_P_REC_PART 0x02 /* can recognise partitions of new media */ #define ST_P_REC_FMT_PART 0x03 /* can recognise format and parts */ u_char reserved1; u_char reserved2; struct { u_char high; u_char low; }part[ST_MAXPARTS]; } medium_partition; struct { struct { u_char high; u_char low; }part[ST_MAXPARTS]; } medium_partition_extra; }pages; }; #ifndef NEW_SCSICONF /********************************************************************** from the scsi2 spec Value Tracks Density(bpi) Code Type Reference Note 0x1 9 800 NRZI R X3.22-1983 2 0x2 9 1600 PE R X3.39-1986 2 0x3 9 6250 GCR R X3.54-1986 2 0x5 4/9 8000 GCR C X3.136-1986 1 0x6 9 3200 PE R X3.157-1987 2 0x7 4 6400 IMFM C X3.116-1986 1 0x8 4 8000 GCR CS X3.158-1986 1 0x9 18 37871 GCR C X3B5/87-099 2 0xA 22 6667 MFM C X3B5/86-199 1 0xB 4 1600 PE C X3.56-1986 1 0xC 24 12690 GCR C HI-TC1 1,5 0xD 24 25380 GCR C HI-TC2 1,5 0xF 15 10000 GCR C QIC-120 1,5 0x10 18 10000 GCR C QIC-150 1,5 0x11 26 16000 GCR C QIC-320(525?) 1,5 0x12 30 51667 RLL C QIC-1350 1,5 0x13 1 61000 DDS CS X3B5/88-185A 4 0x14 1 43245 RLL CS X3.202-1991 4 0x15 1 45434 RLL CS ECMA TC17 4 0x16 48 10000 MFM C X3.193-1990 1 0x17 48 42500 MFM C X3B5/91-174 1 where Code means: NRZI Non Return to Zero, change on ones GCR Group Code Recording PE Phase Encoded IMFM Inverted Modified Frequency Modulation MFM Modified Frequency Modulation DDS Dat Data Storage RLL Run Length Encoding where Type means: R Real-to-Real C Cartridge CS cassette where Notes means: 1 Serial Recorded 2 Parallel Recorded 3 Old format know as QIC-11 4 Helical Scan 5 Not ANSI standard, rather industry standard. ********************************************************************/ #define HALFINCH_800 0x01 #define HALFINCH_1600 0x02 #define HALFINCH_6250 0x03 #define QIC_11 0x04 /* from Archive 150S Theory of Op. XXX */ #define QIC_24 0x05 /* may be bad, works for CIPHER ST150S XXX */ #define QIC_120 0x0f #define QIC_150 0x10 #define QIC_320 0x11 #define QIC_525 0x11 #define QIC_1320 0x12 #define DDS 0x13 #define DAT_1 0x13 #define QIC_3080 0x29 #endif /* NEW_SCSICONF */ #endif /*SCSI_SCSI_TAPE_H*/ Index: head/sys/scsi/sd.c =================================================================== --- head/sys/scsi/sd.c (revision 12702) +++ head/sys/scsi/sd.c (revision 12703) @@ -1,1011 +1,1010 @@ /* * Written by Julian Elischer (julian@dialix.oz.au) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992 * - * $Id: sd.c,v 1.76 1995/12/08 11:18:53 julian Exp $ + * $Id: sd.c,v 1.77 1995/12/08 23:22:25 phk Exp $ */ #define SPLSD splbio #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include /* XXX *//* for aborting dump */ -u_int32 sdstrats, sdqueues; +static u_int32 sdstrats, sdqueues; #define SECSIZE 512 #define SDOUTSTANDING 4 #define SD_RETRIES 4 #define MAXTRANSFER 8 /* 1 page at a time */ #define PARTITION(dev) dkpart(dev) #define SDUNIT(dev) dkunit(dev) /* XXX introduce a dkmodunit() macro for this. */ #define SDSETUNIT(DEV, U) \ makedev(major(DEV), dkmakeminor((U), dkslice(DEV), dkpart(DEV))) static errval sd_get_parms __P((int unit, int flags)); static void sdstrategy1 __P((struct buf *)); static int sd_sense_handler __P((struct scsi_xfer *)); static void sdstart __P((u_int32, u_int32)); struct scsi_data { u_int32 flags; #define SDINIT 0x04 /* device has been init'd */ struct disk_parms { u_char heads; /* Number of heads */ u_int16 cyls; /* Number of cylinders */ u_char sectors; /*dubious *//* Number of sectors/track */ u_int16 secsiz; /* Number of bytes/sector */ u_int32 disksize; /* total number sectors */ } params; struct diskslices *dk_slices; /* virtual drives */ struct buf_queue_head buf_queue; int dkunit; /* disk stats unit number */ #ifdef DEVFS void *c_devfs_token; void *b_devfs_token; #endif }; static int sdunit(dev_t dev) { return SDUNIT(dev); } static dev_t sdsetunit(dev_t dev, int unit) { return SDSETUNIT(dev, unit); } static errval sd_open __P((dev_t dev, int mode, int fmt, struct proc *p, struct scsi_link *sc_link)); static errval sd_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p, struct scsi_link *sc_link); static errval sd_close __P((dev_t dev, int fflag, int fmt, struct proc *p, struct scsi_link *sc_link)); static void sd_strategy(struct buf *bp, struct scsi_link *sc_link); static d_open_t sdopen; static d_close_t sdclose; static d_ioctl_t sdioctl; static d_dump_t sddump; static d_psize_t sdsize; static d_strategy_t sdstrategy; #define CDEV_MAJOR 13 #define BDEV_MAJOR 4 extern struct cdevsw sd_cdevsw; /* hold off the complaints for a second */ static struct bdevsw sd_bdevsw = { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, 0, "sd", &sd_cdevsw, -1 }; static struct cdevsw sd_cdevsw = { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, nostop, nullreset, nodevtotty, seltrue, nommap, sdstrategy, "sd", &sd_bdevsw, -1 }; SCSI_DEVICE_ENTRIES(sd) static struct scsi_device sd_switch = { sd_sense_handler, sdstart, /* have a queue, served by this */ NULL, /* have no async handler */ NULL, /* Use default 'done' routine */ "sd", 0, {0, 0}, 0, /* Link flags */ sdattach, "Direct-Access", sdopen, sizeof(struct scsi_data), T_DIRECT, sdunit, sdsetunit, sd_open, sd_ioctl, sd_close, sd_strategy, }; static struct scsi_xfer sx; static int sd_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return scsi_externalize(SCSI_LINK(&sd_switch, kdc->kdc_unit), req); } static struct kern_devconf kdc_sd_template = { 0, 0, 0, /* filled in by dev_attach */ "sd", 0, MDDC_SCSI, sd_externalize, 0, scsi_goaway, SCSI_EXTERNALLEN, &kdc_scbus0, /* XXX parent */ 0, /* parentdata */ DC_UNKNOWN, /* not supported */ }; static inline void sd_registerdev(int unit) { struct kern_devconf *kdc; MALLOC(kdc, struct kern_devconf *, sizeof *kdc, M_TEMP, M_NOWAIT); if(!kdc) return; *kdc = kdc_sd_template; kdc->kdc_unit = unit; kdc->kdc_description = sd_switch.desc; dev_attach(kdc); if(dk_ndrive < DK_NDRIVE) { sprintf(dk_names[dk_ndrive], "sd%d", unit); dk_wpms[dk_ndrive] = (8*1024*1024/2); SCSI_DATA(&sd_switch, unit)->dkunit = dk_ndrive++; } else { SCSI_DATA(&sd_switch, unit)->dkunit = -1; } } /* * The routine called by the low level scsi routine when it discovers * a device suitable for this driver. */ errval sdattach(struct scsi_link *sc_link) { u_int32 unit; struct disk_parms *dp; - char name[32]; struct scsi_data *sd = sc_link->sd; unit = sc_link->dev_unit; dp = &(sd->params); if (sc_link->opennings > SDOUTSTANDING) sc_link->opennings = SDOUTSTANDING; TAILQ_INIT(&sd->buf_queue); /* * Use the subdriver to request information regarding * the drive. We cannot use interrupts yet, so the * request must specify this. */ sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); /* * if we don't have actual parameters, assume 512 bytes/sec * (could happen on removable media - MOD) * -- this avoids the division below from falling over */ if(dp->secsiz == 0) dp->secsiz = SECSIZE; printf("%ldMB (%ld %d byte sectors)", dp->disksize / ((1024L * 1024L) / dp->secsiz), dp->disksize, dp->secsiz); #ifndef SCSI_REPORT_GEOMETRY if ( (sc_link->flags & SDEV_BOOTVERBOSE) ) #endif { sc_print_addr(sc_link); printf("with %d cyls, %d heads, and an average %d sectors/track", dp->cyls, dp->heads, dp->sectors); } sd->flags |= SDINIT; sd_registerdev(unit); #ifdef DEVFS /* Fix minor numbers */ sprintf(name,"rsd%d",unit); sd->c_devfs_token = devfs_add_devsw( "/", name, &sd_cdevsw, 0, DV_CHR, 0, 0, 0600); sprintf(name,"sd%d",unit); sd->b_devfs_token = devfs_add_devsw( "/", name, &sd_bdevsw, 0, DV_BLK, 0, 0, 0600); #endif return 0; } /* * open the device. Make sure the partition info is a up-to-date as can be. */ errval sd_open(dev, mode, fmt, p, sc_link) dev_t dev; int mode; int fmt; struct proc *p; struct scsi_link *sc_link; { errval errcode = 0; u_int32 unit; struct disklabel label; struct scsi_data *sd; unit = SDUNIT(dev); sd = sc_link->sd; /* * Make sure the disk has been initialised * At some point in the future, get the scsi driver * to look for a new device if we are not initted */ if ((!sd) || (!(sd->flags & SDINIT))) { return (ENXIO); } SC_DEBUG(sc_link, SDEV_DB1, ("sd_open: dev=0x%lx (unit %ld, partition %d)\n", dev, unit, PARTITION(dev))); /* * "unit attention" errors should occur here if the * drive has been restarted or the pack changed. * just ingnore the result, it's a decoy instruction * The error handlers will act on the error though * and invalidate any media information we had. */ scsi_test_unit_ready(sc_link, 0); /* * If it's been invalidated, then forget the label */ sc_link->flags |= SDEV_OPEN; /* unit attn becomes an err now */ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { /* * If somebody still has it open, then forbid re-entry. */ if (dsisopen(sd->dk_slices)) { errcode = ENXIO; goto bad; } if (sd->dk_slices == NULL) Debugger("sdopen: no slices"); else dsgone(&sd->dk_slices); } /* * In case it is a funny one, tell it to start * not needed for most hard drives (ignore failure) */ scsi_start_unit(sc_link, SCSI_ERR_OK | SCSI_SILENT); /* * Check that it is still responding and ok. */ if (scsi_test_unit_ready(sc_link, 0)) { SC_DEBUG(sc_link, SDEV_DB3, ("device not reponding\n")); errcode = ENXIO; goto bad; } SC_DEBUG(sc_link, SDEV_DB3, ("device ok\n")); /* * Load the physical device parameters */ sd_get_parms(unit, 0); /* sets SDEV_MEDIA_LOADED */ if (sd->params.secsiz != SECSIZE) { /* XXX One day... */ printf("sd%ld: Can't deal with %d bytes logical blocks\n", unit, sd->params.secsiz); Debugger("sd"); errcode = ENXIO; goto bad; } SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded ")); /* Lock the pack in. */ scsi_prevent(sc_link, PR_PREVENT, SCSI_ERR_OK | SCSI_SILENT); /* Build label for whole disk. */ bzero(&label, sizeof label); label.d_secsize = sd->params.secsiz; label.d_nsectors = sd->params.sectors; label.d_ntracks = sd->params.heads; label.d_ncylinders = sd->params.cyls; label.d_secpercyl = sd->params.heads * sd->params.sectors; if (label.d_secpercyl == 0) label.d_secpercyl = 100; /* XXX as long as it's not 0 - readdisklabel divides by it (?) */ label.d_secperunit = sd->params.disksize; /* Initialize slice tables. */ errcode = dsopen("sd", dev, fmt, &sd->dk_slices, &label, sdstrategy1, (ds_setgeom_t *)NULL); if (errcode != 0) goto bad; SC_DEBUG(sc_link, SDEV_DB3, ("Slice tables initialized ")); SC_DEBUG(sc_link, SDEV_DB3, ("open %ld %ld\n", sdstrats, sdqueues)); return 0; bad: if (!dsisopen(sd->dk_slices)) { scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT); sc_link->flags &= ~SDEV_OPEN; } return errcode; } /* * close the device.. only called if we are the LAST occurence of an open * device. Convenient now but usually a pain. */ errval sd_close(dev, fflag, fmt, p, sc_link) dev_t dev; int fflag; int fmt; struct proc *p; struct scsi_link *sc_link; { struct scsi_data *sd; sd = sc_link->sd; dsclose(dev, fmt, sd->dk_slices); if (!dsisopen(sd->dk_slices)) { scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK); sc_link->flags &= ~SDEV_OPEN; } return (0); } /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include * only one physical transfer. */ void sd_strategy(struct buf *bp, struct scsi_link *sc_link) { u_int32 opri; struct scsi_data *sd; u_int32 unit; sdstrats++; unit = SDUNIT((bp->b_dev)); sd = sc_link->sd; /* * If the device has been made invalid, error out */ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { bp->b_error = EIO; goto bad; } /* * check it's not too big a transfer for our adapter */ scsi_minphys(bp,&sd_switch); /* * Odd number of bytes */ if (bp->b_bcount % DEV_BSIZE != 0) { bp->b_error = EINVAL; goto bad; } /* * Do bounds checking, adjust transfer, set b_cylin and b_pbklno. */ if (dscheck(bp, sd->dk_slices) <= 0) goto done; /* XXX check b_resid */ opri = SPLSD(); /* * Use a bounce buffer if necessary */ #ifdef BOUNCE_BUFFERS if (sc_link->flags & SDEV_BOUNCE) vm_bounce_alloc(bp); #endif /* * Place it in the queue of disk activities for this disk */ TAILQ_INSERT_TAIL(&sd->buf_queue, bp, b_act); /* * Tell the device to get going on the transfer if it's * not doing anything, otherwise just wait for completion */ sdstart(unit, 0); splx(opri); return /*0*/; bad: bp->b_flags |= B_ERROR; done: /* * Correctly set the buf to indicate a completed xfer */ bp->b_resid = bp->b_bcount; biodone(bp); return /*0*/; } static void sdstrategy1(struct buf *bp) { /* * XXX - do something to make sdstrategy() but not this block while * we're doing dsinit() and dsioctl(). */ sdstrategy(bp); } /* * sdstart looks to see if there is a buf waiting for the device * and that the device is not already busy. If both are true, * It dequeues the buf and creates a scsi command to perform the * transfer in the buf. The transfer request will call scsi_done * on completion, which will in turn call this routine again * so that the next queued transfer is performed. * The bufs are queued by the strategy routine (sdstrategy) * * This routine is also called after other non-queued requests * have been made of the scsi driver, to ensure that the queue * continues to be drained. * * must be called at the correct (highish) spl level * sdstart() is called at SPLSD from sdstrategy and scsi_done */ void sdstart(u_int32 unit, u_int32 flags) { register struct scsi_link *sc_link = SCSI_LINK(&sd_switch, unit); register struct scsi_data *sd = sc_link->sd; struct buf *bp = NULL; struct scsi_rw_big cmd; u_int32 blkno, nblk; SC_DEBUG(sc_link, SDEV_DB2, ("sdstart ")); /* * Check if the device has room for another command */ while (sc_link->opennings) { /* * there is excess capacity, but a special waits * It'll need the adapter as soon as we clear out of the * way and let it run (user level wait). */ if (sc_link->flags & SDEV_WAITING) { return; } /* * See if there is a buf with work for us to do.. */ bp = sd->buf_queue.tqh_first; if (bp == NULL) { /* yes, an assign */ return; } TAILQ_REMOVE(&sd->buf_queue, bp, b_act); /* * If the device has become invalid, abort all the * reads and writes until all files have been closed and * re-openned */ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { goto bad; } /* * We have a buf, now we know we are going to go through * With this thing.. */ blkno = bp->b_pblkno; if (bp->b_bcount & (SECSIZE - 1)) { goto bad; } nblk = bp->b_bcount >> 9; /* * Fill out the scsi command */ cmd.op_code = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG; cmd.addr_3 = (blkno & 0xff000000UL) >> 24; cmd.addr_2 = (blkno & 0xff0000) >> 16; cmd.addr_1 = (blkno & 0xff00) >> 8; cmd.addr_0 = blkno & 0xff; cmd.length2 = (nblk & 0xff00) >> 8; cmd.length1 = (nblk & 0xff); cmd.byte2 = cmd.reserved = cmd.control = 0; /* * Call the routine that chats with the adapter. * Note: we cannot sleep as we may be an interrupt */ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd, sizeof(cmd), (u_char *) bp->b_un.b_addr, bp->b_bcount, SD_RETRIES, 10000, bp, flags | ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT)) == SUCCESSFULLY_QUEUED) { sdqueues++; if(sd->dkunit >= 0) { dk_xfer[sd->dkunit]++; dk_seek[sd->dkunit]++; /* don't know */ dk_wds[sd->dkunit] += bp->b_bcount >> 6; } } else { bad: printf("sd%ld: oops not queued\n", unit); bp->b_error = EIO; bp->b_flags |= B_ERROR; biodone(bp); } } } /* * Perform special action on behalf of the user * Knows about the internals of this device */ errval sd_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p, struct scsi_link *sc_link) { /* struct sd_cmd_buf *args; */ errval error; struct scsi_data *sd; /* * Find the device that the user is talking about */ sd = sc_link->sd; SC_DEBUG(sc_link, SDEV_DB1, ("sdioctl (0x%x)", cmd)); #if 0 /* Wait until we have exclusive access to the device. */ /* XXX this is how wd does it. How did we work without this? */ wdsleep(du->dk_ctrlr, "wdioct"); #endif /* * If the device is not valid.. abandon ship */ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) return (EIO); if (cmd == DIOCSBAD) return (EINVAL); /* XXX */ error = dsioctl("sd", dev, cmd, addr, flag, &sd->dk_slices, sdstrategy1, (ds_setgeom_t *)NULL); if (error != -1) return (error); if (PARTITION(dev) != RAW_PART) return (ENOTTY); return (scsi_do_ioctl(dev, cmd, addr, flag, p, sc_link)); } /* * Find out from the device what it's capacity is */ static u_int32 sd_size(unit, flags) int unit, flags; { struct scsi_read_cap_data rdcap; struct scsi_read_capacity scsi_cmd; u_int32 size; struct scsi_link *sc_link = SCSI_LINK(&sd_switch, unit); /* * make up a scsi command and ask the scsi driver to do * it for you. */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = READ_CAPACITY; /* * If the command works, interpret the result as a 4 byte * number of blocks */ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) & rdcap, sizeof(rdcap), SD_RETRIES, 2000, NULL, flags | SCSI_DATA_IN) != 0) { printf("sd%d: could not get size\n", unit); return (0); } else { size = rdcap.addr_0 + 1; size += rdcap.addr_1 << 8; size += rdcap.addr_2 << 16; size += rdcap.addr_3 << 24; } return (size); } /* * Tell the device to map out a defective block */ static errval sd_reassign_blocks(unit, block) int unit, block; { struct scsi_reassign_blocks scsi_cmd; struct scsi_reassign_blocks_data rbdata; struct scsi_link *sc_link = SCSI_LINK(&sd_switch, unit); bzero(&scsi_cmd, sizeof(scsi_cmd)); bzero(&rbdata, sizeof(rbdata)); scsi_cmd.op_code = REASSIGN_BLOCKS; rbdata.length_msb = 0; rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]); rbdata.defect_descriptor[0].dlbaddr_3 = ((block >> 24) & 0xff); rbdata.defect_descriptor[0].dlbaddr_2 = ((block >> 16) & 0xff); rbdata.defect_descriptor[0].dlbaddr_1 = ((block >> 8) & 0xff); rbdata.defect_descriptor[0].dlbaddr_0 = ((block) & 0xff); return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) & rbdata, sizeof(rbdata), SD_RETRIES, 5000, NULL, SCSI_DATA_OUT)); } #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) /* * Get the scsi driver to send a full inquiry to the * device and use the results to fill out the disk * parameter structure. */ static errval sd_get_parms(unit, flags) int unit, flags; { struct scsi_link *sc_link = SCSI_LINK(&sd_switch, unit); struct scsi_data *sd = sc_link->sd; struct disk_parms *disk_parms = &sd->params; struct scsi_mode_sense scsi_cmd; struct scsi_mode_sense_data { struct scsi_mode_header header; struct blk_desc blk_desc; union disk_pages pages; } scsi_sense; u_int32 sectors; /* * First check if we have it all loaded */ if (sc_link->flags & SDEV_MEDIA_LOADED) return 0; /* * do a "mode sense page 4" */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; scsi_cmd.page = 4; scsi_cmd.length = 0x20; /* * If the command worked, use the results to fill out * the parameter structure */ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) & scsi_sense, sizeof(scsi_sense), SD_RETRIES, 4000, NULL, flags | SCSI_DATA_IN) != 0) { printf("sd%d could not mode sense (4).", unit); printf(" Using ficticious geometry\n"); /* * use adaptec standard ficticious geometry * this depends on which controller (e.g. 1542C is * different. but we have to put SOMETHING here..) */ sectors = sd_size(unit, flags); disk_parms->heads = 64; disk_parms->sectors = 32; disk_parms->cyls = sectors / (64 * 32); disk_parms->secsiz = SECSIZE; disk_parms->disksize = sectors; } else { SC_DEBUG(sc_link, SDEV_DB3, ("%ld cyls, %d heads, %d precomp, %d red_write, %d land_zone\n", scsi_3btou(&scsi_sense.pages.rigid_geometry.ncyl_2), scsi_sense.pages.rigid_geometry.nheads, b2tol(scsi_sense.pages.rigid_geometry.st_cyl_wp), b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc), b2tol(scsi_sense.pages.rigid_geometry.land_zone))); /* * KLUDGE!!(for zone recorded disks) * give a number of sectors so that sec * trks * cyls * is <= disk_size * can lead to wasted space! THINK ABOUT THIS ! */ disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads; disk_parms->cyls = scsi_3btou(&scsi_sense.pages.rigid_geometry.ncyl_2); disk_parms->secsiz = scsi_3btou(scsi_sense.blk_desc.blklen); sectors = sd_size(unit, flags); disk_parms->disksize = sectors; /* Check if none of these values are zero */ if(disk_parms->heads && disk_parms->cyls) { sectors /= (disk_parms->heads * disk_parms->cyls); } else { /* set it to something reasonable */ disk_parms->heads = 64; disk_parms->cyls = sectors / (64 * 32); sectors = 32; } /* keep secsiz sane too - we may divide by it later */ if(disk_parms->secsiz == 0) disk_parms->secsiz = SECSIZE; disk_parms->sectors = sectors; /* dubious on SCSI *//*XXX */ } sc_link->flags |= SDEV_MEDIA_LOADED; return 0; } int sdsize(dev_t dev) { struct scsi_data *sd; sd = SCSI_DATA(&sd_switch, (u_int32) SDUNIT(dev)); if (sd == NULL) return (-1); return (dssize(dev, &sd->dk_slices, sdopen, sdclose)); } /* * sense handler: Called to determine what to do when the * device returns a CHECK CONDITION. * * This will issue a retry when the device returns a * non-media hardware failure. The CDC-WREN IV does this * when you access it during thermal calibrarion, so the drive * is pretty useless without this. * * In general, you probably almost always would like to issue a retry * for your disk I/O. It can't hurt too much (the caller only retries * so many times) and it may save your butt. */ int sd_sense_handler(struct scsi_xfer *xs) { struct scsi_sense_data *sense; struct scsi_inquiry_data *inqbuf; sense = &(xs->sense); /* I don't know what the heck to do with a deferred error, * so I'll just kick it back to the caller. */ if ((sense->error_code & SSD_ERRCODE) == 0x71) return SCSIRET_CONTINUE; if (((sense->error_code & SSD_ERRCODE) == 0x70) && ((sense->ext.extended.flags & SSD_KEY) == 0x05)) /* No point in retrying Illegal Requests */ return SCSIRET_CONTINUE; inqbuf = &(xs->sc_link->inqbuf); /* It is dangerous to retry on removable drives without * looking carefully at the additional sense code * and sense code qualifier and ensuring the disk hasn't changed: */ if (inqbuf->dev_qual2 & SID_REMOVABLE) return SCSIRET_CONTINUE; /* Retry all disk errors. */ scsi_sense_print(xs); if (xs->retries) printf(", retries:%d\n", xs->retries); else printf(", FAILURE\n"); return SCSIRET_DO_RETRY; } /* * dump all of physical memory into the partition specified, starting * at offset 'dumplo' into the partition. */ errval sddump(dev_t dev) { /* dump core after a system crash */ struct disklabel *lp; register struct scsi_data *sd; /* disk unit to do the IO */ struct scsi_link *sc_link; int32 num; /* number of sectors to write */ u_int32 unit, part; int32 blkoff, blknum, blkcnt = MAXTRANSFER; int32 nblocks; char *addr; struct scsi_rw_big cmd; static int sddoingadump = 0; struct scsi_xfer *xs = &sx; errval retval; addr = (char *) 0; /* starting address */ /* toss any characters present prior to dump */ while (cncheckc()) ; /* size of memory to dump */ num = Maxmem; unit = SDUNIT(dev); /* eventually support floppies? */ part = PARTITION(dev); /* file system */ sc_link = SCSI_LINK(&sd_switch, unit); if (!sc_link) return ENXIO; sd = sc_link->sd; /* was it ever initialized etc. ? */ if (!(sd->flags & SDINIT)) return (ENXIO); if ((sc_link->flags & SDEV_MEDIA_LOADED) != SDEV_MEDIA_LOADED) return (ENXIO); if (sd->dk_slices == NULL) Debugger("sddump: no slices"); if ((lp = dsgetlabel(dev, sd->dk_slices)) == NULL) return (ENXIO); /* Convert to disk sectors */ num = (u_int32) num * NBPG / sd->params.secsiz; /* XXX it must be 512 */ /* check if controller active */ if (sddoingadump) return (EFAULT); nblocks = lp->d_partitions[part].p_size; blkoff = lp->d_partitions[part].p_offset; /* XXX */ blkoff += sd->dk_slices->dss_slices[dkslice(dev)].ds_offset; /* check transfer bounds against partition size */ if ((dumplo < 0) || ((dumplo + num) > nblocks)) return (EINVAL); sddoingadump = 1; blknum = dumplo + blkoff; while (num > 0) { *(int *)CMAP1 = /* XXX use pmap_enter() */ PG_V | PG_KW | trunc_page(addr); pmap_update(); /* * Fill out the scsi command */ bzero(&cmd, sizeof(cmd)); cmd.op_code = WRITE_BIG; cmd.addr_3 = (blknum & 0xff000000) >> 24; cmd.addr_2 = (blknum & 0xff0000) >> 16; cmd.addr_1 = (blknum & 0xff00) >> 8; cmd.addr_0 = blknum & 0xff; cmd.length2 = (blkcnt & 0xff00) >> 8; cmd.length1 = (blkcnt & 0xff); /* * Fill out the scsi_xfer structure * Note: we cannot sleep as we may be an interrupt * don't use scsi_scsi_cmd() as it may want * to wait for an xs. */ bzero(xs, sizeof(sx)); xs->flags |= SCSI_NOMASK | SCSI_NOSLEEP | INUSE | SCSI_DATA_OUT; xs->sc_link = sc_link; xs->retries = SD_RETRIES; xs->timeout = 10000; /* 10000 millisecs for a disk ! */ xs->cmd = (struct scsi_generic *) &cmd; xs->cmdlen = sizeof(cmd); xs->resid = 0; xs->error = XS_NOERROR; xs->bp = 0; xs->data = (u_char *) CADDR1; /* XXX use pmap_enter() */ xs->datalen = blkcnt * SECSIZE; /* * Pass all this info to the scsi driver. */ retval = (*(sc_link->adapter->scsi_cmd)) (xs); switch (retval) { case SUCCESSFULLY_QUEUED: case HAD_ERROR: return (ENXIO); /* we said not to sleep! */ case COMPLETE: break; default: return (ENXIO); /* we said not to sleep! */ } if ((unsigned) addr % (1024 * 1024) == 0) printf("%ld ", num / 2048); /* update block count */ num -= blkcnt; blknum += blkcnt; (int) addr += SECSIZE * blkcnt; /* operator aborting dump? */ if (cncheckc()) return (EINTR); } return (0); } static sd_devsw_installed = 0; static void sd_drvinit(void *unused) { dev_t dev; if( ! sd_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&sd_cdevsw, NULL); dev = makedev(BDEV_MAJOR, 0); bdevsw_add(&dev,&sd_bdevsw, NULL); sd_devsw_installed = 1; } } SYSINIT(sddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,sd_drvinit,NULL) Index: head/sys/scsi/st.c =================================================================== --- head/sys/scsi/st.c (revision 12702) +++ head/sys/scsi/st.c (revision 12703) @@ -1,2212 +1,2211 @@ /* * Written by Julian Elischer (julian@tfs.com)(now julian@DIALix.oz.au) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * $Id: st.c,v 1.49 1995/12/08 11:18:59 julian Exp $ + * $Id: st.c,v 1.50 1995/12/08 23:22:28 phk Exp $ */ /* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 */ /* * To do: * work out some better way of guessing what a good timeout is going * to be depending on whether we expect to retension or not. * */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include /* Defines for device specific stuff */ #define PAGE_0_SENSE_DATA_SIZE 12 #define DEF_FIXED_BSIZE 512 #define ST_RETRIES 4 /* only on non IO commands */ #define STUNIT(DEV) ((minor(DEV)&0xF0) >> 4) /* 4 bit unit. */ #define STSETUNIT(DEV, U) makedev(major(DEV), ((U) << 4)) #define MODE(z) ( (minor(z) & 0x03) ) #define DSTY(z) ( ((minor(z) >> 2) & 0x03) ) #define CTLMODE 3 #define IS_CTLMODE(DEV) (MODE(DEV) == CTLMODE) #define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified * in SCSI II spec. */ #ifndef NEW_SCSICONF /* * Define various devices that we know mis-behave in some way, * and note how they are bad, so we can correct for them */ struct modes { u_int32 blksiz; u_int32 quirks; /* same definitions as in rogues */ char density; char spare[3]; }; struct rogues { char *name; char *manu; char *model; char *version; u_int32 quirks; /* valid for all modes */ struct modes modes[4]; }; /* define behaviour codes (quirks) */ #define ST_Q_NEEDS_PAGE_0 0x00001 #define ST_Q_FORCE_FIXED_MODE 0x00002 #define ST_Q_FORCE_VAR_MODE 0x00004 #define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */ #define ST_Q_IGNORE_LOADS 0x00010 #define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */ static struct rogues gallery[] = /* ends with an all-null entry */ { {"Such an old device ", "pre-scsi", " unknown model ", "????", 0, { {512, ST_Q_FORCE_FIXED_MODE, 0}, /* minor 0,1,2,3 */ {512, ST_Q_FORCE_FIXED_MODE, QIC_24}, /* minor 4,5,6,7 */ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_1600}, /* minor 8,9,10,11 */ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_6250} /* minor 12,13,14,15 */ } }, {"Tandberg tdc3600", "TANDBERG", " TDC 3600", "????", ST_Q_NEEDS_PAGE_0, { {0, 0, 0}, /* minor 0,1,2,3 */ {0, ST_Q_FORCE_VAR_MODE, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ } }, {"Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462", "-005", 0, { {0, ST_Q_SNS_HLP, 0}, /* minor 0,1,2,3 */ {0, ST_Q_SNS_HLP, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ } }, {"Archive Viper 150", "ARCHIVE ", "VIPER 150", "????", ST_Q_NEEDS_PAGE_0, { {0, 0, 0}, /* minor 0,1,2,3 */ {0, 0, QIC_150}, /* minor 4,5,6,7 */ {0, 0, QIC_120}, /* minor 8,9,10,11 */ {0, 0, QIC_24} /* minor 12,13,14,15 */ } }, {"Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????", 0, { {0, 0, 0}, /* minor 0,1,2,3 */ {0, ST_Q_BLKSIZ, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ } }, {"WangDAT model 1300", "WangDAT ", "Model 1300", "????", 0, { {0, 0, 0}, /* minor 0,1,2,3 */ {512, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 4,5,6,7 */ {1024, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 8,9,10,11 */ {0, ST_Q_FORCE_VAR_MODE, 0x13} /* minor 12,13,14,15 */ } }, {(char *) 0} }; #endif /* NEW_SCSICONF */ static errval st_space __P((u_int32 unit, int32 number, u_int32 what, u_int32 flags)); static errval st_rewind __P((u_int32 unit, boolean immed, u_int32 flags)); static errval st_erase __P((u_int32 unit, boolean immed, u_int32 flags)); static errval st_mode_sense __P((u_int32 unit, u_int32 flags, \ struct tape_pages *page, u_int32 pagelen, u_int32 pagecode)); static errval st_decide_mode __P((u_int32 unit, boolean first_read)); static errval st_rd_blk_lim __P((u_int32 unit, u_int32 flags)); static errval st_touch_tape __P((u_int32 unit)); static errval st_write_filemarks __P((u_int32 unit, int32 number, u_int32 flags)); static errval st_load __P((u_int32 unit, u_int32 type, u_int32 flags)); static errval st_mode_select __P((u_int32 unit, u_int32 flags, \ struct tape_pages *page, u_int32 pagelen)); static errval st_comp __P((u_int32 unit, u_int32 mode)); static int32 st_chkeod(); static void ststart(u_int32 unit, u_int32 flags); static void st_unmount(); static errval st_mount_tape(); static void st_loadquirks(); #ifndef NEW_SCSICONF static void st_identify_drive(); #endif static errval st_interpret_sense(); #define ESUCCESS 0 #define NOEJECT 0 #define EJECT 1 struct scsi_data { /*--------------------present operating parameters, flags etc.----------------*/ u_int32 flags; /* see below */ u_int32 blksiz; /* blksiz we are using */ u_int32 density; /* present density */ u_int32 comp; /* present compression mode */ u_int32 quirks; /* quirks for the open mode */ u_int32 last_dsty; /* last density openned */ /*--------------------parameters reported by the device ----------------------*/ u_int32 blkmin; /* min blk size */ u_int32 blkmax; /* max blk size */ #ifndef NEW_SCSICONF struct rogues *rogues; /* if we have a rogue entry */ #endif /*--------------------parameters reported by the device for this media--------*/ u_int32 numblks; /* nominal blocks capacity */ u_int32 media_blksiz; /* 0 if not ST_FIXEDBLOCKS */ u_int32 media_density; /* this is what it said when asked */ /*--------------------quirks for the whole drive------------------------------*/ u_int32 drive_quirks; /* quirks of this drive */ /*--------------------How we should set up when openning each minor device----*/ #ifdef NEW_SCSICONF st_modes modes; /* plus more for each mode */ #else struct modes modes[4]; /* plus more for each mode */ #endif u_int8 modeflags[4]; /* flags for the modes */ #define DENSITY_SET_BY_USER 0x01 #define DENSITY_SET_BY_QUIRK 0x02 #define BLKSIZE_SET_BY_USER 0x04 #define BLKSIZE_SET_BY_QUIRK 0x08 #define COMPRES_SET_BY_USER 0x10 #define COMPRES_SET_BY_QUIRK 0x20 /*--------------------storage for sense data returned by the drive------------*/ unsigned char saved_page0[PAGE_0_SENSE_DATA_SIZE]; /* * additional sense data needed * for mode sense/select. */ struct buf_queue_head buf_queue; struct scsi_xfer scsi_xfer; /* scsi xfer struct for this drive */ u_int32 xfer_block_wait; /* is a process waiting? */ #ifdef DEVFS struct { void *rst; void *nrst; void *enrst; /* end of aliases */ void *rst_0; void *nrst_0; void *enrst_0; void *ctl_0; void *rst_1; void *nrst_1; void *enrst_1; void *ctl_1; void *rst_2; void *nrst_2; void *enrst_2; void *ctl_2; void *rst_3; void *nrst_3; void *enrst_3; void *ctl_3; } devfs_token; #endif }; static int stunit(dev_t dev) { return STUNIT(dev); } static dev_t stsetunit(dev_t dev, int unit) { return STSETUNIT(dev, unit); } static errval st_open(dev_t dev, int flags, int fmt, struct proc *p, struct scsi_link *sc_link); static errval st_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p, struct scsi_link *sc_link); static errval st_close(dev_t dev, int flag, int fmt, struct proc *p, struct scsi_link *sc_link); static void st_strategy(struct buf *bp, struct scsi_link *sc_link); d_open_t stopen; d_close_t stclose; d_ioctl_t stioctl; d_strategy_t ststrategy; #define CDEV_MAJOR 14 #define BDEV_MAJOR 5 static struct bdevsw st_bdevsw = { stopen, stclose, ststrategy, stioctl, /*5*/ nxdump, zerosize, 0 }; static struct cdevsw st_cdevsw = { stopen, stclose, rawread, rawwrite, /*14*/ stioctl, nostop, nullreset, nodevtotty,/* st */ seltrue, nommap, ststrategy }; SCSI_DEVICE_ENTRIES(st) static struct scsi_device st_switch = { st_interpret_sense, /* check errors with us first */ ststart, /* we have a queue, and this is how we service it */ NULL, NULL, /* use the default 'done' routine */ "st", 0, {0, 0}, 0, /* Link flags */ stattach, "Sequential-Access", stopen, sizeof(struct scsi_data), T_SEQUENTIAL, stunit, stsetunit, st_open, st_ioctl, st_close, st_strategy, }; #define ST_INITIALIZED 0x01 #define ST_INFO_VALID 0x02 #define ST_OPEN 0x04 #define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */ #define ST_WRITTEN 0x10 /* data have been written, EOD needed */ #define ST_FIXEDBLOCKS 0x20 #define ST_AT_FILEMARK 0x40 #define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data) */ #define ST_NEW_MOUNT 0x100 /* still need to decide mode */ #define ST_READONLY 0x200 /* st_mode_sense says write protected */ #define ST_FM_WRITTEN 0x400 /* * EOF file mark written -- used with * ~ST_WRITTEN to indicate that multiple file * marks have been written */ #define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */ #define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */ #define ST_MOUNTED 0x2000 /* Device is presently mounted */ #define ST_SENSE_READ 0x4000 /* mode sense read from drive */ #define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ) #define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \ ST_FIXEDBLOCKS | ST_READONLY | \ ST_FM_WRITTEN | ST_2FM_AT_EOD | ST_PER_ACTION) static int st_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return scsi_externalize(SCSI_LINK(&st_switch, kdc->kdc_unit), req); } static struct kern_devconf kdc_st_template = { 0, 0, 0, /* filled in by dev_attach */ "st", 0, MDDC_SCSI, st_externalize, 0, scsi_goaway, SCSI_EXTERNALLEN, &kdc_scbus0, /* XXX parent */ 0, /* parentdata */ DC_UNKNOWN, /* not supported */ }; static inline void st_registerdev(int unit) { struct kern_devconf *kdc; MALLOC(kdc, struct kern_devconf *, sizeof *kdc, M_TEMP, M_NOWAIT); if(!kdc) return; *kdc = kdc_st_template; kdc->kdc_unit = unit; kdc->kdc_description = st_switch.desc; dev_attach(kdc); } /* * The routine called by the low level scsi routine when it discovers * a device suitable for this driver */ static errval stattach(struct scsi_link *sc_link) { u_int32 unit; - char name[32]; struct scsi_data *st = sc_link->sd; unit = sc_link->dev_unit; TAILQ_INIT(&st->buf_queue); /* * Check if the drive is a known criminal and take * Any steps needed to bring it into line */ #ifdef NEW_SCSICONF st_loadquirks(sc_link); #else st_identify_drive(unit); #endif /* * Use the subdriver to request information regarding * the drive. We cannot use interrupts yet, so the * request must specify this. */ if (st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT, NULL, 0, 0)) { printf("drive offline"); } else { printf("density code 0x%lx, ", st->media_density); if (!scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) { if (st->media_blksiz) { printf("%ld-byte", st->media_blksiz); } else { printf("variable"); } printf(" blocks, write-%s", (st->flags & ST_READONLY) ? "protected" : "enabled"); } else { printf(" drive empty"); } } /* * Set up the buf queue for this device */ st->flags |= ST_INITIALIZED; st_registerdev(unit); #ifdef DEVFS #define ST_GID 13 #define ST_UID 0 sprintf(name,"rst%d.0",unit); st->devfs_token.rst_0 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 0, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"nrst%d.0",unit); st->devfs_token.nrst_0 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 1, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"enrst%d.0",unit); st->devfs_token.enrst_0 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 2, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"st%dctl.0",unit); st->devfs_token.ctl_0 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 3, DV_CHR, ST_UID, ST_GID, 0600 ); sprintf(name,"rst%d.1",unit); st->devfs_token.rst_1 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 4, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"nrst%d.1",unit); st->devfs_token.nrst_1 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 5, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"enrst%d.1",unit); st->devfs_token.enrst_1 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 6, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"st%dctl.1",unit); st->devfs_token.ctl_1 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 7, DV_CHR, ST_UID, ST_GID, 0600 ); sprintf(name,"rst%d.2",unit); st->devfs_token.rst_2 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 8, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"nrst%d.2",unit); st->devfs_token.nrst_2 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 9, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"enrst%d.2",unit); st->devfs_token.enrst_2 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 10, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"st%dctl.2",unit); st->devfs_token.ctl_2 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 11, DV_CHR, ST_UID, ST_GID, 0600 ); sprintf(name,"rst%d.3",unit); st->devfs_token.rst_3 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 12, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"nrst%d.3",unit); st->devfs_token.nrst_3 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 13, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"enrst%d.3",unit); st->devfs_token.enrst_3 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 14, DV_CHR, ST_UID, ST_GID, 0660 ); sprintf(name,"st%dctl.3",unit); st->devfs_token.ctl_3 = devfs_add_devsw( "/tape", name, &st_cdevsw, (unit << 4 ) + 15, DV_CHR, ST_UID, ST_GID, 0600 ); /** add links **/ sprintf(name,"rst%d",unit); st->devfs_token.rst = dev_link( "/", name, st->devfs_token.rst_0); sprintf(name,"nrst%d",unit); st->devfs_token.nrst = dev_link( "/", name, st->devfs_token.nrst_0); sprintf(name,"enrst%d",unit); st->devfs_token.enrst = dev_link( "/", name, st->devfs_token.enrst_0); #endif return 0; } #ifndef NEW_SCSICONF /* * Use the inquiry routine in 'scsi_base' to get drive info so we can * Further tailor our behaviour. */ static void st_identify_drive(unit) u_int32 unit; { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; struct rogues *finger; char manu[32]; char model[32]; char model2[32]; char version[32]; u_int32 model_len; struct scsi_inquiry_data *inqbuf = &sc_link->inqbuf; /* * Get the device type information */ if (scsi_inquire(sc_link, inqbuf, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != 0) { printf("st%ld: couldn't get device type, using default\n", unit); return; } if ((inqbuf->version & SID_ANSII) == 0) { /* * If not advanced enough, use default values */ strncpy(manu, "pre-scsi", 8); manu[8] = 0; strncpy(model, " unknown model ", 16); model[16] = 0; strncpy(version, "????", 4); version[4] = 0; } else { strncpy(manu, inqbuf->vendor, 8); manu[8] = 0; strncpy(model, inqbuf->product, 16); model[16] = 0; strncpy(version, inqbuf->revision, 4); version[4] = 0; } /* * Load the parameters for this kind of device, so we * treat it as appropriate for each operating mode. * Only check the number of characters in the array's * model entry, not the entire model string returned. */ finger = gallery; while (finger->name) { model_len = 0; while (finger->model[model_len] && (model_len < 32)) { model2[model_len] = model[model_len]; model_len++; } model2[model_len] = 0; if ((strcmp(manu, finger->manu) == 0) && (strcmp(model2, finger->model) == 0 || strcmp("????????????????", finger->model) == 0) && (strcmp(version, finger->version) == 0 || strcmp("????", finger->version) == 0)) { printf("st%ld: %s is a known rogue\n", unit, finger->name); st->rogues = finger; st->drive_quirks = finger->quirks; st->quirks = finger->quirks; /*start value */ st_loadquirks(sc_link); break; } else { finger++; /* go to next suspect */ } } } #endif /* NEW_SCSICONF */ /* * initialise the subdevices to the default (QUIRK) state. * this will remove any setting made by the system operator or previous * operations. */ static void st_loadquirks(sc_link) struct scsi_link *sc_link; { struct scsi_data *st = sc_link->sd; int i; #ifdef NEW_SCSICONF struct st_mode *mode; struct st_mode *mode2; mode = (struct st_mode*) sc_link->devmodes; if (!mode) return; st->quirks = st->drive_quirks = sc_link->quirks; #else struct modes *mode; struct modes *mode2; if (!st->rogues) return; mode = st->rogues->modes; #endif mode2 = st->modes; for (i = 0; i < 4; i++) { bzero(mode2, sizeof(*mode2)); st->modeflags[i] &= ~(BLKSIZE_SET_BY_QUIRK | DENSITY_SET_BY_QUIRK | BLKSIZE_SET_BY_USER | DENSITY_SET_BY_USER); if (mode->blksiz && ((mode->quirks | st->drive_quirks) & (ST_Q_FORCE_FIXED_MODE))) { mode2->blksiz = mode->blksiz; st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK; } else { if ((mode->quirks | st->drive_quirks) & ST_Q_FORCE_VAR_MODE) { mode2->blksiz = 0; st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK; } } if (mode->density) { mode2->density = mode->density; st->modeflags[i] |= DENSITY_SET_BY_QUIRK; } mode++; mode2++; } } /* * open the device. */ static errval st_open(dev_t dev, int flags, int fmt, struct proc *p, struct scsi_link *sc_link) { u_int32 unit, mode, dsty; errval errno = 0; struct scsi_data *st; unit = STUNIT(dev); mode = MODE(dev); dsty = DSTY(dev); st = sc_link->sd; /* * Make sure the device has been initialised */ if ((st == NULL) || (!(st->flags & ST_INITIALIZED))) return (ENXIO); /* * Only allow one at a time */ if (st->flags & ST_OPEN) { return (EBUSY); } /* * Throw out a dummy instruction to catch 'Unit attention * errors (the error handling will invalidate all our * device info if we get one, but otherwise, ignore it) */ scsi_test_unit_ready(sc_link, SCSI_SILENT); sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */ /* * If the mode is 3 (e.g. minor = 3,7,11,15) * then the device has been openned to set defaults * This mode does NOT ALLOW I/O, only ioctls. * XXX: Where do we lock out I/O? */ if (IS_CTLMODE(dev)) return 0; /* * Check that the device is ready to use (media loaded?) * This time take notice of the return result */ if ( (errno = scsi_test_unit_ready(sc_link, 0)) ) { uprintf("st%d: not ready\n", unit); st_unmount(unit, NOEJECT); return (errno); } /* * if it's a different mode, or if the media has been * invalidated, unmount the tape from the previous * session but continue with open processing */ if ((st->last_dsty != dsty) || (!(sc_link->flags & SDEV_MEDIA_LOADED))) { st_unmount(unit, NOEJECT); } /* * If we are not mounted, then we should start a new * mount session. */ if (!(st->flags & ST_MOUNTED)) { st_mount_tape(dev, flags); st->last_dsty = dsty; } /* * Make sure that a tape opened in write-only mode will have * file marks written on it when closed, even if not written to. * This is for SUN compatibility */ if ((flags & O_ACCMODE) == FWRITE) st->flags |= ST_WRITTEN; SC_DEBUG(sc_link, SDEV_DB2, ("Open complete\n")); st->flags |= ST_OPEN; return 0; } /* * close the device.. only called if we are the LAST * occurence of an open device */ static errval st_close(dev_t dev, int flag, int fmt, struct proc *p, struct scsi_link *sc_link) { u_int32 unit, mode; struct scsi_data *st; unit = STUNIT(dev); mode = MODE(dev); st = sc_link->sd; if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN) st_write_filemarks(unit, 1, 0); switch (mode & 0x3) { case 0: case 3: /* for now */ st_unmount(unit, NOEJECT); break; case 1: /*leave mounted unless media seems to have been removed */ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { st_unmount(unit, NOEJECT); } break; case 2: st_unmount(unit, EJECT); break; } sc_link->flags &= ~SDEV_OPEN; st->flags &= ~ST_OPEN; return (0); } /* * Start a new mount session. * Copy in all the default parameters from the selected device mode. * and try guess any that seem to be defaulted. */ static errval st_mount_tape(dev, flags) dev_t dev; u_int32 flags; { u_int32 unit, mode, dsty; struct scsi_data *st; struct scsi_link *sc_link; errval errno = 0; unit = STUNIT(dev); mode = MODE(dev); dsty = DSTY(dev); sc_link = SCSI_LINK(&st_switch, unit); st = sc_link->sd; if (st->flags & ST_MOUNTED) return 0; SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n ")); st->flags |= ST_NEW_MOUNT; st->quirks = st->drive_quirks | st->modes[dsty].quirks; /* * If the media is new, then make sure we give it a chance to * to do a 'load' instruction. ( We assume it is new) */ if ( (errno = st_load(unit, LD_LOAD, 0)) ) { return (errno); } /* * Throw another dummy instruction to catch * 'Unit attention' errors. Some drives appear to give * these after doing a Load instruction. * (noteably some DAT drives) */ scsi_test_unit_ready(sc_link, SCSI_SILENT); /* * Some devices can't tell you much until they have been * asked to look at the media. This quirk does this. */ if (st->quirks & ST_Q_SNS_HLP) { if ( (errno = st_touch_tape(unit)) ) return errno; } /* * Load the physical device parameters * loads: blkmin, blkmax */ if ( (errno = st_rd_blk_lim(unit, 0)) ) { return errno; } /* * Load the media dependent parameters * includes: media_blksiz,media_density,numblks * As we have a tape in, it should be reflected here. * If not you may need the "quirk" above. */ if ( (errno = st_mode_sense(unit, 0, NULL, 0, 0)) ) { return errno; } /* * If we have gained a permanent density from somewhere, * then use it in preference to the one supplied by * default by the driver. */ if (st->modeflags[dsty] & (DENSITY_SET_BY_QUIRK | DENSITY_SET_BY_USER)) { st->density = st->modes[dsty].density; } else { st->density = st->media_density; } /* * If we have gained a permanent blocksize * then use it in preference to the one supplied by * default by the driver. */ st->flags &= ~ST_FIXEDBLOCKS; if (st->modeflags[dsty] & (BLKSIZE_SET_BY_QUIRK | BLKSIZE_SET_BY_USER)) { st->blksiz = st->modes[dsty].blksiz; if (st->blksiz) { st->flags |= ST_FIXEDBLOCKS; } } else { if ( (errno = st_decide_mode(unit, FALSE)) ) { return errno; } } if ( (errno = st_mode_select(unit, 0, NULL, 0)) ) { printf("st%ld: Cannot set selected mode", unit); return errno; } scsi_prevent(sc_link, PR_PREVENT, 0); /* who cares if it fails? */ st->flags &= ~ST_NEW_MOUNT; st->flags |= ST_MOUNTED; sc_link->flags |= SDEV_MEDIA_LOADED; return 0; } /* * End the present mount session. * Rewind, and optionally eject the tape. * Reset various flags to indicate that all new * operations require another mount operation */ void st_unmount(int unit, boolean eject) { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; int32 nmarks; if (!(st->flags & ST_MOUNTED)) return; SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n")); st_chkeod(unit, FALSE, &nmarks, SCSI_SILENT); st_rewind(unit, FALSE, SCSI_SILENT); scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT); if (eject) { st_load(unit, LD_UNLOAD, SCSI_SILENT); } st->flags &= ~(ST_MOUNTED | ST_NEW_MOUNT); sc_link->flags &= ~SDEV_MEDIA_LOADED; } /* * Given all we know about the device, media, mode, 'quirks' and * initial operation, make a decision as to how we should be set * to run (regarding blocking and EOD marks) */ static errval st_decide_mode(unit, first_read) u_int32 unit; boolean first_read; { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n")); /* * If the user hasn't already specified fixed or variable-length * blocks and the block size (zero if variable-length), we'll * have to try to figure them out ourselves. * * Our first shot at a method is, "The quirks made me do it!" */ switch ((int)(st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE))) { case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE): printf("st%ld: bad quirks\n", unit); return (EINVAL); case ST_Q_FORCE_FIXED_MODE: /*specified fixed, but not what size */ st->flags |= ST_FIXEDBLOCKS; if (st->blkmin && (st->blkmin == st->blkmax)) st->blksiz = st->blkmin; else if (st->media_blksiz > 0) st->blksiz = st->media_blksiz; else st->blksiz = DEF_FIXED_BSIZE; SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force fixed mode(%ld)\n", st->blksiz)); goto done; case ST_Q_FORCE_VAR_MODE: st->flags &= ~ST_FIXEDBLOCKS; st->blksiz = 0; SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force variable mode\n")); goto done; } /* * If the drive can only handle fixed-length blocks and only at * one size, perhaps we should just do that. */ if (st->blkmin && (st->blkmin == st->blkmax)) { st->flags |= ST_FIXEDBLOCKS; st->blksiz = st->blkmin; SC_DEBUG(sc_link, SDEV_DB3, ("blkmin == blkmax of %ld\n", st->blkmin)); goto done; } /* * If the tape density mandates (or even suggests) use of fixed * or variable-length blocks, comply. */ switch ((int)st->density) { case HALFINCH_800: case HALFINCH_1600: case HALFINCH_6250: case DDS: st->flags &= ~ST_FIXEDBLOCKS; st->blksiz = 0; SC_DEBUG(sc_link, SDEV_DB3, ("density specified variable\n")); goto done; case QIC_11: case QIC_24: case QIC_120: case QIC_150: case QIC_525: case QIC_1320: case QIC_3080: st->flags |= ST_FIXEDBLOCKS; if (st->media_blksiz > 0) { st->blksiz = st->media_blksiz; } else { st->blksiz = DEF_FIXED_BSIZE; } SC_DEBUG(sc_link, SDEV_DB3, ("density specified fixed\n")); goto done; } /* * If we're about to read the tape, perhaps we should choose * fixed or variable-length blocks and block size according to * what the drive found on the tape. */ if (first_read && (!(st->quirks & ST_Q_BLKSIZ) || (st->media_blksiz == 0) || (st->media_blksiz == DEF_FIXED_BSIZE) || (st->media_blksiz == 1024))) { if (st->media_blksiz == 0) { st->flags &= ~ST_FIXEDBLOCKS; } else { st->flags |= ST_FIXEDBLOCKS; } st->blksiz = st->media_blksiz; SC_DEBUG(sc_link, SDEV_DB3, ("Used media_blksiz of %ld\n", st->media_blksiz)); goto done; } /* * We're getting no hints from any direction. Choose variable- * length blocks arbitrarily. */ st->flags &= ~ST_FIXEDBLOCKS; st->blksiz = 0; SC_DEBUG(sc_link, SDEV_DB3, ("Give up and default to variable mode\n")); done: /* * Decide whether or not to write two file marks to signify end- * of-data. Make the decision as a function of density. If * the decision is not to use a second file mark, the SCSI BLANK * CHECK condition code will be recognized as end-of-data when * first read. * (I think this should be a by-product of fixed/variable..julian) */ switch ((int)st->density) { /* case 8 mm: What is the SCSI density code for 8 mm, anyway? */ case QIC_11: case QIC_24: case QIC_120: case QIC_150: case QIC_525: case QIC_1320: case QIC_3080: st->flags &= ~ST_2FM_AT_EOD; break; default: st->flags |= ST_2FM_AT_EOD; } return 0; } /* * Actually translate the requested transfer into * one the physical driver can understand * The transfer is described by a buf and will include * only one physical transfer. */ static void st_strategy(struct buf *bp, struct scsi_link *sc_link) { u_int32 unit; u_int32 opri; struct scsi_data *st; int len; unit = STUNIT((bp->b_dev)); st = sc_link->sd; /* * If it's a null transfer, return immediatly */ if ((len = bp->b_bcount) == 0) { goto done; } /* * Check the adapter can do it */ scsi_minphys(bp,&st_switch); /* * Odd sized request on fixed drives are verboten */ if (st->flags & ST_FIXEDBLOCKS) { if (bp->b_bcount % st->blksiz) { - printf("st%d: bad request, must be multiple of %ld\n", + printf("st%ld: bad request, must be multiple of %ld\n", unit, st->blksiz); bp->b_error = EIO; goto bad; } } /* * as are out-of-range requests on variable drives. * (or if we got chopped by minphys) */ else { if ((bp->b_bcount < st->blkmin || bp->b_bcount > st->blkmax)) { - printf("st%d: bad request, must be between %ld and %ld\n", + printf("st%ld: bad request, must be between %ld and %ld\n", unit, st->blkmin, st->blkmax); bp->b_error = EIO; goto bad; } if (len != bp->b_bcount) { - printf("st%d: bad request, must be less than %ld bytes\n", + printf("st%ld: bad request, must be less than %ld bytes\n", unit, bp->b_bcount + 1); bp->b_error = EIO; goto bad; } } opri = splbio(); /* * Use a bounce buffer if necessary */ #ifdef BOUNCE_BUFFERS if (sc_link->flags & SDEV_BOUNCE) vm_bounce_alloc(bp); #endif /* * Place it in the queue of activities for this tape * at the end (a bit silly because we only have on user.. * (but it could fork() )) */ TAILQ_INSERT_TAIL(&st->buf_queue, bp, b_act); /* * Tell the device to get going on the transfer if it's * not doing anything, otherwise just wait for completion * (All a bit silly if we're only allowing 1 open but..) */ ststart(unit, 0); splx(opri); return; bad: bp->b_flags |= B_ERROR; done: /* * Correctly set the buf to indicate a completed xfer */ biodone(bp); return; } /* * ststart looks to see if there is a buf waiting for the device * and that the device is not already busy. If both are true, * It dequeues the buf and creates a scsi command to perform the * transfer required. The transfer request will call scsi_done * on completion, which will in turn call this routine again * so that the next queued transfer is performed. * The bufs are queued by the strategy routine (ststrategy) * * This routine is also called after other non-queued requests * have been made of the scsi driver, to ensure that the queue * continues to be drained. * ststart() is called at splbio */ static void ststart(unit, flags) u_int32 unit; u_int32 flags; { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; register struct buf *bp = 0; struct scsi_rw_tape cmd; SC_DEBUG(sc_link, SDEV_DB2, ("ststart ")); /* * See if there is a buf to do and we are not already * doing one */ while (sc_link->opennings != 0) { /* if a special awaits, let it proceed first */ if (sc_link->flags & SDEV_WAITING) { sc_link->flags &= ~SDEV_WAITING; wakeup((caddr_t)sc_link); return; } bp = st->buf_queue.tqh_first; if (bp == NULL) { /* yes, an assign */ return; } TAILQ_REMOVE( &st->buf_queue, bp, b_act); /* * if the device has been unmounted by the user * then throw away all requests until done */ if ((!(st->flags & ST_MOUNTED)) || (!(sc_link->flags & SDEV_MEDIA_LOADED))) { /* make sure that one implies the other.. */ sc_link->flags &= ~SDEV_MEDIA_LOADED; goto badnews; } /* * only FIXEDBLOCK devices have pending operations */ if (st->flags & ST_FIXEDBLOCKS) { /* * If we are at a filemark but have not reported it yet * then we should report it now */ if (st->flags & ST_AT_FILEMARK) { if ((bp->b_flags & B_READ) == B_WRITE) { /* * Handling of ST_AT_FILEMARK in * st_space will fill in the right file * mark count. * Back up over filemark */ if (st_space(unit, 0, SP_FILEMARKS, 0) != ESUCCESS) goto badnews; } else { bp->b_resid = bp->b_bcount; bp->b_error = 0; bp->b_flags &= ~B_ERROR; st->flags &= ~ST_AT_FILEMARK; biodone(bp); continue; /* seek more work */ } } /* * If we are at EIO (e.g. EOM) but have not reported it * yet then we should report it now */ if (st->flags & ST_EIO_PENDING) { bp->b_resid = bp->b_bcount; bp->b_error = EIO; bp->b_flags |= B_ERROR; st->flags &= ~ST_EIO_PENDING; biodone(bp); continue; /* seek more work */ } } /* * Fill out the scsi command */ bzero(&cmd, sizeof(cmd)); if ((bp->b_flags & B_READ) == B_WRITE) { cmd.op_code = WRITE_COMMAND_TAPE; st->flags &= ~ST_FM_WRITTEN; st->flags |= ST_WRITTEN; flags |= SCSI_DATA_OUT; } else { cmd.op_code = READ_COMMAND_TAPE; flags |= SCSI_DATA_IN; } /* * Handle "fixed-block-mode" tape drives by using the * block count instead of the length. */ if (st->flags & ST_FIXEDBLOCKS) { cmd.byte2 |= SRWT_FIXED; scsi_uto3b(bp->b_bcount / st->blksiz, cmd.len); } else { scsi_uto3b(bp->b_bcount, cmd.len); } /* * go ask the adapter to do all this for us */ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd, sizeof(cmd), (u_char *) bp->b_un.b_addr, bp->b_bcount, 0, /* can't retry a read on a tape really */ 100000, bp, flags) == SUCCESSFULLY_QUEUED) { } else { badnews: printf("st%ld: oops not queued\n", unit); bp->b_flags |= B_ERROR; bp->b_error = EIO; biodone(bp); } } /* go back and see if we can cram more work in.. */ } /* * Perform special action on behalf of the user; * knows about the internals of this device */ static errval st_ioctl(dev_t dev, int cmd, caddr_t arg, int flag, struct proc *p, struct scsi_link *sc_link) { errval errcode = 0; u_int32 unit; u_int32 number, flags, dsty; struct scsi_data *st; u_int32 hold_blksiz; u_int32 hold_density; int32 nmarks; struct mtop *mt = (struct mtop *) arg; /* * Find the device that the user is talking about */ flags = 0; /* give error messages, act on errors etc. */ unit = STUNIT(dev); dsty = DSTY(dev); st = sc_link->sd; hold_blksiz = st->blksiz; hold_density = st->density; switch (cmd) { case MTIOCGET: { struct mtget *g = (struct mtget *) arg; SC_DEBUG(sc_link, SDEV_DB1, ("[ioctl: get status]\n")); bzero(g, sizeof(struct mtget)); g->mt_type = 0x7; /* Ultrix compat *//*? */ g->mt_density = st->density; g->mt_blksiz = st->blksiz; g->mt_comp = st->comp; g->mt_density0 = st->modes[0].density; g->mt_density1 = st->modes[1].density; g->mt_density2 = st->modes[2].density; g->mt_density3 = st->modes[3].density; g->mt_blksiz0 = st->modes[0].blksiz; g->mt_blksiz1 = st->modes[1].blksiz; g->mt_blksiz2 = st->modes[2].blksiz; g->mt_blksiz3 = st->modes[3].blksiz; g->mt_comp0 = 0; g->mt_comp1 = 0; g->mt_comp2 = 0; g->mt_comp3 = 0; break; } case MTIOCTOP: { SC_DEBUG(sc_link, SDEV_DB1, ("[ioctl: op=0x%x count=0x%lx]\n", mt->mt_op, mt->mt_count)); /* compat: in U*x it is a short */ number = mt->mt_count; switch ((short) (mt->mt_op)) { case MTWEOF: /* write an end-of-file record */ errcode = st_write_filemarks(unit, number, flags); break; case MTBSF: /* backward space file */ number = -number; case MTFSF: /* forward space file */ errcode = st_chkeod(unit, FALSE, &nmarks, flags); if (errcode == ESUCCESS) errcode = st_space(unit, number - nmarks, SP_FILEMARKS, flags); break; case MTBSR: /* backward space record */ number = -number; case MTFSR: /* forward space record */ errcode = st_chkeod(unit, TRUE, &nmarks, flags); if (errcode == ESUCCESS) errcode = st_space(unit, number, SP_BLKS, flags); break; case MTEOD: /* space to end of recorded medium */ errcode = st_chkeod(unit, FALSE, &nmarks, flags); if (errcode == ESUCCESS) errcode = st_space(unit, 0, SP_EOM, flags); break; case MTREW: /* rewind */ errcode = st_rewind(unit, FALSE, flags); break; case MTERASE: /* erase */ errcode = st_erase(unit, FALSE, flags); break; case MTOFFL: /* rewind and put the drive offline */ st_unmount(unit, EJECT); break; case MTNOP: /* no operation, sets status only */ case MTCACHE: /* enable controller cache */ case MTNOCACHE: /* disable controller cache */ break; case MTSETBSIZ: /* Set block size for device */ #ifdef NOTYET if (!(st->flags & ST_NEW_MOUNT)) { uprintf("re-mount tape before changing blocksize"); errcode = EINVAL; break; } #endif if (number == 0) { st->flags &= ~ST_FIXEDBLOCKS; } else { if ((st->blkmin || st->blkmax) /* they exist */ &&((number < st->blkmin || number > st->blkmax))) { errcode = EINVAL; break; } st->flags |= ST_FIXEDBLOCKS; } st->blksiz = number; st->flags |= ST_BLOCK_SET; /*XXX */ goto try_new_value; case MTSETDNSTY: /* Set density for device and mode */ if (number > SCSI_2_MAX_DENSITY_CODE) { errcode = EINVAL; } else { st->density = number; } goto try_new_value; case MTCOMP: /* enable default compression */ errcode = st_comp(unit,number); break; default: errcode = EINVAL; } break; } case MTIOCIEOT: case MTIOCEEOT: break; default: if(IS_CTLMODE(dev)) errcode = scsi_do_ioctl(dev, cmd, arg, flag, p, sc_link); else errcode = ENOTTY; break; } return errcode; /*-----------------------------*/ try_new_value: /* * Check that the mode being asked for is aggreeable to the * drive. If not, put it back the way it was. */ if ( (errcode = st_mode_select(unit, 0, NULL, 0)) ) { /* put back as it was */ - printf("st%d: Cannot set selected mode", unit); + printf("st%ld: Cannot set selected mode", unit); st->density = hold_density; st->blksiz = hold_blksiz; if (st->blksiz) { st->flags |= ST_FIXEDBLOCKS; } else { st->flags &= ~ST_FIXEDBLOCKS; } return (errcode); } /* * As the drive liked it, if we are setting a new default, * set it into the structures as such. * * The means for deciding this are not finalised yet */ if (IS_CTLMODE(dev)) { /* special mode */ /* XXX */ switch ((short) (mt->mt_op)) { case MTSETBSIZ: st->modes[dsty].blksiz = st->blksiz; st->modeflags[dsty] |= BLKSIZE_SET_BY_USER; break; case MTSETDNSTY: st->modes[dsty].density = st->density; st->modeflags[dsty] |= DENSITY_SET_BY_USER; break; } } return 0; } /* * Do a synchronous read. */ static errval st_read(unit, buf, size, flags) u_int32 unit, size, flags; char *buf; { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; struct scsi_rw_tape scsi_cmd; /* * If it's a null transfer, return immediatly */ if (size == 0) { return (ESUCCESS); } bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = READ_COMMAND_TAPE; if (st->flags & ST_FIXEDBLOCKS) { scsi_cmd.byte2 |= SRWT_FIXED; scsi_uto3b(size / (st->blksiz ? st->blksiz : DEF_FIXED_BSIZE), scsi_cmd.len); } else { scsi_uto3b(size, scsi_cmd.len); } return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) buf, size, 0, /* not on io commands */ 100000, NULL, flags | SCSI_DATA_IN)); } #ifdef __STDC__ #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) #else #define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) #endif /* * Ask the drive what it's min and max blk sizes are. */ static errval st_rd_blk_lim(unit, flags) u_int32 unit, flags; { struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; struct scsi_blk_limits scsi_cmd; struct scsi_blk_limits_data scsi_blkl; errval errno; /* * First check if we have it all loaded */ if ((sc_link->flags & SDEV_MEDIA_LOADED)) return 0; /* * do a 'Read Block Limits' */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = READ_BLK_LIMITS; /* * do the command, update the global values */ if ( (errno = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) & scsi_blkl, sizeof(scsi_blkl), ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN)) ) { return errno; } st->blkmin = b2tol(scsi_blkl.min_length); st->blkmax = scsi_3btou(&scsi_blkl.max_length_2); SC_DEBUG(sc_link, SDEV_DB3, ("(%ld <= blksiz <= %ld)\n", st->blkmin, st->blkmax)); return 0; } /* * Get the scsi driver to send a full inquiry to the * device and use the results to fill out the global * parameter structure. * * called from: * attach * open * ioctl (to reset original blksize) */ static errval st_mode_sense(unit, flags, page, pagelen, pagecode) u_int32 unit, flags; struct tape_pages *page; u_int32 pagelen,pagecode; { u_int32 dat_len; errval errno; struct scsi_mode_sense scsi_cmd; struct { struct scsi_mode_header header; struct blk_desc blk_desc; struct tape_pages page; } dat; /* Tandberg tape drives returns page 00 * with the sense data, whether or not * you want it( ie the don't like you * saying you want anything less!!!!! * They also expect page 00 * back when you issue a mode select */ struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; st->flags &= ~ST_SENSE_READ; /* * Check if we need to use a default page.. */ if ((st->quirks & ST_Q_NEEDS_PAGE_0) && (!page)) { pagelen = PAGE_0_SENSE_DATA_SIZE; page = (struct tape_pages *) st->saved_page0; pagecode = 0; } /* * Now work out the total dat size etc. */ dat_len = sizeof(struct scsi_mode_header) + sizeof(struct blk_desc) + (page ? pagelen : 0); /* * Set up a mode sense */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; scsi_cmd.page = (u_char) pagecode; scsi_cmd.length = dat_len; /* * do the command, * use the results to set blksiz, numblks and density * or if we need it as a template for the mode select * store it away. */ if ( (errno = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) &dat, dat_len, ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN)) ) { return errno; } st->numblks = scsi_3btou(dat.blk_desc.nblocks); st->media_blksiz = scsi_3btou(dat.blk_desc.blklen); st->media_density = dat.blk_desc.density; if (dat.header.dev_spec & SMH_DSP_WRITE_PROT) { st->flags |= ST_READONLY; } SC_DEBUG(sc_link, SDEV_DB3, ("density code 0x%lx, %ld-byte blocks, write-%s, ", st->media_density, st->media_blksiz, st->flags & ST_READONLY ? "protected" : "enabled")); SC_DEBUG(sc_link, SDEV_DB3, ("%sbuffered\n", ((dat.header.dev_spec & SMH_DSP_BUFF_MODE) ? "" : "un"))); if (page) { bcopy(&dat.page, page, pagelen); } st->flags |= ST_SENSE_READ; return 0; } /* * Send a filled out parameter structure to the drive to * set it into the desire modes etc. */ static errval st_mode_select(unit, flags, page, pagelen) u_int32 unit, flags; struct tape_pages *page; u_int32 pagelen; { u_int32 dat_len; struct scsi_mode_select scsi_cmd; struct { struct scsi_mode_header header; struct blk_desc blk_desc; struct tape_pages page; } dat; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; /* * Check if we need to use a default page.. * Gee, hope we saved one before now........ */ if ((st->quirks & ST_Q_NEEDS_PAGE_0) && (!page)) { pagelen = PAGE_0_SENSE_DATA_SIZE; page = (struct tape_pages *) st->saved_page0; } /* * Now work out the total dat size etc. */ dat_len = sizeof(struct scsi_mode_header) + sizeof(struct blk_desc) + (page ? pagelen : 0); /* * Set up for a mode select */ bzero(&dat, dat_len); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SELECT; scsi_cmd.length = dat_len; dat.header.blk_desc_len = sizeof(struct blk_desc); dat.header.dev_spec |= SMH_DSP_BUFF_MODE_ON; dat.blk_desc.density = st->density; if (st->flags & ST_FIXEDBLOCKS) { scsi_uto3b(st->blksiz, dat.blk_desc.blklen); } if (page) { bcopy(page, &dat.page, pagelen); /* the Tandberg tapes need the block size to */ /* be set on each mode sense/select. */ } /* * do the command */ return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), (u_char *) &dat, dat_len, ST_RETRIES, 5000, NULL, flags | SCSI_DATA_OUT)); } static int noisy_st = 0; /***************************************************************\ * Set the compression mode of the drive to on (1) or off (0) * still doesn't work! grrr! \***************************************************************/ static errval st_comp(unit,mode) u_int32 unit,mode; { struct tape_pages page; int pagesize; int retval; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; bzero(&page, sizeof(page)); pagesize = sizeof(page.pages.configuration) + PAGE_HEADERLEN; if ( (retval = st_mode_sense(unit, 0, &page, pagesize, ST_PAGE_CONFIGURATION)) ) { printf("sense returned an error of %d\n",retval); return retval; } if ( noisy_st) printf("drive reports value of %d, setting %ld\n", page.pages.configuration.data_compress_alg,mode); page.pg_code &= ST_P_CODE; page.pg_length = sizeof(page.pages.configuration); switch(mode) { case 0: page.pages.configuration.data_compress_alg = 0; break; case 1: page.pages.configuration.data_compress_alg = 1; break; default: printf("st%ld: bad value for compression mode\n",unit); return EINVAL; } if ( (retval = st_mode_select(unit, 0, &page, pagesize)) ) { printf("select returned an error of %d\n",retval); return retval; } st->comp = mode; return 0; } /* * skip N blocks/filemarks/seq filemarks/eom */ static errval st_space(unit, number, what, flags) u_int32 unit, what, flags; int32 number; { errval error; struct scsi_space scsi_cmd; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; switch ((int)what) { case SP_BLKS: if (st->flags & ST_PER_ACTION) { if (number > 0) { st->flags &= ~ST_PER_ACTION; return (EIO); } else if (number < 0) { if (st->flags & ST_AT_FILEMARK) { /* * Handling of ST_AT_FILEMARK * in st_space will fill in the * right file mark count. */ error = st_space(unit, 0, SP_FILEMARKS, flags); if (error) return (error); } if (st->flags & ST_BLANK_READ) { st->flags &= ~ST_BLANK_READ; return (EIO); } st->flags &= ~ST_EIO_PENDING; } } break; case SP_FILEMARKS: if (st->flags & ST_EIO_PENDING) { if (number > 0) { /* pretend we just discover the error */ st->flags &= ~ST_EIO_PENDING; return (EIO); } else if (number < 0) { /* back away from the error */ st->flags &= ~ST_EIO_PENDING; } } if (st->flags & ST_AT_FILEMARK) { st->flags &= ~ST_AT_FILEMARK; number--; } if ((st->flags & ST_BLANK_READ) && (number < 0)) { /* back away from unwritten tape */ st->flags &= ~ST_BLANK_READ; number++; /* dubious */ } break; case SP_EOM: if (st->flags & ST_EIO_PENDING) { /* we are already at EOM */ st->flags &= ~ST_EIO_PENDING; return(ESUCCESS); } number = 1; /* we have only one end-of-medium */ break; } if (number == 0) { return (ESUCCESS); } bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = SPACE; scsi_cmd.byte2 = what & SS_CODE; scsi_uto3b(number, scsi_cmd.number); return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, 0, /* no retries please , just fail */ 600000, /* 10 mins enough? */ NULL, flags)); } /* * write N filemarks */ static errval st_write_filemarks(unit, number, flags) u_int32 unit, flags; int32 number; { struct scsi_write_filemarks scsi_cmd; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; /* * It's hard to write a negative number of file marks. * Don't try. */ if (number < 0) { return EINVAL; } switch ((int)number) { case 0: /* really a command to sync the drive's buffers */ break; case 1: if (st->flags & ST_FM_WRITTEN) { /* already have one down */ st->flags &= ~ST_WRITTEN; } else { st->flags |= ST_FM_WRITTEN; } st->flags &= ~ST_PER_ACTION; break; default: st->flags &= ~(ST_PER_ACTION | ST_WRITTEN); } bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = WRITE_FILEMARKS; scsi_uto3b(number, scsi_cmd.number); return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, 0, /* no retries, just fail */ 100000, /* 10 secs.. (may need to repos head ) */ NULL, flags); } /* * Make sure the right number of file marks is on tape if the * tape has been written. If the position argument is true, * leave the tape positioned where it was originally. * * nmarks returns the number of marks to skip (or, if position * true, which were skipped) to get back original position. */ static int32 st_chkeod(unit, position, nmarks, flags) u_int32 unit; boolean position; int32 *nmarks; u_int32 flags; { errval error; struct scsi_data *st = SCSI_DATA(&st_switch, unit); switch ((int)(st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD))) { default: *nmarks = 0; return (ESUCCESS); case ST_WRITTEN: case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD: *nmarks = 1; break; case ST_WRITTEN | ST_2FM_AT_EOD: *nmarks = 2; } error = st_write_filemarks(unit, *nmarks, flags); if (position && (error == ESUCCESS)) error = st_space(unit, -*nmarks, SP_FILEMARKS, flags); return (error); } /* * load/unload (with retension if true) */ static errval st_load(unit, type, flags) u_int32 unit, type, flags; { struct scsi_load scsi_cmd; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; bzero(&scsi_cmd, sizeof(scsi_cmd)); if (type != LD_LOAD) { errval error; int32 nmarks; error = st_chkeod(unit, FALSE, &nmarks, flags); if (error != ESUCCESS) return (error); sc_link->flags &= ~SDEV_MEDIA_LOADED; } if (st->quirks & ST_Q_IGNORE_LOADS) return (0); scsi_cmd.op_code = LOAD_UNLOAD; scsi_cmd.how |= type; return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, ST_RETRIES, 300000, /* 5 min */ NULL, flags)); } /* * Rewind the device */ static errval st_rewind(unit, immed, flags) u_int32 unit, flags; boolean immed; { struct scsi_rewind scsi_cmd; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; errval error; int32 nmarks; error = st_chkeod(unit, FALSE, &nmarks, flags); if (error != ESUCCESS) return (error); st->flags &= ~ST_PER_ACTION; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = REWIND; scsi_cmd.byte2 = immed ? SR_IMMED : 0; return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, ST_RETRIES, immed ? 5000 : 300000, /* 5 sec or 5 min */ NULL, flags)); } /* ** Erase the device */ static errval st_erase(unit, immed, flags) u_int32 unit, flags; boolean immed; { struct scsi_erase scsi_cmd; struct scsi_link *sc_link = SCSI_LINK(&st_switch, unit); struct scsi_data *st = sc_link->sd; errval error; int32 nmarks; error = st_chkeod(unit, FALSE, &nmarks, flags); if (error != ESUCCESS) return (error); /* ** Archive Viper 2525 technical manual 5.7 (ERASE 19h): ** tape has to be positioned to BOT first before erase command ** is issued or command is rejected. So we rewind the tape first ** and exit with an error, if the tape can't be rewinded. */ error = st_rewind(unit, FALSE, SCSI_SILENT); if (error != ESUCCESS) return (error); st->flags &= ~ST_PER_ACTION; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = ERASE; scsi_cmd.byte2 = SE_LONG; /* LONG_ERASE */ scsi_cmd.byte2 += immed ? SE_IMMED : 0; /* immed bit is here the 2nd! */ return (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, ST_RETRIES, immed ? 5000 : 300000, /* 5 sec or 5 min */ NULL, flags)); } /* * Look at the returned sense and act on the error and detirmine * The unix error number to pass back... (0 = report no error) * (SCSIRET_CONTINUE = continue processing) */ static errval st_interpret_sense(xs) struct scsi_xfer *xs; { struct scsi_link *sc_link = xs->sc_link; struct scsi_sense_data *sense = &(xs->sense); boolean silent = xs->flags & SCSI_SILENT; u_int32 unit = sc_link->dev_unit; struct scsi_data *st = SCSI_DATA(&st_switch, unit); u_int32 key; int32 info; /* * Get the sense fields and work out what code */ if (sense->error_code & SSD_ERRCODE_VALID) { info = ntohl(*((int32 *) sense->ext.extended.info)); } else { if (st->flags & ST_FIXEDBLOCKS) { info = xs->datalen / st->blksiz; } else { info = xs->datalen; } } if ((sense->error_code & SSD_ERRCODE) != 0x70) { return SCSIRET_CONTINUE;/* let the generic code handle it */ } if(sense->ext.extended.flags & (SSD_EOM|SSD_FILEMARK|SSD_ILI)) { if (st->flags & ST_FIXEDBLOCKS) { xs->resid = info * st->blksiz; xs->flags |= SCSI_RESID_VALID; if (sense->ext.extended.flags & SSD_EOM) { st->flags |= ST_EIO_PENDING; } if (sense->ext.extended.flags & SSD_FILEMARK) { st->flags |= ST_AT_FILEMARK; } if (sense->ext.extended.flags & SSD_ILI) { st->flags |= ST_EIO_PENDING; if (sense->error_code & SSD_ERRCODE_VALID && !silent) printf("st%ld: block wrong size" ", %ld blocks residual\n", unit ,info); /*XXX*/ /* is this how it works ? */ /* check def of ILI for fixed blk tapes */ /* * This quirk code helps the drive read * the first tape block, regardless of * format. That is required for these * drives to return proper MODE SENSE * information. */ if ((st->quirks & ST_Q_SNS_HLP) && !(st->flags & ST_SENSE_READ)) { st->blksiz -= 512; } } /* * If no data was tranfered, do it immediatly */ if (xs->resid >= xs->datalen) { xs->flags &= ~SCSI_RESID_VALID; if (st->flags & ST_AT_FILEMARK) { xs->flags |= SCSI_EOF; st->flags &= ~ST_AT_FILEMARK; return 0; } if (st->flags & ST_EIO_PENDING) { st->flags &= ~ST_EIO_PENDING; return EIO; } } return 0; } else { /* must be variable mode */ xs->resid = xs->datalen; /* to be sure */ if (sense->ext.extended.flags & SSD_EOM) { return (EIO); } if (sense->ext.extended.flags & SSD_FILEMARK) { xs->flags |= SCSI_EOF; } if (sense->ext.extended.flags & SSD_ILI) { if (info < 0) { /* * the record was bigger than the read */ if (!silent) printf("st%ld: %ld-byte record " "too big\n", unit, xs->datalen - info); return (EIO); } xs->resid = info; xs->flags |= SCSI_RESID_VALID; } } return 0; } key = sense->ext.extended.flags & SSD_KEY; if (key == 0x8) { xs->flags |= SCSI_EOF; /* some drives need this */ /* * This quirk code helps the drive read the * first tape block, regardless of format. That * is required for these drives to return proper * MODE SENSE information. */ if ((st->quirks & ST_Q_SNS_HLP) && !(st->flags & ST_SENSE_READ)) { /* still starting */ st->blksiz -= 512; } else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) { st->flags |= ST_BLANK_READ; xs->flags |= SCSI_EOF; return (ESUCCESS); } } return SCSIRET_CONTINUE; /* Use the the generic handler */ } /* * The quirk here is that the drive returns some value to st_mode_sense * incorrectly until the tape has actually passed by the head. * * The method is to set the drive to large fixed-block state (user-specified * density and 1024-byte blocks), then read and rewind to get it to sense the * tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't * work, as a last resort, try variable- length blocks. The result will be * the ability to do an accurate st_mode_sense. * * We know we can do a rewind because we just did a load, which implies rewind. * Rewind seems preferable to space backward if we have a virgin tape. * * The rest of the code for this quirk is in ILI processing and BLANK CHECK * error processing, both part of st_interpret_sense. */ static errval st_touch_tape(unit) u_int32 unit; { struct scsi_data *st = SCSI_DATA(&st_switch, unit); char *buf; u_int32 readsiz; errval errno; buf = malloc(1024, M_TEMP, M_NOWAIT); if (!buf) return (ENOMEM); if (( errno = st_mode_sense(unit, 0, NULL, 0, 0)) ) { goto bad; } st->blksiz = 1024; do { switch ((int)st->blksiz) { case 512: case 1024: readsiz = st->blksiz; st->flags |= ST_FIXEDBLOCKS; break; default: readsiz = 1; st->flags &= ~ST_FIXEDBLOCKS; } if ( (errno = st_mode_select(unit, 0, NULL, 0)) ) { goto bad; } st_read(unit, buf, readsiz, SCSI_SILENT); if ( (errno = st_rewind(unit, FALSE, 0)) ) { bad: free(buf, M_TEMP); return (errno); } } while (readsiz != 1 && readsiz > st->blksiz); free(buf, M_TEMP); return 0; } static st_devsw_installed = 0; static void st_drvinit(void *unused) { dev_t dev; if( ! st_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&st_cdevsw, NULL); dev = makedev(BDEV_MAJOR, 0); bdevsw_add(&dev,&st_bdevsw, NULL); st_devsw_installed = 1; } } SYSINIT(stdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,st_drvinit,NULL)