#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: st_scsi.c,v 1.37 2015/08/24 23:13:15 pooka Exp $");
#ifdef _KERNEL_OPT
#include "opt_scsi.h"
#endif
#include <sys/param.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsi_tape.h>
#include <dev/scsipi/stvar.h>
static int st_scsibus_match(device_t, cfdata_t, void *);
static void st_scsibus_attach(device_t, device_t, void *);
static int st_scsibus_ops(struct st_softc *, int, int);
static int st_scsibus_read_block_limits(struct st_softc *, int);
static int st_scsibus_mode_sense(struct st_softc *, int);
static int st_scsibus_cmprss(struct st_softc *, int, int);
CFATTACH_DECL_NEW(
st_scsibus,
sizeof(struct st_softc),
st_scsibus_match,
st_scsibus_attach,
stdetach,
NULL
);
static const struct scsipi_inquiry_pattern st_scsibus_patterns[] = {
{T_SEQUENTIAL, T_REMOV,
"", "", ""},
};
static int
st_scsibus_match(device_t parent, cfdata_t match, void *aux)
{
struct scsipibus_attach_args *sa = aux;
int priority;
if (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(sa->sa_periph)) !=
SCSIPI_BUSTYPE_SCSI)
return 0;
(void)scsipi_inqmatch(&sa->sa_inqbuf,
st_scsibus_patterns,
sizeof(st_scsibus_patterns)/sizeof(st_scsibus_patterns[0]),
sizeof(st_scsibus_patterns[0]), &priority);
return priority;
}
static void
st_scsibus_attach(device_t parent, device_t self, void *aux)
{
struct st_softc *st = device_private(self);
st->ops = st_scsibus_ops;
stattach(parent, self, aux);
}
static int
st_scsibus_ops(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_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;
}
}
static int
st_scsibus_read_block_limits(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;
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = READ_BLOCK_LIMITS;
error = scsipi_command(periph, (void *)&cmd, sizeof(cmd),
(void *)&block_limits, sizeof(block_limits),
ST_RETRIES, ST_CTL_TIME, NULL, flags | XS_CTL_DATA_IN);
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;
}
static int
st_scsibus_mode_sense(struct st_softc *st, int flags)
{
u_int scsipi_sense_len;
int error;
struct scsipi_sense {
struct scsi_mode_parameter_header_6 header;
struct scsi_general_block_descriptor blk_desc;
u_char sense_data[MAX_PAGE_0_SIZE];
} scsipi_sense;
struct scsipi_periph *periph = st->sc_periph;
scsipi_sense_len = sizeof(scsipi_sense.header) +
sizeof(scsipi_sense.blk_desc) +
st->page_0_size;
error = scsipi_mode_sense(st->sc_periph, 0, SMS_PCTRL_CURRENT,
&scsipi_sense.header, scsipi_sense_len, flags,
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)
memcpy(st->sense_data, scsipi_sense.sense_data,
st->page_0_size);
periph->periph_flags |= PERIPH_MEDIA_LOADED;
return 0;
}
static int
st_scsibus_cmprss(struct st_softc *st, int flags, int onoff)
{
u_int scsi_dlen;
int byte2, page;
struct scsi_select {
struct scsi_mode_parameter_header_6 header;
struct scsi_general_block_descriptor 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);
page = SMS_PCTRL_CURRENT | 0xf;
byte2 = 0;
again:
memset(&scsi_pdata, 0, scsi_dlen);
error = scsipi_mode_sense(periph, byte2, page,
&scsi_pdata.header, scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME);
if (error) {
if (byte2 != SMS_DBD) {
byte2 = SMS_DBD;
goto again;
}
if (page == (SMS_PCTRL_CURRENT | 0xf)) {
page = SMS_PCTRL_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_MASK) == 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;
}
if (onoff == ison)
return 0;
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;
}
error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header,
scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME);
if (error && (page & SMS_PAGE_MASK) == 0xf) {
page = SMS_PCTRL_CURRENT | 0x10;
goto again;
}
return error;
}