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

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

Prepare for supporting multiple LUNs.

  • Property mode set to 100644
File size: 7.7 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
63/** Perform SCSI Inquiry command on USB mass storage device.
64 *
65 * @param mfun Mass storage function
66 * @param inquiry_result Where to store parsed inquiry result
67 * @return Error code
68 */
69int usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
70{
71 scsi_std_inquiry_data_t inq_data;
72 size_t response_len;
73 scsi_cdb_inquiry_t cdb;
74 int rc;
75
76 memset(&cdb, 0, sizeof(cdb));
77 cdb.op_code = SCSI_CMD_INQUIRY;
78 cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
79
80 rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
81 sizeof(cdb), &inq_data, sizeof(inq_data), &response_len);
82
83 if (rc != EOK) {
84 usb_log_error("Inquiry failed, device %s: %s.\n",
85 mfun->usb_dev->ddf_dev->name, str_error(rc));
86 return rc;
87 }
88
89 if (response_len < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
90 usb_log_error("SCSI Inquiry response too short (%zu).\n",
91 response_len);
92 return EIO;
93 }
94
95 /*
96 * Parse inquiry data and fill in the result structure.
97 */
98
99 bzero(inq_res, sizeof(*inq_res));
100
101 inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
102 inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
103
104 inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
105 inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
106
107 spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
108 inq_data.vendor, sizeof(inq_data.vendor));
109
110 spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
111 inq_data.product, sizeof(inq_data.product));
112
113 spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
114 inq_data.revision, sizeof(inq_data.revision));
115
116 return EOK;
117}
118
119/** Perform SCSI Request Sense command on USB mass storage device.
120 *
121 * @param mfun Mass storage function
122 * @param buf Destination buffer
123 * @param size Size of @a buf
124 *
125 * @return Error code.
126 */
127int usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
128{
129 scsi_cdb_request_sense_t cdb;
130 size_t data_len;
131 int rc;
132
133 memset(&cdb, 0, sizeof(cdb));
134 cdb.op_code = SCSI_CMD_REQUEST_SENSE;
135 cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
136
137 rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
138 sizeof(cdb), buf, size, &data_len);
139
140 if (rc != EOK) {
141 usb_log_error("Request Sense failed, device %s: %s.\n",
142 mfun->usb_dev->ddf_dev->name, str_error(rc));
143 return rc;
144 }
145
146 if (data_len < SCSI_SENSE_DATA_MIN_SIZE) {
147 /* The missing bytes should be considered to be zeroes. */
148 memset((uint8_t *)buf + data_len, 0,
149 SCSI_SENSE_DATA_MIN_SIZE - data_len);
150 }
151
152 return EOK;
153}
154
155/** Perform SCSI Read Capacity command on USB mass storage device.
156 *
157 * @param mfun Mass storage function
158 * @param nblocks Output, number of blocks
159 * @param block_size Output, block size in bytes
160 *
161 * @return Error code.
162 */
163int usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
164 uint32_t *block_size)
165{
166 scsi_cdb_read_capacity_10_t cdb;
167 scsi_read_capacity_10_data_t data;
168 size_t data_len;
169 int rc;
170
171 memset(&cdb, 0, sizeof(cdb));
172 cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
173
174 rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
175 sizeof(cdb), &data, sizeof(data), &data_len);
176
177 if (rc != EOK) {
178 usb_log_error("Read Capacity (10) failed, device %s: %s.\n",
179 mfun->usb_dev->ddf_dev->name, str_error(rc));
180 return rc;
181 }
182
183 if (data_len < sizeof(data)) {
184 usb_log_error("SCSI Read Capacity response too short (%zu).\n",
185 data_len);
186 return EIO;
187 }
188
189 *nblocks = uint32_t_be2host(data.last_lba) + 1;
190 *block_size = uint32_t_be2host(data.block_size);
191
192 return EOK;
193}
194
195/** Perform SCSI Read command on USB mass storage device.
196 *
197 * @param mfun Mass storage function
198 * @param ba Address of first block
199 * @param nblocks Number of blocks to read
200 *
201 * @return Error code
202 */
203int usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
204{
205 scsi_cdb_read_12_t cdb;
206 size_t data_len;
207 int rc;
208
209 /* XXX Need softstate to store block size. */
210
211 if (ba > UINT32_MAX)
212 return ELIMIT;
213
214 if ((uint64_t)nblocks * mfun->block_size > UINT32_MAX)
215 return ELIMIT;
216
217 memset(&cdb, 0, sizeof(cdb));
218 cdb.op_code = SCSI_CMD_READ_12;
219 cdb.lba = host2uint32_t_be(ba);
220 cdb.xfer_len = host2uint32_t_be(nblocks);
221
222 rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
223 sizeof(cdb), buf, nblocks * mfun->block_size, &data_len);
224
225 if (rc != EOK) {
226 usb_log_error("Read (12) failed, device %s: %s.\n",
227 mfun->usb_dev->ddf_dev->name, str_error(rc));
228 return rc;
229 }
230
231 if (data_len < nblocks * mfun->block_size) {
232 usb_log_error("SCSI Read response too short (%zu).\n",
233 data_len);
234 return EIO;
235 }
236
237 return EOK;
238}
239
240/** Perform SCSI Write command on USB mass storage device.
241 *
242 * @param mfun Mass storage function
243 * @param ba Address of first block
244 * @param nblocks Number of blocks to read
245 * @param data Data to write
246 *
247 * @return Error code
248 */
249int usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
250 const void *data)
251{
252 scsi_cdb_write_12_t cdb;
253 size_t sent_len;
254 int rc;
255
256 if (ba > UINT32_MAX)
257 return ELIMIT;
258
259 if ((uint64_t)nblocks * mfun->block_size > UINT32_MAX)
260 return ELIMIT;
261
262 memset(&cdb, 0, sizeof(cdb));
263 cdb.op_code = SCSI_CMD_WRITE_12;
264 cdb.lba = host2uint32_t_be(ba);
265 cdb.xfer_len = host2uint32_t_be(nblocks);
266
267 rc = usb_massstor_data_out(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
268 sizeof(cdb), data, nblocks * mfun->block_size, &sent_len);
269
270 if (rc != EOK) {
271 usb_log_error("Write (12) failed, device %s: %s.\n",
272 mfun->usb_dev->ddf_dev->name, str_error(rc));
273 return rc;
274 }
275
276 if (sent_len < nblocks * mfun->block_size) {
277 usb_log_error("SCSI Write not all bytes transferred (%zu).\n",
278 sent_len);
279 return EIO;
280 }
281
282 return EOK;
283}
284
285/**
286 * @}
287 */
Note: See TracBrowser for help on using the repository browser.