source: mainline/uspace/lib/usbdev/src/devdrv.c@ 3e4f2e0

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

libusb divided into sublibraries

Also removed address keeper test from tester as it is useless.

Directory reorganization of the include/ will follow.

  • Property mode set to 100644
File size: 13.6 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
[69334af]126/** Callback when new device is supposed to be controlled by this driver.
127 *
128 * This callback is a wrapper for USB specific version of @c add_device.
129 *
130 * @param gen_dev Device structure as prepared by DDF.
131 * @return Error code.
132 */
[6105fc0]133int generic_add_device(ddf_dev_t *gen_dev)
134{
135 assert(driver);
136 assert(driver->ops);
137 assert(driver->ops->add_device);
138
139 int rc;
140
[6ee6e6f]141 usb_device_t *dev = NULL;
142 const char *err_msg = NULL;
143 rc = usb_device_create(gen_dev, driver->endpoints, &dev, &err_msg);
[69334af]144 if (rc != EOK) {
[6ee6e6f]145 usb_log_error("USB device `%s' creation failed (%s): %s.\n",
146 gen_dev->name, err_msg, str_error(rc));
[69334af]147 return rc;
[6105fc0]148 }
149
150 return driver->ops->add_device(dev);
151}
152
[0b4e7ca]153/** Destroy existing pipes of a USB device.
154 *
155 * @param dev Device where to destroy the pipes.
156 * @return Error code.
157 */
158static int destroy_current_pipes(usb_device_t *dev)
159{
[c1b1944]160 int rc = usb_device_destroy_pipes(dev->ddf_dev,
161 dev->pipes, dev->pipes_count);
[0b4e7ca]162 if (rc != EOK) {
163 return rc;
164 }
165
166 dev->pipes = NULL;
167 dev->pipes_count = 0;
168
169 return EOK;
170}
171
172/** Change interface setting of a device.
173 * This function selects new alternate setting of an interface by issuing
174 * proper USB command to the device and also creates new USB pipes
175 * under @c dev->pipes.
176 *
177 * @warning This function is intended for drivers working at interface level.
178 * For drivers controlling the whole device, you need to change interface
179 * manually using usb_request_set_interface() and creating new pipes
180 * with usb_pipe_initialize_from_configuration().
181 *
[3f2af64]182 * @warning This is a wrapper function that does several operations that
183 * can fail and that cannot be rollbacked easily. That means that a failure
184 * during the SET_INTERFACE request would result in having a device with
185 * no pipes at all (except the default control one). That is because the old
186 * pipes needs to be unregistered at HC first and the new ones could not
187 * be created.
188 *
[0b4e7ca]189 * @param dev USB device.
190 * @param alternate_setting Alternate setting to choose.
191 * @param endpoints New endpoint descriptions.
192 * @return Error code.
193 */
194int usb_device_select_interface(usb_device_t *dev, uint8_t alternate_setting,
195 usb_endpoint_description_t **endpoints)
196{
197 if (dev->interface_no < 0) {
198 return EINVAL;
199 }
200
201 int rc;
202
203 /* Destroy existing pipes. */
204 rc = destroy_current_pipes(dev);
205 if (rc != EOK) {
206 return rc;
207 }
208
209 /* Change the interface itself. */
210 rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
211 alternate_setting);
212 if (rc != EOK) {
213 return rc;
214 }
215
216 /* Create new pipes. */
[c1b1944]217 rc = initialize_other_pipes(endpoints, dev, (int) alternate_setting);
218
219 return rc;
220}
221
222/** Retrieve basic descriptors from the device.
223 *
[4fa0a384]224 * @param[in] ctrl_pipe Control endpoint pipe.
[c1b1944]225 * @param[out] descriptors Where to store the descriptors.
226 * @return Error code.
227 */
228int usb_device_retrieve_descriptors(usb_pipe_t *ctrl_pipe,
229 usb_device_descriptors_t *descriptors)
230{
231 assert(descriptors != NULL);
232
233 descriptors->configuration = NULL;
234
235 int rc;
236
[4fa0a384]237 /* It is worth to start a long transfer. */
[2c2cbcf]238 usb_pipe_start_long_transfer(ctrl_pipe);
[4fa0a384]239
[c1b1944]240 /* Get the device descriptor. */
241 rc = usb_request_get_device_descriptor(ctrl_pipe, &descriptors->device);
242 if (rc != EOK) {
[4fa0a384]243 goto leave;
[c1b1944]244 }
245
246 /* Get the full configuration descriptor. */
247 rc = usb_request_get_full_configuration_descriptor_alloc(
248 ctrl_pipe, 0, (void **) &descriptors->configuration,
249 &descriptors->configuration_size);
250
[4fa0a384]251leave:
252 usb_pipe_end_long_transfer(ctrl_pipe);
253
254 return rc;
[c1b1944]255}
256
257/** Create pipes for a device.
258 *
259 * This is more or less a wrapper that does following actions:
260 * - allocate and initialize pipes
261 * - map endpoints to the pipes based on the descriptions
262 * - registers endpoints with the host controller
263 *
264 * @param[in] dev Generic DDF device backing the USB one.
265 * @param[in] wire Initialized backing connection to the host controller.
266 * @param[in] endpoints Endpoints description, NULL terminated.
267 * @param[in] config_descr Configuration descriptor of active configuration.
268 * @param[in] config_descr_size Size of @p config_descr in bytes.
269 * @param[in] interface_no Interface to map from.
270 * @param[in] interface_setting Interface setting (default is usually 0).
271 * @param[out] pipes_ptr Where to store array of created pipes
272 * (not NULL terminated).
273 * @param[out] pipes_count_ptr Where to store number of pipes
274 * (set to if you wish to ignore the count).
275 * @return Error code.
276 */
277int usb_device_create_pipes(ddf_dev_t *dev, usb_device_connection_t *wire,
278 usb_endpoint_description_t **endpoints,
279 uint8_t *config_descr, size_t config_descr_size,
280 int interface_no, int interface_setting,
281 usb_endpoint_mapping_t **pipes_ptr, size_t *pipes_count_ptr)
282{
283 assert(dev != NULL);
284 assert(wire != NULL);
285 assert(endpoints != NULL);
286 assert(config_descr != NULL);
287 assert(config_descr_size > 0);
288 assert(pipes_ptr != NULL);
289
290 size_t i;
291 int rc;
292
293 size_t pipe_count = count_other_pipes(endpoints);
294 if (pipe_count == 0) {
295 *pipes_ptr = NULL;
296 return EOK;
297 }
298
299 usb_endpoint_mapping_t *pipes
300 = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
301 if (pipes == NULL) {
302 return ENOMEM;
303 }
304
305 /* Initialize to NULL to allow smooth rollback. */
306 for (i = 0; i < pipe_count; i++) {
307 pipes[i].pipe = NULL;
308 }
309
310 /* Now allocate and fully initialize. */
311 for (i = 0; i < pipe_count; i++) {
312 pipes[i].pipe = malloc(sizeof(usb_pipe_t));
313 if (pipes[i].pipe == NULL) {
314 rc = ENOMEM;
315 goto rollback_free_only;
316 }
317 pipes[i].description = endpoints[i];
318 pipes[i].interface_no = interface_no;
319 pipes[i].interface_setting = interface_setting;
320 }
321
322 /* Find the mapping from configuration descriptor. */
323 rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
324 config_descr, config_descr_size, wire);
325 if (rc != EOK) {
326 goto rollback_free_only;
327 }
328
329 /* Register the endpoints with HC. */
330 usb_hc_connection_t hc_conn;
331 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
332 if (rc != EOK) {
333 goto rollback_free_only;
334 }
335
336 rc = usb_hc_connection_open(&hc_conn);
337 if (rc != EOK) {
338 goto rollback_free_only;
339 }
340
341 for (i = 0; i < pipe_count; i++) {
342 if (pipes[i].present) {
343 rc = usb_pipe_register(pipes[i].pipe,
344 pipes[i].descriptor->poll_interval, &hc_conn);
345 if (rc != EOK) {
346 goto rollback_unregister_endpoints;
347 }
348 }
349 }
350
351 usb_hc_connection_close(&hc_conn);
352
353 *pipes_ptr = pipes;
354 if (pipes_count_ptr != NULL) {
355 *pipes_count_ptr = pipe_count;
356 }
357
358 return EOK;
359
360 /*
361 * Jump here if something went wrong after endpoints have
362 * been registered.
363 * This is also the target when the registration of
364 * endpoints fails.
365 */
366rollback_unregister_endpoints:
367 for (i = 0; i < pipe_count; i++) {
368 if (pipes[i].present) {
369 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
370 }
371 }
372
373 usb_hc_connection_close(&hc_conn);
374
375 /*
376 * Jump here if something went wrong before some actual communication
377 * with HC. Then the only thing that needs to be done is to free
378 * allocated memory.
379 */
380rollback_free_only:
381 for (i = 0; i < pipe_count; i++) {
382 if (pipes[i].pipe != NULL) {
383 free(pipes[i].pipe);
384 }
385 }
386 free(pipes);
[0b4e7ca]387
388 return rc;
389}
[159b91f4]390
[c1b1944]391/** Destroy pipes previously created by usb_device_create_pipes.
392 *
393 * @param[in] dev Generic DDF device backing the USB one.
394 * @param[in] pipes Endpoint mapping to be destroyed.
395 * @param[in] pipes_count Number of endpoints.
396 */
397int usb_device_destroy_pipes(ddf_dev_t *dev,
398 usb_endpoint_mapping_t *pipes, size_t pipes_count)
399{
400 assert(dev != NULL);
401 assert(((pipes != NULL) && (pipes_count > 0))
402 || ((pipes == NULL) && (pipes_count == 0)));
403
404 if (pipes_count == 0) {
405 return EOK;
406 }
407
408 int rc;
409
410 /* Prepare connection to HC to allow endpoint unregistering. */
411 usb_hc_connection_t hc_conn;
412 rc = usb_hc_connection_initialize_from_device(&hc_conn, dev);
413 if (rc != EOK) {
414 return rc;
415 }
416 rc = usb_hc_connection_open(&hc_conn);
417 if (rc != EOK) {
418 return rc;
419 }
420
421 /* Destroy the pipes. */
422 size_t i;
423 for (i = 0; i < pipes_count; i++) {
424 usb_pipe_unregister(pipes[i].pipe, &hc_conn);
425 free(pipes[i].pipe);
426 }
427
428 usb_hc_connection_close(&hc_conn);
429
430 free(pipes);
431
432 return EOK;
433}
434
[3f2af64]435/** Initialize control pipe in a device.
436 *
437 * @param dev USB device in question.
438 * @param errmsg Where to store error context.
439 * @return
440 */
441static int init_wire_and_ctrl_pipe(usb_device_t *dev, const char **errmsg)
[6ee6e6f]442{
443 int rc;
444
445 rc = usb_device_connection_initialize_from_device(&dev->wire,
446 dev->ddf_dev);
447 if (rc != EOK) {
448 *errmsg = "device connection initialization";
449 return rc;
450 }
451
452 rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
453 &dev->wire);
454 if (rc != EOK) {
455 *errmsg = "default control pipe initialization";
456 return rc;
457 }
458
[3f2af64]459 return EOK;
[6ee6e6f]460}
461
462
463/** Create new instance of USB device.
464 *
465 * @param[in] ddf_dev Generic DDF device backing the USB one.
466 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
467 * @param[out] dev_ptr Where to store pointer to the new device.
468 * @param[out] errstr_ptr Where to store description of context
469 * (in case error occurs).
470 * @return Error code.
471 */
472int usb_device_create(ddf_dev_t *ddf_dev,
473 usb_endpoint_description_t **endpoints,
474 usb_device_t **dev_ptr, const char **errstr_ptr)
475{
476 assert(dev_ptr != NULL);
477 assert(ddf_dev != NULL);
478
479 int rc;
480
481 usb_device_t *dev = malloc(sizeof(usb_device_t));
482 if (dev == NULL) {
483 *errstr_ptr = "structure allocation";
484 return ENOMEM;
485 }
486
[3f2af64]487 // FIXME: proper deallocation in case of errors
488
[6ee6e6f]489 dev->ddf_dev = ddf_dev;
490 dev->driver_data = NULL;
491 dev->descriptors.configuration = NULL;
492 dev->alternate_interfaces = NULL;
493
494 dev->pipes_count = 0;
495 dev->pipes = NULL;
496
[3f2af64]497 /* Initialize backing wire and control pipe. */
498 rc = init_wire_and_ctrl_pipe(dev, errstr_ptr);
499 if (rc != EOK) {
500 return rc;
501 }
502
503 /* Get our interface. */
504 dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
505
506 /* Retrieve standard descriptors. */
507 rc = usb_device_retrieve_descriptors(&dev->ctrl_pipe,
508 &dev->descriptors);
[6ee6e6f]509 if (rc != EOK) {
[3f2af64]510 *errstr_ptr = "descriptor retrieval";
[6ee6e6f]511 return rc;
512 }
513
[3f2af64]514 /* Create alternate interfaces. */
[231748a]515 rc = usb_alternate_interfaces_create(dev->descriptors.configuration,
516 dev->descriptors.configuration_size, dev->interface_no,
517 &dev->alternate_interfaces);
[6ee6e6f]518 if (rc != EOK) {
519 /* We will try to silently ignore this. */
520 dev->alternate_interfaces = NULL;
521 }
522
523 rc = initialize_other_pipes(endpoints, dev, 0);
524 if (rc != EOK) {
525 *errstr_ptr = "pipes initialization";
526 return rc;
527 }
528
529 *errstr_ptr = NULL;
530 *dev_ptr = dev;
531
532 return EOK;
533}
534
[6105fc0]535/**
536 * @}
537 */
Note: See TracBrowser for help on using the repository browser.