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

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

Descriptor retrieval start long transfer by itself

  • Property mode set to 100644
File size: 16.4 KB
RevLine 
[6105fc0]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>
[7526e3d9]38#include <usb/dp.h>
[6105fc0]39#include <errno.h>
[69334af]40#include <str_error.h>
[6105fc0]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
[69334af]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 */
[6105fc0]62int usb_driver_main(usb_driver_t *drv)
63{
[69334af]64 assert(drv != NULL);
65
[6105fc0]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
[69334af]74/** Count number of pipes the driver expects.
75 *
76 * @param drv USB driver.
77 * @return Number of pipes (excluding default control pipe).
78 */
[0b4e7ca]79static size_t count_other_pipes(usb_endpoint_description_t **endpoints)
[6105fc0]80{
81 size_t count = 0;
[0b4e7ca]82 if (endpoints == NULL) {
[6105fc0]83 return 0;
84 }
85
[0b4e7ca]86 while (endpoints[count] != NULL) {
[6105fc0]87 count++;
88 }
89
90 return count;
91}
92
[69334af]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 */
[0b4e7ca]99static int initialize_other_pipes(usb_endpoint_description_t **endpoints,
[c1b1944]100 usb_device_t *dev, int alternate_setting)
[6105fc0]101{
[6ee6e6f]102 if (endpoints == NULL) {
103 dev->pipes = NULL;
104 dev->pipes_count = 0;
105 return EOK;
106 }
107
[c1b1944]108 usb_endpoint_mapping_t *pipes;
109 size_t pipes_count;
[6105fc0]110
[c1b1944]111 int rc = usb_device_create_pipes(dev->ddf_dev, &dev->wire, endpoints,
[e484f3b]112 dev->descriptors.configuration, dev->descriptors.configuration_size,
[c1b1944]113 dev->interface_no, alternate_setting,
114 &pipes, &pipes_count);
[6105fc0]115
[5fd22d8]116 if (rc != EOK) {
[c1b1944]117 return rc;
[5fd22d8]118 }
119
[c1b1944]120 dev->pipes = pipes;
121 dev->pipes_count = pipes_count;
[0b4e7ca]122
[6105fc0]123 return EOK;
124}
125
[7526e3d9]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 */
[c1b1944]133size_t usb_interface_count_alternates(uint8_t *config_descr,
134 size_t config_descr_size, uint8_t interface_no)
[7526e3d9]135{
136 assert(config_descr != NULL);
[c1b1944]137 assert(config_descr_size > 0);
138
[7526e3d9]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
[c1b1944]187 = usb_interface_count_alternates(dev->descriptors.configuration,
[7526e3d9]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
[69334af]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 */
[6105fc0]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
[6ee6e6f]267 usb_device_t *dev = NULL;
268 const char *err_msg = NULL;
269 rc = usb_device_create(gen_dev, driver->endpoints, &dev, &err_msg);
[69334af]270 if (rc != EOK) {
[6ee6e6f]271 usb_log_error("USB device `%s' creation failed (%s): %s.\n",
272 gen_dev->name, err_msg, str_error(rc));
[69334af]273 return rc;
[6105fc0]274 }
275
276 return driver->ops->add_device(dev);
277}
278
[0b4e7ca]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{
[c1b1944]286 int rc = usb_device_destroy_pipes(dev->ddf_dev,
287 dev->pipes, dev->pipes_count);
[0b4e7ca]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. */
[c1b1944]338 rc = initialize_other_pipes(endpoints, dev, (int) alternate_setting);
339
340 return rc;
341}
342
343/** Retrieve basic descriptors from the device.
344 *
[4fa0a384]345 * @param[in] ctrl_pipe Control endpoint pipe.
[c1b1944]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
354 descriptors->configuration = NULL;
355
356 int rc;
357
[4fa0a384]358 /* It is worth to start a long transfer. */
359 rc = usb_pipe_start_long_transfer(ctrl_pipe);
360 if (rc != EOK) {
361 return rc;
362 }
363
[c1b1944]364 /* Get the device descriptor. */
365 rc = usb_request_get_device_descriptor(ctrl_pipe, &descriptors->device);
366 if (rc != EOK) {
[4fa0a384]367 goto leave;
[c1b1944]368 }
369
370 /* Get the full configuration descriptor. */
371 rc = usb_request_get_full_configuration_descriptor_alloc(
372 ctrl_pipe, 0, (void **) &descriptors->configuration,
373 &descriptors->configuration_size);
374
[4fa0a384]375leave:
376 usb_pipe_end_long_transfer(ctrl_pipe);
377
378 return rc;
[c1b1944]379}
380
381/** Create pipes for a device.
382 *
383 * This is more or less a wrapper that does following actions:
384 * - allocate and initialize pipes
385 * - map endpoints to the pipes based on the descriptions
386 * - registers endpoints with the host controller
387 *
388 * @param[in] dev Generic DDF device backing the USB one.
389 * @param[in] wire Initialized backing connection to the host controller.
390 * @param[in] endpoints Endpoints description, NULL terminated.
391 * @param[in] config_descr Configuration descriptor of active configuration.
392 * @param[in] config_descr_size Size of @p config_descr in bytes.
393 * @param[in] interface_no Interface to map from.
394 * @param[in] interface_setting Interface setting (default is usually 0).
395 * @param[out] pipes_ptr Where to store array of created pipes
396 * (not NULL terminated).
397 * @param[out] pipes_count_ptr Where to store number of pipes
398 * (set to if you wish to ignore the count).
399 * @return Error code.
400 */
401int usb_device_create_pipes(ddf_dev_t *dev, usb_device_connection_t *wire,
402 usb_endpoint_description_t **endpoints,
403 uint8_t *config_descr, size_t config_descr_size,
404 int interface_no, int interface_setting,
405 usb_endpoint_mapping_t **pipes_ptr, size_t *pipes_count_ptr)
406{
407 assert(dev != NULL);
408 assert(wire != NULL);
409 assert(endpoints != NULL);
410 assert(config_descr != NULL);
411 assert(config_descr_size > 0);
412 assert(pipes_ptr != NULL);
413
414 size_t i;
415 int rc;
416
417 size_t pipe_count = count_other_pipes(endpoints);
418 if (pipe_count == 0) {
419 *pipes_ptr = NULL;
420 return EOK;
421 }
422
423 usb_endpoint_mapping_t *pipes
424 = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
425 if (pipes == NULL) {
426 return ENOMEM;
427 }
428
429 /* Initialize to NULL to allow smooth rollback. */
430 for (i = 0; i < pipe_count; i++) {
431 pipes[i].pipe = NULL;
432 }
433
434 /* Now allocate and fully initialize. */
435 for (i = 0; i < pipe_count; i++) {
436 pipes[i].pipe = malloc(sizeof(usb_pipe_t));
437 if (pipes[i].pipe == NULL) {
438 rc = ENOMEM;
439 goto rollback_free_only;
440 }
441 pipes[i].description = endpoints[i];
442 pipes[i].interface_no = interface_no;
443 pipes[i].interface_setting = interface_setting;
444 }
445
446 /* Find the mapping from configuration descriptor. */
447 rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
448 config_descr, config_descr_size, wire);
449 if (rc != EOK) {
450 goto rollback_free_only;
451 }
452
453 /* Register the endpoints with HC. */
454 usb_hc_connection_t hc_conn;
455 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
456 if (rc != EOK) {
457 goto rollback_free_only;
458 }
459
460 rc = usb_hc_connection_open(&hc_conn);
461 if (rc != EOK) {
462 goto rollback_free_only;
463 }
464
465 for (i = 0; i < pipe_count; i++) {
466 if (pipes[i].present) {
467 rc = usb_pipe_register(pipes[i].pipe,
468 pipes[i].descriptor->poll_interval, &hc_conn);
469 if (rc != EOK) {
470 goto rollback_unregister_endpoints;
471 }
472 }
473 }
474
475 usb_hc_connection_close(&hc_conn);
476
477 *pipes_ptr = pipes;
478 if (pipes_count_ptr != NULL) {
479 *pipes_count_ptr = pipe_count;
480 }
481
482 return EOK;
483
484 /*
485 * Jump here if something went wrong after endpoints have
486 * been registered.
487 * This is also the target when the registration of
488 * endpoints fails.
489 */
490rollback_unregister_endpoints:
491 for (i = 0; i < pipe_count; i++) {
492 if (pipes[i].present) {
493 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
494 }
495 }
496
497 usb_hc_connection_close(&hc_conn);
498
499 /*
500 * Jump here if something went wrong before some actual communication
501 * with HC. Then the only thing that needs to be done is to free
502 * allocated memory.
503 */
504rollback_free_only:
505 for (i = 0; i < pipe_count; i++) {
506 if (pipes[i].pipe != NULL) {
507 free(pipes[i].pipe);
508 }
509 }
510 free(pipes);
[0b4e7ca]511
512 return rc;
513}
[159b91f4]514
[c1b1944]515/** Destroy pipes previously created by usb_device_create_pipes.
516 *
517 * @param[in] dev Generic DDF device backing the USB one.
518 * @param[in] pipes Endpoint mapping to be destroyed.
519 * @param[in] pipes_count Number of endpoints.
520 */
521int usb_device_destroy_pipes(ddf_dev_t *dev,
522 usb_endpoint_mapping_t *pipes, size_t pipes_count)
523{
524 assert(dev != NULL);
525 assert(((pipes != NULL) && (pipes_count > 0))
526 || ((pipes == NULL) && (pipes_count == 0)));
527
528 if (pipes_count == 0) {
529 return EOK;
530 }
531
532 int rc;
533
534 /* Prepare connection to HC to allow endpoint unregistering. */
535 usb_hc_connection_t hc_conn;
536 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
537 if (rc != EOK) {
538 return rc;
539 }
540 rc = usb_hc_connection_open(&hc_conn);
541 if (rc != EOK) {
542 return rc;
543 }
544
545 /* Destroy the pipes. */
546 size_t i;
547 for (i = 0; i < pipes_count; i++) {
548 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
549 free(pipes[i].pipe);
550 }
551
552 usb_hc_connection_close(&hc_conn);
553
554 free(pipes);
555
556 return EOK;
557}
558
[6ee6e6f]559/** Initialize control pipe and device descriptors. */
560static int initialize_ctrl_pipe_and_descriptors(usb_device_t *dev,
561 const char **errmsg)
562{
563 int rc;
564
565 rc = usb_device_connection_initialize_from_device(&dev->wire,
566 dev->ddf_dev);
567 if (rc != EOK) {
568 *errmsg = "device connection initialization";
569 return rc;
570 }
571
572 rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
573 &dev->wire);
574 if (rc != EOK) {
575 *errmsg = "default control pipe initialization";
576 return rc;
577 }
578
579 /* Get our interface. */
580 dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
581
582 /* Retrieve the descriptors. */
583 rc = usb_device_retrieve_descriptors(&dev->ctrl_pipe,
584 &dev->descriptors);
585 if (rc != EOK) {
586 *errmsg = "descriptor retrieval";
587 }
588
589 return rc;
590}
591
592
593/** Create new instance of USB device.
594 *
595 * @param[in] ddf_dev Generic DDF device backing the USB one.
596 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
597 * @param[out] dev_ptr Where to store pointer to the new device.
598 * @param[out] errstr_ptr Where to store description of context
599 * (in case error occurs).
600 * @return Error code.
601 */
602int usb_device_create(ddf_dev_t *ddf_dev,
603 usb_endpoint_description_t **endpoints,
604 usb_device_t **dev_ptr, const char **errstr_ptr)
605{
606 assert(dev_ptr != NULL);
607 assert(ddf_dev != NULL);
608
609 int rc;
610
611 usb_device_t *dev = malloc(sizeof(usb_device_t));
612 if (dev == NULL) {
613 *errstr_ptr = "structure allocation";
614 return ENOMEM;
615 }
616
617 dev->ddf_dev = ddf_dev;
618 dev->driver_data = NULL;
619 dev->descriptors.configuration = NULL;
620 dev->alternate_interfaces = NULL;
621
622 dev->pipes_count = 0;
623 dev->pipes = NULL;
624
625 rc = initialize_ctrl_pipe_and_descriptors(dev, errstr_ptr);
626 if (rc != EOK) {
627 return rc;
628 }
629
630 rc = initialize_alternate_interfaces(dev);
631 if (rc != EOK) {
632 /* We will try to silently ignore this. */
633 dev->alternate_interfaces = NULL;
634 }
635
636 rc = initialize_other_pipes(endpoints, dev, 0);
637 if (rc != EOK) {
638 *errstr_ptr = "pipes initialization";
639 /* TODO: deallocate */
640 return rc;
641 }
642
643 *errstr_ptr = NULL;
644 *dev_ptr = dev;
645
646 return EOK;
647}
648
[6105fc0]649/**
650 * @}
651 */
Note: See TracBrowser for help on using the repository browser.