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

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

Merge development/ changes

  • 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 * We will do some querying of the device, it is worth to prepare
164 * the long transfer.
165 */
166 rc = usb_pipe_start_long_transfer(&dev->ctrl_pipe);
167 if (rc != EOK) {
168 usb_log_error("Failed to start transfer: %s.\n",
169 str_error(rc));
170 return rc;
171 }
172
173 /* Retrieve the descriptors. */
174 rc = usb_device_retrieve_descriptors(&dev->ctrl_pipe,
175 &dev->descriptors);
176 if (rc != EOK) {
177 usb_log_error("Failed to retrieve standard device " \
178 "descriptors of %s: %s.\n",
179 dev->ddf_dev->name, str_error(rc));
180 return rc;
181 }
182
183
184 if (driver->endpoints != NULL) {
185 rc = initialize_other_pipes(driver->endpoints, dev, 0);
186 }
187
188 usb_pipe_end_long_transfer(&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.