source: mainline/uspace/lib/usbhost/src/ddf_helpers.c@ 3bacee1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3bacee1 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: 13.2 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch, Petr Manek
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 libusbhost
31 * @{
32 */
33/** @file
34 * Helpers to work with the DDF interface.
35 */
36
37#include <adt/list.h>
38#include <assert.h>
39#include <async.h>
40#include <ddf/driver.h>
41#include <ddf/interrupt.h>
42#include <device/hw_res_parsed.h>
43#include <errno.h>
44#include <str_error.h>
45#include <usb/classes/classes.h>
46#include <usb/debug.h>
47#include <usb/descriptor.h>
48#include <usb/usb.h>
49#include <usb/dma_buffer.h>
50#include <usb_iface.h>
51#include <usbhc_iface.h>
52
53#include "bus.h"
54#include "endpoint.h"
55
56#include "ddf_helpers.h"
57
58/**
59 * DDF usbhc_iface callback. Passes the endpoint descriptors, fills the pipe
60 * descriptor according to the contents of the endpoint.
61 *
62 * @param[in] fun DDF function of the device in question.
63 * @param[out] pipe_desc The pipe descriptor to be filled.
64 * @param[in] endpoint_desc Endpoint descriptors from the device.
65 * @return Error code.
66 */
67static errno_t register_endpoint(ddf_fun_t *fun, usb_pipe_desc_t *pipe_desc,
68 const usb_endpoint_descriptors_t *ep_desc)
69{
70 assert(fun);
71 hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
72 device_t *dev = ddf_fun_data_get(fun);
73 assert(hcd);
74 assert(hcd->bus);
75 assert(dev);
76
77 endpoint_t *ep;
78 const int err = bus_endpoint_add(dev, ep_desc, &ep);
79 if (err)
80 return err;
81
82 if (pipe_desc) {
83 pipe_desc->endpoint_no = ep->endpoint;
84 pipe_desc->direction = ep->direction;
85 pipe_desc->transfer_type = ep->transfer_type;
86 pipe_desc->max_transfer_size = ep->max_transfer_size;
87 pipe_desc->transfer_buffer_policy = ep->transfer_buffer_policy;
88 }
89 endpoint_del_ref(ep);
90
91 return EOK;
92}
93
94 /**
95 * DDF usbhc_iface callback. Unregister endpoint that makes the other end of
96 * the pipe described.
97 *
98 * @param fun DDF function of the device in question.
99 * @param pipe_desc Pipe description.
100 * @return Error code.
101 */
102static errno_t unregister_endpoint(ddf_fun_t *fun, const usb_pipe_desc_t *pipe_desc)
103{
104 assert(fun);
105 hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
106 device_t *dev = ddf_fun_data_get(fun);
107 assert(hcd);
108 assert(hcd->bus);
109 assert(dev);
110
111 endpoint_t *ep = bus_find_endpoint(dev, pipe_desc->endpoint_no, pipe_desc->direction);
112 if (!ep)
113 return ENOENT;
114
115 const errno_t err = bus_endpoint_remove(ep);
116
117 endpoint_del_ref(ep);
118 return err;
119}
120
121/**
122 * DDF usbhc_iface callback. Calls the respective bus operation directly.
123 *
124 * @param fun DDF function of the device (hub) requesting the address.
125 */
126static errno_t default_address_reservation(ddf_fun_t *fun, bool reserve)
127{
128 assert(fun);
129 hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
130 device_t *dev = ddf_fun_data_get(fun);
131 assert(hcd);
132 assert(hcd->bus);
133 assert(dev);
134
135 usb_log_debug("Device %d %s default address", dev->address, reserve ? "requested" : "releasing");
136 if (reserve) {
137 return bus_reserve_default_address(hcd->bus, dev);
138 } else {
139 bus_release_default_address(hcd->bus, dev);
140 return EOK;
141 }
142}
143
144/**
145 * DDF usbhc_iface callback. Calls the bus operation directly.
146 *
147 * @param fun DDF function of the device (hub) requesting the address.
148 * @param speed USB speed of the new device
149 */
150static errno_t device_enumerate(ddf_fun_t *fun, unsigned port, usb_speed_t speed)
151{
152 assert(fun);
153 ddf_dev_t *hc = ddf_fun_get_dev(fun);
154 assert(hc);
155 hc_device_t *hcd = dev_to_hcd(hc);
156 assert(hcd);
157 device_t *hub = ddf_fun_data_get(fun);
158 assert(hub);
159
160 errno_t err;
161
162 if (!usb_speed_is_valid(speed))
163 return EINVAL;
164
165 usb_log_debug("Hub %d reported a new %s speed device on port: %u",
166 hub->address, usb_str_speed(speed), port);
167
168 device_t *dev = hcd_ddf_fun_create(hcd, speed);
169 if (!dev) {
170 usb_log_error("Failed to create USB device function.");
171 return ENOMEM;
172 }
173
174 dev->hub = hub;
175 dev->tier = hub->tier + 1;
176 dev->port = port;
177 dev->speed = speed;
178
179 if ((err = bus_device_enumerate(dev))) {
180 usb_log_error("Failed to initialize USB dev memory structures.");
181 goto err_usb_dev;
182 }
183
184 /* If the driver didn't name the dev when enumerating,
185 * do it in some generic way.
186 */
187 if (!ddf_fun_get_name(dev->fun)) {
188 bus_device_set_default_name(dev);
189 }
190
191 if ((err = ddf_fun_bind(dev->fun))) {
192 usb_log_error("Device(%d): Failed to register: %s.", dev->address, str_error(err));
193 goto err_usb_dev;
194 }
195
196 return EOK;
197
198err_usb_dev:
199 hcd_ddf_fun_destroy(dev);
200 return err;
201}
202
203static errno_t device_remove(ddf_fun_t *fun, unsigned port)
204{
205 assert(fun);
206 device_t *hub = ddf_fun_data_get(fun);
207 assert(hub);
208 usb_log_debug("Hub `%s' reported removal of device on port %u",
209 ddf_fun_get_name(fun), port);
210
211 device_t *victim = NULL;
212
213 fibril_mutex_lock(&hub->guard);
214 list_foreach(hub->devices, link, device_t, it) {
215 if (it->port == port) {
216 victim = it;
217 break;
218 }
219 }
220 fibril_mutex_unlock(&hub->guard);
221
222 if (!victim) {
223 usb_log_warning("Hub '%s' tried to remove non-existent"
224 " device.", ddf_fun_get_name(fun));
225 return ENOENT;
226 }
227
228 assert(victim->fun);
229 assert(victim->port == port);
230 assert(victim->hub == hub);
231
232 bus_device_gone(victim);
233 return EOK;
234}
235
236/**
237 * Gets description of the device that is calling.
238 *
239 * @param[in] fun Device function.
240 * @param[out] desc Device descriptor to be filled.
241 * @return Error code.
242 */
243static errno_t get_device_description(ddf_fun_t *fun, usb_device_desc_t *desc)
244{
245 assert(fun);
246 device_t *dev = ddf_fun_data_get(fun);
247 assert(dev);
248
249 if (!desc)
250 return EOK;
251
252 *desc = (usb_device_desc_t) {
253 .address = dev->address,
254 .depth = dev->tier,
255 .speed = dev->speed,
256 .handle = ddf_fun_get_handle(fun),
257 .iface = -1,
258 };
259 return EOK;
260}
261
262/**
263 * Transfer issuing interface function.
264 *
265 * @param fun DDF function.
266 * @param target Communication target.
267 * @param dir Communication direction.
268 * @param setup_data Data to use in setup stage (control transfers).
269 * @param data Pointer to data buffer.
270 * @param size Size of the data buffer.
271 * @param callback Function to call on communication end.
272 * @param arg Argument passed to the callback function.
273 * @return Error code.
274 */
275static errno_t transfer(ddf_fun_t *fun,
276 const usbhc_iface_transfer_request_t *ifreq,
277 usbhc_iface_transfer_callback_t callback, void *arg)
278{
279 assert(fun);
280 device_t *dev = ddf_fun_data_get(fun);
281 assert(dev);
282
283 const usb_target_t target = {{
284 .address = dev->address,
285 .endpoint = ifreq->endpoint,
286 .stream = ifreq->stream,
287 }};
288
289 if (!usb_target_is_valid(&target))
290 return EINVAL;
291
292 if (ifreq->offset > 0 && ifreq->size == 0)
293 return EINVAL;
294
295 if (ifreq->size > 0 && !dma_buffer_is_set(&ifreq->buffer))
296 return EBADMEM;
297
298 if (!callback && arg)
299 return EBADMEM;
300
301 const transfer_request_t request = {
302 .target = target,
303 .dir = ifreq->dir,
304 .buffer = ifreq->buffer,
305 .offset = ifreq->offset,
306 .size = ifreq->size,
307 .setup = ifreq->setup,
308 .on_complete = callback,
309 .arg = arg,
310 .name = (ifreq->dir == USB_DIRECTION_IN) ? "READ" : "WRITE",
311 };
312
313 return bus_issue_transfer(dev, &request);
314}
315
316/** USB device interface */
317static usb_iface_t usb_iface = {
318 .get_my_description = get_device_description,
319};
320
321/** USB host controller interface */
322static usbhc_iface_t usbhc_iface = {
323 .default_address_reservation = default_address_reservation,
324
325 .device_enumerate = device_enumerate,
326 .device_remove = device_remove,
327
328 .register_endpoint = register_endpoint,
329 .unregister_endpoint = unregister_endpoint,
330
331 .transfer = transfer,
332};
333
334/** Standard USB device interface) */
335static ddf_dev_ops_t usb_ops = {
336 .interfaces[USB_DEV_IFACE] = &usb_iface,
337 .interfaces[USBHC_DEV_IFACE] = &usbhc_iface,
338};
339
340
341/* DDF HELPERS */
342
343#define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
344do { \
345 match_id_t *mid = malloc(sizeof(match_id_t)); \
346 if (!mid) { \
347 clean_match_ids(list); \
348 return ENOMEM; \
349 } \
350 char *id = NULL; \
351 int ret = asprintf(&id, str, ##__VA_ARGS__); \
352 if (ret < 0) { \
353 clean_match_ids(list); \
354 free(mid); \
355 return ENOMEM; \
356 } \
357 mid->score = sc; \
358 mid->id = id; \
359 add_match_id(list, mid); \
360} while (0)
361
362/* This is a copy of lib/usbdev/src/recognise.c */
363static errno_t create_match_ids(match_id_list_t *l,
364 usb_standard_device_descriptor_t *d)
365{
366 assert(l);
367 assert(d);
368
369 if (d->vendor_id != 0) {
370 /* First, with release number. */
371 ADD_MATCHID_OR_RETURN(l, 100,
372 "usb&vendor=%#04x&product=%#04x&release=%x.%x",
373 d->vendor_id, d->product_id, (d->device_version >> 8),
374 (d->device_version & 0xff));
375
376 /* Next, without release number. */
377 ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
378 d->vendor_id, d->product_id);
379 }
380
381 /* Class match id */
382 ADD_MATCHID_OR_RETURN(l, 50, "usb&class=%s",
383 usb_str_class(d->device_class));
384
385 /* As a last resort, try fallback driver. */
386 ADD_MATCHID_OR_RETURN(l, 10, "usb&fallback");
387
388 return EOK;
389}
390
391device_t *hcd_ddf_fun_create(hc_device_t *hc, usb_speed_t speed)
392{
393 /* Create DDF function for the new device */
394 ddf_fun_t *fun = ddf_fun_create(hc->ddf_dev, fun_inner, NULL);
395 if (!fun)
396 return NULL;
397
398 ddf_fun_set_ops(fun, &usb_ops);
399
400 /* Create USB device node for the new device */
401 device_t *dev = ddf_fun_data_alloc(fun, hc->bus->device_size);
402 if (!dev) {
403 ddf_fun_destroy(fun);
404 return NULL;
405 }
406
407 bus_device_init(dev, hc->bus);
408 dev->fun = fun;
409 dev->speed = speed;
410 return dev;
411}
412
413void hcd_ddf_fun_destroy(device_t *dev)
414{
415 assert(dev);
416 assert(dev->fun);
417 ddf_fun_destroy(dev->fun);
418}
419
420errno_t hcd_ddf_setup_match_ids(device_t *device, usb_standard_device_descriptor_t *desc)
421{
422 errno_t err;
423 match_id_list_t mids;
424
425 init_match_ids(&mids);
426
427 /* Create match ids from the device descriptor */
428 usb_log_debug("Device(%d): Creating match IDs.", device->address);
429 if ((err = create_match_ids(&mids, desc))) {
430 return err;
431 }
432
433 list_foreach(mids.ids, link, const match_id_t, mid) {
434 ddf_fun_add_match_id(device->fun, mid->id, mid->score);
435 }
436
437 return EOK;
438}
439
440/** Initialize hc structures.
441 *
442 * @param[in] device DDF instance of the device to use.
443 * @param[in] max_speed Maximum supported USB speed.
444 * @param[in] bw available bandwidth.
445 * @param[in] bw_count Function to compute required ep bandwidth.
446 *
447 * @return Error code.
448 * This function does all the ddf work for hc driver.
449 */
450errno_t hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
451{
452 assert(device);
453
454 hc_device_t *instance = ddf_dev_data_alloc(device, size);
455 if (instance == NULL) {
456 usb_log_error("Failed to allocate HCD ddf structure.");
457 return ENOMEM;
458 }
459 instance->ddf_dev = device;
460
461 errno_t ret = ENOMEM;
462 instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
463 if (!instance->ctl_fun) {
464 usb_log_error("Failed to create HCD ddf fun.");
465 goto err_destroy_fun;
466 }
467
468 ret = ddf_fun_bind(instance->ctl_fun);
469 if (ret != EOK) {
470 usb_log_error("Failed to bind ctl_fun: %s.", str_error(ret));
471 goto err_destroy_fun;
472 }
473
474 ret = ddf_fun_add_to_category(instance->ctl_fun, USB_HC_CATEGORY);
475 if (ret != EOK) {
476 usb_log_error("Failed to add fun to category: %s.",
477 str_error(ret));
478 ddf_fun_unbind(instance->ctl_fun);
479 goto err_destroy_fun;
480 }
481
482 /* HC should be ok at this point (except it can't do anything) */
483 return EOK;
484
485err_destroy_fun:
486 ddf_fun_destroy(instance->ctl_fun);
487 instance->ctl_fun = NULL;
488 return ret;
489}
490
491void hcd_ddf_clean_hc(hc_device_t *hcd)
492{
493 if (ddf_fun_unbind(hcd->ctl_fun) == EOK)
494 ddf_fun_destroy(hcd->ctl_fun);
495}
496
497/** Call the parent driver with a request to enable interrupt
498 *
499 * @param[in] device Device asking for interrupts
500 * @param[in] inum Interrupt number
501 * @return Error code.
502 */
503errno_t hcd_ddf_enable_interrupt(hc_device_t *hcd, int inum)
504{
505 async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
506 if (parent_sess == NULL)
507 return EIO;
508
509 return hw_res_enable_interrupt(parent_sess, inum);
510}
511
512errno_t hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res)
513{
514 async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
515 if (parent_sess == NULL)
516 return EIO;
517
518 hw_res_list_parsed_init(hw_res);
519 const errno_t ret = hw_res_get_list_parsed(parent_sess, hw_res, 0);
520 if (ret != EOK)
521 hw_res_list_parsed_clean(hw_res);
522 return ret;
523}
524
525/**
526 * @}
527 */
Note: See TracBrowser for help on using the repository browser.