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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2018 Ondrej Hlavaty, Petr Manek, Michal Staruch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup libusbdev
32 * @{
33 */
34/** @file
35 * USB device driver framework.
36 */
37
38#include <usb_iface.h>
39#include <usb/dev/alternate_ifaces.h>
40#include <usb/dev/device.h>
41#include <usb/dev/pipes.h>
42#include <usb/dev/request.h>
43#include <usb/debug.h>
44#include <usb/descriptor.h>
45#include <usb/usb.h>
46
47#include <assert.h>
48#include <async.h>
49#include <devman.h>
50#include <errno.h>
51#include <str_error.h>
52#include <stdlib.h>
53
54#include <ddf/driver.h>
55
56/** USB device structure. */
57struct usb_device {
58 /** Connection to device on USB bus */
59 usb_dev_session_t *bus_session;
60
61 /** devman handle */
62 devman_handle_t handle;
63
64 /** The default control pipe. */
65 usb_pipe_t ctrl_pipe;
66
67 /** Other endpoint pipes.
68 *
69 * This is an array of other endpoint pipes in the same order as
70 * in usb_driver_t.
71 */
72 usb_endpoint_mapping_t *pipes;
73
74 /** Number of other endpoint pipes. */
75 size_t pipes_count;
76
77 /** USB address of this device */
78 usb_address_t address;
79
80 /** Depth in the USB hub hiearchy */
81 unsigned depth;
82
83 /** USB speed of this device */
84 usb_speed_t speed;
85
86 /** Current interface.
87 *
88 * Usually, drivers operate on single interface only.
89 * This item contains the value of the interface or -1 for any.
90 */
91 int interface_no;
92
93 /** Alternative interfaces. */
94 usb_alternate_interfaces_t alternate_interfaces;
95
96 /** Some useful descriptors for USB device. */
97 usb_device_descriptors_t descriptors;
98
99 /** Generic DDF device backing this one. DO NOT TOUCH! */
100 ddf_dev_t *ddf_dev;
101
102 /** Custom driver data.
103 *
104 * Do not use the entry in generic device, that is already used
105 * by the framework.
106 */
107 void *driver_data;
108};
109
110/** Count number of pipes the driver expects.
111 *
112 * @param drv USB driver.
113 * @return Number of pipes (excluding default control pipe).
114 */
115static inline size_t count_pipes(const usb_endpoint_description_t **endpoints)
116{
117 size_t count;
118 for (count = 0; endpoints != NULL && endpoints[count] != NULL; ++count);
119 return count;
120}
121
122/** Change interface setting of a device.
123 * This function selects new alternate setting of an interface by issuing
124 * proper USB command to the device and also creates new USB pipes
125 * under @c dev->pipes.
126 *
127 * @warning This function is intended for drivers working at interface level.
128 * For drivers controlling the whole device, you need to change interface
129 * manually using usb_request_set_interface() and creating new pipes
130 * with usb_pipe_initialize_from_configuration().
131 *
132 * @warning This is a wrapper function that does several operations that
133 * can fail and that cannot be rollbacked easily. That means that a failure
134 * during the SET_INTERFACE request would result in having a device with
135 * no pipes at all (except the default control one). That is because the old
136 * pipes needs to be unregistered at HC first and the new ones could not
137 * be created.
138 *
139 * @param dev USB device.
140 * @param alternate_setting Alternate setting to choose.
141 * @param endpoints New endpoint descriptions.
142 * @return Error code.
143 */
144errno_t usb_device_select_interface(usb_device_t *usb_dev,
145 uint8_t alternate_setting, const usb_endpoint_description_t **endpoints)
146{
147 assert(usb_dev);
148
149 if (usb_dev->interface_no < 0) {
150 return EINVAL;
151 }
152
153 /* Change the interface itself. */
154 errno_t rc = usb_request_set_interface(&usb_dev->ctrl_pipe,
155 usb_dev->interface_no, alternate_setting);
156 if (rc != EOK) {
157 return rc;
158 }
159
160 /* Change current alternative */
161 usb_dev->alternate_interfaces.current = alternate_setting;
162
163 /* Destroy existing pipes. */
164 usb_device_destroy_pipes(usb_dev);
165
166 /* Create new pipes. */
167 rc = usb_device_create_pipes(usb_dev, endpoints);
168
169 return rc;
170}
171
172/** Retrieve basic descriptors from the device.
173 *
174 * @param[in] ctrl_pipe Control endpoint pipe.
175 * @param[out] descriptors Where to store the descriptors.
176 * @return Error code.
177 */
178static errno_t usb_device_retrieve_descriptors(usb_device_t *usb_dev)
179{
180 assert(usb_dev);
181 assert(usb_dev->descriptors.full_config == NULL);
182
183 /* Get the device descriptor. */
184 errno_t rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
185 &usb_dev->descriptors.device);
186 if (rc != EOK) {
187 return rc;
188 }
189
190 /* Get the full configuration descriptor. */
191 rc = usb_request_get_full_configuration_descriptor_alloc(
192 &usb_dev->ctrl_pipe, 0,
193 &usb_dev->descriptors.full_config,
194 &usb_dev->descriptors.full_config_size);
195
196
197 return rc;
198}
199
200/** Cleanup structure initialized via usb_device_retrieve_descriptors.
201 *
202 * @param[in] descriptors Where to store the descriptors.
203 */
204static void usb_device_release_descriptors(usb_device_t *usb_dev)
205{
206 assert(usb_dev);
207 free(usb_dev->descriptors.full_config);
208 usb_dev->descriptors.full_config = NULL;
209 usb_dev->descriptors.full_config_size = 0;
210}
211
212/** Create pipes for a device.
213 *
214 * This is more or less a wrapper that does following actions:
215 * - allocate and initialize pipes
216 * - map endpoints to the pipes based on the descriptions
217 * - registers endpoints with the host controller
218 *
219 * @param[in] endpoints Endpoints description, NULL terminated.
220 * @param[in] config_descr Configuration descriptor of active configuration.
221 * @param[in] config_descr_size Size of @p config_descr in bytes.
222 * @param[in] interface_no Interface to map from.
223 * @param[in] interface_setting Interface setting (default is usually 0).
224 * @param[out] pipes_ptr Where to store array of created pipes
225 * (not NULL terminated).
226 * @param[out] pipes_count_ptr Where to store number of pipes
227 * (set to NULL if you wish to ignore the count).
228 * @return Error code.
229 */
230errno_t usb_device_create_pipes(usb_device_t *usb_dev,
231 const usb_endpoint_description_t **endpoints)
232{
233 assert(usb_dev);
234 assert(usb_dev->descriptors.full_config);
235 assert(usb_dev->pipes == NULL);
236 assert(usb_dev->pipes_count == 0);
237
238 size_t pipe_count = count_pipes(endpoints);
239 if (pipe_count == 0) {
240 return EOK;
241 }
242
243 usb_endpoint_mapping_t *pipes =
244 calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
245 if (pipes == NULL) {
246 return ENOMEM;
247 }
248
249 /* Now initialize. */
250 for (size_t i = 0; i < pipe_count; i++) {
251 pipes[i].description = endpoints[i];
252 pipes[i].interface_no = usb_dev->interface_no;
253 pipes[i].interface_setting =
254 usb_dev->alternate_interfaces.current;
255 }
256
257 /* Find the mapping from configuration descriptor. */
258 errno_t rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
259 usb_dev->descriptors.full_config,
260 usb_dev->descriptors.full_config_size,
261 usb_dev->bus_session);
262 if (rc != EOK) {
263 free(pipes);
264 return rc;
265 }
266
267 /* Register created pipes. */
268 unsigned pipes_registered = 0;
269 for (size_t i = 0; i < pipe_count; i++) {
270 if (pipes[i].present) {
271 rc = usb_pipe_register(&pipes[i].pipe, pipes[i].descriptor, pipes[i].companion_descriptor);
272 if (rc != EOK) {
273 goto rollback_unregister_endpoints;
274 }
275 }
276 pipes_registered++;
277 }
278
279 usb_dev->pipes = pipes;
280 usb_dev->pipes_count = pipe_count;
281
282 return EOK;
283
284 /*
285 * Jump here if something went wrong after endpoints have
286 * been registered.
287 * This is also the target when the registration of
288 * endpoints fails.
289 */
290rollback_unregister_endpoints:
291 for (size_t i = 0; i < pipes_registered; i++) {
292 if (pipes[i].present) {
293 usb_pipe_unregister(&pipes[i].pipe);
294 }
295 }
296
297 free(pipes);
298 return rc;
299}
300
301/** Destroy pipes previously created by usb_device_create_pipes.
302 *
303 * @param[in] usb_dev USB device.
304 *
305 */
306void usb_device_destroy_pipes(usb_device_t *usb_dev)
307{
308 assert(usb_dev);
309 assert(usb_dev->pipes || usb_dev->pipes_count == 0);
310
311 /* Destroy the pipes. */
312 int rc;
313 for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
314 usb_log_debug2("Unregistering pipe %zu: %spresent.",
315 i, usb_dev->pipes[i].present ? "" : "not ");
316
317 rc = usb_device_unmap_ep(usb_dev->pipes + i);
318 if (rc != EOK && rc != ENOENT)
319 usb_log_warning("Unregistering pipe %zu failed: %s", i, str_error(rc));
320 }
321
322 free(usb_dev->pipes);
323 usb_dev->pipes = NULL;
324 usb_dev->pipes_count = 0;
325}
326
327usb_pipe_t *usb_device_get_default_pipe(usb_device_t *usb_dev)
328{
329 assert(usb_dev);
330 return &usb_dev->ctrl_pipe;
331}
332
333usb_endpoint_mapping_t *usb_device_get_mapped_ep_desc(usb_device_t *usb_dev,
334 const usb_endpoint_description_t *desc)
335{
336 assert(usb_dev);
337 for (unsigned i = 0; i < usb_dev->pipes_count; ++i) {
338 if (usb_dev->pipes[i].description == desc)
339 return &usb_dev->pipes[i];
340 }
341 return NULL;
342}
343
344int usb_device_unmap_ep(usb_endpoint_mapping_t *epm)
345{
346 assert(epm);
347
348 if (!epm->present)
349 return ENOENT;
350
351 const int rc = usb_pipe_unregister(&epm->pipe);
352 if (rc != EOK)
353 return rc;
354
355 epm->present = false;
356 return EOK;
357}
358
359usb_address_t usb_device_get_address(const usb_device_t *usb_dev)
360{
361 assert(usb_dev);
362 return usb_dev->depth;
363}
364
365unsigned usb_device_get_depth(const usb_device_t *usb_dev)
366{
367 assert(usb_dev);
368 return usb_dev->depth;
369}
370
371usb_speed_t usb_device_get_speed(const usb_device_t *usb_dev)
372{
373 assert(usb_dev);
374 return usb_dev->speed;
375}
376
377int usb_device_get_iface_number(const usb_device_t *usb_dev)
378{
379 assert(usb_dev);
380 return usb_dev->interface_no;
381}
382
383devman_handle_t usb_device_get_devman_handle(const usb_device_t *usb_dev)
384{
385 assert(usb_dev);
386 return usb_dev->handle;
387}
388
389const usb_device_descriptors_t *usb_device_descriptors(usb_device_t *usb_dev)
390{
391 assert(usb_dev);
392 return &usb_dev->descriptors;
393}
394
395const usb_alternate_interfaces_t * usb_device_get_alternative_ifaces(
396 usb_device_t *usb_dev)
397{
398 assert(usb_dev);
399 return &usb_dev->alternate_interfaces;
400}
401
402/** Clean instance of a USB device.
403 *
404 * @param dev Device to be de-initialized.
405 *
406 * Does not free/destroy supplied pointer.
407 */
408static void usb_device_fini(usb_device_t *usb_dev)
409{
410 if (usb_dev) {
411 /* Destroy existing pipes. */
412 usb_device_destroy_pipes(usb_dev);
413 /* Ignore errors and hope for the best. */
414 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
415 usb_device_release_descriptors(usb_dev);
416 free(usb_dev->driver_data);
417 usb_dev->driver_data = NULL;
418 usb_dev_disconnect(usb_dev->bus_session);
419 usb_dev->bus_session = NULL;
420 }
421}
422
423/** Initialize new instance of USB device.
424 *
425 * @param[in] usb_dev Pointer to the new device.
426 * @param[in] ddf_dev Generic DDF device backing the USB one.
427 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
428 * @param[out] errstr_ptr Where to store description of context
429 * (in case error occurs).
430 * @return Error code.
431 */
432static errno_t usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
433 const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
434{
435 assert(usb_dev != NULL);
436 assert(errstr_ptr);
437
438 *errstr_ptr = NULL;
439
440 usb_dev->ddf_dev = ddf_dev;
441 usb_dev->driver_data = NULL;
442 usb_dev->descriptors.full_config = NULL;
443 usb_dev->descriptors.full_config_size = 0;
444 usb_dev->pipes_count = 0;
445 usb_dev->pipes = NULL;
446
447 usb_dev->bus_session = usb_dev_connect(usb_dev->handle);
448
449 if (!usb_dev->bus_session) {
450 *errstr_ptr = "device bus session create";
451 return ENOMEM;
452 }
453
454 /* This pipe was registered by the hub driver,
455 * during device initialization. */
456 errno_t rc = usb_pipe_initialize_default_control(&usb_dev->ctrl_pipe, usb_dev->bus_session);
457 if (rc != EOK) {
458 usb_dev_disconnect(usb_dev->bus_session);
459 *errstr_ptr = "default control pipe initialization";
460 return rc;
461 }
462
463 /* Retrieve standard descriptors. */
464 rc = usb_device_retrieve_descriptors(usb_dev);
465 if (rc != EOK) {
466 *errstr_ptr = "descriptor retrieval";
467 usb_dev_disconnect(usb_dev->bus_session);
468 return rc;
469 }
470
471 /* Create alternate interfaces. We will silently ignore failure.
472 * We might either control one interface or an entire device,
473 * it makes no sense to speak about alternate interfaces when
474 * controlling a device. */
475 usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
476 usb_dev->descriptors.full_config,
477 usb_dev->descriptors.full_config_size, usb_dev->interface_no);
478
479 if (endpoints) {
480 /* Create and register other pipes than default control (EP 0)*/
481 rc = usb_device_create_pipes(usb_dev, endpoints);
482 if (rc != EOK) {
483 usb_device_fini(usb_dev);
484 *errstr_ptr = "pipes initialization";
485 return rc;
486 }
487 }
488
489 return EOK;
490}
491
492static errno_t usb_device_get_info(async_sess_t *sess, usb_device_t *dev)
493{
494 assert(dev);
495
496 async_exch_t *exch = async_exchange_begin(sess);
497 if (!exch)
498 return EPARTY;
499
500 usb_device_desc_t dev_desc;
501 const errno_t ret = usb_get_my_description(exch, &dev_desc);
502
503 if (ret == EOK) {
504 dev->address = dev_desc.address;
505 dev->depth = dev_desc.depth;
506 dev->speed = dev_desc.speed;
507 dev->handle = dev_desc.handle;
508 dev->interface_no = dev_desc.iface;
509 }
510
511 async_exchange_end(exch);
512 return ret;
513}
514
515errno_t usb_device_create_ddf(ddf_dev_t *ddf_dev,
516 const usb_endpoint_description_t **desc, const char **err)
517{
518 assert(ddf_dev);
519 assert(err);
520
521 async_sess_t *sess = ddf_dev_parent_sess_get(ddf_dev);
522 if (sess == NULL)
523 return ENOMEM;
524
525 usb_device_t *usb_dev =
526 ddf_dev_data_alloc(ddf_dev, sizeof(usb_device_t));
527 if (usb_dev == NULL) {
528 *err = "DDF data alloc";
529 return ENOMEM;
530 }
531
532 const errno_t ret = usb_device_get_info(sess, usb_dev);
533 if (ret != EOK)
534 return ret;
535
536 return usb_device_init(usb_dev, ddf_dev, desc, err);
537}
538
539void usb_device_destroy_ddf(ddf_dev_t *ddf_dev)
540{
541 assert(ddf_dev);
542 usb_device_t *usb_dev = ddf_dev_data_get(ddf_dev);
543 assert(usb_dev);
544 usb_device_fini(usb_dev);
545 return;
546}
547
548usb_device_t * usb_device_create(devman_handle_t handle)
549{
550 usb_device_t *usb_dev = malloc(sizeof(usb_device_t));
551 if (!usb_dev)
552 return NULL;
553
554 async_sess_t *sess = devman_device_connect(handle, IPC_FLAG_BLOCKING);
555 errno_t ret = usb_device_get_info(sess, usb_dev);
556 if (sess)
557 async_hangup(sess);
558 if (ret != EOK) {
559 free(usb_dev);
560 return NULL;
561 }
562
563 const char* dummy = NULL;
564 ret = usb_device_init(usb_dev, NULL, NULL, &dummy);
565 if (ret != EOK) {
566 free(usb_dev);
567 usb_dev = NULL;
568 }
569 return usb_dev;
570}
571
572void usb_device_destroy(usb_device_t *usb_dev)
573{
574 if (usb_dev) {
575 usb_device_fini(usb_dev);
576 free(usb_dev);
577 }
578}
579
580const char *usb_device_get_name(usb_device_t *usb_dev)
581{
582 assert(usb_dev);
583 if (usb_dev->ddf_dev)
584 return ddf_dev_get_name(usb_dev->ddf_dev);
585 return NULL;
586}
587
588ddf_fun_t *usb_device_ddf_fun_create(usb_device_t *usb_dev, fun_type_t ftype,
589 const char* name)
590{
591 assert(usb_dev);
592 if (usb_dev->ddf_dev)
593 return ddf_fun_create(usb_dev->ddf_dev, ftype, name);
594 return NULL;
595}
596
597async_exch_t * usb_device_bus_exchange_begin(usb_device_t *usb_dev)
598{
599 assert(usb_dev);
600 return async_exchange_begin(usb_dev->bus_session);
601}
602
603void usb_device_bus_exchange_end(async_exch_t *exch)
604{
605 async_exchange_end(exch);
606}
607
608/** Allocate driver specific data.
609 * @param usb_dev usb_device structure.
610 * @param size requested data size.
611 * @return Pointer to the newly allocated space, NULL on failure.
612 */
613void * usb_device_data_alloc(usb_device_t *usb_dev, size_t size)
614{
615 assert(usb_dev);
616 assert(usb_dev->driver_data == NULL);
617 return usb_dev->driver_data = calloc(1, size);
618
619}
620
621void * usb_device_data_get(usb_device_t *usb_dev)
622{
623 assert(usb_dev);
624 return usb_dev->driver_data;
625}
626
627/**
628 * @}
629 */
Note: See TracBrowser for help on using the repository browser.