source: mainline/uspace/drv/block/usbmast/scsi_ms.c@ 1433ecda

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

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

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