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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e0a5d4c was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • 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.