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

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

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

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