source: mainline/uspace/drv/block/usbmast/main.c@ 74c0de0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 74c0de0 was f9d0a86, checked in by Aearsis <Hlavaty.Ondrej@…>, 8 years ago

Merge tag '0.7.1'

The merge wasn't clean, because of changes in build system. The most
significant change was partial revert of usbhc callback refactoring,
which now does not take usb transfer batch, but few named fields again.

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