source: mainline/uspace/drv/block/usbmast/main.c@ 9bfa8c8

Last change on this file since 9bfa8c8 was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

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