From 0a63f01fef2f8a410486341d313995ae0a85d513 Mon Sep 17 00:00:00 2001 From: bouyer Date: Mon, 18 Jun 2001 09:05:05 +0000 Subject: [PATCH] Snapshot of ATAPI tapes support. Known to be able to read/write to tape with: st0 at atapibus0 drive 1: type 1 sequential removable Major changes may still happen in order to properly support other ATAPI tape drives. --- sys/dev/scsipi/atapi_tape.h | 122 ++++++++++++ sys/dev/scsipi/st.c | 363 ++++-------------------------------- sys/dev/scsipi/st_atapi.c | 109 ++++++++++- sys/dev/scsipi/st_scsi.c | 326 +++++++++++++++++++++++++++++++- sys/dev/scsipi/stvar.h | 42 ++++- 5 files changed, 626 insertions(+), 336 deletions(-) create mode 100644 sys/dev/scsipi/atapi_tape.h diff --git a/sys/dev/scsipi/atapi_tape.h b/sys/dev/scsipi/atapi_tape.h new file mode 100644 index 000000000000..2e6458a33654 --- /dev/null +++ b/sys/dev/scsipi/atapi_tape.h @@ -0,0 +1,122 @@ +/* $NetBSD: atapi_tape.h,v 1.1 2001/06/18 09:05:05 bouyer Exp $ */ + + +/*- + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/ata/atapi-tape.h,v 1.15 2001/03/14 12:05:44 sos Exp $ + */ + +/* defines for the device specific byte in the mode select/sense header */ +#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 + +/* ATAPI tape drive Capabilities and Mechanical Status Page */ +struct atapi_cappage { + /* mode page data header */ + struct scsipi_mode_header header; + /* capabilities page */ + u_int8_t page_code; +#define ATAPI_TAPE_CAP_PAGE 0x2a + u_int8_t page_length; /* page Length == 0x12 */ + u_int8_t reserved2; + u_int8_t reserved3; + u_int8_t cap1; +#define ATAPI_TAPE_CAP_PAGE_RO 0x01; /* read Only Mode */ +#define ATAPI_TAPE_CAP_PAGE_REV 0x20; /* supports reverse direction */ +#define ATAPI_TAPE_CAP_PAGE_ + u_int8_t cap2; +#define ATAPI_TAPE_CAP_PAGE_EF 0x08; /* supports ERASE formatting */ +#define ATAPI_TAPE_CAP_PAGE_QFA 0x20 /* supports QFA formats */ + u_int8_t cap3; +#define ATAPI_TAPE_CAP_PAGE_LCK 0x01 /* supports locking media */ +#define ATAPI_TAPE_CAP_PAGE_LCKED 0x02 /* the media is locked */ +#define ATAPI_TAPE_CAP_PAGE_PRV 0x04 /* defaults to prevent state */ +#define ATAPI_TAPE_CAP_PAGE_EJ 0x08 /* supports eject */ +#define ATAPI_TAPE_CAP_PAGE_DIS 0x10 /* can break request > ctl */ +#define ATAPI_TAPE_CAP_PAGE_ECC 0x40 /* supports error correction */ +#define ATAPI_TAPE_CAP_PAGE_CMP 0x80 /* supports data compression */ + u_int8_t cap4; +#define ATAPI_TAPE_CAP_PAGE_BLK512 0x02 /* supports 512b block size */ +#define ATAPI_TAPE_CAP_PAGE_BLK1K 0x04 /* supports 1024b block size */ +#define ATAPI_TAPE_CAP_PAGE_BLK32K 0x80 /* supports 32kb block size */ + u_int16_t max_speed; /* supported speed in KBps */ + u_int16_t max_defects; /* max stored defect entries */ + u_int16_t ctl; /* continuous transfer limit */ + u_int16_t speed; /* current Speed, in KBps */ + u_int16_t buffer_size; /* buffer Size, in 512 bytes */ + u_int8_t reserved18; + u_int8_t reserved19; +}; + +/* ATAPI OnStream ADR data transfer mode page (ADR unique) */ +struct ast_transferpage { + /* mode page data header */ + u_int8_t data_length; /* total length of data */ + u_int8_t medium_type; /* medium type (if any) */ + u_int8_t dsp; /* device specific parameter */ + u_int8_t blk_desc_len; /* block Descriptor Length */ + + /* data transfer page */ + u_int8_t page_code :6; +#define ATAPI_TAPE_TRANSFER_PAGE 0x30 + + u_int8_t reserved0_6 :1; + u_int8_t ps :1; /* parameters saveable */ + u_int8_t page_length; /* page Length == 0x02 */ + u_int8_t reserved2; + u_int8_t read32k :1; /* 32k blk size (data only) */ + u_int8_t read32k5 :1; /* 32.5k blk size (data&AUX) */ + u_int8_t reserved3_23 :2; + u_int8_t write32k :1; /* 32k blk size (data only) */ + u_int8_t write32k5 :1; /* 32.5k blk size (data&AUX) */ + u_int8_t reserved3_6 :1; + u_int8_t streaming :1; /* streaming mode enable */ +}; + +/* ATAPI OnStream ADR vendor identification mode page (ADR unique) */ +struct ast_identifypage { + /* mode page data header */ + u_int8_t data_length; /* total length of data */ + u_int8_t medium_type; /* medium type (if any) */ + u_int8_t dsp; /* device specific parameter */ + u_int8_t blk_desc_len; /* block Descriptor Length */ + + /* data transfer page */ + u_int8_t page_code :6; +#define ATAPI_TAPE_IDENTIFY_PAGE 0x36 + + u_int8_t reserved0_6 :1; + u_int8_t ps :1; /* parameters saveable */ + u_int8_t page_length; /* page Length == 0x06 */ + u_int8_t ident[4]; /* host id string */ + u_int8_t reserved6; + u_int8_t reserved7; +}; diff --git a/sys/dev/scsipi/st.c b/sys/dev/scsipi/st.c index bb09cecac613..60a079283994 100644 --- a/sys/dev/scsipi/st.c +++ b/sys/dev/scsipi/st.c @@ -1,4 +1,4 @@ -/* $NetBSD: st.c,v 1.140 2001/05/30 20:28:17 bouyer Exp $ */ +/* $NetBSD: st.c,v 1.141 2001/06/18 09:05:05 bouyer Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -79,7 +79,6 @@ /* Defines for device specific stuff */ #define DEF_FIXED_BSIZE 512 -#define ST_RETRIES 4 /* only on non IO commands */ #define STMODE(z) ( minor(z) & 0x03) #define STDSTY(z) ((minor(z) >> 2) & 0x03) @@ -93,10 +92,6 @@ #define FALSE 0 #define TRUE 1 -#define ST_IO_TIME (3 * 60 * 1000) /* 3 minutes */ -#define ST_CTL_TIME (30 * 1000) /* 30 seconds */ -#define ST_SPC_TIME (4 * 60 * 60 * 1000) /* 4 hours */ - #ifndef ST_MOUNT_DELAY #define ST_MOUNT_DELAY 0 #endif @@ -279,6 +274,13 @@ const struct st_quirk_inquiry_pattern st_quirk_patterns[] = { {0, 0, 0}, /* minor 8-11 */ {0, 0, 0} /* minor 12-15 */ }}}, + {{T_SEQUENTIAL, T_REMOV, + "OnStream DI-30", "", "1.0"}, {ST_Q_IGNORE_LOADS, 0, { + {0, 0, 0}, /* minor 0-3 */ + {0, 0, 0}, /* minor 4-7 */ + {0, 0, 0}, /* minor 8-11 */ + {0, 0, 0} /* minor 12-15 */ + }}}, }; #define NOEJECT 0 @@ -293,10 +295,6 @@ int st_decide_mode __P((struct st_softc *, boolean)); void ststart __P((struct scsipi_periph *)); void stdone __P((struct scsipi_xfer *)); int st_read __P((struct st_softc *, char *, int, int)); -int st_read_block_limits __P((struct st_softc *, int)); -int st_mode_sense __P((struct st_softc *, int)); -int st_mode_select __P((struct st_softc *, int)); -int st_cmprss __P((struct st_softc *, int)); int st_space __P((struct st_softc *, int, u_int, int)); int st_write_filemarks __P((struct st_softc *, int, int)); int st_check_eod __P((struct st_softc *, boolean, int *, int)); @@ -315,32 +313,6 @@ const struct scsipi_periphsw st_switch = { stdone }; -#define ST_INFO_VALID 0x0001 -#define ST_BLOCK_SET 0x0002 /* block size, mode set by ioctl */ -#define ST_WRITTEN 0x0004 /* data has been written, EOD needed */ -#define ST_FIXEDBLOCKS 0x0008 -#define ST_AT_FILEMARK 0x0010 -#define ST_EIO_PENDING 0x0020 /* error reporting deferred until next op */ -#define ST_NEW_MOUNT 0x0040 /* still need to decide mode */ -#define ST_READONLY 0x0080 /* st_mode_sense says write protected */ -#define ST_FM_WRITTEN 0x0100 /* - * EOF file mark written -- used with - * ~ST_WRITTEN to indicate that multiple file - * marks have been written - */ -#define ST_BLANK_READ 0x0200 /* BLANK CHECK encountered already */ -#define ST_2FM_AT_EOD 0x0400 /* write 2 file marks at EOD */ -#define ST_MOUNTED 0x0800 /* Device is presently mounted */ -#define ST_DONTBUFFER 0x1000 /* Disable buffering/caching */ -#define ST_EARLYWARN 0x2000 /* Do (deferred) EOM for variable mode */ -#define ST_EOM_PENDING 0x4000 /* EOM reporting deferred until next op */ - -#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_EOM_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) - #if defined(ST_ENABLE_EARLYWARN) #define ST_INIT_FLAGS ST_EARLYWARN #else @@ -352,11 +324,11 @@ const struct scsipi_periphsw st_switch = { * A device suitable for this driver */ void -stattach(parent, self, aux) - struct device *parent, *self; +stattach(parent, st, aux) + struct device *parent; + struct st_softc *st; void *aux; { - struct st_softc *st = (void *)self; struct scsipibus_attach_args *sa = aux; struct scsipi_periph *periph = sa->sa_periph; @@ -387,7 +359,7 @@ stattach(parent, self, aux) printf("%s: %s", st->sc_dev.dv_xname, st->quirkdata ? "rogue, " : ""); if (scsipi_test_unit_ready(periph, XS_CTL_DISCOVERY | XS_CTL_SILENT | XS_CTL_IGNORE_MEDIA_CHANGE) || - st_mode_sense(st, + st->ops(st, ST_OPS_MODESENSE, XS_CTL_DISCOVERY | XS_CTL_SILENT | XS_CTL_IGNORE_MEDIA_CHANGE)) printf("drive empty\n"); else { @@ -800,7 +772,7 @@ st_mount_tape(dev, flags) * Load the physical device parameters * loads: blkmin, blkmax */ - if ((error = st_read_block_limits(st, 0)) != 0) + if ((error = st->ops(st, ST_OPS_RBL, 0)) != 0) return (error); /* * Load the media dependent parameters @@ -808,7 +780,7 @@ st_mount_tape(dev, flags) * As we have a tape in, it should be reflected here. * If not you may need the "quirk" above. */ - if ((error = st_mode_sense(st, 0)) != 0) + if ((error = st->ops(st, ST_OPS_MODESENSE, 0)) != 0) return (error); /* * If we have gained a permanent density from somewhere, @@ -834,9 +806,13 @@ st_mount_tape(dev, flags) if ((error = st_decide_mode(st, FALSE)) != 0) return (error); } - if ((error = st_mode_select(st, 0)) != 0) { - printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname); - return (error); + if ((error = st->ops(st, ST_OPS_MODESELECT, 0)) != 0) { + /* ATAPI will return ENODEV for this, and this may be OK */ + if (error != ENODEV) { + printf("%s: cannot set selected mode\n", + st->sc_dev.dv_xname); + return (error); + } } if (!(st->quirks & ST_Q_NOPREVENT)) { scsipi_prevent(periph, PR_PREVENT, @@ -880,7 +856,7 @@ st_unmount(st, eject) * in st_touch_tape(). */ st->density = 0; - if (st_mode_select(st, 0) != 0) { + if (st->ops(st, ST_OPS_MODESELECT, 0) != 0) { printf("%s: WARNING: cannot revert to default density\n", st->sc_dev.dv_xname); } @@ -1298,7 +1274,7 @@ stioctl(dev, cmd, arg, flag, p) /* * (to get the current state of READONLY) */ - error = st_mode_sense(st, XS_CTL_SILENT); + error = st->ops(st, ST_OPS_MODESENSE, XS_CTL_SILENT); if (error) { /* * Ignore the error if in control mode; @@ -1426,7 +1402,9 @@ stioctl(dev, cmd, arg, flag, p) goto try_new_value; case MTCMPRESS: - error = st_cmprss(st, number); + error = st->ops(st, (number == 0) ? + ST_OPS_CMPRSS_OFF : ST_OPS_CMPRSS_ON, + XS_CTL_SILENT); break; case MTEWARN: @@ -1478,7 +1456,7 @@ try_new_value: * even if no medium is loaded (see st(4)). */ if ((STMODE(dev) != CTRL_MODE || (st->flags & ST_MOUNTED) != 0) && - (error = st_mode_select(st, 0)) != 0) { + (error = st->ops(st, ST_OPS_MODESELECT, 0)) != 0) { /* put it back as it was */ printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname); st->density = hold_density; @@ -1542,282 +1520,6 @@ st_read(st, buf, size, flags) (u_char *)buf, size, 0, ST_IO_TIME, NULL, flags | XS_CTL_DATA_IN)); } -/* - * Ask the drive what it's min and max blk sizes are. - */ -int -st_read_block_limits(st, flags) - struct st_softc *st; - int flags; -{ - struct scsi_block_limits cmd; - struct scsi_block_limits_data block_limits; - struct scsipi_periph *periph = st->sc_periph; - int error; - - /* - * do a 'Read Block Limits' - */ - bzero(&cmd, sizeof(cmd)); - cmd.opcode = READ_BLOCK_LIMITS; - - /* - * do the command, update the global values - */ - error = scsipi_command(periph, (struct scsipi_generic *)&cmd, - sizeof(cmd), (u_char *)&block_limits, sizeof(block_limits), - ST_RETRIES, ST_CTL_TIME, NULL, - flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK); - if (error) - return (error); - - st->blkmin = _2btol(block_limits.min_length); - st->blkmax = _3btol(block_limits.max_length); - - SC_DEBUG(periph, SCSIPI_DB3, - ("(%d <= blksize <= %d)\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) - */ -int -st_mode_sense(st, flags) - struct st_softc *st; - int flags; -{ - u_int scsipi_sense_len; - int error; - struct scsipi_sense { - struct scsipi_mode_header header; - struct scsi_blk_desc blk_desc; - u_char sense_data[MAX_PAGE_0_SIZE]; - } scsipi_sense; - struct scsipi_periph *periph = st->sc_periph; - - scsipi_sense_len = 12 + st->page_0_size; - - /* - * Set up a mode sense - * We don't need the results. Just print them for our interest's sake, - * if asked, or if we need it as a template for the mode select store - * it away. - */ - error = scsipi_mode_sense(st->sc_periph, 0, SMS_PAGE_CTRL_CURRENT, - &scsipi_sense.header, scsipi_sense_len, flags | XS_CTL_DATA_ONSTACK, - ST_RETRIES, ST_CTL_TIME); - - if (error) - return (error); - - st->numblks = _3btol(scsipi_sense.blk_desc.nblocks); - st->media_blksize = _3btol(scsipi_sense.blk_desc.blklen); - st->media_density = scsipi_sense.blk_desc.density; - if (scsipi_sense.header.dev_spec & SMH_DSP_WRITE_PROT) - st->flags |= ST_READONLY; - else - st->flags &= ~ST_READONLY; - SC_DEBUG(periph, SCSIPI_DB3, - ("density code %d, %d-byte blocks, write-%s, ", - st->media_density, st->media_blksize, - st->flags & ST_READONLY ? "protected" : "enabled")); - SC_DEBUG(periph, SCSIPI_DB3, - ("%sbuffered\n", - scsipi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un")); - if (st->page_0_size) - bcopy(scsipi_sense.sense_data, st->sense_data, - st->page_0_size); - periph->periph_flags |= PERIPH_MEDIA_LOADED; - return (0); -} - -/* - * Send a filled out parameter structure to the drive to - * set it into the desire modes etc. - */ -int -st_mode_select(st, flags) - struct st_softc *st; - int flags; -{ - u_int scsi_select_len; - struct scsi_select { - struct scsipi_mode_header header; - struct scsi_blk_desc blk_desc; - u_char sense_data[MAX_PAGE_0_SIZE]; - } scsi_select; - struct scsipi_periph *periph = st->sc_periph; - - scsi_select_len = 12 + st->page_0_size; - - /* - * This quirk deals with drives that have only one valid mode - * and think this gives them license to reject all mode selects, - * even if the selected mode is the one that is supported. - */ - if (st->quirks & ST_Q_UNIMODAL) { - SC_DEBUG(periph, SCSIPI_DB3, - ("not setting density 0x%x blksize 0x%x\n", - st->density, st->blksize)); - return (0); - } - - /* - * Set up for a mode select - */ - bzero(&scsi_select, scsi_select_len); - scsi_select.header.blk_desc_len = sizeof(struct scsi_blk_desc); - scsi_select.header.dev_spec &= ~SMH_DSP_BUFF_MODE; - scsi_select.blk_desc.density = st->density; - if (st->flags & ST_DONTBUFFER) - scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_OFF; - else - scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON; - if (st->flags & ST_FIXEDBLOCKS) - _lto3b(st->blksize, scsi_select.blk_desc.blklen); - if (st->page_0_size) - bcopy(st->sense_data, scsi_select.sense_data, st->page_0_size); - - /* - * do the command - */ - return scsipi_mode_select(periph, 0, &scsi_select.header, - scsi_select_len, flags | XS_CTL_DATA_ONSTACK, - ST_RETRIES, ST_CTL_TIME); -} - -int -st_cmprss(st, onoff) - struct st_softc *st; - int onoff; -{ - u_int scsi_dlen; - int byte2, page; - struct scsi_select { - struct scsipi_mode_header header; - struct scsi_blk_desc blk_desc; - u_char pdata[MAX(sizeof(struct scsi_tape_dev_conf_page), - sizeof(struct scsi_tape_dev_compression_page))]; - } scsi_pdata; - struct scsi_tape_dev_conf_page *ptr; - struct scsi_tape_dev_compression_page *cptr; - struct scsipi_periph *periph = st->sc_periph; - int error, ison, flags = 0; - - scsi_dlen = sizeof(scsi_pdata); - - /* - * Do DATA COMPRESSION page first. - */ - page = SMS_PAGE_CTRL_CURRENT | 0xf; - byte2 = 0; - - /* - * Do the MODE SENSE command... - */ -again: - bzero(&scsi_pdata, scsi_dlen); - error = scsipi_mode_sense(periph, byte2, page, - &scsi_pdata.header, scsi_dlen, flags | XS_CTL_DATA_ONSTACK, - ST_RETRIES, ST_CTL_TIME); - - if (error) { - if (byte2 != SMS_DBD) { - byte2 = SMS_DBD; - goto again; - } - /* - * Try a different page? - */ - if (page == (SMS_PAGE_CTRL_CURRENT | 0xf)) { - page = SMS_PAGE_CTRL_CURRENT | 0x10; - byte2 = 0; - goto again; - } - return (error); - } - - if (scsi_pdata.header.blk_desc_len) - ptr = (struct scsi_tape_dev_conf_page *) scsi_pdata.pdata; - else - ptr = (struct scsi_tape_dev_conf_page *) &scsi_pdata.blk_desc; - - if ((page & SMS_PAGE_CODE) == 0xf) { - cptr = (struct scsi_tape_dev_compression_page *) ptr; - ison = (cptr->dce_dcc & DCP_DCE) != 0; - if (onoff) - cptr->dce_dcc |= DCP_DCE; - else - cptr->dce_dcc &= ~DCP_DCE; - cptr->pagecode &= ~0x80; - } else { - ison = (ptr->sel_comp_alg != 0); - if (onoff) - ptr->sel_comp_alg = 1; - else - ptr->sel_comp_alg = 0; - ptr->pagecode &= ~0x80; - ptr->byte2 = 0; - ptr->active_partition = 0; - ptr->wb_full_ratio = 0; - ptr->rb_empty_ratio = 0; - ptr->byte8 &= ~0x30; - ptr->gap_size = 0; - ptr->byte10 &= ~0xe7; - ptr->ew_bufsize[0] = 0; - ptr->ew_bufsize[1] = 0; - ptr->ew_bufsize[2] = 0; - ptr->reserved = 0; - } - onoff = onoff ? 1 : 0; - /* - * There might be a virtue in actually doing the MODE SELECTS, - * but let's not clog the bus over it. - */ - if (onoff == ison) - return (0); - - /* - * Set up for a mode select - */ - - scsi_pdata.header.data_length = 0; - scsi_pdata.header.medium_type = 0; - if ((st->flags & ST_DONTBUFFER) == 0) - scsi_pdata.header.dev_spec = SMH_DSP_BUFF_MODE_ON; - else - scsi_pdata.header.dev_spec = 0; - - if (scsi_pdata.header.blk_desc_len) { - scsi_pdata.blk_desc.density = 0; - scsi_pdata.blk_desc.nblocks[0] = 0; - scsi_pdata.blk_desc.nblocks[1] = 0; - scsi_pdata.blk_desc.nblocks[2] = 0; - } - - /* - * Do the command - */ - error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header, - scsi_dlen, flags | XS_CTL_DATA_ONSTACK, ST_RETRIES, ST_CTL_TIME); - - if (error && (page & SMS_PAGE_CODE) == 0xf) { - /* - * Try DEVICE CONFIGURATION page. - */ - page = SMS_PAGE_CTRL_CURRENT | 0x10; - goto again; - } - return (error); -} /* * issue an erase command */ @@ -2085,6 +1787,12 @@ st_rewind(st, immediate, flags) } st->flags &= ~ST_PER_ACTION; + /* + * ATAPI tapes always need foo to be set + */ + if (scsipi_periph_bustype(st->sc_periph) == SCSIPI_BUSTYPE_ATAPI) + immediate = SR_IMMED; + bzero(&cmd, sizeof(cmd)); cmd.opcode = REWIND; cmd.byte2 = immediate; @@ -2426,7 +2134,7 @@ st_touch_tape(st) if (buf == NULL) return (ENOMEM); - if ((error = st_mode_sense(st, 0)) != 0) + if ((error = st->ops(st, ST_OPS_MODESENSE, 0)) != 0) goto bad; /* @@ -2449,7 +2157,8 @@ st_touch_tape(st) readsize = 1; st->flags &= ~ST_FIXEDBLOCKS; } - if ((error = st_mode_select(st, XS_CTL_SILENT)) != 0) { + if ((error = st->ops(st, ST_OPS_MODESELECT, XS_CTL_SILENT)) + != 0) { /* * The device did not agree with the proposed * block size. If we exhausted our options, diff --git a/sys/dev/scsipi/st_atapi.c b/sys/dev/scsipi/st_atapi.c index 31d4dc7a6ffd..c3f8cd13ab9f 100644 --- a/sys/dev/scsipi/st_atapi.c +++ b/sys/dev/scsipi/st_atapi.c @@ -1,4 +1,4 @@ -/* $NetBSD: st_atapi.c,v 1.1 2001/05/04 07:48:57 bouyer Exp $ */ +/* $NetBSD: st_atapi.c,v 1.2 2001/06/18 09:05:05 bouyer Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -17,8 +17,8 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. @@ -65,18 +65,25 @@ #include #include #include +#include #include +#include int st_atapibus_match __P((struct device *, struct cfdata *, void *)); +void st_atapibus_attach __P((struct device *, struct device *, void *)); +int st_atapibus_ops __P((struct st_softc *, int, int)); +int st_atapibus_mode_sense __P((struct st_softc *, int)); +int st_atapibus_mode_select __P((struct st_softc *, int)); +int st_atapibus_do_ms __P((struct st_softc *, int, void *, int, int)); struct cfattach st_atapibus_ca = { - sizeof(struct st_softc), st_atapibus_match, stattach + sizeof(struct st_softc), st_atapibus_match, st_atapibus_attach }; const struct scsipi_inquiry_pattern st_atapibus_patterns[] = { {T_SEQUENTIAL, T_REMOV, - "", "", ""}, + "", "", ""}, }; int @@ -88,9 +95,101 @@ st_atapibus_match(parent, match, aux) struct scsipibus_attach_args *sa = aux; int priority; + if (scsipi_periph_bustype(sa->sa_periph) != SCSIPI_BUSTYPE_ATAPI) + return (0); + (void)scsipi_inqmatch(&sa->sa_inqbuf, (caddr_t)st_atapibus_patterns, sizeof(st_atapibus_patterns)/sizeof(st_atapibus_patterns[0]), sizeof(st_atapibus_patterns[0]), &priority); return (priority); } + +void +st_atapibus_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct st_softc *st = (void *)self; + + st->ops = st_atapibus_ops; + stattach(parent, st, aux); +} + +int +st_atapibus_ops(st, op, flags) + struct st_softc *st; + int op; + int flags; +{ + switch(op) { + case ST_OPS_RBL: + /* done in mode_sense */ + return 0; + case ST_OPS_MODESENSE: + return st_atapibus_mode_sense(st, flags); + case ST_OPS_MODESELECT: + return st_atapibus_mode_select(st, flags); + case ST_OPS_CMPRSS_ON: + case ST_OPS_CMPRSS_OFF: + return ENODEV; + default: + panic("st_scsibus_ops: invalid op"); + /* NOTREACHED */ + } +} + +int +st_atapibus_mode_sense(st, flags) + struct st_softc *st; + int flags; +{ + int count, error; + struct atapi_cappage cappage; + struct scsipi_periph *periph = st->sc_periph; + + /* get drive capabilities, some drives needs this repeated */ + for (count = 0 ; count < 5 ; count++) { + error = scsipi_mode_sense(periph, SMS_DBD, + ATAPI_TAPE_CAP_PAGE, &cappage.header, sizeof(cappage), + flags | XS_CTL_DATA_ONSTACK, ST_RETRIES, ST_CTL_TIME); + if (error == 0) { + st->numblks = 0; /* unused anyway */ + if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK32K) + st->media_blksize = 32768; + else if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK1K) + st->media_blksize = 1024; + else if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK512) + st->media_blksize = 512; + else { + error = ENODEV; + continue; + } + st->blkmin = st->blkmax = st->media_blksize; + st->media_density = 0; + if (cappage.header.dev_spec & SMH_DSP_WRITE_PROT) + st->flags |= ST_READONLY; + else + st->flags &= ~ST_READONLY; + SC_DEBUG(periph, SCSIPI_DB3, + ("density code %d, %d-byte blocks, write-%s, ", + st->media_density, st->media_blksize, + st->flags & ST_READONLY ? "protected" : "enabled")); + SC_DEBUG(periph, SCSIPI_DB3, + ("%sbuffered\n", + cappage.header.dev_spec & SMH_DSP_BUFF_MODE ? + "" : "un")); + periph->periph_flags |= PERIPH_MEDIA_LOADED; + return 0; + } + } + return error; +} + +int +st_atapibus_mode_select(st, flags) + struct st_softc *st; + int flags; +{ + return ENODEV; /* for now ... */ +} diff --git a/sys/dev/scsipi/st_scsi.c b/sys/dev/scsipi/st_scsi.c index b1c1fe3ed189..c7f91157512a 100644 --- a/sys/dev/scsipi/st_scsi.c +++ b/sys/dev/scsipi/st_scsi.c @@ -1,4 +1,4 @@ -/* $NetBSD: st_scsi.c,v 1.1 2001/05/04 07:48:57 bouyer Exp $ */ +/* $NetBSD: st_scsi.c,v 1.2 2001/06/18 09:05:05 bouyer Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -65,13 +65,22 @@ #include #include #include +#include #include +#include +#include int st_scsibus_match __P((struct device *, struct cfdata *, void *)); +void st_scsibus_attach __P((struct device *, struct device *, void *)); +int st_scsibus_ops __P((struct st_softc *, int, int)); +int st_scsibus_read_block_limits __P((struct st_softc *, int)); +int st_scsibus_mode_sense __P((struct st_softc *, int)); +int st_scsibus_mode_select __P((struct st_softc *, int)); +int st_scsibus_cmprss __P((struct st_softc *, int, int)); struct cfattach st_scsibus_ca = { - sizeof(struct st_softc), st_scsibus_match, stattach + sizeof(struct st_softc), st_scsibus_match, st_scsibus_attach }; const struct scsipi_inquiry_pattern st_scsibus_patterns[] = { @@ -88,9 +97,322 @@ st_scsibus_match(parent, match, aux) struct scsipibus_attach_args *sa = aux; int priority; + if (scsipi_periph_bustype(sa->sa_periph) != SCSIPI_BUSTYPE_SCSI) + return (0); + (void)scsipi_inqmatch(&sa->sa_inqbuf, (caddr_t)st_scsibus_patterns, sizeof(st_scsibus_patterns)/sizeof(st_scsibus_patterns[0]), sizeof(st_scsibus_patterns[0]), &priority); return (priority); } + +void +st_scsibus_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct st_softc *st = (void *)self; + + st->ops = st_scsibus_ops; + stattach(parent, st, aux); +} + +int +st_scsibus_ops(st, op, flags) + struct st_softc *st; + int op; + int flags; +{ + switch(op) { + case ST_OPS_RBL: + return st_scsibus_read_block_limits(st, flags); + case ST_OPS_MODESENSE: + return st_scsibus_mode_sense(st, flags); + case ST_OPS_MODESELECT: + return st_scsibus_mode_select(st, flags); + case ST_OPS_CMPRSS_ON: + case ST_OPS_CMPRSS_OFF: + return st_scsibus_cmprss(st, flags, + (op == ST_OPS_CMPRSS_ON) ? 1 : 0); + default: + panic("st_scsibus_ops: invalid op"); + return 0; /* XXX to appease gcc */ + } + /* NOTREACHED */ +} + +/* + * Ask the drive what it's min and max blk sizes are. + */ +int +st_scsibus_read_block_limits(st, flags) + struct st_softc *st; + int flags; +{ + struct scsi_block_limits cmd; + struct scsi_block_limits_data block_limits; + struct scsipi_periph *periph = st->sc_periph; + int error; + + /* + * do a 'Read Block Limits' + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ_BLOCK_LIMITS; + + /* + * do the command, update the global values + */ + error = scsipi_command(periph, (struct scsipi_generic *)&cmd, + sizeof(cmd), (u_char *)&block_limits, sizeof(block_limits), + ST_RETRIES, ST_CTL_TIME, NULL, + flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK); + if (error) + return (error); + + st->blkmin = _2btol(block_limits.min_length); + st->blkmax = _3btol(block_limits.max_length); + + SC_DEBUG(periph, SCSIPI_DB3, + ("(%d <= blksize <= %d)\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) + */ +int +st_scsibus_mode_sense(st, flags) + struct st_softc *st; + int flags; +{ + u_int scsipi_sense_len; + int error; + struct scsipi_sense { + struct scsipi_mode_header header; + struct scsi_blk_desc blk_desc; + u_char sense_data[MAX_PAGE_0_SIZE]; + } scsipi_sense; + struct scsipi_periph *periph = st->sc_periph; + + scsipi_sense_len = 12 + st->page_0_size; + + /* + * Set up a mode sense + * We don't need the results. Just print them for our interest's sake, + * if asked, or if we need it as a template for the mode select store + * it away. + */ + error = scsipi_mode_sense(st->sc_periph, 0, SMS_PAGE_CTRL_CURRENT, + &scsipi_sense.header, scsipi_sense_len, flags | XS_CTL_DATA_ONSTACK, + ST_RETRIES, ST_CTL_TIME); + if (error) + return (error); + + st->numblks = _3btol(scsipi_sense.blk_desc.nblocks); + st->media_blksize = _3btol(scsipi_sense.blk_desc.blklen); + st->media_density = scsipi_sense.blk_desc.density; + if (scsipi_sense.header.dev_spec & SMH_DSP_WRITE_PROT) + st->flags |= ST_READONLY; + else + st->flags &= ~ST_READONLY; + SC_DEBUG(periph, SCSIPI_DB3, + ("density code %d, %d-byte blocks, write-%s, ", + st->media_density, st->media_blksize, + st->flags & ST_READONLY ? "protected" : "enabled")); + SC_DEBUG(periph, SCSIPI_DB3, + ("%sbuffered\n", + scsipi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un")); + if (st->page_0_size) + bcopy(scsipi_sense.sense_data, st->sense_data, + st->page_0_size); + periph->periph_flags |= PERIPH_MEDIA_LOADED; + return (0); +} + +/* + * Send a filled out parameter structure to the drive to + * set it into the desire modes etc. + */ +int +st_scsibus_mode_select(st, flags) + struct st_softc *st; + int flags; +{ + u_int scsi_select_len; + struct scsi_select { + struct scsipi_mode_header header; + struct scsi_blk_desc blk_desc; + u_char sense_data[MAX_PAGE_0_SIZE]; + } scsi_select; + struct scsipi_periph *periph = st->sc_periph; + + scsi_select_len = 12 + st->page_0_size; + + /* + * This quirk deals with drives that have only one valid mode + * and think this gives them license to reject all mode selects, + * even if the selected mode is the one that is supported. + */ + if (st->quirks & ST_Q_UNIMODAL) { + SC_DEBUG(periph, SCSIPI_DB3, + ("not setting density 0x%x blksize 0x%x\n", + st->density, st->blksize)); + return (0); + } + + /* + * Set up for a mode select + */ + bzero(&scsi_select, scsi_select_len); + scsi_select.header.blk_desc_len = sizeof(struct scsi_blk_desc); + scsi_select.header.dev_spec &= ~SMH_DSP_BUFF_MODE; + scsi_select.blk_desc.density = st->density; + if (st->flags & ST_DONTBUFFER) + scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_OFF; + else + scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON; + if (st->flags & ST_FIXEDBLOCKS) + _lto3b(st->blksize, scsi_select.blk_desc.blklen); + if (st->page_0_size) + bcopy(st->sense_data, scsi_select.sense_data, st->page_0_size); + + /* + * do the command + */ + return scsipi_mode_select(periph, 0, &scsi_select.header, + scsi_select_len, flags | XS_CTL_DATA_ONSTACK, + ST_RETRIES, ST_CTL_TIME); +} + +int +st_scsibus_cmprss(st, flags, onoff) + struct st_softc *st; + int flags; + int onoff; +{ + u_int scsi_dlen; + int byte2, page; + struct scsi_select { + struct scsipi_mode_header header; + struct scsi_blk_desc blk_desc; + u_char pdata[MAX(sizeof(struct scsi_tape_dev_conf_page), + sizeof(struct scsi_tape_dev_compression_page))]; + } scsi_pdata; + struct scsi_tape_dev_conf_page *ptr; + struct scsi_tape_dev_compression_page *cptr; + struct scsipi_periph *periph = st->sc_periph; + int error, ison; + + scsi_dlen = sizeof(scsi_pdata); + /* + * Do DATA COMPRESSION page first. + */ + page = SMS_PAGE_CTRL_CURRENT | 0xf; + byte2 = 0; + + /* + * Do the MODE SENSE command... + */ +again: + bzero(&scsi_pdata, scsi_dlen); + error = scsipi_mode_sense(periph, byte2, page, + &scsi_pdata.header, scsi_dlen, flags | XS_CTL_DATA_ONSTACK, + ST_RETRIES, ST_CTL_TIME); + + if (error) { + if (byte2 != SMS_DBD) { + byte2 = SMS_DBD; + goto again; + } + /* + * Try a different page? + */ + if (page == (SMS_PAGE_CTRL_CURRENT | 0xf)) { + page = SMS_PAGE_CTRL_CURRENT | 0x10; + byte2 = 0; + goto again; + } + return (error); + } + + if (scsi_pdata.header.blk_desc_len) + ptr = (struct scsi_tape_dev_conf_page *) scsi_pdata.pdata; + else + ptr = (struct scsi_tape_dev_conf_page *) &scsi_pdata.blk_desc; + + if ((page & SMS_PAGE_CODE) == 0xf) { + cptr = (struct scsi_tape_dev_compression_page *) ptr; + ison = (cptr->dce_dcc & DCP_DCE) != 0; + if (onoff) + cptr->dce_dcc |= DCP_DCE; + else + cptr->dce_dcc &= ~DCP_DCE; + cptr->pagecode &= ~0x80; + } else { + ison = (ptr->sel_comp_alg != 0); + if (onoff) + ptr->sel_comp_alg = 1; + else + ptr->sel_comp_alg = 0; + ptr->pagecode &= ~0x80; + ptr->byte2 = 0; + ptr->active_partition = 0; + ptr->wb_full_ratio = 0; + ptr->rb_empty_ratio = 0; + ptr->byte8 &= ~0x30; + ptr->gap_size = 0; + ptr->byte10 &= ~0xe7; + ptr->ew_bufsize[0] = 0; + ptr->ew_bufsize[1] = 0; + ptr->ew_bufsize[2] = 0; + ptr->reserved = 0; + } + /* + * There might be a virtue in actually doing the MODE SELECTS, + * but let's not clog the bus over it. + */ + if (onoff == ison) + return (0); + + /* + * Set up for a mode select + */ + + scsi_pdata.header.data_length = 0; + scsi_pdata.header.medium_type = 0; + if ((st->flags & ST_DONTBUFFER) == 0) + scsi_pdata.header.dev_spec = SMH_DSP_BUFF_MODE_ON; + else + scsi_pdata.header.dev_spec = 0; + + if (scsi_pdata.header.blk_desc_len) { + scsi_pdata.blk_desc.density = 0; + scsi_pdata.blk_desc.nblocks[0] = 0; + scsi_pdata.blk_desc.nblocks[1] = 0; + scsi_pdata.blk_desc.nblocks[2] = 0; + } + + /* + * Do the command + */ + error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header, + scsi_dlen, flags | XS_CTL_DATA_ONSTACK, ST_RETRIES, ST_CTL_TIME); + + if (error && (page & SMS_PAGE_CODE) == 0xf) { + /* + * Try DEVICE CONFIGURATION page. + */ + page = SMS_PAGE_CTRL_CURRENT | 0x10; + goto again; + } + return (error); +} diff --git a/sys/dev/scsipi/stvar.h b/sys/dev/scsipi/stvar.h index f45e05329aaf..6b2a2ae1f2e6 100644 --- a/sys/dev/scsipi/stvar.h +++ b/sys/dev/scsipi/stvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: stvar.h,v 1.1 2001/05/04 07:48:57 bouyer Exp $ */ +/* $NetBSD: stvar.h,v 1.2 2001/06/18 09:05:05 bouyer Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -64,6 +64,12 @@ #include #include +#define ST_IO_TIME (3 * 60 * 1000) /* 3 minutes */ +#define ST_CTL_TIME (30 * 1000) /* 30 seconds */ +#define ST_SPC_TIME (4 * 60 * 60 * 1000) /* 4 hours */ + +#define ST_RETRIES 4 /* only on non IO commands */ + struct modes { u_int quirks; /* same definitions as in quirkdata */ int blksize; @@ -91,6 +97,13 @@ struct st_quirk_inquiry_pattern { struct st_softc { struct device sc_dev; +/*--------------------callback to bus-specific code--------------------------*/ + int (*ops) __P((struct st_softc *, int, int)); +#define ST_OPS_RBL 0x00 /* read block limit */ +#define ST_OPS_MODESENSE 0x01 /* mode sense */ +#define ST_OPS_MODESELECT 0x02 /* mode select */ +#define ST_OPS_CMPRSS_ON 0x03 /* turn on compression */ +#define ST_OPS_CMPRSS_OFF 0x04 /* turn off compression */ /*--------------------present operating parameters, flags etc.---------------*/ int flags; /* see below */ u_int quirks; /* quirks for the open mode */ @@ -134,7 +147,32 @@ struct st_softc { #endif }; +#define ST_INFO_VALID 0x0001 +#define ST_BLOCK_SET 0x0002 /* block size, mode set by ioctl */ +#define ST_WRITTEN 0x0004 /* data has been written, EOD needed */ +#define ST_FIXEDBLOCKS 0x0008 +#define ST_AT_FILEMARK 0x0010 +#define ST_EIO_PENDING 0x0020 /* error reporting deferred until next op */ +#define ST_NEW_MOUNT 0x0040 /* still need to decide mode */ +#define ST_READONLY 0x0080 /* st_mode_sense says write protected */ +#define ST_FM_WRITTEN 0x0100 /* + * EOF file mark written -- used with + * ~ST_WRITTEN to indicate that multiple file + * marks have been written + */ +#define ST_BLANK_READ 0x0200 /* BLANK CHECK encountered already */ +#define ST_2FM_AT_EOD 0x0400 /* write 2 file marks at EOD */ +#define ST_MOUNTED 0x0800 /* Device is presently mounted */ +#define ST_DONTBUFFER 0x1000 /* Disable buffering/caching */ +#define ST_EARLYWARN 0x2000 /* Do (deferred) EOM for variable mode */ +#define ST_EOM_PENDING 0x4000 /* EOM reporting deferred until next op */ -void stattach __P((struct device *, struct device *, void *)); +#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_EOM_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) + +void stattach __P((struct device *, struct st_softc *, void *)); extern struct cfdriver st_cd;