source: mainline/uspace/lib/usb/src/devdrv.c@ 1998bcd

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

libusb: refactoring, some functions made public

The aim is to make usage of the USB framework more optional by making
most of the initializer functions public.

This commit shall not change any functionality. Actually, one bug was
fixed (alternate interface switching shall now actually work).

  • Property mode set to 100644
File size: 16.3 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/** Count number of pipes the driver expects.
75 *
76 * @param drv USB driver.
77 * @return Number of pipes (excluding default control pipe).
78 */
79static size_t count_other_pipes(usb_endpoint_description_t **endpoints)
80{
81 size_t count = 0;
82 if (endpoints == NULL) {
83 return 0;
84 }
85
86 while (endpoints[count] != NULL) {
87 count++;
88 }
89
90 return count;
91}
92
93/** Initialize endpoint pipes, excluding default control one.
94 *
95 * @param drv The device driver.
96 * @param dev Device to be initialized.
97 * @return Error code.
98 */
99static int initialize_other_pipes(usb_endpoint_description_t **endpoints,
100 usb_device_t *dev, int alternate_setting)
101{
102 usb_endpoint_mapping_t *pipes;
103 size_t pipes_count;
104
105 int rc = usb_device_create_pipes(dev->ddf_dev, &dev->wire, endpoints,
106 dev->descriptors.configuration, dev->descriptors.configuration_size,
107 dev->interface_no, alternate_setting,
108 &pipes, &pipes_count);
109
110 if (rc != EOK) {
111 usb_log_error(
112 "Failed to create endpoint pipes for `%s': %s.\n",
113 dev->ddf_dev->name, str_error(rc));
114 return rc;
115 }
116
117 dev->pipes = pipes;
118 dev->pipes_count = pipes_count;
119
120 return EOK;
121}
122
123/** Initialize all endpoint pipes.
124 *
125 * @param drv The driver.
126 * @param dev The device to be initialized.
127 * @return Error code.
128 */
129static int initialize_pipes(usb_device_t *dev)
130{
131 int rc;
132
133 rc = usb_device_connection_initialize_from_device(&dev->wire,
134 dev->ddf_dev);
135 if (rc != EOK) {
136 usb_log_error(
137 "Failed initializing connection on device `%s'. %s.\n",
138 dev->ddf_dev->name, str_error(rc));
139 return rc;
140 }
141
142 rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
143 &dev->wire);
144 if (rc != EOK) {
145 usb_log_error("Failed to initialize default control pipe " \
146 "on device `%s': %s.\n",
147 dev->ddf_dev->name, str_error(rc));
148 return rc;
149 }
150
151 rc = usb_pipe_probe_default_control(&dev->ctrl_pipe);
152 if (rc != EOK) {
153 usb_log_error(
154 "Probing default control pipe on device `%s' failed: %s.\n",
155 dev->ddf_dev->name, str_error(rc));
156 return rc;
157 }
158
159 /* Get our interface. */
160 dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
161
162 /*
163 * For further actions, we need open session on default control pipe.
164 */
165 rc = usb_pipe_start_session(&dev->ctrl_pipe);
166 if (rc != EOK) {
167 usb_log_error("Failed to start an IPC session: %s.\n",
168 str_error(rc));
169 return rc;
170 }
171
172 /* Retrieve the descriptors. */
173 rc = usb_device_retrieve_descriptors(&dev->ctrl_pipe,
174 &dev->descriptors);
175 if (rc != EOK) {
176 usb_log_error("Failed to retrieve standard device " \
177 "descriptors of %s: %s.\n",
178 dev->ddf_dev->name, str_error(rc));
179 return rc;
180 }
181
182
183 if (driver->endpoints != NULL) {
184 rc = initialize_other_pipes(driver->endpoints, dev, 0);
185 }
186
187 /* No checking here. */
188 usb_pipe_end_session(&dev->ctrl_pipe);
189
190 /* Rollback actions. */
191 if (rc != EOK) {
192 if (dev->descriptors.configuration != NULL) {
193 free(dev->descriptors.configuration);
194 }
195 }
196
197 return rc;
198}
199
200/** Count number of alternate settings of a interface.
201 *
202 * @param config_descr Full configuration descriptor.
203 * @param config_descr_size Size of @p config_descr in bytes.
204 * @param interface_no Interface number.
205 * @return Number of alternate interfaces for @p interface_no interface.
206 */
207size_t usb_interface_count_alternates(uint8_t *config_descr,
208 size_t config_descr_size, uint8_t interface_no)
209{
210 assert(config_descr != NULL);
211 assert(config_descr_size > 0);
212
213 usb_dp_parser_t dp_parser = {
214 .nesting = usb_dp_standard_descriptor_nesting
215 };
216 usb_dp_parser_data_t dp_data = {
217 .data = config_descr,
218 .size = config_descr_size,
219 .arg = NULL
220 };
221
222 size_t alternate_count = 0;
223
224 uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
225 &dp_data, config_descr);
226 while (iface_ptr != NULL) {
227 usb_standard_interface_descriptor_t *iface
228 = (usb_standard_interface_descriptor_t *) iface_ptr;
229 if (iface->descriptor_type == USB_DESCTYPE_INTERFACE) {
230 if (iface->interface_number == interface_no) {
231 alternate_count++;
232 }
233 }
234 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
235 config_descr, iface_ptr);
236 }
237
238 return alternate_count;
239}
240
241/** Initialize structures related to alternate interfaces.
242 *
243 * @param dev Device where alternate settings shall be initialized.
244 * @return Error code.
245 */
246static int initialize_alternate_interfaces(usb_device_t *dev)
247{
248 if (dev->interface_no < 0) {
249 dev->alternate_interfaces = NULL;
250 return EOK;
251 }
252
253 usb_alternate_interfaces_t *alternates
254 = malloc(sizeof(usb_alternate_interfaces_t));
255
256 if (alternates == NULL) {
257 return ENOMEM;
258 }
259
260 alternates->alternative_count
261 = usb_interface_count_alternates(dev->descriptors.configuration,
262 dev->descriptors.configuration_size, dev->interface_no);
263
264 if (alternates->alternative_count == 0) {
265 free(alternates);
266 return ENOENT;
267 }
268
269 alternates->alternatives = malloc(alternates->alternative_count
270 * sizeof(usb_alternate_interface_descriptors_t));
271 if (alternates->alternatives == NULL) {
272 free(alternates);
273 return ENOMEM;
274 }
275
276 alternates->current = 0;
277
278 usb_dp_parser_t dp_parser = {
279 .nesting = usb_dp_standard_descriptor_nesting
280 };
281 usb_dp_parser_data_t dp_data = {
282 .data = dev->descriptors.configuration,
283 .size = dev->descriptors.configuration_size,
284 .arg = NULL
285 };
286
287 usb_alternate_interface_descriptors_t *cur_alt_iface
288 = &alternates->alternatives[0];
289
290 uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
291 &dp_data, dp_data.data);
292 while (iface_ptr != NULL) {
293 usb_standard_interface_descriptor_t *iface
294 = (usb_standard_interface_descriptor_t *) iface_ptr;
295 if ((iface->descriptor_type != USB_DESCTYPE_INTERFACE)
296 || (iface->interface_number != dev->interface_no)) {
297 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser,
298 &dp_data,
299 dp_data.data, iface_ptr);
300 continue;
301 }
302
303 cur_alt_iface->interface = iface;
304 cur_alt_iface->nested_descriptors = iface_ptr + sizeof(*iface);
305
306 /* Find next interface to count size of nested descriptors. */
307 iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
308 dp_data.data, iface_ptr);
309 if (iface_ptr == NULL) {
310 uint8_t *next = dp_data.data + dp_data.size;
311 cur_alt_iface->nested_descriptors_size
312 = next - cur_alt_iface->nested_descriptors;
313 } else {
314 cur_alt_iface->nested_descriptors_size
315 = iface_ptr - cur_alt_iface->nested_descriptors;
316 }
317
318 cur_alt_iface++;
319 }
320
321 dev->alternate_interfaces = alternates;
322
323 return EOK;
324}
325
326/** Callback when new device is supposed to be controlled by this driver.
327 *
328 * This callback is a wrapper for USB specific version of @c add_device.
329 *
330 * @param gen_dev Device structure as prepared by DDF.
331 * @return Error code.
332 */
333int generic_add_device(ddf_dev_t *gen_dev)
334{
335 assert(driver);
336 assert(driver->ops);
337 assert(driver->ops->add_device);
338
339 int rc;
340
341 usb_device_t *dev = malloc(sizeof(usb_device_t));
342 if (dev == NULL) {
343 usb_log_error("Out of memory when adding device `%s'.\n",
344 gen_dev->name);
345 return ENOMEM;
346 }
347
348
349 dev->ddf_dev = gen_dev;
350 dev->ddf_dev->driver_data = dev;
351 dev->driver_data = NULL;
352 dev->descriptors.configuration = NULL;
353
354 dev->pipes_count = 0;
355 dev->pipes = NULL;
356
357 rc = initialize_pipes(dev);
358 if (rc != EOK) {
359 free(dev);
360 return rc;
361 }
362
363 (void) initialize_alternate_interfaces(dev);
364
365 return driver->ops->add_device(dev);
366}
367
368/** Destroy existing pipes of a USB device.
369 *
370 * @param dev Device where to destroy the pipes.
371 * @return Error code.
372 */
373static int destroy_current_pipes(usb_device_t *dev)
374{
375 int rc = usb_device_destroy_pipes(dev->ddf_dev,
376 dev->pipes, dev->pipes_count);
377 if (rc != EOK) {
378 return rc;
379 }
380
381 dev->pipes = NULL;
382 dev->pipes_count = 0;
383
384 return EOK;
385}
386
387/** Change interface setting of a device.
388 * This function selects new alternate setting of an interface by issuing
389 * proper USB command to the device and also creates new USB pipes
390 * under @c dev->pipes.
391 *
392 * @warning This function is intended for drivers working at interface level.
393 * For drivers controlling the whole device, you need to change interface
394 * manually using usb_request_set_interface() and creating new pipes
395 * with usb_pipe_initialize_from_configuration().
396 *
397 * @param dev USB device.
398 * @param alternate_setting Alternate setting to choose.
399 * @param endpoints New endpoint descriptions.
400 * @return Error code.
401 */
402int usb_device_select_interface(usb_device_t *dev, uint8_t alternate_setting,
403 usb_endpoint_description_t **endpoints)
404{
405 if (dev->interface_no < 0) {
406 return EINVAL;
407 }
408
409 int rc;
410
411 /* TODO: more transactional behavior. */
412
413 /* Destroy existing pipes. */
414 rc = destroy_current_pipes(dev);
415 if (rc != EOK) {
416 return rc;
417 }
418
419 /* Change the interface itself. */
420 rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
421 alternate_setting);
422 if (rc != EOK) {
423 return rc;
424 }
425
426 /* Create new pipes. */
427 rc = initialize_other_pipes(endpoints, dev, (int) alternate_setting);
428
429 return rc;
430}
431
432/** Retrieve basic descriptors from the device.
433 *
434 * @param[in] ctrl_pipe Control pipe with opened session.
435 * @param[out] descriptors Where to store the descriptors.
436 * @return Error code.
437 */
438int usb_device_retrieve_descriptors(usb_pipe_t *ctrl_pipe,
439 usb_device_descriptors_t *descriptors)
440{
441 assert(descriptors != NULL);
442 assert(usb_pipe_is_session_started(ctrl_pipe));
443
444 descriptors->configuration = NULL;
445
446 int rc;
447
448 /* Get the device descriptor. */
449 rc = usb_request_get_device_descriptor(ctrl_pipe, &descriptors->device);
450 if (rc != EOK) {
451 return rc;
452 }
453
454 /* Get the full configuration descriptor. */
455 rc = usb_request_get_full_configuration_descriptor_alloc(
456 ctrl_pipe, 0, (void **) &descriptors->configuration,
457 &descriptors->configuration_size);
458 if (rc != EOK) {
459 return rc;
460 }
461
462 return EOK;
463}
464
465/** Create pipes for a device.
466 *
467 * This is more or less a wrapper that does following actions:
468 * - allocate and initialize pipes
469 * - map endpoints to the pipes based on the descriptions
470 * - registers endpoints with the host controller
471 *
472 * @param[in] dev Generic DDF device backing the USB one.
473 * @param[in] wire Initialized backing connection to the host controller.
474 * @param[in] endpoints Endpoints description, NULL terminated.
475 * @param[in] config_descr Configuration descriptor of active configuration.
476 * @param[in] config_descr_size Size of @p config_descr in bytes.
477 * @param[in] interface_no Interface to map from.
478 * @param[in] interface_setting Interface setting (default is usually 0).
479 * @param[out] pipes_ptr Where to store array of created pipes
480 * (not NULL terminated).
481 * @param[out] pipes_count_ptr Where to store number of pipes
482 * (set to if you wish to ignore the count).
483 * @return Error code.
484 */
485int usb_device_create_pipes(ddf_dev_t *dev, usb_device_connection_t *wire,
486 usb_endpoint_description_t **endpoints,
487 uint8_t *config_descr, size_t config_descr_size,
488 int interface_no, int interface_setting,
489 usb_endpoint_mapping_t **pipes_ptr, size_t *pipes_count_ptr)
490{
491 assert(dev != NULL);
492 assert(wire != NULL);
493 assert(endpoints != NULL);
494 assert(config_descr != NULL);
495 assert(config_descr_size > 0);
496 assert(pipes_ptr != NULL);
497
498 size_t i;
499 int rc;
500
501 size_t pipe_count = count_other_pipes(endpoints);
502 if (pipe_count == 0) {
503 *pipes_ptr = NULL;
504 return EOK;
505 }
506
507 usb_endpoint_mapping_t *pipes
508 = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
509 if (pipes == NULL) {
510 return ENOMEM;
511 }
512
513 /* Initialize to NULL to allow smooth rollback. */
514 for (i = 0; i < pipe_count; i++) {
515 pipes[i].pipe = NULL;
516 }
517
518 /* Now allocate and fully initialize. */
519 for (i = 0; i < pipe_count; i++) {
520 pipes[i].pipe = malloc(sizeof(usb_pipe_t));
521 if (pipes[i].pipe == NULL) {
522 rc = ENOMEM;
523 goto rollback_free_only;
524 }
525 pipes[i].description = endpoints[i];
526 pipes[i].interface_no = interface_no;
527 pipes[i].interface_setting = interface_setting;
528 }
529
530 /* Find the mapping from configuration descriptor. */
531 rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
532 config_descr, config_descr_size, wire);
533 if (rc != EOK) {
534 goto rollback_free_only;
535 }
536
537 /* Register the endpoints with HC. */
538 usb_hc_connection_t hc_conn;
539 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
540 if (rc != EOK) {
541 goto rollback_free_only;
542 }
543
544 rc = usb_hc_connection_open(&hc_conn);
545 if (rc != EOK) {
546 goto rollback_free_only;
547 }
548
549 for (i = 0; i < pipe_count; i++) {
550 if (pipes[i].present) {
551 rc = usb_pipe_register(pipes[i].pipe,
552 pipes[i].descriptor->poll_interval, &hc_conn);
553 if (rc != EOK) {
554 goto rollback_unregister_endpoints;
555 }
556 }
557 }
558
559 usb_hc_connection_close(&hc_conn);
560
561 *pipes_ptr = pipes;
562 if (pipes_count_ptr != NULL) {
563 *pipes_count_ptr = pipe_count;
564 }
565
566 return EOK;
567
568 /*
569 * Jump here if something went wrong after endpoints have
570 * been registered.
571 * This is also the target when the registration of
572 * endpoints fails.
573 */
574rollback_unregister_endpoints:
575 for (i = 0; i < pipe_count; i++) {
576 if (pipes[i].present) {
577 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
578 }
579 }
580
581 usb_hc_connection_close(&hc_conn);
582
583 /*
584 * Jump here if something went wrong before some actual communication
585 * with HC. Then the only thing that needs to be done is to free
586 * allocated memory.
587 */
588rollback_free_only:
589 for (i = 0; i < pipe_count; i++) {
590 if (pipes[i].pipe != NULL) {
591 free(pipes[i].pipe);
592 }
593 }
594 free(pipes);
595
596 return rc;
597}
598
599/** Destroy pipes previously created by usb_device_create_pipes.
600 *
601 * @param[in] dev Generic DDF device backing the USB one.
602 * @param[in] pipes Endpoint mapping to be destroyed.
603 * @param[in] pipes_count Number of endpoints.
604 */
605int usb_device_destroy_pipes(ddf_dev_t *dev,
606 usb_endpoint_mapping_t *pipes, size_t pipes_count)
607{
608 assert(dev != NULL);
609 assert(((pipes != NULL) && (pipes_count > 0))
610 || ((pipes == NULL) && (pipes_count == 0)));
611
612 if (pipes_count == 0) {
613 return EOK;
614 }
615
616 int rc;
617
618 /* Prepare connection to HC to allow endpoint unregistering. */
619 usb_hc_connection_t hc_conn;
620 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
621 if (rc != EOK) {
622 return rc;
623 }
624 rc = usb_hc_connection_open(&hc_conn);
625 if (rc != EOK) {
626 return rc;
627 }
628
629 /* Destroy the pipes. */
630 size_t i;
631 for (i = 0; i < pipes_count; i++) {
632 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
633 free(pipes[i].pipe);
634 }
635
636 usb_hc_connection_close(&hc_conn);
637
638 free(pipes);
639
640 return EOK;
641}
642
643/**
644 * @}
645 */
Note: See TracBrowser for help on using the repository browser.