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

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

Write support in USB mass storage driver.

  • 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 * Main routines of USB mass storage driver.
36 */
37#include <as.h>
38#include <async.h>
39#include <ipc/bd.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 "cmds.h"
48#include "mast.h"
49#include "scsi_ms.h"
50
51#define NAME "usbmast"
52
53#define GET_BULK_IN(dev) ((dev)->pipes[BULK_IN_EP].pipe)
54#define GET_BULK_OUT(dev) ((dev)->pipes[BULK_OUT_EP].pipe)
55
56static 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 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
73usb_endpoint_description_t *mast_endpoints[] = {
74 &bulk_in_ep,
75 &bulk_out_ep,
76 NULL
77};
78
79/** Mass storage function.
80 *
81 * Serves as soft state for function/LUN.
82 */
83typedef struct {
84 /** DDF function */
85 ddf_fun_t *ddf_fun;
86 /** Total number of blocks. */
87 uint64_t nblocks;
88 /** Block size in bytes. */
89 size_t block_size;
90 /** USB device function belongs to */
91 usb_device_t *usb_dev;
92} usbmast_fun_t;
93
94static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
95 void *arg);
96
97/** Callback when new device is attached and recognized as a mass storage.
98 *
99 * @param dev Representation of a the USB device.
100 * @return Error code.
101 */
102static int usbmast_add_device(usb_device_t *dev)
103{
104 int rc;
105 const char *fun_name = "a";
106 ddf_fun_t *fun = NULL;
107 usbmast_fun_t *msfun = NULL;
108
109 /* Allocate softstate */
110 msfun = calloc(1, sizeof(usbmast_fun_t));
111 if (msfun == NULL) {
112 usb_log_error("Failed allocating softstate.\n");
113 rc = ENOMEM;
114 goto error;
115 }
116
117 fun = ddf_fun_create(dev->ddf_dev, fun_exposed, fun_name);
118 if (fun == NULL) {
119 usb_log_error("Failed to create DDF function %s.\n", fun_name);
120 rc = ENOMEM;
121 goto error;
122 }
123
124 /* Set up a connection handler. */
125 fun->conn_handler = usbmast_bd_connection;
126 fun->driver_data = msfun;
127
128 usb_log_info("Initializing mass storage `%s'.\n",
129 dev->ddf_dev->name);
130 usb_log_debug(" Bulk in endpoint: %d [%zuB].\n",
131 dev->pipes[BULK_IN_EP].pipe->endpoint_no,
132 (size_t) dev->pipes[BULK_IN_EP].descriptor->max_packet_size);
133 usb_log_debug("Bulk out endpoint: %d [%zuB].\n",
134 dev->pipes[BULK_OUT_EP].pipe->endpoint_no,
135 (size_t) dev->pipes[BULK_OUT_EP].descriptor->max_packet_size);
136
137 usb_log_debug("Get LUN count...\n");
138 size_t lun_count = usb_masstor_get_lun_count(dev);
139
140 /* XXX Handle more than one LUN properly. */
141 if (lun_count > 1) {
142 usb_log_warning ("Mass storage has %zu LUNs. Ignoring all "
143 "but first.\n", lun_count);
144 }
145
146 usb_log_debug("Inquire...\n");
147 usbmast_inquiry_data_t inquiry;
148 rc = usbmast_inquiry(dev, &inquiry);
149 if (rc != EOK) {
150 usb_log_warning("Failed to inquire device `%s': %s.\n",
151 dev->ddf_dev->name, str_error(rc));
152 rc = EIO;
153 goto error;
154 }
155
156 usb_log_info("Mass storage `%s': " \
157 "%s by %s rev. %s is %s (%s), %zu LUN(s).\n",
158 dev->ddf_dev->name,
159 inquiry.product,
160 inquiry.vendor,
161 inquiry.revision,
162 usbmast_scsi_dev_type_str(inquiry.device_type),
163 inquiry.removable ? "removable" : "non-removable",
164 lun_count);
165
166 uint32_t nblocks, block_size;
167
168 rc = usbmast_read_capacity(dev, &nblocks, &block_size);
169 if (rc != EOK) {
170 usb_log_warning("Failed to read capacity, device `%s': %s.\n",
171 dev->ddf_dev->name, str_error(rc));
172 rc = EIO;
173 goto error;
174 }
175
176 usb_log_info("Read Capacity: nblocks=%" PRIu32 ", "
177 "block_size=%" PRIu32 "\n", nblocks, block_size);
178
179 msfun->nblocks = nblocks;
180 msfun->block_size = block_size;
181 msfun->usb_dev = dev;
182
183 rc = ddf_fun_bind(fun);
184 if (rc != EOK) {
185 usb_log_error("Failed to bind DDF function %s: %s.\n",
186 fun_name, str_error(rc));
187 goto error;
188 }
189
190 return EOK;
191
192 /* Error cleanup */
193error:
194 if (fun != NULL)
195 ddf_fun_destroy(fun);
196 if (msfun != NULL)
197 free(msfun);
198 return rc;
199}
200
201/** Blockdev client connection handler. */
202static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
203 void *arg)
204{
205 usbmast_fun_t *msfun;
206 void *comm_buf = NULL;
207 size_t comm_size;
208 ipc_callid_t callid;
209 ipc_call_t call;
210 unsigned int flags;
211 sysarg_t method;
212 uint64_t ba;
213 size_t cnt;
214 int retval;
215
216 async_answer_0(iid, EOK);
217
218 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
219 async_answer_0(callid, EHANGUP);
220 return;
221 }
222
223 comm_buf = as_get_mappable_page(comm_size);
224 if (comm_buf == NULL) {
225 async_answer_0(callid, EHANGUP);
226 return;
227 }
228
229 (void) async_share_out_finalize(callid, comm_buf);
230
231 msfun = (usbmast_fun_t *) ((ddf_fun_t *)arg)->driver_data;
232
233 while (true) {
234 callid = async_get_call(&call);
235 method = IPC_GET_IMETHOD(call);
236
237 if (!method) {
238 /* The other side hung up. */
239 async_answer_0(callid, EOK);
240 return;
241 }
242
243 switch (method) {
244 case BD_GET_BLOCK_SIZE:
245 async_answer_1(callid, EOK, msfun->block_size);
246 break;
247 case BD_GET_NUM_BLOCKS:
248 async_answer_2(callid, EOK, LOWER32(msfun->nblocks),
249 UPPER32(msfun->nblocks));
250 break;
251 case BD_READ_BLOCKS:
252 ba = MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
253 cnt = IPC_GET_ARG3(call);
254 retval = usbmast_read(msfun->usb_dev, ba, cnt,
255 msfun->block_size, comm_buf);
256 async_answer_0(callid, retval);
257 break;
258 case BD_WRITE_BLOCKS:
259 ba = MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
260 cnt = IPC_GET_ARG3(call);
261 retval = usbmast_write(msfun->usb_dev, ba, cnt,
262 msfun->block_size, comm_buf);
263 async_answer_0(callid, retval);
264 break;
265 default:
266 async_answer_0(callid, EINVAL);
267 }
268 }
269}
270
271/** USB mass storage driver ops. */
272static usb_driver_ops_t usbmast_driver_ops = {
273 .add_device = usbmast_add_device,
274};
275
276/** USB mass storage driver. */
277static usb_driver_t usbmast_driver = {
278 .name = NAME,
279 .ops = &usbmast_driver_ops,
280 .endpoints = mast_endpoints
281};
282
283int main(int argc, char *argv[])
284{
285 usb_log_enable(USB_LOG_LEVEL_DEFAULT, NAME);
286
287 return usb_driver_main(&usbmast_driver);
288}
289
290/**
291 * @}
292 */
Note: See TracBrowser for help on using the repository browser.