source: mainline/uspace/drv/block/usbmast/scsi_ms.c

Last change on this file 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
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 /*
96 * Ignore command error here. If there's something wrong
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.",
101 usb_device_get_name(mfun->mdev->usb_dev));
102
103 return EOK;
104}
105
106/** Run SCSI command.
107 *
108 * Run command and repeat in case of unit attention.
109 * XXX This is too simplified.
110 */
111static errno_t usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
112{
113 uint8_t sense_key;
114 scsi_sense_data_t sense_buf;
115 errno_t rc;
116
117 do {
118 rc = usb_massstor_unit_ready(mfun);
119 if (rc != EOK) {
120 usb_log_error("Inquiry transport failed, device %s: %s.",
121 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
122 return rc;
123 }
124
125 rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
126 if (rc != EOK) {
127 usb_log_error("Inquiry transport failed, device %s: %s.",
128 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
129 return rc;
130 }
131
132 if (cmd->status == CMDS_GOOD)
133 return EOK;
134
135 usb_log_error("SCSI command failed, device %s.",
136 usb_device_get_name(mfun->mdev->usb_dev));
137
138 rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
139 if (rc != EOK) {
140 usb_log_error("Failed to read sense data.");
141 return EIO;
142 }
143
144 /* Dump sense data to log */
145 usbmast_dump_sense(&sense_buf);
146
147 /* Get sense key */
148 sense_key = sense_buf.flags_key & 0x0f;
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;
158}
159
160/** Perform SCSI Inquiry command on USB mass storage device.
161 *
162 * @param mfun Mass storage function
163 * @param inquiry_result Where to store parsed inquiry result
164 * @return Error code
165 */
166errno_t usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
167{
168 scsi_std_inquiry_data_t inq_data;
169 scsi_cmd_t cmd;
170 scsi_cdb_inquiry_t cdb;
171 errno_t rc;
172
173 memset(&cdb, 0, sizeof(cdb));
174 cdb.op_code = SCSI_CMD_INQUIRY;
175 cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
176
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);
184
185 if (rc != EOK) {
186 usb_log_error("Inquiry transport failed, device %s: %s.",
187 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
188 return rc;
189 }
190
191 if (cmd.status != CMDS_GOOD) {
192 usb_log_error("Inquiry command failed, device %s.",
193 usb_device_get_name(mfun->mdev->usb_dev));
194 return EIO;
195 }
196
197 if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
198 usb_log_error("SCSI Inquiry response too short (%zu).",
199 cmd.rcvd_size);
200 return EIO;
201 }
202
203 /*
204 * Parse inquiry data and fill in the result structure.
205 */
206
207 memset(inq_res, 0, sizeof(*inq_res));
208
209 inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
210 inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
211
212 inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
213 inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
214
215 spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
216 inq_data.vendor, sizeof(inq_data.vendor));
217
218 spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
219 inq_data.product, sizeof(inq_data.product));
220
221 spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
222 inq_data.revision, sizeof(inq_data.revision));
223
224 return EOK;
225}
226
227/** Perform SCSI Request Sense command on USB mass storage device.
228 *
229 * @param mfun Mass storage function
230 * @param buf Destination buffer
231 * @param size Size of @a buf
232 *
233 * @return Error code.
234 */
235errno_t usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
236{
237 scsi_cmd_t cmd;
238 scsi_cdb_request_sense_t cdb;
239 errno_t rc;
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
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);
252
253 if (rc != EOK || cmd.status != CMDS_GOOD) {
254 usb_log_error("Request Sense failed, device %s: %s.",
255 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
256 return rc;
257 }
258
259 if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
260 /* The missing bytes should be considered to be zeroes. */
261 memset((uint8_t *)buf + cmd.rcvd_size, 0,
262 SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
263 }
264
265 return EOK;
266}
267
268/** Perform SCSI Read Capacity command on USB mass storage device.
269 *
270 * @param mfun Mass storage function
271 * @param nblocks Output, number of blocks
272 * @param block_size Output, block size in bytes
273 *
274 * @return Error code.
275 */
276errno_t usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
277 uint32_t *block_size)
278{
279 scsi_cmd_t cmd;
280 scsi_cdb_read_capacity_10_t cdb;
281 scsi_read_capacity_10_data_t data;
282 errno_t rc;
283
284 memset(&cdb, 0, sizeof(cdb));
285 cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
286
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
293 rc = usbmast_run_cmd(mfun, &cmd);
294
295 if (rc != EOK) {
296 usb_log_error("Read Capacity (10) transport failed, device %s: %s.",
297 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
298 return rc;
299 }
300
301 if (cmd.status != CMDS_GOOD) {
302 usb_log_error("Read Capacity (10) command failed, device %s.",
303 usb_device_get_name(mfun->mdev->usb_dev));
304 return EIO;
305 }
306
307 if (cmd.rcvd_size < sizeof(data)) {
308 usb_log_error("SCSI Read Capacity response too short (%zu).",
309 cmd.rcvd_size);
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
319/** Perform SCSI Read command on USB mass storage device.
320 *
321 * @param mfun Mass storage function
322 * @param ba Address of first block
323 * @param nblocks Number of blocks to read
324 *
325 * @return Error code
326 */
327errno_t usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
328{
329 scsi_cmd_t cmd;
330 scsi_cdb_read_10_t cdb;
331 errno_t rc;
332
333 if (ba > UINT32_MAX)
334 return ELIMIT;
335
336 if (nblocks > UINT16_MAX)
337 return ELIMIT;
338
339 memset(&cdb, 0, sizeof(cdb));
340 cdb.op_code = SCSI_CMD_READ_10;
341 cdb.lba = host2uint32_t_be(ba);
342 cdb.xfer_len = host2uint16_t_be(nblocks);
343
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
350 rc = usbmast_run_cmd(mfun, &cmd);
351
352 if (rc != EOK) {
353 usb_log_error("Read (10) transport failed, device %s: %s.",
354 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
355 return rc;
356 }
357
358 if (cmd.status != CMDS_GOOD) {
359 usb_log_error("Read (10) command failed, device %s.",
360 usb_device_get_name(mfun->mdev->usb_dev));
361 return EIO;
362 }
363
364 if (cmd.rcvd_size < nblocks * mfun->block_size) {
365 usb_log_error("SCSI Read response too short (%zu).",
366 cmd.rcvd_size);
367 return EIO;
368 }
369
370 return EOK;
371}
372
373/** Perform SCSI Write command on USB mass storage device.
374 *
375 * @param mfun Mass storage function
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 */
382errno_t usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
383 const void *data)
384{
385 scsi_cmd_t cmd;
386 scsi_cdb_write_10_t cdb;
387 errno_t rc;
388
389 if (ba > UINT32_MAX)
390 return ELIMIT;
391
392 if (nblocks > UINT16_MAX)
393 return ELIMIT;
394
395 memset(&cdb, 0, sizeof(cdb));
396 cdb.op_code = SCSI_CMD_WRITE_10;
397 cdb.lba = host2uint32_t_be(ba);
398 cdb.xfer_len = host2uint16_t_be(nblocks);
399
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
406 rc = usbmast_run_cmd(mfun, &cmd);
407
408 if (rc != EOK) {
409 usb_log_error("Write (10) transport failed, device %s: %s.",
410 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
411 return rc;
412 }
413
414 if (cmd.status != CMDS_GOOD) {
415 usb_log_error("Write (10) command failed, device %s.",
416 usb_device_get_name(mfun->mdev->usb_dev));
417 return EIO;
418 }
419
420 return EOK;
421}
422
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 */
432errno_t usbmast_sync_cache(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks)
433{
434 if (ba > UINT32_MAX)
435 return ELIMIT;
436
437 if (nblocks > UINT16_MAX)
438 return ELIMIT;
439
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 };
445
446 scsi_cmd_t cmd = {
447 .cdb = &cdb,
448 .cdb_size = sizeof(cdb),
449 };
450
451 const errno_t rc = usbmast_run_cmd(mfun, &cmd);
452
453 if (rc != EOK) {
454 usb_log_error("Synchronize Cache (10) transport failed, device %s: %s.",
455 usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
456 return rc;
457 }
458
459 if (cmd.status != CMDS_GOOD) {
460 usb_log_error("Synchronize Cache (10) command failed, device %s.",
461 usb_device_get_name(mfun->mdev->usb_dev));
462 return EIO;
463 }
464
465 return EOK;
466}
467
468/**
469 * @}
470 */
Note: See TracBrowser for help on using the repository browser.