source: mainline/uspace/drv/bus/usb/usbmast/scsi_ms.c@ 48ae3ef

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 48ae3ef was 132a6073, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Repeat SCSI command in case of unit attention condition.
Use Read/Write (10) instead of Read/Write (12).

  • Property mode set to 100644
File size: 9.8 KB
RevLine 
[70c12d6]1/*
2 * Copyright (c) 2011 Vojtech Horky
[71fa44c]3 * Copyright (c) 2011 Jiri Svoboda
[70c12d6]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup drvusbmast
31 * @{
32 */
33/**
34 * @file
[6430ac6]35 * SCSI functions for USB mass storage driver.
[70c12d6]36 */
[64e6945d]37#include <bitops.h>
[71fa44c]38#include <byteorder.h>
39#include <inttypes.h>
[f7a55f9]40#include <macros.h>
[7d521e24]41#include <usb/dev/driver.h>
[70c12d6]42#include <usb/debug.h>
43#include <errno.h>
44#include <str_error.h>
45#include <str.h>
[71fa44c]46#include <scsi/sbc.h>
[0feaae4]47#include <scsi/spc.h>
[89d3f3c7]48#include "cmdw.h"
49#include "bo_trans.h"
[6430ac6]50#include "scsi_ms.h"
[7190bbc]51#include "usbmast.h"
[70c12d6]52
[cf002dbf]53/** Get string representation for SCSI peripheral device type.
54 *
[6430ac6]55 * @param type SCSI peripheral device type code.
56 * @return String representation.
[cf002dbf]57 */
[6430ac6]58const char *usbmast_scsi_dev_type_str(unsigned type)
[70c12d6]59{
[0feaae4]60 return scsi_get_dev_type_str(type);
[70c12d6]61}
62
[132a6073]63static void usbmast_dump_sense(scsi_sense_data_t *sense_buf)
[45cae6b]64{
[582fe388]65 unsigned sense_key;
[132a6073]66
67 sense_key = sense_buf->flags_key & 0x0f;
68 printf("Got sense data. Sense key: 0x%x (%s), ASC 0x%02x, "
69 "ASCQ 0x%02x.\n", sense_key,
70 scsi_get_sense_key_str(sense_key),
71 sense_buf->additional_code,
72 sense_buf->additional_cqual);
73}
74
75/** Run SCSI command.
76 *
77 * Run command and repeat in case of unit attention.
78 * XXX This is too simplified.
79 */
80static int usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
81{
82 uint8_t sense_key;
83 scsi_sense_data_t sense_buf;
[45cae6b]84 int rc;
85
[132a6073]86 do {
87 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
88 if (rc != EOK) {
89 usb_log_error("Inquiry transport failed, device %s: %s.\n",
90 mfun->mdev->ddf_dev->name, str_error(rc));
91 return rc;
92 }
93
94 if (cmd->status == CMDS_GOOD)
95 return EOK;
96
97 usb_log_error("SCSI command failed, device %s.\n",
98 mfun->mdev->ddf_dev->name);
99
100 rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
101 if (rc != EOK) {
102 usb_log_error("Failed to read sense data.\n");
103 return EIO;
104 }
105
106 /* Dump sense data to log */
107 usbmast_dump_sense(&sense_buf);
108
109 /* Get sense key */
[582fe388]110 sense_key = sense_buf.flags_key & 0x0f;
[132a6073]111
112 if (sense_key == SCSI_SK_UNIT_ATTENTION) {
113 printf("Got unit attention. Re-trying command.\n");
114 }
115
116 } while (sense_key == SCSI_SK_UNIT_ATTENTION);
117
118 /* Command status is not good, nevertheless transport succeeded. */
119 return EOK;
[45cae6b]120}
121
[71fa44c]122/** Perform SCSI Inquiry command on USB mass storage device.
[cf002dbf]123 *
[7190bbc]124 * @param mfun Mass storage function
125 * @param inquiry_result Where to store parsed inquiry result
126 * @return Error code
[cf002dbf]127 */
[7190bbc]128int usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
[70c12d6]129{
[7b2c17c]130 scsi_std_inquiry_data_t inq_data;
[de3432b]131 scsi_cmd_t cmd;
[71fa44c]132 scsi_cdb_inquiry_t cdb;
[70c12d6]133 int rc;
134
[71fa44c]135 memset(&cdb, 0, sizeof(cdb));
136 cdb.op_code = SCSI_CMD_INQUIRY;
137 cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
138
[de3432b]139 memset(&cmd, 0, sizeof(cmd));
140 cmd.cdb = &cdb;
141 cmd.cdb_size = sizeof(cdb);
142 cmd.data_in = &inq_data;
143 cmd.data_in_size = sizeof(inq_data);
144
145 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
[70c12d6]146
147 if (rc != EOK) {
[45cae6b]148 usb_log_error("Inquiry transport failed, device %s: %s.\n",
[2aceec5]149 mfun->mdev->ddf_dev->name, str_error(rc));
[70c12d6]150 return rc;
151 }
152
[de3432b]153 if (cmd.status != CMDS_GOOD) {
[45cae6b]154 usb_log_error("Inquiry command failed, device %s.\n",
155 mfun->mdev->ddf_dev->name);
156 return EIO;
157 }
158
[de3432b]159 if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
[71fa44c]160 usb_log_error("SCSI Inquiry response too short (%zu).\n",
[de3432b]161 cmd.rcvd_size);
[7b2c17c]162 return EIO;
[70c12d6]163 }
164
165 /*
[7b2c17c]166 * Parse inquiry data and fill in the result structure.
[70c12d6]167 */
[7b2c17c]168
[6430ac6]169 bzero(inq_res, sizeof(*inq_res));
[70c12d6]170
[6430ac6]171 inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
[64e6945d]172 inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
[70c12d6]173
[6430ac6]174 inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
[64e6945d]175 inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
[d2fac08c]176
[6430ac6]177 spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
[dcb74c0a]178 inq_data.vendor, sizeof(inq_data.vendor));
[70c12d6]179
[6430ac6]180 spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
[dcb74c0a]181 inq_data.product, sizeof(inq_data.product));
[70c12d6]182
[6430ac6]183 spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
[dcb74c0a]184 inq_data.revision, sizeof(inq_data.revision));
[70c12d6]185
186 return EOK;
187}
188
[f7a55f9]189/** Perform SCSI Request Sense command on USB mass storage device.
190 *
[7190bbc]191 * @param mfun Mass storage function
[f7a55f9]192 * @param buf Destination buffer
193 * @param size Size of @a buf
194 *
195 * @return Error code.
196 */
[7190bbc]197int usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
[f7a55f9]198{
[de3432b]199 scsi_cmd_t cmd;
[f7a55f9]200 scsi_cdb_request_sense_t cdb;
201 int rc;
202
203 memset(&cdb, 0, sizeof(cdb));
204 cdb.op_code = SCSI_CMD_REQUEST_SENSE;
205 cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
206
[de3432b]207 memset(&cmd, 0, sizeof(cmd));
208 cmd.cdb = &cdb;
209 cmd.cdb_size = sizeof(cdb);
210 cmd.data_in = buf;
211 cmd.data_in_size = size;
212
213 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
[f7a55f9]214
[de3432b]215 if (rc != EOK || cmd.status != CMDS_GOOD) {
[f7a55f9]216 usb_log_error("Request Sense failed, device %s: %s.\n",
[2aceec5]217 mfun->mdev->ddf_dev->name, str_error(rc));
[f7a55f9]218 return rc;
219 }
220
[de3432b]221 if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
[f7a55f9]222 /* The missing bytes should be considered to be zeroes. */
[de3432b]223 memset((uint8_t *)buf + cmd.rcvd_size, 0,
224 SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
[f7a55f9]225 }
226
227 return EOK;
228}
229
[71fa44c]230/** Perform SCSI Read Capacity command on USB mass storage device.
231 *
[7190bbc]232 * @param mfun Mass storage function
233 * @param nblocks Output, number of blocks
234 * @param block_size Output, block size in bytes
[71fa44c]235 *
236 * @return Error code.
237 */
[7190bbc]238int usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
[71fa44c]239 uint32_t *block_size)
240{
[de3432b]241 scsi_cmd_t cmd;
[71fa44c]242 scsi_cdb_read_capacity_10_t cdb;
243 scsi_read_capacity_10_data_t data;
244 int rc;
245
246 memset(&cdb, 0, sizeof(cdb));
247 cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
248
[de3432b]249 memset(&cmd, 0, sizeof(cmd));
250 cmd.cdb = &cdb;
251 cmd.cdb_size = sizeof(cdb);
252 cmd.data_in = &data;
253 cmd.data_in_size = sizeof(data);
254
[132a6073]255 rc = usbmast_run_cmd(mfun, &cmd);
[71fa44c]256
257 if (rc != EOK) {
[45cae6b]258 usb_log_error("Read Capacity (10) transport failed, device %s: %s.\n",
[2aceec5]259 mfun->mdev->ddf_dev->name, str_error(rc));
[71fa44c]260 return rc;
261 }
262
[de3432b]263 if (cmd.status != CMDS_GOOD) {
[45cae6b]264 usb_log_error("Read Capacity (10) command failed, device %s.\n",
265 mfun->mdev->ddf_dev->name);
266 return EIO;
267 }
268
[de3432b]269 if (cmd.rcvd_size < sizeof(data)) {
[71fa44c]270 usb_log_error("SCSI Read Capacity response too short (%zu).\n",
[de3432b]271 cmd.rcvd_size);
[71fa44c]272 return EIO;
273 }
274
275 *nblocks = uint32_t_be2host(data.last_lba) + 1;
276 *block_size = uint32_t_be2host(data.block_size);
277
278 return EOK;
279}
280
[f7a55f9]281/** Perform SCSI Read command on USB mass storage device.
282 *
[7190bbc]283 * @param mfun Mass storage function
284 * @param ba Address of first block
285 * @param nblocks Number of blocks to read
[f7a55f9]286 *
[7190bbc]287 * @return Error code
[f7a55f9]288 */
[7190bbc]289int usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
[f7a55f9]290{
[de3432b]291 scsi_cmd_t cmd;
[132a6073]292 scsi_cdb_read_10_t cdb;
[f7a55f9]293 int rc;
294
295 if (ba > UINT32_MAX)
296 return ELIMIT;
297
[132a6073]298 if (nblocks > UINT16_MAX)
[f7a55f9]299 return ELIMIT;
300
301 memset(&cdb, 0, sizeof(cdb));
[132a6073]302 cdb.op_code = SCSI_CMD_READ_10;
[f7a55f9]303 cdb.lba = host2uint32_t_be(ba);
[132a6073]304 cdb.xfer_len = host2uint16_t_be(nblocks);
[f7a55f9]305
[de3432b]306 memset(&cmd, 0, sizeof(cmd));
307 cmd.cdb = &cdb;
308 cmd.cdb_size = sizeof(cdb);
309 cmd.data_in = buf;
310 cmd.data_in_size = nblocks * mfun->block_size;
311
[132a6073]312 rc = usbmast_run_cmd(mfun, &cmd);
[f7a55f9]313
314 if (rc != EOK) {
[132a6073]315 usb_log_error("Read (10) transport failed, device %s: %s.\n",
[2aceec5]316 mfun->mdev->ddf_dev->name, str_error(rc));
[f7a55f9]317 return rc;
318 }
319
[de3432b]320 if (cmd.status != CMDS_GOOD) {
[132a6073]321 usb_log_error("Read (10) command failed, device %s.\n",
[45cae6b]322 mfun->mdev->ddf_dev->name);
323 return EIO;
324 }
325
[de3432b]326 if (cmd.rcvd_size < nblocks * mfun->block_size) {
[f7a55f9]327 usb_log_error("SCSI Read response too short (%zu).\n",
[de3432b]328 cmd.rcvd_size);
[f7a55f9]329 return EIO;
330 }
331
332 return EOK;
333}
334
[38c9505]335/** Perform SCSI Write command on USB mass storage device.
336 *
[7190bbc]337 * @param mfun Mass storage function
[38c9505]338 * @param ba Address of first block
339 * @param nblocks Number of blocks to read
340 * @param data Data to write
341 *
342 * @return Error code
343 */
[7190bbc]344int usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
[38c9505]345 const void *data)
346{
[de3432b]347 scsi_cmd_t cmd;
[132a6073]348 scsi_cdb_write_10_t cdb;
[38c9505]349 int rc;
350
351 if (ba > UINT32_MAX)
352 return ELIMIT;
353
[132a6073]354 if (nblocks > UINT16_MAX)
[38c9505]355 return ELIMIT;
356
357 memset(&cdb, 0, sizeof(cdb));
[132a6073]358 cdb.op_code = SCSI_CMD_WRITE_10;
[38c9505]359 cdb.lba = host2uint32_t_be(ba);
[132a6073]360 cdb.xfer_len = host2uint16_t_be(nblocks);
[38c9505]361
[de3432b]362 memset(&cmd, 0, sizeof(cmd));
363 cmd.cdb = &cdb;
364 cmd.cdb_size = sizeof(cdb);
365 cmd.data_out = data;
366 cmd.data_out_size = nblocks * mfun->block_size;
367
[132a6073]368 rc = usbmast_run_cmd(mfun, &cmd);
[38c9505]369
370 if (rc != EOK) {
[132a6073]371 usb_log_error("Write (10) transport failed, device %s: %s.\n",
[2aceec5]372 mfun->mdev->ddf_dev->name, str_error(rc));
[38c9505]373 return rc;
374 }
375
[de3432b]376 if (cmd.status != CMDS_GOOD) {
[132a6073]377 usb_log_error("Write (10) command failed, device %s.\n",
[45cae6b]378 mfun->mdev->ddf_dev->name);
379 return EIO;
380 }
381
[38c9505]382 return EOK;
383}
384
[70c12d6]385/**
386 * @}
387 */
Note: See TracBrowser for help on using the repository browser.