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

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

libusb refactoring, add usb_device_create()

The refactoring is mostly about moving code between functions.

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