source: mainline/uspace/drv/bus/usb/usbmast/main.c@ 1d5a540

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

Merge DDF interface tightening.

  • Property mode set to 100644
File size: 10.1 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 * Main routines of USB mass storage driver.
36 */
37#include <as.h>
38#include <async.h>
39#include <bd_srv.h>
40#include <macros.h>
41#include <usb/dev/driver.h>
42#include <usb/debug.h>
43#include <usb/classes/classes.h>
44#include <usb/classes/massstor.h>
45#include <errno.h>
46#include <str_error.h>
47#include "cmdw.h"
48#include "bo_trans.h"
49#include "scsi_ms.h"
50#include "usbmast.h"
51
52#define NAME "usbmast"
53
54#define GET_BULK_IN(dev) ((dev)->pipes[BULK_IN_EP].pipe)
55#define GET_BULK_OUT(dev) ((dev)->pipes[BULK_OUT_EP].pipe)
56
57static const usb_endpoint_description_t bulk_in_ep = {
58 .transfer_type = USB_TRANSFER_BULK,
59 .direction = USB_DIRECTION_IN,
60 .interface_class = USB_CLASS_MASS_STORAGE,
61 .interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
62 .interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
63 .flags = 0
64};
65static const usb_endpoint_description_t bulk_out_ep = {
66 .transfer_type = USB_TRANSFER_BULK,
67 .direction = USB_DIRECTION_OUT,
68 .interface_class = USB_CLASS_MASS_STORAGE,
69 .interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
70 .interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
71 .flags = 0
72};
73
74static const usb_endpoint_description_t *mast_endpoints[] = {
75 &bulk_in_ep,
76 &bulk_out_ep,
77 NULL
78};
79
80static int usbmast_fun_create(usbmast_dev_t *mdev, unsigned lun);
81static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
82 void *arg);
83
84static int usbmast_bd_open(bd_srvs_t *, bd_srv_t *);
85static int usbmast_bd_close(bd_srv_t *);
86static int usbmast_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
87static int usbmast_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
88static int usbmast_bd_get_block_size(bd_srv_t *, size_t *);
89static int usbmast_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
90
91static bd_ops_t usbmast_bd_ops = {
92 .open = usbmast_bd_open,
93 .close = usbmast_bd_close,
94 .read_blocks = usbmast_bd_read_blocks,
95 .write_blocks = usbmast_bd_write_blocks,
96 .get_block_size = usbmast_bd_get_block_size,
97 .get_num_blocks = usbmast_bd_get_num_blocks
98};
99
100static usbmast_fun_t *bd_srv_usbmast(bd_srv_t *bd)
101{
102 return (usbmast_fun_t *) bd->srvs->sarg;
103}
104
105/** Callback when a device is removed from the system.
106 *
107 * @param dev Representation of USB device.
108 * @return Error code.
109 */
110static int usbmast_device_gone(usb_device_t *dev)
111{
112 usbmast_dev_t *mdev = dev->driver_data;
113 assert(mdev);
114
115 for (size_t i = 0; i < mdev->lun_count; ++i) {
116 const int rc = ddf_fun_unbind(mdev->luns[i]);
117 if (rc != EOK) {
118 usb_log_error("Failed to unbind LUN function %zu: "
119 "%s\n", i, str_error(rc));
120 return rc;
121 }
122 ddf_fun_destroy(mdev->luns[i]);
123 mdev->luns[i] = NULL;
124 }
125 free(mdev->luns);
126 return EOK;
127}
128
129/** Callback when a device is about to be removed.
130 *
131 * @param dev Representation of USB device.
132 * @return Error code.
133 */
134static int usbmast_device_remove(usb_device_t *dev)
135{
136 //TODO: flush buffers, or whatever.
137 //TODO: remove device
138 return ENOTSUP;
139}
140
141/** Callback when new device is attached and recognized as a mass storage.
142 *
143 * @param dev Representation of USB device.
144 * @return Error code.
145 */
146static int usbmast_device_add(usb_device_t *dev)
147{
148 int rc;
149 usbmast_dev_t *mdev = NULL;
150 unsigned i;
151
152 /* Allocate softstate */
153 mdev = usb_device_data_alloc(dev, sizeof(usbmast_dev_t));
154 if (mdev == NULL) {
155 usb_log_error("Failed allocating softstate.\n");
156 return ENOMEM;
157 }
158
159 mdev->ddf_dev = dev->ddf_dev;
160 mdev->usb_dev = dev;
161
162 usb_log_info("Initializing mass storage `%s'.\n", ddf_dev_get_name(dev->ddf_dev));
163 usb_log_debug("Bulk in endpoint: %d [%zuB].\n",
164 dev->pipes[BULK_IN_EP].pipe.endpoint_no,
165 dev->pipes[BULK_IN_EP].pipe.max_packet_size);
166 usb_log_debug("Bulk out endpoint: %d [%zuB].\n",
167 dev->pipes[BULK_OUT_EP].pipe.endpoint_no,
168 dev->pipes[BULK_OUT_EP].pipe.max_packet_size);
169
170 usb_log_debug("Get LUN count...\n");
171 mdev->lun_count = usb_masstor_get_lun_count(mdev);
172 mdev->luns = calloc(mdev->lun_count, sizeof(ddf_fun_t*));
173 if (mdev->luns == NULL) {
174 rc = ENOMEM;
175 usb_log_error("Failed allocating luns table.\n");
176 goto error;
177 }
178
179 for (i = 0; i < mdev->lun_count; i++) {
180 rc = usbmast_fun_create(mdev, i);
181 if (rc != EOK)
182 goto error;
183 }
184
185 return EOK;
186error:
187 /* Destroy functions */
188 for (size_t i = 0; i < mdev->lun_count; ++i) {
189 if (mdev->luns[i] == NULL)
190 continue;
191 const int rc = ddf_fun_unbind(mdev->luns[i]);
192 if (rc != EOK) {
193 usb_log_warning("Failed to unbind LUN function %zu: "
194 "%s.\n", i, str_error(rc));
195 }
196 ddf_fun_destroy(mdev->luns[i]);
197 }
198 free(mdev->luns);
199 return rc;
200}
201
202/** Create mass storage function.
203 *
204 * Called once for each LUN.
205 *
206 * @param mdev Mass storage device
207 * @param lun LUN
208 * @return EOK on success or negative error code.
209 */
210static int usbmast_fun_create(usbmast_dev_t *mdev, unsigned lun)
211{
212 int rc;
213 char *fun_name = NULL;
214 ddf_fun_t *fun = NULL;
215 usbmast_fun_t *mfun = NULL;
216
217 if (asprintf(&fun_name, "l%u", lun) < 0) {
218 usb_log_error("Out of memory.\n");
219 rc = ENOMEM;
220 goto error;
221 }
222
223 fun = ddf_fun_create(mdev->ddf_dev, fun_exposed, fun_name);
224 if (fun == NULL) {
225 usb_log_error("Failed to create DDF function %s.\n", fun_name);
226 rc = ENOMEM;
227 goto error;
228 }
229
230 /* Allocate soft state */
231 mfun = ddf_fun_data_alloc(fun, sizeof(usbmast_fun_t));
232 if (mfun == NULL) {
233 usb_log_error("Failed allocating softstate.\n");
234 rc = ENOMEM;
235 goto error;
236 }
237
238 mfun->ddf_fun = fun;
239 mfun->mdev = mdev;
240 mfun->lun = lun;
241
242 bd_srvs_init(&mfun->bds);
243 mfun->bds.ops = &usbmast_bd_ops;
244 mfun->bds.sarg = mfun;
245
246 /* Set up a connection handler. */
247 ddf_fun_set_conn_handler(fun, usbmast_bd_connection);
248
249 usb_log_debug("Inquire...\n");
250 usbmast_inquiry_data_t inquiry;
251 rc = usbmast_inquiry(mfun, &inquiry);
252 if (rc != EOK) {
253 usb_log_warning("Failed to inquire device `%s': %s.\n",
254 ddf_dev_get_name(mdev->ddf_dev), str_error(rc));
255 rc = EIO;
256 goto error;
257 }
258
259 usb_log_info("Mass storage `%s' LUN %u: " \
260 "%s by %s rev. %s is %s (%s).\n",
261 ddf_dev_get_name(mdev->ddf_dev),
262 lun,
263 inquiry.product,
264 inquiry.vendor,
265 inquiry.revision,
266 usbmast_scsi_dev_type_str(inquiry.device_type),
267 inquiry.removable ? "removable" : "non-removable");
268
269 uint32_t nblocks, block_size;
270
271 rc = usbmast_read_capacity(mfun, &nblocks, &block_size);
272 if (rc != EOK) {
273 usb_log_warning("Failed to read capacity, device `%s': %s.\n",
274 ddf_dev_get_name(mdev->ddf_dev), str_error(rc));
275 rc = EIO;
276 goto error;
277 }
278
279 usb_log_info("Read Capacity: nblocks=%" PRIu32 ", "
280 "block_size=%" PRIu32 "\n", nblocks, block_size);
281
282 mfun->nblocks = nblocks;
283 mfun->block_size = block_size;
284
285 rc = ddf_fun_bind(fun);
286 if (rc != EOK) {
287 usb_log_error("Failed to bind DDF function %s: %s.\n",
288 fun_name, str_error(rc));
289 goto error;
290 }
291
292 free(fun_name);
293 mdev->luns[lun] = fun;
294
295 return EOK;
296
297 /* Error cleanup */
298error:
299 if (fun != NULL)
300 ddf_fun_destroy(fun);
301 if (fun_name != NULL)
302 free(fun_name);
303 return rc;
304}
305
306/** Blockdev client connection handler. */
307static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
308 void *arg)
309{
310 usbmast_fun_t *mfun;
311
312 mfun = (usbmast_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
313 bd_conn(iid, icall, &mfun->bds);
314}
315
316/** Open device. */
317static int usbmast_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
318{
319 return EOK;
320}
321
322/** Close device. */
323static int usbmast_bd_close(bd_srv_t *bd)
324{
325 return EOK;
326}
327
328/** Read blocks from the device. */
329static int usbmast_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt, void *buf,
330 size_t size)
331{
332 usbmast_fun_t *mfun = bd_srv_usbmast(bd);
333
334 if (size < cnt * mfun->block_size)
335 return EINVAL;
336
337 return usbmast_read(mfun, ba, cnt, buf);
338}
339
340/** Write blocks to the device. */
341static int usbmast_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
342 const void *buf, size_t size)
343{
344 usbmast_fun_t *mfun = bd_srv_usbmast(bd);
345
346 if (size < cnt * mfun->block_size)
347 return EINVAL;
348
349 return usbmast_write(mfun, ba, cnt, buf);
350}
351
352/** Get device block size. */
353static int usbmast_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
354{
355 usbmast_fun_t *mfun = bd_srv_usbmast(bd);
356 *rsize = mfun->block_size;
357 return EOK;
358}
359
360/** Get number of blocks on device. */
361static int usbmast_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
362{
363 usbmast_fun_t *mfun = bd_srv_usbmast(bd);
364 *rnb = mfun->nblocks;
365 return EOK;
366}
367
368
369/** USB mass storage driver ops. */
370static const usb_driver_ops_t usbmast_driver_ops = {
371 .device_add = usbmast_device_add,
372 .device_rem = usbmast_device_remove,
373 .device_gone = usbmast_device_gone,
374};
375
376/** USB mass storage driver. */
377static const usb_driver_t usbmast_driver = {
378 .name = NAME,
379 .ops = &usbmast_driver_ops,
380 .endpoints = mast_endpoints
381};
382
383int main(int argc, char *argv[])
384{
385 usb_log_enable(USB_LOG_LEVEL_DEFAULT, NAME);
386
387 return usb_driver_main(&usbmast_driver);
388}
389
390/**
391 * @}
392 */
Note: See TracBrowser for help on using the repository browser.