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
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jiri Svoboda
4 * Copyright (c) 2018 Ondrej Hlavaty
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
36 * SCSI functions for USB mass storage driver.
37 */
38
39#include <bitops.h>
40#include <byteorder.h>
41#include <inttypes.h>
42#include <macros.h>
43#include <usb/dev/driver.h>
44#include <usb/debug.h>
45#include <errno.h>
46#include <str_error.h>
47#include <str.h>
48#include <scsi/sbc.h>
49#include <scsi/spc.h>
50#include "cmdw.h"
51#include "bo_trans.h"
52#include "scsi_ms.h"
53#include "usbmast.h"
54
55/** Get string representation for SCSI peripheral device type.
56 *
57 * @param type SCSI peripheral device type code.
58 * @return String representation.
59 */
60const char *usbmast_scsi_dev_type_str(unsigned type)
61{
62 return scsi_get_dev_type_str(type);
63}
64
65static void usbmast_dump_sense(scsi_sense_data_t *sense_buf)
66{
67 const unsigned 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
75static errno_t usb_massstor_unit_ready(usbmast_fun_t *mfun)
76{
77 scsi_cmd_t cmd;
78 scsi_cdb_test_unit_ready_t cdb;
79 errno_t rc;
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
90 if (rc != EOK) {
91 usb_log_error("Test Unit Ready failed on device %s: %s.",
92 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
93 return rc;
94 }
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.",
100 usb_device_get_name(mfun->mdev->usb_dev));
101
102 return EOK;
103}
104
105/** Run SCSI command.
106 *
107 * Run command and repeat in case of unit attention.
108 * XXX This is too simplified.
109 */
110static errno_t usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
111{
112 uint8_t sense_key;
113 scsi_sense_data_t sense_buf;
114 errno_t rc;
115
116 do {
117 rc = usb_massstor_unit_ready(mfun);
118 if (rc != EOK) {
119 usb_log_error("Inquiry transport failed, device %s: %s.",
120 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
121 return rc;
122 }
123
124 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
125 if (rc != EOK) {
126 usb_log_error("Inquiry transport failed, device %s: %s.",
127 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
128 return rc;
129 }
130
131 if (cmd->status == CMDS_GOOD)
132 return EOK;
133
134 usb_log_error("SCSI command failed, device %s.",
135 usb_device_get_name(mfun->mdev->usb_dev));
136
137 rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
138 if (rc != EOK) {
139 usb_log_error("Failed to read sense data.");
140 return EIO;
141 }
142
143 /* Dump sense data to log */
144 usbmast_dump_sense(&sense_buf);
145
146 /* Get sense key */
147 sense_key = sense_buf.flags_key & 0x0f;
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;
157}
158
159/** Perform SCSI Inquiry command on USB mass storage device.
160 *
161 * @param mfun Mass storage function
162 * @param inquiry_result Where to store parsed inquiry result
163 * @return Error code
164 */
165errno_t usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
166{
167 scsi_std_inquiry_data_t inq_data;
168 scsi_cmd_t cmd;
169 scsi_cdb_inquiry_t cdb;
170 errno_t rc;
171
172 memset(&cdb, 0, sizeof(cdb));
173 cdb.op_code = SCSI_CMD_INQUIRY;
174 cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
175
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);
183
184 if (rc != EOK) {
185 usb_log_error("Inquiry transport failed, device %s: %s.",
186 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
187 return rc;
188 }
189
190 if (cmd.status != CMDS_GOOD) {
191 usb_log_error("Inquiry command failed, device %s.",
192 usb_device_get_name(mfun->mdev->usb_dev));
193 return EIO;
194 }
195
196 if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
197 usb_log_error("SCSI Inquiry response too short (%zu).",
198 cmd.rcvd_size);
199 return EIO;
200 }
201
202 /*
203 * Parse inquiry data and fill in the result structure.
204 */
205
206 memset(inq_res, 0, sizeof(*inq_res));
207
208 inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
209 inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
210
211 inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
212 inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
213
214 spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
215 inq_data.vendor, sizeof(inq_data.vendor));
216
217 spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
218 inq_data.product, sizeof(inq_data.product));
219
220 spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
221 inq_data.revision, sizeof(inq_data.revision));
222
223 return EOK;
224}
225
226/** Perform SCSI Request Sense command on USB mass storage device.
227 *
228 * @param mfun Mass storage function
229 * @param buf Destination buffer
230 * @param size Size of @a buf
231 *
232 * @return Error code.
233 */
234errno_t usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
235{
236 scsi_cmd_t cmd;
237 scsi_cdb_request_sense_t cdb;
238 errno_t rc;
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
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);
251
252 if (rc != EOK || cmd.status != CMDS_GOOD) {
253 usb_log_error("Request Sense failed, device %s: %s.",
254 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
255 return rc;
256 }
257
258 if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
259 /* The missing bytes should be considered to be zeroes. */
260 memset((uint8_t *)buf + cmd.rcvd_size, 0,
261 SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
262 }
263
264 return EOK;
265}
266
267/** Perform SCSI Read Capacity command on USB mass storage device.
268 *
269 * @param mfun Mass storage function
270 * @param nblocks Output, number of blocks
271 * @param block_size Output, block size in bytes
272 *
273 * @return Error code.
274 */
275errno_t usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
276 uint32_t *block_size)
277{
278 scsi_cmd_t cmd;
279 scsi_cdb_read_capacity_10_t cdb;
280 scsi_read_capacity_10_data_t data;
281 errno_t rc;
282
283 memset(&cdb, 0, sizeof(cdb));
284 cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
285
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
292 rc = usbmast_run_cmd(mfun, &cmd);
293
294 if (rc != EOK) {
295 usb_log_error("Read Capacity (10) transport failed, device %s: %s.",
296 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
297 return rc;
298 }
299
300 if (cmd.status != CMDS_GOOD) {
301 usb_log_error("Read Capacity (10) command failed, device %s.",
302 usb_device_get_name(mfun->mdev->usb_dev));
303 return EIO;
304 }
305
306 if (cmd.rcvd_size < sizeof(data)) {
307 usb_log_error("SCSI Read Capacity response too short (%zu).",
308 cmd.rcvd_size);
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
318/** Perform SCSI Read command on USB mass storage device.
319 *
320 * @param mfun Mass storage function
321 * @param ba Address of first block
322 * @param nblocks Number of blocks to read
323 *
324 * @return Error code
325 */
326errno_t usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
327{
328 scsi_cmd_t cmd;
329 scsi_cdb_read_10_t cdb;
330 errno_t rc;
331
332 if (ba > UINT32_MAX)
333 return ELIMIT;
334
335 if (nblocks > UINT16_MAX)
336 return ELIMIT;
337
338 memset(&cdb, 0, sizeof(cdb));
339 cdb.op_code = SCSI_CMD_READ_10;
340 cdb.lba = host2uint32_t_be(ba);
341 cdb.xfer_len = host2uint16_t_be(nblocks);
342
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
349 rc = usbmast_run_cmd(mfun, &cmd);
350
351 if (rc != EOK) {
352 usb_log_error("Read (10) transport failed, device %s: %s.",
353 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
354 return rc;
355 }
356
357 if (cmd.status != CMDS_GOOD) {
358 usb_log_error("Read (10) command failed, device %s.",
359 usb_device_get_name(mfun->mdev->usb_dev));
360 return EIO;
361 }
362
363 if (cmd.rcvd_size < nblocks * mfun->block_size) {
364 usb_log_error("SCSI Read response too short (%zu).",
365 cmd.rcvd_size);
366 return EIO;
367 }
368
369 return EOK;
370}
371
372/** Perform SCSI Write command on USB mass storage device.
373 *
374 * @param mfun Mass storage function
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 */
381errno_t usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
382 const void *data)
383{
384 scsi_cmd_t cmd;
385 scsi_cdb_write_10_t cdb;
386 errno_t rc;
387
388 if (ba > UINT32_MAX)
389 return ELIMIT;
390
391 if (nblocks > UINT16_MAX)
392 return ELIMIT;
393
394 memset(&cdb, 0, sizeof(cdb));
395 cdb.op_code = SCSI_CMD_WRITE_10;
396 cdb.lba = host2uint32_t_be(ba);
397 cdb.xfer_len = host2uint16_t_be(nblocks);
398
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
405 rc = usbmast_run_cmd(mfun, &cmd);
406
407 if (rc != EOK) {
408 usb_log_error("Write (10) transport failed, device %s: %s.",
409 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
410 return rc;
411 }
412
413 if (cmd.status != CMDS_GOOD) {
414 usb_log_error("Write (10) command failed, device %s.",
415 usb_device_get_name(mfun->mdev->usb_dev));
416 return EIO;
417 }
418
419 return EOK;
420}
421
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 */
431errno_t usbmast_sync_cache(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks)
432{
433 if (ba > UINT32_MAX)
434 return ELIMIT;
435
436 if (nblocks > UINT16_MAX)
437 return ELIMIT;
438
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 };
444
445 scsi_cmd_t cmd = {
446 .cdb = &cdb,
447 .cdb_size = sizeof(cdb),
448 };
449
450 const errno_t rc = usbmast_run_cmd(mfun, &cmd);
451
452 if (rc != EOK) {
453 usb_log_error("Synchronize Cache (10) transport failed, device %s: %s.",
454 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
455 return rc;
456 }
457
458 if (cmd.status != CMDS_GOOD) {
459 usb_log_error("Synchronize Cache (10) command failed, device %s.",
460 usb_device_get_name(mfun->mdev->usb_dev));
461 return EIO;
462 }
463
464 return EOK;
465}
466
467/**
468 * @}
469 */
Note: See TracBrowser for help on using the repository browser.