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

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

usb: remove misleading usb_device_get_mapped_ep

Even though this method may seem very convenient to use, it's actually
wrong. The devices are usually not required to have strict endpoint
numbers. That's why the mapping mechanism exists. Therefore, it's just
not possible to rely on fixed endpoint mapping.

There could be similar method, that would take the transfer type and
direction, but it's much better to ask for the complete mapping then.

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