source: mainline/uspace/drv/block/usbmast/scsi_ms.c@ 79949f3

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

Fix block comment formatting (ccheck).

  • 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 }
[7c3fb9b]95 /*
96 * Ignore command error here. If there's something wrong
[7a1757e]97 * with the device the following commands will fail too.
98 */
99 if (cmd.status != CMDS_GOOD)
100 usb_log_warning("Test Unit Ready command failed on device %s.",
[ae3a941]101 usb_device_get_name(mfun->mdev->usb_dev));
[ce25903]102
103 return EOK;
104}
105
[132a6073]106/** Run SCSI command.
107 *
108 * Run command and repeat in case of unit attention.
109 * XXX This is too simplified.
110 */
[5a6cc679]111static errno_t usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
[132a6073]112{
113 uint8_t sense_key;
114 scsi_sense_data_t sense_buf;
[5a6cc679]115 errno_t rc;
[45cae6b]116
[132a6073]117 do {
[ce25903]118 rc = usb_massstor_unit_ready(mfun);
119 if (rc != EOK) {
[a1732929]120 usb_log_error("Inquiry transport failed, device %s: %s.",
[ae3a941]121 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[ce25903]122 return rc;
123 }
124
[132a6073]125 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
126 if (rc != EOK) {
[a1732929]127 usb_log_error("Inquiry transport failed, device %s: %s.",
[ae3a941]128 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[132a6073]129 return rc;
130 }
131
132 if (cmd->status == CMDS_GOOD)
133 return EOK;
134
[a1732929]135 usb_log_error("SCSI command failed, device %s.",
[7c69861]136 usb_device_get_name(mfun->mdev->usb_dev));
[132a6073]137
138 rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
139 if (rc != EOK) {
[a1732929]140 usb_log_error("Failed to read sense data.");
[132a6073]141 return EIO;
142 }
143
144 /* Dump sense data to log */
145 usbmast_dump_sense(&sense_buf);
146
147 /* Get sense key */
[582fe388]148 sense_key = sense_buf.flags_key & 0x0f;
[132a6073]149
150 if (sense_key == SCSI_SK_UNIT_ATTENTION) {
151 printf("Got unit attention. Re-trying command.\n");
152 }
153
154 } while (sense_key == SCSI_SK_UNIT_ATTENTION);
155
156 /* Command status is not good, nevertheless transport succeeded. */
157 return EOK;
[45cae6b]158}
159
[71fa44c]160/** Perform SCSI Inquiry command on USB mass storage device.
[cf002dbf]161 *
[7190bbc]162 * @param mfun Mass storage function
163 * @param inquiry_result Where to store parsed inquiry result
164 * @return Error code
[cf002dbf]165 */
[5a6cc679]166errno_t usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
[70c12d6]167{
[7b2c17c]168 scsi_std_inquiry_data_t inq_data;
[de3432b]169 scsi_cmd_t cmd;
[71fa44c]170 scsi_cdb_inquiry_t cdb;
[5a6cc679]171 errno_t rc;
[70c12d6]172
[71fa44c]173 memset(&cdb, 0, sizeof(cdb));
174 cdb.op_code = SCSI_CMD_INQUIRY;
175 cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
176
[de3432b]177 memset(&cmd, 0, sizeof(cmd));
178 cmd.cdb = &cdb;
179 cmd.cdb_size = sizeof(cdb);
180 cmd.data_in = &inq_data;
181 cmd.data_in_size = sizeof(inq_data);
182
183 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
[70c12d6]184
185 if (rc != EOK) {
[a1732929]186 usb_log_error("Inquiry transport failed, device %s: %s.",
[ae3a941]187 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[70c12d6]188 return rc;
189 }
190
[de3432b]191 if (cmd.status != CMDS_GOOD) {
[a1732929]192 usb_log_error("Inquiry command failed, device %s.",
[ae3a941]193 usb_device_get_name(mfun->mdev->usb_dev));
[45cae6b]194 return EIO;
195 }
196
[de3432b]197 if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
[a1732929]198 usb_log_error("SCSI Inquiry response too short (%zu).",
[1433ecda]199 cmd.rcvd_size);
[7b2c17c]200 return EIO;
[70c12d6]201 }
202
203 /*
[7b2c17c]204 * Parse inquiry data and fill in the result structure.
[70c12d6]205 */
[7b2c17c]206
[acdb5bac]207 memset(inq_res, 0, sizeof(*inq_res));
[70c12d6]208
[6430ac6]209 inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
[64e6945d]210 inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
[70c12d6]211
[6430ac6]212 inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
[64e6945d]213 inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
[d2fac08c]214
[6430ac6]215 spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
[dcb74c0a]216 inq_data.vendor, sizeof(inq_data.vendor));
[70c12d6]217
[6430ac6]218 spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
[dcb74c0a]219 inq_data.product, sizeof(inq_data.product));
[70c12d6]220
[6430ac6]221 spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
[dcb74c0a]222 inq_data.revision, sizeof(inq_data.revision));
[70c12d6]223
224 return EOK;
225}
226
[f7a55f9]227/** Perform SCSI Request Sense command on USB mass storage device.
228 *
[7190bbc]229 * @param mfun Mass storage function
[f7a55f9]230 * @param buf Destination buffer
231 * @param size Size of @a buf
232 *
233 * @return Error code.
234 */
[5a6cc679]235errno_t usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
[f7a55f9]236{
[de3432b]237 scsi_cmd_t cmd;
[f7a55f9]238 scsi_cdb_request_sense_t cdb;
[5a6cc679]239 errno_t rc;
[f7a55f9]240
241 memset(&cdb, 0, sizeof(cdb));
242 cdb.op_code = SCSI_CMD_REQUEST_SENSE;
243 cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
244
[de3432b]245 memset(&cmd, 0, sizeof(cmd));
246 cmd.cdb = &cdb;
247 cmd.cdb_size = sizeof(cdb);
248 cmd.data_in = buf;
249 cmd.data_in_size = size;
250
251 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
[f7a55f9]252
[ae3a941]253 if (rc != EOK || cmd.status != CMDS_GOOD) {
[a1732929]254 usb_log_error("Request Sense failed, device %s: %s.",
[ae3a941]255 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[f7a55f9]256 return rc;
257 }
258
[de3432b]259 if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
[f7a55f9]260 /* The missing bytes should be considered to be zeroes. */
[de3432b]261 memset((uint8_t *)buf + cmd.rcvd_size, 0,
262 SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
[f7a55f9]263 }
264
265 return EOK;
266}
267
[71fa44c]268/** Perform SCSI Read Capacity command on USB mass storage device.
269 *
[7190bbc]270 * @param mfun Mass storage function
271 * @param nblocks Output, number of blocks
272 * @param block_size Output, block size in bytes
[71fa44c]273 *
274 * @return Error code.
275 */
[5a6cc679]276errno_t usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
[71fa44c]277 uint32_t *block_size)
278{
[de3432b]279 scsi_cmd_t cmd;
[71fa44c]280 scsi_cdb_read_capacity_10_t cdb;
281 scsi_read_capacity_10_data_t data;
[5a6cc679]282 errno_t rc;
[71fa44c]283
284 memset(&cdb, 0, sizeof(cdb));
285 cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
286
[de3432b]287 memset(&cmd, 0, sizeof(cmd));
288 cmd.cdb = &cdb;
289 cmd.cdb_size = sizeof(cdb);
290 cmd.data_in = &data;
291 cmd.data_in_size = sizeof(data);
292
[132a6073]293 rc = usbmast_run_cmd(mfun, &cmd);
[71fa44c]294
[f5718c79]295 if (rc != EOK) {
[a1732929]296 usb_log_error("Read Capacity (10) transport failed, device %s: %s.",
[ae3a941]297 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[71fa44c]298 return rc;
299 }
300
[de3432b]301 if (cmd.status != CMDS_GOOD) {
[a1732929]302 usb_log_error("Read Capacity (10) command failed, device %s.",
[ae3a941]303 usb_device_get_name(mfun->mdev->usb_dev));
[45cae6b]304 return EIO;
305 }
306
[de3432b]307 if (cmd.rcvd_size < sizeof(data)) {
[a1732929]308 usb_log_error("SCSI Read Capacity response too short (%zu).",
[1433ecda]309 cmd.rcvd_size);
[71fa44c]310 return EIO;
311 }
312
313 *nblocks = uint32_t_be2host(data.last_lba) + 1;
314 *block_size = uint32_t_be2host(data.block_size);
315
316 return EOK;
317}
318
[f7a55f9]319/** Perform SCSI Read command on USB mass storage device.
320 *
[7190bbc]321 * @param mfun Mass storage function
322 * @param ba Address of first block
323 * @param nblocks Number of blocks to read
[f7a55f9]324 *
[7190bbc]325 * @return Error code
[f7a55f9]326 */
[5a6cc679]327errno_t usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
[f7a55f9]328{
[de3432b]329 scsi_cmd_t cmd;
[132a6073]330 scsi_cdb_read_10_t cdb;
[5a6cc679]331 errno_t rc;
[f7a55f9]332
333 if (ba > UINT32_MAX)
334 return ELIMIT;
335
[132a6073]336 if (nblocks > UINT16_MAX)
[f7a55f9]337 return ELIMIT;
338
339 memset(&cdb, 0, sizeof(cdb));
[132a6073]340 cdb.op_code = SCSI_CMD_READ_10;
[f7a55f9]341 cdb.lba = host2uint32_t_be(ba);
[132a6073]342 cdb.xfer_len = host2uint16_t_be(nblocks);
[f7a55f9]343
[de3432b]344 memset(&cmd, 0, sizeof(cmd));
345 cmd.cdb = &cdb;
346 cmd.cdb_size = sizeof(cdb);
347 cmd.data_in = buf;
348 cmd.data_in_size = nblocks * mfun->block_size;
349
[132a6073]350 rc = usbmast_run_cmd(mfun, &cmd);
[f7a55f9]351
[1433ecda]352 if (rc != EOK) {
[a1732929]353 usb_log_error("Read (10) transport failed, device %s: %s.",
[ae3a941]354 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[f7a55f9]355 return rc;
356 }
357
[de3432b]358 if (cmd.status != CMDS_GOOD) {
[a1732929]359 usb_log_error("Read (10) command failed, device %s.",
[ae3a941]360 usb_device_get_name(mfun->mdev->usb_dev));
[45cae6b]361 return EIO;
362 }
363
[de3432b]364 if (cmd.rcvd_size < nblocks * mfun->block_size) {
[a1732929]365 usb_log_error("SCSI Read response too short (%zu).",
[de3432b]366 cmd.rcvd_size);
[f7a55f9]367 return EIO;
368 }
369
370 return EOK;
371}
372
[38c9505]373/** Perform SCSI Write command on USB mass storage device.
374 *
[7190bbc]375 * @param mfun Mass storage function
[38c9505]376 * @param ba Address of first block
377 * @param nblocks Number of blocks to read
378 * @param data Data to write
379 *
380 * @return Error code
381 */
[5a6cc679]382errno_t usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
[38c9505]383 const void *data)
384{
[de3432b]385 scsi_cmd_t cmd;
[132a6073]386 scsi_cdb_write_10_t cdb;
[5a6cc679]387 errno_t rc;
[38c9505]388
389 if (ba > UINT32_MAX)
390 return ELIMIT;
391
[132a6073]392 if (nblocks > UINT16_MAX)
[38c9505]393 return ELIMIT;
394
395 memset(&cdb, 0, sizeof(cdb));
[132a6073]396 cdb.op_code = SCSI_CMD_WRITE_10;
[38c9505]397 cdb.lba = host2uint32_t_be(ba);
[132a6073]398 cdb.xfer_len = host2uint16_t_be(nblocks);
[38c9505]399
[de3432b]400 memset(&cmd, 0, sizeof(cmd));
401 cmd.cdb = &cdb;
402 cmd.cdb_size = sizeof(cdb);
403 cmd.data_out = data;
404 cmd.data_out_size = nblocks * mfun->block_size;
405
[132a6073]406 rc = usbmast_run_cmd(mfun, &cmd);
[38c9505]407
[ae3a941]408 if (rc != EOK) {
[a1732929]409 usb_log_error("Write (10) transport failed, device %s: %s.",
[ae3a941]410 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[38c9505]411 return rc;
412 }
413
[de3432b]414 if (cmd.status != CMDS_GOOD) {
[a1732929]415 usb_log_error("Write (10) command failed, device %s.",
[ae3a941]416 usb_device_get_name(mfun->mdev->usb_dev));
[45cae6b]417 return EIO;
418 }
419
[38c9505]420 return EOK;
421}
422
[dd8b6a8]423/** Perform SCSI Synchronize Cache command on USB mass storage device.
424 *
425 * @param mfun Mass storage function
426 * @param ba Address of first block
427 * @param nblocks Number of blocks to read
428 * @param data Data to write
429 *
430 * @return Error code
431 */
[5a6cc679]432errno_t usbmast_sync_cache(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks)
[dd8b6a8]433{
434 if (ba > UINT32_MAX)
435 return ELIMIT;
436
437 if (nblocks > UINT16_MAX)
438 return ELIMIT;
439
[83062ff]440 const scsi_cdb_sync_cache_10_t cdb = {
441 .op_code = SCSI_CMD_SYNC_CACHE_10,
442 .lba = host2uint32_t_be(ba),
443 .numlb = host2uint16_t_be(nblocks),
444 };
[dd8b6a8]445
[83062ff]446 scsi_cmd_t cmd = {
447 .cdb = &cdb,
448 .cdb_size = sizeof(cdb),
449 };
[dd8b6a8]450
[5a6cc679]451 const errno_t rc = usbmast_run_cmd(mfun, &cmd);
[dd8b6a8]452
[f5718c79]453 if (rc != EOK) {
[a1732929]454 usb_log_error("Synchronize Cache (10) transport failed, device %s: %s.",
[ae3a941]455 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
[dd8b6a8]456 return rc;
457 }
458
459 if (cmd.status != CMDS_GOOD) {
[a1732929]460 usb_log_error("Synchronize Cache (10) command failed, device %s.",
[ae3a941]461 usb_device_get_name(mfun->mdev->usb_dev));
[dd8b6a8]462 return EIO;
463 }
464
465 return EOK;
466}
467
[70c12d6]468/**
469 * @}
470 */
Note: See TracBrowser for help on using the repository browser.