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]; |
