[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:
committed by
Jeff Garzik
parent
a21a84a375
commit
00ac37f508
@@ -51,6 +51,45 @@ typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scs
|
|||||||
static struct ata_device *
|
static struct ata_device *
|
||||||
ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev);
|
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,
|
static void ata_scsi_invalid_field(struct scsi_cmnd *cmd,
|
||||||
void (*done)(struct scsi_cmnd *))
|
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,
|
static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
|
||||||
const u8 *last)
|
const u8 *last)
|
||||||
{
|
{
|
||||||
u8 page[] = {
|
u8 page[CACHE_MPAGE_LEN];
|
||||||
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 */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
memcpy(page, def_cache_mpage, sizeof(page));
|
||||||
if (ata_id_wcache_enabled(id))
|
if (ata_id_wcache_enabled(id))
|
||||||
page[2] |= (1 << 2); /* write cache enable */
|
page[2] |= (1 << 2); /* write cache enable */
|
||||||
if (!ata_id_rahead_enabled(id))
|
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)
|
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};
|
ata_msense_push(ptr_io, last, def_control_mpage,
|
||||||
|
sizeof(def_control_mpage));
|
||||||
/* byte 2: set the descriptor format sense data bit (bit 2)
|
return sizeof(def_control_mpage);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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)
|
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));
|
ata_msense_push(ptr_io, last, def_rw_recovery_mpage,
|
||||||
return sizeof(page);
|
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.
|
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
|
||||||
* @buflen: Response buffer length.
|
* @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:
|
* LOCKING:
|
||||||
* spin_lock_irqsave(host_set lock)
|
* 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)
|
unsigned int buflen)
|
||||||
{
|
{
|
||||||
u8 *scsicmd = args->cmd->cmnd, *p, *last;
|
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");
|
VPRINTK("ENTER\n");
|
||||||
|
|
||||||
six_byte = (scsicmd[0] == MODE_SENSE);
|
six_byte = (scsicmd[0] == MODE_SENSE);
|
||||||
|
ebd = !(scsicmd[1] & 0x8); /* dbd bit inverted == edb */
|
||||||
/* we only support saved and current values (which we treat
|
/*
|
||||||
* in the same manner)
|
* LLBA bit in msense(10) ignored (compliant)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
page_control = scsicmd[2] >> 6;
|
page_control = scsicmd[2] >> 6;
|
||||||
switch (page_control) {
|
switch (page_control) {
|
||||||
case 0: /* current */
|
case 0: /* current */
|
||||||
@@ -1686,29 +1719,42 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
|
|||||||
goto invalid_fld;
|
goto invalid_fld;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (six_byte)
|
if (six_byte) {
|
||||||
output_len = 4;
|
output_len = 4 + (ebd ? 8 : 0);
|
||||||
else
|
alloc_len = scsicmd[4];
|
||||||
output_len = 8;
|
} 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;
|
p = rbuf + output_len;
|
||||||
last = rbuf + buflen - 1;
|
last = rbuf + minlen - 1;
|
||||||
|
|
||||||
switch(scsicmd[2] & 0x3f) {
|
pg = scsicmd[2] & 0x3f;
|
||||||
case 0x01: /* r/w error recovery */
|
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);
|
output_len += ata_msense_rw_recovery(&p, last);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x08: /* caching */
|
case CACHE_MPAGE:
|
||||||
output_len += ata_msense_caching(args->id, &p, last);
|
output_len += ata_msense_caching(args->id, &p, last);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0a: { /* control mode */
|
case CONTROL_MPAGE: {
|
||||||
output_len += ata_msense_ctl_mode(&p, last);
|
output_len += ata_msense_ctl_mode(&p, last);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x3f: /* all pages */
|
case ALL_MPAGES:
|
||||||
output_len += ata_msense_rw_recovery(&p, last);
|
output_len += ata_msense_rw_recovery(&p, last);
|
||||||
output_len += ata_msense_caching(args->id, &p, last);
|
output_len += ata_msense_caching(args->id, &p, last);
|
||||||
output_len += ata_msense_ctl_mode(&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;
|
goto invalid_fld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (minlen < 1)
|
||||||
|
return 0;
|
||||||
if (six_byte) {
|
if (six_byte) {
|
||||||
output_len--;
|
output_len--;
|
||||||
rbuf[0] = 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 {
|
} else {
|
||||||
output_len -= 2;
|
output_len -= 2;
|
||||||
rbuf[0] = output_len >> 8;
|
rbuf[0] = output_len >> 8;
|
||||||
|
if (minlen > 1)
|
||||||
rbuf[1] = output_len;
|
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;
|
return 0;
|
||||||
|
|
||||||
invalid_fld:
|
invalid_fld:
|
||||||
@@ -2156,7 +2218,7 @@ ata_scsi_map_proto(u8 byte1)
|
|||||||
* Zero on success, non-zero on failure.
|
* Zero on success, non-zero on failure.
|
||||||
*/
|
*/
|
||||||
static unsigned int
|
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 ata_taskfile *tf = &(qc->tf);
|
||||||
struct scsi_cmnd *cmd = qc->scsicmd;
|
struct scsi_cmnd *cmd = qc->scsicmd;
|
||||||
|
Reference in New Issue
Block a user