The attachment is for discussion. It adds MODE SELECT
support to libata allowing the write(back) cache and
read ahead to be manipulated by users [i.e. the WCE and
DRA ** bits in the SCSI Caching mode page].
The patch is against lk 2.6.13-rc6 and includes the
SSU patch (but not the TUR patch).
It highlights several issues with libata:
- may need hook so SCSI command specific logic can be
executed _after_ a ATA command has completed successfully
- one SCSI command can map to two or more
ATA commands (e.g. a MODE SELECT caching
page that changes the state of both WCE and DRA)
I cannot see a graceful way to do this with libata.
- patch generalizes error processing but probably
not enough
- may need lighter weight version of
ata_dev_identify() when the "dev->id" array needs
to be resync-ed (e.g. as required by the ATA information
VPD page in sat-r05)
ChangeLog:
- generalize SCSI error processing
- add MODE SELECT (6 and 10) handling; active for
WCE and DRA in Caching mode page
- add block descriptor to MODE SENSE command
- make various changes to sync with sat-r05 (as
noted in source)
- answer some "FIXME" questions in code and flag
points for extra work
** Testing this patch highlighted a bug in sdparm effecting
DRA (but not WCE). A beta version of sdparm-0.95 can be
found at http://www.torque.net/sg/sdparm.html that fixes
the problem.
Not to be applied to mainline kernel yet.
Doug Gilbert
--- linux/include/linux/ata.h 2005-07-30 10:22:09.000000000 +1000
+++ linux/include/linux/ata.h2613rc4standby 2005-07-31 12:21:55.000000000 +1000
@@ -108,6 +108,8 @@
/* ATA device commands */
ATA_CMD_CHK_POWER = 0xE5, /* check power mode */
+ ATA_CMD_STANDBY = 0xE2, /* place in standby power mode */
+ ATA_CMD_IDLE = 0xE3, /* place in idle power mode */
ATA_CMD_EDD = 0x90, /* execute device diagnostic */
ATA_CMD_FLUSH = 0xE7,
ATA_CMD_FLUSH_EXT = 0xEA,
--- linux/drivers/scsi/libata.h 2005-08-16 12:45:37.000000000 +1000
+++ linux/drivers/scsi/libata.h2613rc6xa 2005-08-17 23:01:08.000000000 +1000
@@ -69,21 +69,9 @@
unsigned int buflen);
extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen);
-extern void ata_scsi_badcmd(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *),
- u8 asc, u8 ascq);
+extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq);
extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
unsigned int (*actor) (struct ata_scsi_args *args,
u8 *rbuf, unsigned int buflen));
-static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
-{
- ata_scsi_badcmd(cmd, done, 0x20, 0x00);
-}
-
-static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
-{
- ata_scsi_badcmd(cmd, done, 0x24, 0x00);
-}
-
#endif /* __LIBATA_H__ */
--- linux/drivers/scsi/libata-scsi.c 2005-08-21 14:20:30.000000000 +1000
+++ linux/drivers/scsi/libata-scsi.c2613rc6xd 2005-08-21 14:16:52.000000000 +1000
@@ -37,6 +37,14 @@
static struct ata_device *
ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev);
+static void ata_scsi_invalid_field(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0);
+ /* "Invalid field in cbd" */
+ done(cmd);
+}
+
/**
* ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd.
@@ -171,7 +179,6 @@
{
struct scsi_cmnd *cmd = qc->scsicmd;
u8 err = 0;
- unsigned char *sb = cmd->sense_buffer;
/* Based on the 3ware driver translation table */
static unsigned char sense_table[][4] = {
/* BBD|ECC|ID|MAR */
@@ -214,8 +221,6 @@
};
int i = 0;
- cmd->result = SAM_STAT_CHECK_CONDITION;
-
/*
* Is this an error we can process/parse
*/
@@ -270,11 +275,9 @@
/* Look for best matches first */
if((sense_table[i][0] & err) == sense_table[i][0])
{
- sb[0] = 0x70;
- sb[2] = sense_table[i][1];
- sb[7] = 0x0a;
- sb[12] = sense_table[i][2];
- sb[13] = sense_table[i][3];
+ ata_scsi_set_sense(cmd, sense_table[i][1] /* sk */,
+ sense_table[i][2] /* asc */,
+ sense_table[i][3] /* ascq */ );
return;
}
i++;
@@ -289,11 +292,9 @@
{
if(stat_table[i][0] & drv_stat)
{
- sb[0] = 0x70;
- sb[2] = stat_table[i][1];
- sb[7] = 0x0a;
- sb[12] = stat_table[i][2];
- sb[13] = stat_table[i][3];
+ ata_scsi_set_sense(cmd, sense_table[i][1] /* sk */,
+ sense_table[i][2] /* asc */,
+ sense_table[i][3] /* ascq */ );
return;
}
i++;
@@ -302,15 +303,12 @@
printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, drv_stat);
/* additional-sense-code[-qualifier] */
- sb[0] = 0x70;
- sb[2] = MEDIUM_ERROR;
- sb[7] = 0x0A;
if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
- sb[12] = 0x11; /* "unrecovered read error" */
- sb[13] = 0x04;
+ ata_scsi_set_sense(cmd, MEDIUM_ERROR, 0x11, 0x4);
+ /* "unrecovered read error" */
} else {
- sb[12] = 0x0C; /* "write error - */
- sb[13] = 0x02; /* auto-reallocation failed" */
+ ata_scsi_set_sense(cmd, MEDIUM_ERROR, 0xc, 0x2);
+ /* "write error - auto-reallocation failed" */
}
}
@@ -391,6 +389,60 @@
}
/**
+ * ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate
+ *
+ * Sets up an ATA taskfile to issue STANDBY (to stop) or READ VERIFY
+ * (to start). Perhaps these commands should be preceded by
+ * CHECK POWER MODE to see what power mode the device is already in.
+ * [See SAT revision 5 at www.t10.org]
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc,
+ u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf->protocol = ATA_PROT_NODATA;
+ if (scsicmd[1] & 0x1) {
+ ; /* ignore IMMED bit, violates sat-r05 */
+ }
+ if (scsicmd[4] & 0x2)
+ return 1; /* LOEJ bit set not supported */
+ if (((scsicmd[4] >> 4) & 0xf) != 0)
+ return 1; /* power conditions not supported */
+ if (scsicmd[4] & 0x1) {
+ tf->nsect = 1; /* 1 sector, lba=0 */
+ tf->lbah = 0x0;
+ tf->lbam = 0x0;
+ tf->lbal = 0x0;
+ tf->device |= ATA_LBA;
+ tf->command = ATA_CMD_VERIFY; /* READ VERIFY */
+ } else {
+ tf->nsect = 0; /* time period value (0 implies now) */
+ tf->command = ATA_CMD_STANDBY;
+ /* Consider: ATA STANDBY IMMEDIATE command */
+ }
+ /*
+ * Standby and Idle condition timers could be implemented but that
+ * would require libata to implement the Power condition mode page
+ * and allow the user to change it. Changing mode pages requires
+ * MODE SELECT to be implemented.
+ */
+
+ return 0;
+}
+
+
+/**
* ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
* @qc: Storage for translated ATA taskfile
* @scsicmd: SCSI command to translate (ignored)
@@ -579,6 +631,10 @@
}
if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) {
+ /*
+ * FIXME: for READ_6 and WRITE_6 (only)
+ * transfer_len==0 -> 256 blocks !!
+ */
qc->nsect = tf->nsect = scsicmd[4];
tf->lbal = scsicmd[3];
tf->lbam = scsicmd[2];
@@ -655,6 +711,10 @@
* This function sets up an ata_queued_cmd structure for the
* SCSI command, and sends that ata_queued_cmd to the hardware.
*
+ * xlat_func argument (actor) returns 0 if ok, 1 for invalid field
+ * or 2 for a detected problem (or early termination) for which
+ * the actor has already setup the result, sense key and sense data.
+ *
* LOCKING:
* spin_lock_irqsave(host_set lock)
*/
@@ -666,12 +726,13 @@
{
struct ata_queued_cmd *qc;
u8 *scsicmd = cmd->cmnd;
+ unsigned int res;
VPRINTK("ENTER\n");
qc = ata_scsi_qc_new(ap, dev, cmd, done);
if (!qc)
- return;
+ goto err_mem;
/* data is present; dma-map it */
if (cmd->sc_data_direction == DMA_FROM_DEVICE ||
@@ -679,7 +740,7 @@
if (unlikely(cmd->request_bufflen < 1)) {
printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n",
ap->id, dev->devno);
- goto err_out;
+ goto err_did;
}
if (cmd->use_sg)
@@ -693,20 +754,38 @@
qc->complete_fn = ata_scsi_qc_complete;
- if (xlat_func(qc, scsicmd))
- goto err_out;
+ res = xlat_func(qc, scsicmd);
+ if (res == 1)
+ goto err_invalid;
+ else if (res > 1)
+ goto err_sense;
/* select device, send command to hardware */
if (ata_qc_issue(qc))
- goto err_out;
+ goto err_did;
VPRINTK("EXIT\n");
return;
-err_out:
+err_invalid:
ata_qc_free(qc);
- ata_bad_cdb(cmd, done);
- DPRINTK("EXIT - badcmd\n");
+ ata_scsi_invalid_field(cmd, done);
+ DPRINTK("EXIT - invalid field\n");
+ return;
+
+err_sense:
+ ata_qc_free(qc);
+ done(cmd);
+ DPRINTK("EXIT - check condition (or do nothing)\n");
+ return;
+
+err_did:
+ ata_qc_free(qc);
+err_mem:
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ DPRINTK("EXIT - internal\n");
+ return;
}
/**
@@ -772,8 +851,9 @@
* Takes care of the hard work of simulating a SCSI command...
* Mapping the response buffer, calling the command's handler,
* and handling the handler's return value. This return value
- * indicates whether the handler wishes the SCSI command to be
- * completed successfully, or not.
+ * (i.e. @actor) is 0 for success, 1 for "Invalid field in cdb"
+ * needs to be set, 2 for sense data and status already set
+ * by actor.
*
* LOCKING:
* spin_lock_irqsave(host_set lock)
@@ -792,11 +872,14 @@
rc = actor(args, rbuf, buflen);
ata_scsi_rbuf_put(cmd, rbuf);
- if (rc)
- ata_bad_cdb(cmd, args->done);
- else {
+ if (rc == 0) {
cmd->result = SAM_STAT_GOOD;
args->done(cmd);
+ } else if (rc == 1)
+ ata_scsi_invalid_field(cmd, args->done);
+ else {
+ /* assume sense data and status set by actor */
+ args->done(cmd);
}
}
@@ -875,7 +958,8 @@
const u8 pages[] = {
0x00, /* page 0x00, this page */
0x80, /* page 0x80, unit serial no page */
- 0x83 /* page 0x83, device ident page */
+ 0x83, /* page 0x83, device ident page */
+ /* page 0x89, ATA information page */
};
rbuf[3] = sizeof(pages); /* number of supported EVPD pages */
@@ -994,6 +1078,13 @@
*ptr_io = ptr;
}
+static const u8 def_caching_mpage[] = {
+ 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 */
+ };
+
/**
* ata_msense_caching - Simulate MODE SENSE caching info page
* @id: device IDENTIFY data
@@ -1011,13 +1102,9 @@
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[0x12 + 2];
+ memcpy(page, def_caching_mpage, sizeof(page));
if (ata_id_wcache_enabled(id))
page[2] |= (1 << 2); /* write cache enable */
if (!ata_id_rahead_enabled(id))
@@ -1027,6 +1114,13 @@
return sizeof(page);
}
+/* As of sat-r05: considering defining this page (for QErr).
+ * D_SENSE is now 0 (fixed sense data format);
+ * guess extended self test completion time: 30 seconds (why?).
+ */
+static const u8 def_control_mpage[] = {
+ 0xa, 0xa, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30};
+
/**
* ata_msense_ctl_mode - Simulate MODE SENSE control mode page
* @dev: Device associated with this MODE SENSE command
@@ -1041,16 +1135,17 @@
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);
}
+
+static const u8 def_rw_recovery_mpage[] = {
+ 0x1, /* page code */
+ 0xa, /* page length */
+ (1 << 6), /* note: ARRE=1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */
+ }; /* sat-r05 says AWRE will be 0 */
/**
* ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page
@@ -1066,15 +1161,9 @@
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);
}
/**
@@ -1093,28 +1182,52 @@
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, 0, 0, 0x2, 0x0};
+ /* 512 byte blocks, number of blocks = 0, (sat-r05) */
+ u8 pg, spg;
+ unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen;
VPRINTK("ENTER\n");
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 MS(10) ignored (permitted)
+ * Only support current values (sat-r05)
*/
page_control = scsicmd[2] >> 6;
- if ((page_control != 0) && (page_control != 3))
- return 1;
+ if (page_control != 0) {
+ if (page_control == 3) {
+ ata_scsi_set_sense(args->cmd,
+ ILLEGAL_REQUEST, 0x39, 0x0);
+ /* "Saving parameters not supported" */
+ return 2;
+ } else
+ return 1;
+ }
- 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) {
+ pg = scsicmd[2] & 0x3f;
+ spg = scsicmd[3];
+ /* No mode subpages supported (yet) but asking for _all_
+ * subpages may be valid
+ */
+ if (spg && (spg != 0xff))
+ return 1;
+
+ switch(pg) {
case 0x01: /* r/w error recovery */
output_len += ata_msense_rw_recovery(&p, last);
break;
@@ -1128,6 +1241,8 @@
break;
}
+ /* case 0x1c: Informational Exception Control mode page */
+
case 0x3f: /* all pages */
output_len += ata_msense_rw_recovery(&p, last);
output_len += ata_msense_caching(args->id, &p, last);
@@ -1138,13 +1253,30 @@
return 1;
}
+ 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;
- rbuf[1] = output_len;
+ 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;
@@ -1237,6 +1369,151 @@
}
/**
+ * ata_scsi_mode_select_xlat - Translate SCSI MODE SELECT commands
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate
+ *
+ * Sets up an ATA taskfile to issue SET FEATURES, if required.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc,
+ u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ struct scsi_cmnd *cmd = qc->scsicmd;
+ unsigned int param_len, ten, buflen, minlen, bdlen, offset;
+ unsigned int pglen, spf, mpg, wce, dra;
+ u8 *rbuf;
+ u8 apage[48];
+
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf->protocol = ATA_PROT_NODATA;
+ cmd->result = SAM_STAT_GOOD;
+ if (! (scsicmd[1] & 0x10))
+ return 1; /* require PF bit set */
+ ten = (scsicmd[0] == MODE_SELECT_10) ? 1 : 0;
+ param_len = ten ? ((scsicmd[7] << 8) + scsicmd[8]) : scsicmd[4];
+ if (param_len < (ten ? 8 : 4))
+ return 2;
+ buflen = ata_scsi_rbuf_get(cmd, &rbuf);
+ minlen = (param_len < buflen) ? param_len : buflen;
+ bdlen = ten ? ((rbuf[6] << 8) + rbuf[7]) : rbuf[3];
+ offset = (ten ? 8 : 4) + bdlen;
+ if (minlen > offset) {
+ memset(apage, 0, sizeof(apage));
+ pglen = minlen - offset;
+ pglen = (pglen < sizeof(apage)) ? pglen : sizeof(apage);
+ memcpy(apage, rbuf + offset, pglen);
+ }
+ ata_scsi_rbuf_put(cmd, rbuf);
+ if (minlen == offset)
+ return 2;
+ if (minlen < offset)
+ goto bad_param;
+ minlen -= offset;
+ spf = !!(apage[0] & 0x40);
+ mpg = apage[0] & 0x3f;
+ /* mspg = spf ? apage[1] : 0; */
+ if (spf)
+ goto bad_param;
+ else
+ pglen = apage[1];
+ if (pglen + 2 < minlen)
+ goto bad_param;
+ switch (mpg) {
+ case 0x1:
+ if (pglen != def_rw_recovery_mpage[1])
+ goto bad_param;
+ break;
+ case 0x8:
+ if (pglen != def_caching_mpage[1])
+ goto bad_param;
+ wce = !!(apage[2] & 0x4);
+ dra = !!(apage[12] & 0x20);
+ if (wce != !!(ata_id_wcache_enabled(qc->dev->id))) {
+ /* write(back) cache */
+ if (wce)
+ tf->feature = 0x2; /* WCE -> enable */
+ else
+ tf->feature = 0x82; /* disable */
+ tf->command = ATA_CMD_SET_FEATURES;
+ /* <<< If success, should dev->id be updated? >>> */
+ /* vvvvvvv start of dubious code vvvvvvvvvv */
+ if (wce)
+ qc->dev->id[85] |= 0x20;
+ else
+ qc->dev->id[85] &= ~0x20;
+ /* vvvvvvv end of dubious code vvvvvvvvvv */
+ return 0;
+ }
+/* FIXME: we may want to issue two SET FEATURES commands here */
+ if (dra != !(ata_id_rahead_enabled(qc->dev->id))) {
+ /* read look ahead */
+ if (dra)
+ tf->feature = 0x55; /* DRA -> disable */
+ else
+ tf->feature = 0xaa; /* enable */
+ tf->command = ATA_CMD_SET_FEATURES;
+ /* <<< If success, should dev->id be updated? >>> */
+ /* vvvvvvv start of dubious code vvvvvvvvvv */
+ if (dra)
+ qc->dev->id[85] &= ~0x40;
+ else
+ qc->dev->id[85] |= 0x40;
+ /* vvvvvvv end of dubious code vvvvvvvvvv */
+ return 0;
+ }
+ break;
+ case 0xa:
+ if (pglen != def_control_mpage[1])
+ goto bad_param;
+ break;
+ default:
+ goto bad_param;
+ }
+ /* GOOD response, no ATA command issued */
+ return 2;
+
+bad_param:
+ ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x26, 0x0);
+ /* "Invalid field in parameter list" */
+ return 2;
+}
+
+/**
+ * ata_scsi_set_sense - Set SCSI sense data and status
+ * @cmd: SCSI request to be handled
+ * @sk: SCSI-defined sense key
+ * @asc: SCSI-defined additional sense code
+ * @ascq: SCSI-defined additional sense code qualifier
+ *
+ * Helper function that builds a valid fixed format, current
+ * response code and the given sense key (sk), additional sense
+ * code (asc) and additional sense code qualifier (ascq) with
+ * a SCSI command status of %SAM_STAT_CHECK_CONDITION .
+ *
+ * LOCKING:
+ * Not required
+ */
+
+void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq)
+{
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ cmd->sense_buffer[0] = 0x70; /* fixed format, current */
+ cmd->sense_buffer[2] = sk;
+ cmd->sense_buffer[7] = 18 - 8; /* additional sense length */
+ cmd->sense_buffer[12] = asc;
+ cmd->sense_buffer[13] = ascq;
+}
+
+/**
* ata_scsi_badcmd - End a SCSI request with an error
* @cmd: SCSI request to be handled
* @done: SCSI command completion function
@@ -1254,13 +1531,7 @@
void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq)
{
DPRINTK("ENTER\n");
- cmd->result = SAM_STAT_CHECK_CONDITION;
-
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = ILLEGAL_REQUEST;
- cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */
- cmd->sense_buffer[12] = asc;
- cmd->sense_buffer[13] = ascq;
+ ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, asc, ascq);
done(cmd);
}
@@ -1414,9 +1685,10 @@
* Pointer to translation function if possible, %NULL if not.
*/
-static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
+static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev,
+ u8 *cmd)
{
- switch (cmd) {
+ switch (cmd[0]) {
case READ_6:
case READ_10:
case READ_16:
@@ -1426,6 +1698,16 @@
case WRITE_16:
return ata_scsi_rw_xlat;
+ case INQUIRY:
+ if (((cmd[1] & 0x3) == 0x1) && (cmd[2] == 0x89)) {
+ /* needs to re-issue an ATA IDENTIFY cmd
+ * and refill dev->id array. Then put this
+ * data and the signature in the vpd response.
+ */
+ /* return ata_scsi_ata_info_vpd_xlat; ?? */
+ }
+ break;
+
case SYNCHRONIZE_CACHE:
if (ata_try_flush_cache(dev))
return ata_scsi_flush_xlat;
@@ -1434,6 +1716,13 @@
case VERIFY:
case VERIFY_16:
return ata_scsi_verify_xlat;
+
+ case START_STOP:
+ return ata_scsi_start_stop_xlat;
+
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ return ata_scsi_mode_select_xlat;
}
return NULL;
@@ -1501,7 +1790,7 @@
if (dev->class == ATA_DEV_ATA) {
ata_xlat_func_t xlat_func = ata_get_xlat_func(dev,
- cmd->cmnd[0]);
+ cmd->cmnd);
if (xlat_func)
ata_scsi_translate(ap, dev, cmd, done, xlat_func);
@@ -1547,12 +1836,13 @@
case TEST_UNIT_READY:
case FORMAT_UNIT: /* FIXME: correct? */
case SEND_DIAGNOSTIC: /* FIXME: correct? */
+ /* sat-r05: both should be translated */
ata_scsi_rbuf_fill(&args, ata_scsiop_noop);
break;
case INQUIRY:
if (scsicmd[1] & 2) /* is CmdDt set? */
- ata_bad_cdb(cmd, done);
+ ata_scsi_invalid_field(cmd, done);
else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
else if (scsicmd[2] == 0x00)
@@ -1562,7 +1852,7 @@
else if (scsicmd[2] == 0x83)
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
else
- ata_bad_cdb(cmd, done);
+ ata_scsi_invalid_field(cmd, done);
break;
case MODE_SENSE:
@@ -1570,11 +1860,6 @@
ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense);
break;
- case MODE_SELECT: /* unconditionally return */
- case MODE_SELECT_10: /* bad-field-in-cdb */
- ata_bad_cdb(cmd, done);
- break;
-
case READ_CAPACITY:
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
break;
@@ -1583,7 +1868,7 @@
if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
else
- ata_bad_cdb(cmd, done);
+ ata_scsi_invalid_field(cmd, done);
break;
case REPORT_LUNS:
@@ -1595,7 +1880,9 @@
/* all other commands */
default:
- ata_bad_scsiop(cmd, done);
+ ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0);
+ /* "Invalid command operation code" */
+ done(cmd);
break;
}
}