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

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

libdrv: usb iface callbacks joined

In addition to handle and current interface, it is good for the device
to know its address and speed. Also, it is expected to add some other
fields (stats, info tied to devices of specific speed and so on) in the
future. Instead of adding yet another call, join those two and let HC
fill a description structure.

  • Property mode set to 100644
File size: 15.9 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 /** USB speed of this device */
80 usb_speed_t speed;
81
82 /** Current interface.
83 *
84 * Usually, drivers operate on single interface only.
85 * This item contains the value of the interface or -1 for any.
86 */
87 int interface_no;
88
89 /** Alternative interfaces. */
90 usb_alternate_interfaces_t alternate_interfaces;
91
92 /** Some useful descriptors for USB device. */
93 usb_device_descriptors_t descriptors;
94
95 /** Generic DDF device backing this one. DO NOT TOUCH! */
96 ddf_dev_t *ddf_dev;
97
98 /** Custom driver data.
99 *
100 * Do not use the entry in generic device, that is already used
101 * by the framework.
102 */
103 void *driver_data;
104};
105
106/** Count number of pipes the driver expects.
107 *
108 * @param drv USB driver.
109 * @return Number of pipes (excluding default control pipe).
110 */
111static inline size_t count_pipes(const usb_endpoint_description_t **endpoints)
112{
113 size_t count;
114 for (count = 0; endpoints != NULL && endpoints[count] != NULL; ++count);
115 return count;
116}
117
118/** Change interface setting of a device.
119 * This function selects new alternate setting of an interface by issuing
120 * proper USB command to the device and also creates new USB pipes
121 * under @c dev->pipes.
122 *
123 * @warning This function is intended for drivers working at interface level.
124 * For drivers controlling the whole device, you need to change interface
125 * manually using usb_request_set_interface() and creating new pipes
126 * with usb_pipe_initialize_from_configuration().
127 *
128 * @warning This is a wrapper function that does several operations that
129 * can fail and that cannot be rollbacked easily. That means that a failure
130 * during the SET_INTERFACE request would result in having a device with
131 * no pipes at all (except the default control one). That is because the old
132 * pipes needs to be unregistered at HC first and the new ones could not
133 * be created.
134 *
135 * @param dev USB device.
136 * @param alternate_setting Alternate setting to choose.
137 * @param endpoints New endpoint descriptions.
138 * @return Error code.
139 */
140int usb_device_select_interface(usb_device_t *usb_dev,
141 uint8_t alternate_setting, const usb_endpoint_description_t **endpoints)
142{
143 assert(usb_dev);
144
145 if (usb_dev->interface_no < 0) {
146 return EINVAL;
147 }
148
149 /* Change the interface itself. */
150 int rc = usb_request_set_interface(&usb_dev->ctrl_pipe,
151 usb_dev->interface_no, alternate_setting);
152 if (rc != EOK) {
153 return rc;
154 }
155
156 /* Change current alternative */
157 usb_dev->alternate_interfaces.current = alternate_setting;
158
159 /* Destroy existing pipes. */
160 usb_device_destroy_pipes(usb_dev);
161
162 /* Create new pipes. */
163 rc = usb_device_create_pipes(usb_dev, endpoints);
164
165 return rc;
166}
167
168/** Retrieve basic descriptors from the device.
169 *
170 * @param[in] ctrl_pipe Control endpoint pipe.
171 * @param[out] descriptors Where to store the descriptors.
172 * @return Error code.
173 */
174static int usb_device_retrieve_descriptors(usb_device_t *usb_dev)
175{
176 assert(usb_dev);
177 assert(usb_dev->descriptors.full_config == NULL);
178
179 /* Get the device descriptor. */
180 int rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
181 &usb_dev->descriptors.device);
182 if (rc != EOK) {
183 return rc;
184 }
185
186 /* Get the full configuration descriptor. */
187 rc = usb_request_get_full_configuration_descriptor_alloc(
188 &usb_dev->ctrl_pipe, 0,
189 &usb_dev->descriptors.full_config,
190 &usb_dev->descriptors.full_config_size);
191
192
193 return rc;
194}
195
196/** Cleanup structure initialized via usb_device_retrieve_descriptors.
197 *
198 * @param[in] descriptors Where to store the descriptors.
199 */
200static void usb_device_release_descriptors(usb_device_t *usb_dev)
201{
202 assert(usb_dev);
203 free(usb_dev->descriptors.full_config);
204 usb_dev->descriptors.full_config = NULL;
205 usb_dev->descriptors.full_config_size = 0;
206}
207
208/** Create pipes for a device.
209 *
210 * This is more or less a wrapper that does following actions:
211 * - allocate and initialize pipes
212 * - map endpoints to the pipes based on the descriptions
213 * - registers endpoints with the host controller
214 *
215 * @param[in] endpoints Endpoints description, NULL terminated.
216 * @param[in] config_descr Configuration descriptor of active configuration.
217 * @param[in] config_descr_size Size of @p config_descr in bytes.
218 * @param[in] interface_no Interface to map from.
219 * @param[in] interface_setting Interface setting (default is usually 0).
220 * @param[out] pipes_ptr Where to store array of created pipes
221 * (not NULL terminated).
222 * @param[out] pipes_count_ptr Where to store number of pipes
223 * (set to NULL if you wish to ignore the count).
224 * @return Error code.
225 */
226int usb_device_create_pipes(usb_device_t *usb_dev,
227 const usb_endpoint_description_t **endpoints)
228{
229 assert(usb_dev);
230 assert(usb_dev->descriptors.full_config);
231 assert(usb_dev->pipes == NULL);
232 assert(usb_dev->pipes_count == 0);
233
234 size_t pipe_count = count_pipes(endpoints);
235 if (pipe_count == 0) {
236 return EOK;
237 }
238
239 usb_endpoint_mapping_t *pipes =
240 calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
241 if (pipes == NULL) {
242 return ENOMEM;
243 }
244
245 /* Now initialize. */
246 for (size_t i = 0; i < pipe_count; i++) {
247 pipes[i].description = endpoints[i];
248 pipes[i].interface_no = usb_dev->interface_no;
249 pipes[i].interface_setting =
250 usb_dev->alternate_interfaces.current;
251 }
252
253 /* Find the mapping from configuration descriptor. */
254 int rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
255 usb_dev->descriptors.full_config,
256 usb_dev->descriptors.full_config_size,
257 usb_dev->bus_session);
258 if (rc != EOK) {
259 free(pipes);
260 return rc;
261 }
262
263 /* Register created pipes. */
264 unsigned pipes_registered = 0;
265 for (size_t i = 0; i < pipe_count; i++) {
266 if (pipes[i].present) {
267 rc = usb_pipe_register(&pipes[i].pipe, pipes[i].descriptor, pipes[i].companion_descriptor);
268 if (rc != EOK) {
269 goto rollback_unregister_endpoints;
270 }
271 }
272 pipes_registered++;
273 }
274
275 usb_dev->pipes = pipes;
276 usb_dev->pipes_count = pipe_count;
277
278 return EOK;
279
280 /*
281 * Jump here if something went wrong after endpoints have
282 * been registered.
283 * This is also the target when the registration of
284 * endpoints fails.
285 */
286rollback_unregister_endpoints:
287 for (size_t i = 0; i < pipes_registered; i++) {
288 if (pipes[i].present) {
289 usb_pipe_unregister(&pipes[i].pipe);
290 }
291 }
292
293 free(pipes);
294 return rc;
295}
296
297/** Destroy pipes previously created by usb_device_create_pipes.
298 *
299 * @param[in] usb_dev USB device.
300 *
301 */
302void usb_device_destroy_pipes(usb_device_t *usb_dev)
303{
304 assert(usb_dev);
305 assert(usb_dev->pipes || usb_dev->pipes_count == 0);
306
307 /* Destroy the pipes. */
308 int rc;
309 for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
310 usb_log_debug2("Unregistering pipe %zu: %spresent.",
311 i, usb_dev->pipes[i].present ? "" : "not ");
312
313 rc = usb_device_unmap_ep(usb_dev->pipes + i);
314 if (rc != EOK && rc != ENOENT)
315 usb_log_warning("Unregistering pipe %zu failed: %s", i, str_error(rc));
316 }
317
318 free(usb_dev->pipes);
319 usb_dev->pipes = NULL;
320 usb_dev->pipes_count = 0;
321}
322
323usb_pipe_t *usb_device_get_default_pipe(usb_device_t *usb_dev)
324{
325 assert(usb_dev);
326 return &usb_dev->ctrl_pipe;
327}
328
329usb_endpoint_mapping_t *usb_device_get_mapped_ep_desc(usb_device_t *usb_dev,
330 const usb_endpoint_description_t *desc)
331{
332 assert(usb_dev);
333 for (unsigned i = 0; i < usb_dev->pipes_count; ++i) {
334 if (usb_dev->pipes[i].description == desc)
335 return &usb_dev->pipes[i];
336 }
337 return NULL;
338}
339
340usb_endpoint_mapping_t * usb_device_get_mapped_ep(
341 usb_device_t *usb_dev, usb_endpoint_t ep)
342{
343 assert(usb_dev);
344 for (unsigned i = 0; i < usb_dev->pipes_count; ++i) {
345 if (usb_dev->pipes[i].pipe.desc.endpoint_no == ep)
346 return &usb_dev->pipes[i];
347 }
348 return NULL;
349}
350
351int usb_device_unmap_ep(usb_endpoint_mapping_t *epm)
352{
353 assert(epm);
354
355 if (!epm->present)
356 return ENOENT;
357
358 const int rc = usb_pipe_unregister(&epm->pipe);
359 if (rc != EOK)
360 return rc;
361
362 epm->present = false;
363 return EOK;
364}
365
366int usb_device_get_iface_number(usb_device_t *usb_dev)
367{
368 assert(usb_dev);
369 return usb_dev->interface_no;
370}
371
372devman_handle_t usb_device_get_devman_handle(usb_device_t *usb_dev)
373{
374 assert(usb_dev);
375 return usb_dev->handle;
376}
377
378const usb_device_descriptors_t *usb_device_descriptors(usb_device_t *usb_dev)
379{
380 assert(usb_dev);
381 return &usb_dev->descriptors;
382}
383
384const usb_alternate_interfaces_t * usb_device_get_alternative_ifaces(
385 usb_device_t *usb_dev)
386{
387 assert(usb_dev);
388 return &usb_dev->alternate_interfaces;
389}
390
391/** Clean instance of a USB device.
392 *
393 * @param dev Device to be de-initialized.
394 *
395 * Does not free/destroy supplied pointer.
396 */
397static void usb_device_fini(usb_device_t *usb_dev)
398{
399 if (usb_dev) {
400 /* Destroy existing pipes. */
401 usb_device_destroy_pipes(usb_dev);
402 /* Ignore errors and hope for the best. */
403 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
404 usb_device_release_descriptors(usb_dev);
405 free(usb_dev->driver_data);
406 usb_dev->driver_data = NULL;
407 usb_dev_disconnect(usb_dev->bus_session);
408 usb_dev->bus_session = NULL;
409 }
410}
411
412/** Initialize new instance of USB device.
413 *
414 * @param[in] usb_dev Pointer to the new device.
415 * @param[in] ddf_dev Generic DDF device backing the USB one.
416 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
417 * @param[out] errstr_ptr Where to store description of context
418 * (in case error occurs).
419 * @return Error code.
420 */
421static int usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
422 const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
423{
424 assert(usb_dev != NULL);
425 assert(errstr_ptr);
426
427 *errstr_ptr = NULL;
428
429 usb_dev->ddf_dev = ddf_dev;
430 usb_dev->driver_data = NULL;
431 usb_dev->descriptors.full_config = NULL;
432 usb_dev->descriptors.full_config_size = 0;
433 usb_dev->pipes_count = 0;
434 usb_dev->pipes = NULL;
435
436 usb_dev->bus_session = usb_dev_connect(usb_dev->handle);
437
438 if (!usb_dev->bus_session) {
439 *errstr_ptr = "device bus session create";
440 return ENOMEM;
441 }
442
443 /* This pipe was registered by the hub driver,
444 * during device initialization. */
445 int rc = usb_pipe_initialize_default_control(&usb_dev->ctrl_pipe, usb_dev->bus_session);
446 if (rc != EOK) {
447 usb_dev_disconnect(usb_dev->bus_session);
448 *errstr_ptr = "default control pipe initialization";
449 return rc;
450 }
451
452 /* Retrieve standard descriptors. */
453 rc = usb_device_retrieve_descriptors(usb_dev);
454 if (rc != EOK) {
455 *errstr_ptr = "descriptor retrieval";
456 usb_dev_disconnect(usb_dev->bus_session);
457 return rc;
458 }
459
460 /* Create alternate interfaces. We will silently ignore failure.
461 * We might either control one interface or an entire device,
462 * it makes no sense to speak about alternate interfaces when
463 * controlling a device. */
464 usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
465 usb_dev->descriptors.full_config,
466 usb_dev->descriptors.full_config_size, usb_dev->interface_no);
467
468 if (endpoints) {
469 /* Create and register other pipes than default control (EP 0)*/
470 rc = usb_device_create_pipes(usb_dev, endpoints);
471 if (rc != EOK) {
472 usb_device_fini(usb_dev);
473 *errstr_ptr = "pipes initialization";
474 return rc;
475 }
476 }
477
478 return EOK;
479}
480
481static int usb_device_get_info(async_sess_t *sess, usb_device_t *dev)
482{
483 assert(dev);
484
485 async_exch_t *exch = async_exchange_begin(sess);
486 if (!exch)
487 return EPARTY;
488
489 usb_device_desc_t dev_desc;
490 const int ret = usb_get_my_description(exch, &dev_desc);
491
492 if (ret == EOK) {
493 dev->address = dev_desc.address;
494 dev->speed = dev_desc.speed;
495 dev->handle = dev_desc.handle;
496 dev->interface_no = dev_desc.iface;
497 }
498
499 async_exchange_end(exch);
500 return ret;
501}
502
503int usb_device_create_ddf(ddf_dev_t *ddf_dev,
504 const usb_endpoint_description_t **desc, const char **err)
505{
506 assert(ddf_dev);
507 assert(err);
508
509 async_sess_t *sess = ddf_dev_parent_sess_get(ddf_dev);
510 if (sess == NULL)
511 return ENOMEM;
512
513 usb_device_t *usb_dev =
514 ddf_dev_data_alloc(ddf_dev, sizeof(usb_device_t));
515 if (usb_dev == NULL) {
516 *err = "DDF data alloc";
517 return ENOMEM;
518 }
519
520 const int ret = usb_device_get_info(sess, usb_dev);
521 if (ret != EOK)
522 return ret;
523
524 return usb_device_init(usb_dev, ddf_dev, desc, err);
525}
526
527void usb_device_destroy_ddf(ddf_dev_t *ddf_dev)
528{
529 assert(ddf_dev);
530 usb_device_t *usb_dev = ddf_dev_data_get(ddf_dev);
531 assert(usb_dev);
532 usb_device_fini(usb_dev);
533 return;
534}
535
536usb_device_t * usb_device_create(devman_handle_t handle)
537{
538 usb_device_t *usb_dev = malloc(sizeof(usb_device_t));
539 if (!usb_dev)
540 return NULL;
541
542 async_sess_t *sess = devman_device_connect(handle, IPC_FLAG_BLOCKING);
543 int ret = usb_device_get_info(sess, usb_dev);
544 if (sess)
545 async_hangup(sess);
546 if (ret != EOK)
547 return NULL;
548
549 const char* dummy = NULL;
550 ret = usb_device_init(usb_dev, NULL, NULL, &dummy);
551 if (ret != EOK) {
552 free(usb_dev);
553 usb_dev = NULL;
554 }
555 return usb_dev;
556}
557
558void usb_device_destroy(usb_device_t *usb_dev)
559{
560 if (usb_dev) {
561 usb_device_fini(usb_dev);
562 free(usb_dev);
563 }
564}
565
566const char *usb_device_get_name(usb_device_t *usb_dev)
567{
568 assert(usb_dev);
569 if (usb_dev->ddf_dev)
570 return ddf_dev_get_name(usb_dev->ddf_dev);
571 return NULL;
572}
573
574ddf_fun_t *usb_device_ddf_fun_create(usb_device_t *usb_dev, fun_type_t ftype,
575 const char* name)
576{
577 assert(usb_dev);
578 if (usb_dev->ddf_dev)
579 return ddf_fun_create(usb_dev->ddf_dev, ftype, name);
580 return NULL;
581}
582
583async_exch_t * usb_device_bus_exchange_begin(usb_device_t *usb_dev)
584{
585 assert(usb_dev);
586 return async_exchange_begin(usb_dev->bus_session);
587}
588
589void usb_device_bus_exchange_end(async_exch_t *exch)
590{
591 async_exchange_end(exch);
592}
593
594/** Allocate driver specific data.
595 * @param usb_dev usb_device structure.
596 * @param size requested data size.
597 * @return Pointer to the newly allocated space, NULL on failure.
598 */
599void * usb_device_data_alloc(usb_device_t *usb_dev, size_t size)
600{
601 assert(usb_dev);
602 assert(usb_dev->driver_data == NULL);
603 return usb_dev->driver_data = calloc(1, size);
604
605}
606
607void * usb_device_data_get(usb_device_t *usb_dev)
608{
609 assert(usb_dev);
610 return usb_dev->driver_data;
611}
612
613/**
614 * @}
615 */
Note: See TracBrowser for help on using the repository browser.