Snapshot of ATAPI tapes support. Known to be able to read/write to tape with:

st0 at atapibus0 drive 1: <Seagate STT8000A, , 5.51> type 1 sequential
removable
Major changes may still happen in order to properly support other ATAPI
tape drives.
This commit is contained in:
bouyer 2001-06-18 09:05:05 +00:00
parent d135da5614
commit 0a63f01fef
5 changed files with 626 additions and 336 deletions

122
sys/dev/scsipi/atapi_tape.h Normal file
View File

@ -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;
};

View File

@ -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,

View File

@ -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 <sys/buf.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <dev/scsipi/stvar.h>
#include <dev/scsipi/atapi_tape.h>
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 ... */
}

View File

@ -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 <sys/buf.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <dev/scsipi/stvar.h>
#include <dev/scsipi/scsi_tape.h>
#include <dev/scsipi/scsi_all.h>
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);
}

View File

@ -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 <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#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;