source: mainline/uspace/lib/usb/src/devdrv.c@ 4ede178

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4ede178 was 4ede178, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

No start/end pipe session calls in libusb

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusb
30 * @{
31 */
32/** @file
33 * USB device driver framework.
34 */
35#include <usb/devdrv.h>
36#include <usb/request.h>
37#include <usb/debug.h>
38#include <usb/dp.h>
39#include <errno.h>
40#include <str_error.h>
41#include <assert.h>
42
43static int generic_add_device(ddf_dev_t *);
44
45static driver_ops_t generic_driver_ops = {
46 .add_device = generic_add_device
47};
48static driver_t generic_driver = {
49 .driver_ops = &generic_driver_ops
50};
51
52static usb_driver_t *driver = NULL;
53
54
55/** Main routine of USB device driver.
56 *
57 * Under normal conditions, this function never returns.
58 *
59 * @param drv USB device driver structure.
60 * @return Task exit status.
61 */
62int usb_driver_main(usb_driver_t *drv)
63{
64 assert(drv != NULL);
65
66 /* Prepare the generic driver. */
67 generic_driver.name = drv->name;
68
69 driver = drv;
70
71 return ddf_driver_main(&generic_driver);
72}
73
74/** Log out of memory error on given device.
75 *
76 * @param dev Device causing the trouble.
77 */
78static void usb_log_oom(ddf_dev_t *dev)
79{
80 usb_log_error("Out of memory when adding device `%s'.\n",
81 dev->name);
82}
83
84/** Count number of pipes the driver expects.
85 *
86 * @param drv USB driver.
87 * @return Number of pipes (excluding default control pipe).
88 */
89static size_t count_other_pipes(usb_endpoint_description_t **endpoints)
90{
91 size_t count = 0;
92 if (endpoints == NULL) {
93 return 0;
94 }
95
96 while (endpoints[count] != NULL) {
97 count++;
98 }
99
100 return count;
101}
102
103/** Initialize endpoint pipes, excluding default control one.
104 *
105 * @param drv The device driver.
106 * @param dev Device to be initialized.
107 * @return Error code.
108 */
109static int initialize_other_pipes(usb_endpoint_description_t **endpoints,
110 usb_device_t *dev)
111{
112 int rc;
113
114 size_t pipe_count = count_other_pipes(endpoints);
115 if (pipe_count == 0) {
116 return EOK;
117 }
118
119 dev->pipes = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
120 if (dev->pipes == NULL) {
121 usb_log_oom(dev->ddf_dev);
122 return ENOMEM;
123 }
124
125 size_t i;
126
127 /* Initialize to NULL first for rollback purposes. */
128 for (i = 0; i < pipe_count; i++) {
129 dev->pipes[i].pipe = NULL;
130 }
131
132 for (i = 0; i < pipe_count; i++) {
133 dev->pipes[i].pipe = malloc(sizeof(usb_pipe_t));
134 if (dev->pipes[i].pipe == NULL) {
135 usb_log_oom(dev->ddf_dev);
136 rc = ENOMEM;
137 goto rollback;
138 }
139
140 dev->pipes[i].description = endpoints[i];
141 dev->pipes[i].interface_no = dev->interface_no;
142 dev->pipes[i].interface_setting = 0;
143 }
144
145 rc = usb_pipe_initialize_from_configuration(dev->pipes, pipe_count,
146 dev->descriptors.configuration, dev->descriptors.configuration_size,
147 &dev->wire);
148 if (rc != EOK) {
149 usb_log_error("Failed initializing USB endpoints: %s.\n",
150 str_error(rc));
151 goto rollback;
152 }
153
154 /* Register the endpoints. */
155 usb_hc_connection_t hc_conn;
156 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev->ddf_dev);
157 if (rc != EOK) {
158 usb_log_error(
159 "Failed initializing connection to host controller: %s.\n",
160 str_error(rc));
161 goto rollback;
162 }
163 rc = usb_hc_connection_open(&hc_conn);
164 if (rc != EOK) {
165 usb_log_error("Failed to connect to host controller: %s.\n",
166 str_error(rc));
167 goto rollback;
168 }
169 for (i = 0; i < pipe_count; i++) {
170 if (dev->pipes[i].present) {
171 rc = usb_pipe_register(dev->pipes[i].pipe,
172 dev->pipes[i].descriptor->poll_interval,
173 &hc_conn);
174 /* Ignore error when operation not supported by HC. */
175 if ((rc != EOK) && (rc != ENOTSUP)) {
176 /* FIXME: what shall we do? */
177 dev->pipes[i].present = false;
178 free(dev->pipes[i].pipe);
179 dev->pipes[i].pipe = NULL;
180 }
181 }
182 }
183 /* Ignoring errors here. */
184 usb_hc_connection_close(&hc_conn);
185
186 dev->pipes_count = pipe_count;
187
188 return EOK;
189
190rollback:
191 for (i = 0; i < pipe_count; i++) {
192 if (dev->pipes[i].pipe != NULL) {
193 free(dev->pipes[i].pipe);
194 }
195 }
196 free(dev->pipes);
197
198 return rc;
199}
200
201/** Initialize all endpoint pipes.
202 *
203 * @param drv The driver.
204 * @param dev The device to be initialized.
205 * @return Error code.
206 */
207static int initialize_pipes(usb_device_t *dev)
208{
209 int rc;
210
211 rc = usb_device_connection_initialize_from_device(&dev->wire,
212 dev->ddf_dev);
213 if (rc != EOK) {
214 usb_log_error(
215 "Failed initializing connection on device `%s'. %s.\n",
216 dev->ddf_dev->name, str_error(rc));
217 return rc;
218 }
219
220 rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
221 &dev->wire);
222 if (rc != EOK) {
223 usb_log_error("Failed to initialize default control pipe " \
224 "on device `%s': %s.\n",
225 dev->ddf_dev->name, str_error(rc));
226 return rc;
227 }
228
229 rc = usb_pipe_probe_default_control(&dev->ctrl_pipe);
230 if (rc != EOK) {
231 usb_log_error(
232 "Probing default control pipe on device `%s' failed: %s.\n",
233 dev->ddf_dev->name, str_error(rc));
234 return rc;
235 }
236
237 /* Get our interface. */
238 dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
239
240 /*
241 * We will do some querying of the device, it is worth to prepare
242 * the long transfer.
243 */
244 rc = usb_pipe_start_long_transfer(&dev->ctrl_pipe);
245 if (rc != EOK) {
246 usb_log_error("Failed to start transfer: %s.\n",
247 str_error(rc));
248 return rc;
249 }
250
251 /* Get the device descriptor. */
252 rc = usb_request_get_device_descriptor(&dev->ctrl_pipe,
253 &dev->descriptors.device);
254 if (rc != EOK) {
255 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
256 usb_log_error("Failed to retrieve device descriptor: %s.\n",
257 str_error(rc));
258 return rc;
259 }
260
261 /* Get the full configuration descriptor. */
262 rc = usb_request_get_full_configuration_descriptor_alloc(
263 &dev->ctrl_pipe, 0, (void **) &dev->descriptors.configuration,
264 &dev->descriptors.configuration_size);
265 if (rc != EOK) {
266 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
267 usb_log_error("Failed retrieving configuration descriptor: %s. %s\n",
268 dev->ddf_dev->name, str_error(rc));
269 return rc;
270 }
271
272 if (driver->endpoints != NULL) {
273 rc = initialize_other_pipes(driver->endpoints, dev);
274 }
275
276 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
277
278 /* Rollback actions. */
279 if (rc != EOK) {
280 if (dev->descriptors.configuration != NULL) {
281 free(dev->descriptors.configuration);
282 }
283 }
284
285 return rc;
286}
287
288/** Count number of alternate settings of a interface.
289 *
290 * @param config_descr Full configuration descriptor.
291 * @param config_descr_size Size of @p config_descr in bytes.
292 * @param interface_no Interface number.
293 * @return Number of alternate interfaces for @p interface_no interface.
294 */
295static size_t count_alternate_interfaces(uint8_t *config_descr,
296 size_t config_descr_size, int interface_no)
297{
298 assert(config_descr != NULL);
299 usb_dp_parser_t dp_parser = {
300 .nesting = usb_dp_standard_descriptor_nesting
301 };
302 usb_dp_parser_data_t dp_data = {
303 .data = config_descr,
304 .size = config_descr_size,
305 .arg = NULL
306 };
307
308 size_t alternate_count = 0;
309
310 uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
311 &dp_data, config_descr);
312 while (iface_ptr != NULL) {
313 usb_standard_interface_descriptor_t *iface
314 = (usb_standard_interface_descriptor_t *) iface_ptr;
315 if (iface->descriptor_type == USB_DESCTYPE_INTERFACE) {
316 if (iface->interface_number == interface_no) {
317 alternate_count++;
318 }
319 }
320 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
321 config_descr, iface_ptr);
322 }
323
324 return alternate_count;
325}
326
327/** Initialize structures related to alternate interfaces.
328 *
329 * @param dev Device where alternate settings shall be initialized.
330 * @return Error code.
331 */
332static int initialize_alternate_interfaces(usb_device_t *dev)
333{
334 if (dev->interface_no < 0) {
335 dev->alternate_interfaces = NULL;
336 return EOK;
337 }
338
339 usb_alternate_interfaces_t *alternates
340 = malloc(sizeof(usb_alternate_interfaces_t));
341
342 if (alternates == NULL) {
343 return ENOMEM;
344 }
345
346 alternates->alternative_count
347 = count_alternate_interfaces(dev->descriptors.configuration,
348 dev->descriptors.configuration_size, dev->interface_no);
349
350 if (alternates->alternative_count == 0) {
351 free(alternates);
352 return ENOENT;
353 }
354
355 alternates->alternatives = malloc(alternates->alternative_count
356 * sizeof(usb_alternate_interface_descriptors_t));
357 if (alternates->alternatives == NULL) {
358 free(alternates);
359 return ENOMEM;
360 }
361
362 alternates->current = 0;
363
364 usb_dp_parser_t dp_parser = {
365 .nesting = usb_dp_standard_descriptor_nesting
366 };
367 usb_dp_parser_data_t dp_data = {
368 .data = dev->descriptors.configuration,
369 .size = dev->descriptors.configuration_size,
370 .arg = NULL
371 };
372
373 usb_alternate_interface_descriptors_t *cur_alt_iface
374 = &alternates->alternatives[0];
375
376 uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
377 &dp_data, dp_data.data);
378 while (iface_ptr != NULL) {
379 usb_standard_interface_descriptor_t *iface
380 = (usb_standard_interface_descriptor_t *) iface_ptr;
381 if ((iface->descriptor_type != USB_DESCTYPE_INTERFACE)
382 || (iface->interface_number != dev->interface_no)) {
383 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser,
384 &dp_data,
385 dp_data.data, iface_ptr);
386 continue;
387 }
388
389 cur_alt_iface->interface = iface;
390 cur_alt_iface->nested_descriptors = iface_ptr + sizeof(*iface);
391
392 /* Find next interface to count size of nested descriptors. */
393 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
394 dp_data.data, iface_ptr);
395 if (iface_ptr == NULL) {
396 uint8_t *next = dp_data.data + dp_data.size;
397 cur_alt_iface->nested_descriptors_size
398 = next - cur_alt_iface->nested_descriptors;
399 } else {
400 cur_alt_iface->nested_descriptors_size
401 = iface_ptr - cur_alt_iface->nested_descriptors;
402 }
403
404 cur_alt_iface++;
405 }
406
407 dev->alternate_interfaces = alternates;
408
409 return EOK;
410}
411
412/** Callback when new device is supposed to be controlled by this driver.
413 *
414 * This callback is a wrapper for USB specific version of @c add_device.
415 *
416 * @param gen_dev Device structure as prepared by DDF.
417 * @return Error code.
418 */
419int generic_add_device(ddf_dev_t *gen_dev)
420{
421 assert(driver);
422 assert(driver->ops);
423 assert(driver->ops->add_device);
424
425 int rc;
426
427 usb_device_t *dev = malloc(sizeof(usb_device_t));
428 if (dev == NULL) {
429 usb_log_error("Out of memory when adding device `%s'.\n",
430 gen_dev->name);
431 return ENOMEM;
432 }
433
434
435 dev->ddf_dev = gen_dev;
436 dev->ddf_dev->driver_data = dev;
437 dev->driver_data = NULL;
438 dev->descriptors.configuration = NULL;
439
440 dev->pipes_count = 0;
441 dev->pipes = NULL;
442
443 rc = initialize_pipes(dev);
444 if (rc != EOK) {
445 free(dev);
446 return rc;
447 }
448
449 (void) initialize_alternate_interfaces(dev);
450
451 return driver->ops->add_device(dev);
452}
453
454/** Destroy existing pipes of a USB device.
455 *
456 * @param dev Device where to destroy the pipes.
457 * @return Error code.
458 */
459static int destroy_current_pipes(usb_device_t *dev)
460{
461 size_t i;
462 int rc;
463
464 /* TODO: this shall be done under some device mutex. */
465
466 /* First check that no session is opened. */
467 for (i = 0; i < dev->pipes_count; i++) {
468 if (usb_pipe_is_session_started(dev->pipes[i].pipe)) {
469 return EBUSY;
470 }
471 }
472
473 /* Prepare connection to HC. */
474 usb_hc_connection_t hc_conn;
475 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev->ddf_dev);
476 if (rc != EOK) {
477 return rc;
478 }
479 rc = usb_hc_connection_open(&hc_conn);
480 if (rc != EOK) {
481 return rc;
482 }
483
484 /* Destroy the pipes. */
485 for (i = 0; i < dev->pipes_count; i++) {
486 usb_pipe_unregister(dev->pipes[i].pipe, &hc_conn);
487 free(dev->pipes[i].pipe);
488 }
489
490 usb_hc_connection_close(&hc_conn);
491
492 free(dev->pipes);
493 dev->pipes = NULL;
494 dev->pipes_count = 0;
495
496 return EOK;
497}
498
499/** Change interface setting of a device.
500 * This function selects new alternate setting of an interface by issuing
501 * proper USB command to the device and also creates new USB pipes
502 * under @c dev->pipes.
503 *
504 * @warning This function is intended for drivers working at interface level.
505 * For drivers controlling the whole device, you need to change interface
506 * manually using usb_request_set_interface() and creating new pipes
507 * with usb_pipe_initialize_from_configuration().
508 *
509 * @param dev USB device.
510 * @param alternate_setting Alternate setting to choose.
511 * @param endpoints New endpoint descriptions.
512 * @return Error code.
513 */
514int usb_device_select_interface(usb_device_t *dev, uint8_t alternate_setting,
515 usb_endpoint_description_t **endpoints)
516{
517 if (dev->interface_no < 0) {
518 return EINVAL;
519 }
520
521 int rc;
522
523 /* TODO: more transactional behavior. */
524
525 /* Destroy existing pipes. */
526 rc = destroy_current_pipes(dev);
527 if (rc != EOK) {
528 return rc;
529 }
530
531 /* Change the interface itself. */
532 rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
533 alternate_setting);
534 if (rc != EOK) {
535 return rc;
536 }
537
538 /* Create new pipes. */
539 rc = initialize_other_pipes(endpoints, dev);
540
541 return rc;
542}
543
544/**
545 * @}
546 */
Note: See TracBrowser for help on using the repository browser.