scst: Parse ATA pass-through CDBs

Signed-off-by: Bart Van Assche <bvanassche@acm.org>



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4326 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2012-05-25 19:09:36 +00:00
parent ab9478776d
commit 06b0e944e1
3 changed files with 113 additions and 22 deletions

View File

@@ -1156,17 +1156,9 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
TRACE_ENTRY();
if (unlikely(!(cmd->op_flags & SCST_INFO_VALID))) {
/*
* Let's ignore reporting T10/04-262r7 16-byte and 12-byte ATA
* pass-thru commands to not pollute logs (udev(?) checks them
* for some reason). If somebody has their description, please,
* update scst_scsi_op_table.
*/
if ((cmd->cdb[0] != 0x85) && (cmd->cdb[0] != 0xa1))
PRINT_ERROR("Unknown opcode 0x%02x", cmd->cdb[0]);
else
TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
res = false;
goto out;
}

View File

@@ -138,6 +138,8 @@ static int get_cdb_info_write_same10(struct scst_cmd *cmd,
const struct scst_sdbops *sdbops);
static int get_cdb_info_write_same16(struct scst_cmd *cmd,
const struct scst_sdbops *sdbops);
static int get_cdb_info_apt(struct scst_cmd *cmd,
const struct scst_sdbops *sdbops);
/*
+=====================================-============-======-
@@ -197,6 +199,7 @@ static int scst_scsi_op_list[256];
#define FLAG_NONE 0
/* See also http://www.t10.org/lists/op-num.htm */
static const struct scst_sdbops scst_scsi_op_table[] = {
/*
* +-------------------> TYPE_IS_DISK (0)
@@ -900,6 +903,12 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
.info_op_flags = FLAG_NONE,
.info_len_off = 10, .info_len_len = 4,
.get_cdb_info = get_cdb_info_len_4},
{.ops = 0x85, .devkey = "O O O ",
.info_op_name = "ATA PASS-THROUGH(16)",
.info_data_direction = SCST_DATA_NONE,
.info_op_flags = FLAG_NONE,
.info_lba_off = 7, .info_lba_len = 6,
.get_cdb_info = get_cdb_info_apt},
{.ops = 0x86, .devkey = "OOOOOOOOOO ",
.info_op_name = "ACCESS CONTROL IN",
.info_data_direction = SCST_DATA_NONE,
@@ -1016,6 +1025,12 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED,
.info_len_off = 6, .info_len_len = 4,
.get_cdb_info = get_cdb_info_len_4},
{.ops = 0xA1, .devkey = "O O O ",
.info_op_name = "ATA PASS-THROUGH(12)",
.info_data_direction = SCST_DATA_NONE,
.info_op_flags = FLAG_NONE,
.info_lba_off = 5, .info_lba_len = 3,
.get_cdb_info = get_cdb_info_apt},
{.ops = 0xA1, .devkey = " O ",
.info_op_name = "BLANK",
.info_data_direction = SCST_DATA_NONE,
@@ -6300,6 +6315,100 @@ static int get_cdb_info_write_same16(struct scst_cmd *cmd,
return 0;
}
/**
* scst_get_cdb_info_apt() - Parse ATA PASS-THROUGH CDB.
*
* Parse ATA PASS-THROUGH(12) and ATA PASS-THROUGH(16). See also SAT-3 for a
* detailed description of these commands.
*/
static int get_cdb_info_apt(struct scst_cmd *cmd,
const struct scst_sdbops *sdbops)
{
const u8 *const cdb = cmd->cdb;
const u8 op = cdb[0];
const u8 extend = cdb[1] & 1;
const u8 multiple = cdb[1] >> 5;
const u8 protocol = (cdb[1] >> 1) & 0xf;
const u8 t_type = (cdb[2] >> 4) & 1;
const u8 t_dir = (cdb[2] >> 3) & 1;
const u8 byte_block = (cdb[2] >> 2) & 1;
const u8 t_length = cdb[2] & 3;
int bufflen = 0;
/*
* If the PROTOCOL field contains Fh (i.e., Return Response
* Information), then the SATL shall ignore all fields in the CDB
* except for the PROTOCOL field.
*/
if (protocol == 0xf)
goto out;
switch (op) {
case ATA_12:
switch (t_length) {
case 0:
bufflen = 0;
break;
case 1:
bufflen = cdb[3];
break;
case 2:
bufflen = cdb[4];
break;
case 3:
/*
* Not yet implemented: "The transfer length is an
* unsigned integer specified in the TPSIU (see
* 3.1.97)."
*/
WARN_ON(true);
break;
}
break;
case ATA_16:
switch (t_length) {
case 0:
bufflen = 0;
break;
case 1:
bufflen = extend ? get_unaligned_be16(&cdb[3]) : cdb[4];
break;
case 2:
bufflen = extend ? get_unaligned_be16(&cdb[5]) : cdb[6];
break;
case 3:
WARN_ON(true);
break;
}
break;
}
/* See also "Table 133 - Mapping of BYTE_BLOCK, T_TYPE, and T_LENGTH" */
cmd->cdb_len = SCST_GET_CDB_LEN(op);
if (t_length != 0 && byte_block != 0) {
/*
* "The number of ATA logical sector size (see 3.1.16) blocks
* to be transferred"
*/
bufflen *= t_type ? cmd->dev->block_size : 512;
}
/*
* If the T_DIR bit is set to zero, then the SATL shall transfer data
* from the application client to the ATA device. If the T_DIR bit is
* set to one, then the SATL shall transfer data from the ATA device
* to the application client. The SATL shall ignore the T_DIR bit if
* the T_LENGTH field is set to zero.
*/
cmd->data_direction = (t_length == 0 ? SCST_DATA_NONE : t_dir ?
SCST_DATA_READ : SCST_DATA_WRITE);
cmd->lba = 0;
cmd->bufflen = bufflen << multiple;
cmd->data_len = cmd->bufflen;
out:
cmd->op_flags = SCST_INFO_VALID;
return 0;
}
/**
* scst_get_cdb_info() - fill various info about the command's CDB
*

View File

@@ -636,17 +636,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
goto out_done;
}
#else
/*
* Let's ignore reporting T10/04-262r7 16-byte and 12-byte ATA
* pass-thru commands to not pollute logs (udev(?) checks them
* for some reason). If somebody has their description, please,
* update scst_scsi_op_table.
*/
if ((cmd->cdb[0] != 0x85) && (cmd->cdb[0] != 0xa1))
PRINT_ERROR("Refusing unknown opcode %x", cmd->cdb[0]);
else
TRACE(TRACE_MINOR, "Refusing unknown opcode %x",
cmd->cdb[0]);
PRINT_ERROR("Refusing unknown opcode %x", cmd->cdb[0]);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
goto out_done;