[libata scsi] MODE SELECT, strengthen mode sense
- move default mode pages to the front of libata-scsi.c so various functions can access them - partial annotation of these pages, point out divergence from sat-r06 - replace various mode page magic numbers with defines - strengthen MODE SENSE command decoding: handle DBD bit in cdb, yield block descriptor (per sat-r06) and handle mode sub pages Signed-off-by: Douglas Gilbert <dougg@torque.net> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
parent
a21a84a375
commit
00ac37f508
1 changed files with 103 additions and 41 deletions
|
@ -51,6 +51,45 @@ typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scs
|
|||
static struct ata_device *
|
||||
ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev);
|
||||
|
||||
#define RW_RECOVERY_MPAGE 0x1
|
||||
#define RW_RECOVERY_MPAGE_LEN 12
|
||||
#define CACHE_MPAGE 0x8
|
||||
#define CACHE_MPAGE_LEN 20
|
||||
#define CONTROL_MPAGE 0xa
|
||||
#define CONTROL_MPAGE_LEN 12
|
||||
#define ALL_MPAGES 0x3f
|
||||
#define ALL_SUB_MPAGES 0xff
|
||||
|
||||
|
||||
static const u8 def_rw_recovery_mpage[] = {
|
||||
RW_RECOVERY_MPAGE,
|
||||
RW_RECOVERY_MPAGE_LEN - 2,
|
||||
(1 << 7) | /* AWRE, sat-r06 say it shall be 0 */
|
||||
(1 << 6), /* ARRE (auto read reallocation) */
|
||||
0, /* read retry count */
|
||||
0, 0, 0, 0,
|
||||
0, /* write retry count */
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
static const u8 def_cache_mpage[CACHE_MPAGE_LEN] = {
|
||||
CACHE_MPAGE,
|
||||
CACHE_MPAGE_LEN - 2,
|
||||
0, /* contains WCE, needs to be 0 for logic */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, /* contains DRA, needs to be 0 for logic */
|
||||
0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
|
||||
CONTROL_MPAGE,
|
||||
CONTROL_MPAGE_LEN - 2,
|
||||
2, /* DSENSE=0, GLTSD=1 */
|
||||
0, /* [QAM+QERR may be 1, see 05-359r1] */
|
||||
0, 0, 0, 0, 0xff, 0xff,
|
||||
0, 30 /* extended self test time, see 05-359r1 */
|
||||
};
|
||||
|
||||
|
||||
static void ata_scsi_invalid_field(struct scsi_cmnd *cmd,
|
||||
void (*done)(struct scsi_cmnd *))
|
||||
|
@ -1583,13 +1622,9 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last,
|
|||
static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
|
||||
const u8 *last)
|
||||
{
|
||||
u8 page[] = {
|
||||
0x8, /* page code */
|
||||
0x12, /* page length */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 zeroes */
|
||||
0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */
|
||||
};
|
||||
u8 page[CACHE_MPAGE_LEN];
|
||||
|
||||
memcpy(page, def_cache_mpage, sizeof(page));
|
||||
if (ata_id_wcache_enabled(id))
|
||||
page[2] |= (1 << 2); /* write cache enable */
|
||||
if (!ata_id_rahead_enabled(id))
|
||||
|
@ -1613,15 +1648,9 @@ static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
|
|||
|
||||
static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
|
||||
{
|
||||
const u8 page[] = {0xa, 0xa, 6, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30};
|
||||
|
||||
/* byte 2: set the descriptor format sense data bit (bit 2)
|
||||
* since we need to support returning this format for SAT
|
||||
* commands and any SCSI commands against a 48b LBA device.
|
||||
*/
|
||||
|
||||
ata_msense_push(ptr_io, last, page, sizeof(page));
|
||||
return sizeof(page);
|
||||
ata_msense_push(ptr_io, last, def_control_mpage,
|
||||
sizeof(def_control_mpage));
|
||||
return sizeof(def_control_mpage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1638,15 +1667,10 @@ static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
|
|||
|
||||
static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
|
||||
{
|
||||
const u8 page[] = {
|
||||
0x1, /* page code */
|
||||
0xa, /* page length */
|
||||
(1 << 7) | (1 << 6), /* note auto r/w reallocation */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */
|
||||
};
|
||||
|
||||
ata_msense_push(ptr_io, last, page, sizeof(page));
|
||||
return sizeof(page);
|
||||
ata_msense_push(ptr_io, last, def_rw_recovery_mpage,
|
||||
sizeof(def_rw_recovery_mpage));
|
||||
return sizeof(def_rw_recovery_mpage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1655,7 +1679,9 @@ static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
|
|||
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
|
||||
* @buflen: Response buffer length.
|
||||
*
|
||||
* Simulate MODE SENSE commands.
|
||||
* Simulate MODE SENSE commands. Assume this is invoked for direct
|
||||
* access devices (e.g. disks) only. There should be no block
|
||||
* descriptor for other device types.
|
||||
*
|
||||
* LOCKING:
|
||||
* spin_lock_irqsave(host_set lock)
|
||||
|
@ -1665,15 +1691,22 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
|
|||
unsigned int buflen)
|
||||
{
|
||||
u8 *scsicmd = args->cmd->cmnd, *p, *last;
|
||||
unsigned int page_control, six_byte, output_len;
|
||||
const u8 sat_blk_desc[] = {
|
||||
0, 0, 0, 0, /* number of blocks: sat unspecified */
|
||||
0,
|
||||
0, 0x2, 0x0 /* block length: 512 bytes */
|
||||
};
|
||||
u8 pg, spg;
|
||||
unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen;
|
||||
|
||||
VPRINTK("ENTER\n");
|
||||
|
||||
six_byte = (scsicmd[0] == MODE_SENSE);
|
||||
|
||||
/* we only support saved and current values (which we treat
|
||||
* in the same manner)
|
||||
ebd = !(scsicmd[1] & 0x8); /* dbd bit inverted == edb */
|
||||
/*
|
||||
* LLBA bit in msense(10) ignored (compliant)
|
||||
*/
|
||||
|
||||
page_control = scsicmd[2] >> 6;
|
||||
switch (page_control) {
|
||||
case 0: /* current */
|
||||
|
@ -1686,29 +1719,42 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
|
|||
goto invalid_fld;
|
||||
}
|
||||
|
||||
if (six_byte)
|
||||
output_len = 4;
|
||||
else
|
||||
output_len = 8;
|
||||
if (six_byte) {
|
||||
output_len = 4 + (ebd ? 8 : 0);
|
||||
alloc_len = scsicmd[4];
|
||||
} else {
|
||||
output_len = 8 + (ebd ? 8 : 0);
|
||||
alloc_len = (scsicmd[7] << 8) + scsicmd[8];
|
||||
}
|
||||
minlen = (alloc_len < buflen) ? alloc_len : buflen;
|
||||
|
||||
p = rbuf + output_len;
|
||||
last = rbuf + buflen - 1;
|
||||
last = rbuf + minlen - 1;
|
||||
|
||||
switch(scsicmd[2] & 0x3f) {
|
||||
case 0x01: /* r/w error recovery */
|
||||
pg = scsicmd[2] & 0x3f;
|
||||
spg = scsicmd[3];
|
||||
/*
|
||||
* No mode subpages supported (yet) but asking for _all_
|
||||
* subpages may be valid
|
||||
*/
|
||||
if (spg && (spg != ALL_SUB_MPAGES))
|
||||
goto invalid_fld;
|
||||
|
||||
switch(pg) {
|
||||
case RW_RECOVERY_MPAGE:
|
||||
output_len += ata_msense_rw_recovery(&p, last);
|
||||
break;
|
||||
|
||||
case 0x08: /* caching */
|
||||
case CACHE_MPAGE:
|
||||
output_len += ata_msense_caching(args->id, &p, last);
|
||||
break;
|
||||
|
||||
case 0x0a: { /* control mode */
|
||||
case CONTROL_MPAGE: {
|
||||
output_len += ata_msense_ctl_mode(&p, last);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x3f: /* all pages */
|
||||
case ALL_MPAGES:
|
||||
output_len += ata_msense_rw_recovery(&p, last);
|
||||
output_len += ata_msense_caching(args->id, &p, last);
|
||||
output_len += ata_msense_ctl_mode(&p, last);
|
||||
|
@ -1718,15 +1764,31 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
|
|||
goto invalid_fld;
|
||||
}
|
||||
|
||||
if (minlen < 1)
|
||||
return 0;
|
||||
if (six_byte) {
|
||||
output_len--;
|
||||
rbuf[0] = output_len;
|
||||
if (ebd) {
|
||||
if (minlen > 3)
|
||||
rbuf[3] = sizeof(sat_blk_desc);
|
||||
if (minlen > 11)
|
||||
memcpy(rbuf + 4, sat_blk_desc,
|
||||
sizeof(sat_blk_desc));
|
||||
}
|
||||
} else {
|
||||
output_len -= 2;
|
||||
rbuf[0] = output_len >> 8;
|
||||
if (minlen > 1)
|
||||
rbuf[1] = output_len;
|
||||
if (ebd) {
|
||||
if (minlen > 7)
|
||||
rbuf[7] = sizeof(sat_blk_desc);
|
||||
if (minlen > 15)
|
||||
memcpy(rbuf + 8, sat_blk_desc,
|
||||
sizeof(sat_blk_desc));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
invalid_fld:
|
||||
|
@ -2156,7 +2218,7 @@ ata_scsi_map_proto(u8 byte1)
|
|||
* Zero on success, non-zero on failure.
|
||||
*/
|
||||
static unsigned int
|
||||
ata_scsi_pass_thru(struct ata_queued_cmd *qc, u8 *scsicmd)
|
||||
ata_scsi_pass_thru(struct ata_queued_cmd *qc, const u8 *scsicmd)
|
||||
{
|
||||
struct ata_taskfile *tf = &(qc->tf);
|
||||
struct scsi_cmnd *cmd = qc->scsicmd;
|
||||
|
|
Loading…
Reference in a new issue