source: mainline/uspace/drv/bus/usb/usbmast/scsi_ms.c@ 5357620

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

Repeat SCSI command in case of unit attention condition.
Use Read/Write (10) instead of Read/Write (12).

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