aboutsummaryrefslogtreecommitdiff
path: root/drivers/target/target_core_spc.c
diff options
context:
space:
mode:
authorRoland Dreier <roland@purestorage.com>2012-10-31 09:16:50 -0700
committerNicholas Bellinger <nab@linux-iscsi.org>2012-11-06 20:55:46 -0800
commit3a3c5e4a672c5cd61cbdcedcd027312577f9ab7c (patch)
tree9e05bfd495e4acc8e2241fe685cca166d1033acf /drivers/target/target_core_spc.c
parent0f6d64cee9c518f5d3138a90cead62fba2031074 (diff)
target: Add emulation for MODE SELECT
This is another thing that compliance tests try, and it's easy to implement on top of the MODE SENSE refactoring; since we don't claim to support any changeable values, all we need to do is check that the page contents sent by the initiator match what we would return. Signed-off-by: Roland Dreier <roland@purestorage.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target/target_core_spc.c')
-rw-r--r--drivers/target/target_core_spc.c53
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index 6c10fce9ef0..33022a3f31f 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -975,6 +975,57 @@ out:
return 0;
}
+static int spc_emulate_modeselect(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ char *cdb = cmd->t_task_cdb;
+ bool ten = cdb[0] == MODE_SELECT_10;
+ int off = ten ? 8 : 4;
+ bool pf = !!(cdb[1] & 0x10);
+ u8 page, subpage;
+ unsigned char *buf;
+ unsigned char tbuf[SE_MODE_PAGE_BUF];
+ int length;
+ int ret = 0;
+ int i;
+
+ buf = transport_kmap_data_sg(cmd);
+
+ if (!pf) {
+ cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ page = buf[off] & 0x3f;
+ subpage = buf[off] & 0x40 ? buf[off + 1] : 0;
+
+ for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i)
+ if (modesense_handlers[i].page == page &&
+ modesense_handlers[i].subpage == subpage) {
+ memset(tbuf, 0, SE_MODE_PAGE_BUF);
+ length = modesense_handlers[i].emulate(dev, 0, tbuf);
+ goto check_contents;
+ }
+
+ cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE;
+ ret = -EINVAL;
+ goto out;
+
+check_contents:
+ if (memcmp(buf + off, tbuf, length)) {
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+ ret = -EINVAL;
+ }
+
+out:
+ transport_kunmap_data_sg(cmd);
+
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
+}
+
static int spc_emulate_request_sense(struct se_cmd *cmd)
{
unsigned char *cdb = cmd->t_task_cdb;
@@ -1113,9 +1164,11 @@ int spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
switch (cdb[0]) {
case MODE_SELECT:
*size = cdb[4];
+ cmd->execute_cmd = spc_emulate_modeselect;
break;
case MODE_SELECT_10:
*size = (cdb[7] << 8) + cdb[8];
+ cmd->execute_cmd = spc_emulate_modeselect;
break;
case MODE_SENSE:
*size = cdb[4];