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
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/** 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 */
[0b4e7ca]89static size_t count_other_pipes(usb_endpoint_description_t **endpoints)
[6105fc0]90{
91 size_t count = 0;
[0b4e7ca]92 if (endpoints == NULL) {
[6105fc0]93 return 0;
94 }
95
[0b4e7ca]96 while (endpoints[count] != NULL) {
[6105fc0]97 count++;
98 }
99
100 return count;
101}
102
[69334af]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 */
[0b4e7ca]109static int initialize_other_pipes(usb_endpoint_description_t **endpoints,
110 usb_device_t *dev)
[6105fc0]111{
[69334af]112 int rc;
[6105fc0]113
[0b4e7ca]114 size_t pipe_count = count_other_pipes(endpoints);
115 if (pipe_count == 0) {
116 return EOK;
117 }
118
[6105fc0]119 dev->pipes = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
[69334af]120 if (dev->pipes == NULL) {
121 usb_log_oom(dev->ddf_dev);
122 return ENOMEM;
123 }
[6105fc0]124
125 size_t i;
[69334af]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
[6105fc0]132 for (i = 0; i < pipe_count; i++) {
[a372663]133 dev->pipes[i].pipe = malloc(sizeof(usb_pipe_t));
[69334af]134 if (dev->pipes[i].pipe == NULL) {
135 usb_log_oom(dev->ddf_dev);
136 rc = ENOMEM;
137 goto rollback;
138 }
139
[0b4e7ca]140 dev->pipes[i].description = endpoints[i];
[d71691d]141 dev->pipes[i].interface_no = dev->interface_no;
[159b91f4]142 dev->pipes[i].interface_setting = 0;
[6105fc0]143 }
144
[e484f3b]145 rc = usb_pipe_initialize_from_configuration(dev->pipes, pipe_count,
146 dev->descriptors.configuration, dev->descriptors.configuration_size,
147 &dev->wire);
[69334af]148 if (rc != EOK) {
149 usb_log_error("Failed initializing USB endpoints: %s.\n",
150 str_error(rc));
151 goto rollback;
152 }
[6105fc0]153
[5fd22d8]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) {
[3954a63b]171 rc = usb_pipe_register(dev->pipes[i].pipe,
[5fd22d8]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
[0b4e7ca]186 dev->pipes_count = pipe_count;
187
[6105fc0]188 return EOK;
[69334af]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;
[6105fc0]199}
200
[69334af]201/** Initialize all endpoint pipes.
202 *
203 * @param drv The driver.
204 * @param dev The device to be initialized.
205 * @return Error code.
206 */
[09daa8b]207static int initialize_pipes(usb_device_t *dev)
[69334af]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
[3954a63b]220 rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
[69334af]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
[3954a63b]229 rc = usb_pipe_probe_default_control(&dev->ctrl_pipe);
[206f71a]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
[bb18a59]237 /* Get our interface. */
238 dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
239
[69334af]240 /*
[4ede178]241 * We will do some querying of the device, it is worth to prepare
242 * the long transfer.
[69334af]243 */
[4ede178]244 rc = usb_pipe_start_long_transfer(&dev->ctrl_pipe);
[69334af]245 if (rc != EOK) {
[4ede178]246 usb_log_error("Failed to start transfer: %s.\n",
[69334af]247 str_error(rc));
248 return rc;
249 }
250
[e484f3b]251 /* Get the device descriptor. */
252 rc = usb_request_get_device_descriptor(&dev->ctrl_pipe,
253 &dev->descriptors.device);
254 if (rc != EOK) {
[4ede178]255 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
[e484f3b]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) {
[4ede178]266 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
[66a54cc]267 usb_log_error("Failed retrieving configuration descriptor: %s. %s\n",
[e484f3b]268 dev->ddf_dev->name, str_error(rc));
269 return rc;
270 }
271
[69334af]272 if (driver->endpoints != NULL) {
[0b4e7ca]273 rc = initialize_other_pipes(driver->endpoints, dev);
[69334af]274 }
275
[4ede178]276 usb_pipe_end_long_transfer(&dev->ctrl_pipe);
[69334af]277
[e484f3b]278 /* Rollback actions. */
279 if (rc != EOK) {
280 if (dev->descriptors.configuration != NULL) {
281 free(dev->descriptors.configuration);
282 }
283 }
284
[69334af]285 return rc;
286}
287
[7526e3d9]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
[69334af]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 */
[6105fc0]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));
[69334af]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
[6105fc0]434
435 dev->ddf_dev = gen_dev;
[69334af]436 dev->ddf_dev->driver_data = dev;
[6105fc0]437 dev->driver_data = NULL;
[e484f3b]438 dev->descriptors.configuration = NULL;
[6105fc0]439
[0b4e7ca]440 dev->pipes_count = 0;
441 dev->pipes = NULL;
442
[09daa8b]443 rc = initialize_pipes(dev);
[69334af]444 if (rc != EOK) {
445 free(dev);
446 return rc;
[6105fc0]447 }
448
[7526e3d9]449 (void) initialize_alternate_interfaces(dev);
450
[6105fc0]451 return driver->ops->add_device(dev);
452}
453
[0b4e7ca]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}
[159b91f4]543
[6105fc0]544/**
545 * @}
546 */
Note: See TracBrowser for help on using the repository browser.