diff options
author | Roland Dreier <roland@purestorage.com> | 2012-10-31 12:16:50 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-11-06 23:55:46 -0500 |
commit | 3a3c5e4a672c5cd61cbdcedcd027312577f9ab7c (patch) | |
tree | 9e05bfd495e4acc8e2241fe685cca166d1033acf | |
parent | 0f6d64cee9c518f5d3138a90cead62fba2031074 (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>
-rw-r--r-- | drivers/target/target_core_spc.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 6c10fce9ef09..33022a3f31fc 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c | |||
@@ -975,6 +975,57 @@ out: | |||
975 | return 0; | 975 | return 0; |
976 | } | 976 | } |
977 | 977 | ||
978 | static int spc_emulate_modeselect(struct se_cmd *cmd) | ||
979 | { | ||
980 | struct se_device *dev = cmd->se_dev; | ||
981 | char *cdb = cmd->t_task_cdb; | ||
982 | bool ten = cdb[0] == MODE_SELECT_10; | ||
983 | int off = ten ? 8 : 4; | ||
984 | bool pf = !!(cdb[1] & 0x10); | ||
985 | u8 page, subpage; | ||
986 | unsigned char *buf; | ||
987 | unsigned char tbuf[SE_MODE_PAGE_BUF]; | ||
988 | int length; | ||
989 | int ret = 0; | ||
990 | int i; | ||
991 | |||
992 | buf = transport_kmap_data_sg(cmd); | ||
993 | |||
994 | if (!pf) { | ||
995 | cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; | ||
996 | ret = -EINVAL; | ||
997 | goto out; | ||
998 | } | ||
999 | |||
1000 | page = buf[off] & 0x3f; | ||
1001 | subpage = buf[off] & 0x40 ? buf[off + 1] : 0; | ||
1002 | |||
1003 | for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i) | ||
1004 | if (modesense_handlers[i].page == page && | ||
1005 | modesense_handlers[i].subpage == subpage) { | ||
1006 | memset(tbuf, 0, SE_MODE_PAGE_BUF); | ||
1007 | length = modesense_handlers[i].emulate(dev, 0, tbuf); | ||
1008 | goto check_contents; | ||
1009 | } | ||
1010 | |||
1011 | cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE; | ||
1012 | ret = -EINVAL; | ||
1013 | goto out; | ||
1014 | |||
1015 | check_contents: | ||
1016 | if (memcmp(buf + off, tbuf, length)) { | ||
1017 | cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST; | ||
1018 | ret = -EINVAL; | ||
1019 | } | ||
1020 | |||
1021 | out: | ||
1022 | transport_kunmap_data_sg(cmd); | ||
1023 | |||
1024 | if (!ret) | ||
1025 | target_complete_cmd(cmd, GOOD); | ||
1026 | return ret; | ||
1027 | } | ||
1028 | |||
978 | static int spc_emulate_request_sense(struct se_cmd *cmd) | 1029 | static int spc_emulate_request_sense(struct se_cmd *cmd) |
979 | { | 1030 | { |
980 | unsigned char *cdb = cmd->t_task_cdb; | 1031 | unsigned char *cdb = cmd->t_task_cdb; |
@@ -1113,9 +1164,11 @@ int spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) | |||
1113 | switch (cdb[0]) { | 1164 | switch (cdb[0]) { |
1114 | case MODE_SELECT: | 1165 | case MODE_SELECT: |
1115 | *size = cdb[4]; | 1166 | *size = cdb[4]; |
1167 | cmd->execute_cmd = spc_emulate_modeselect; | ||
1116 | break; | 1168 | break; |
1117 | case MODE_SELECT_10: | 1169 | case MODE_SELECT_10: |
1118 | *size = (cdb[7] << 8) + cdb[8]; | 1170 | *size = (cdb[7] << 8) + cdb[8]; |
1171 | cmd->execute_cmd = spc_emulate_modeselect; | ||
1119 | break; | 1172 | break; |
1120 | case MODE_SENSE: | 1173 | case MODE_SENSE: |
1121 | *size = cdb[4]; | 1174 | *size = cdb[4]; |