source: mainline/uspace/lib/usbhost/src/ddf_helpers.c@ 91ca111

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

usbhost: refactor the initialization

Before that, drivers had to setup MMIO range multiple times, or even parse hw
resources themselves again. The former init method was split in half - init and
start. Init shall allocate and initialize inner structures, start shall start
the HC.

In the XHCI it is demonstrated how to isolate inner HC implementation from the
fact this driver is using libusbhost. It adds some boilerplate code, but
I think it leads to cleaner design.

  • Property mode set to 100644
File size: 26.9 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 *
34 */
35
36#include <usb/classes/classes.h>
37#include <usb/debug.h>
38#include <usb/descriptor.h>
39#include <usb/request.h>
40#include <usb/usb.h>
41
42#include <adt/list.h>
43#include <assert.h>
44#include <async.h>
45#include <ddf/driver.h>
46#include <ddf/interrupt.h>
47#include <device/hw_res_parsed.h>
48#include <devman.h>
49#include <errno.h>
50#include <fibril_synch.h>
51#include <macros.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <str_error.h>
55#include <usb_iface.h>
56
57#include "ddf_helpers.h"
58
59#define CTRL_PIPE_MIN_PACKET_SIZE 8
60
61typedef struct usb_dev {
62 link_t link;
63 list_t devices;
64 fibril_mutex_t guard;
65 ddf_fun_t *fun;
66 usb_address_t address;
67 usb_speed_t speed;
68 usb_address_t tt_address;
69 unsigned port;
70} usb_dev_t;
71
72typedef struct hc_dev {
73 ddf_fun_t *ctl_fun;
74 hcd_t hcd;
75 usb_dev_t *root_hub;
76} hc_dev_t;
77
78static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
79{
80 return ddf_dev_data_get(dev);
81}
82
83hcd_t *dev_to_hcd(ddf_dev_t *dev)
84{
85 hc_dev_t *hc_dev = dev_to_hc_dev(dev);
86 if (!hc_dev) {
87 usb_log_error("Invalid HCD device.\n");
88 return NULL;
89 }
90 return &hc_dev->hcd;
91}
92
93
94static int hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
95static int hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
96
97
98/* DDF INTERFACE */
99
100/** Register endpoint interface function.
101 * @param fun DDF function.
102 * @param address USB address of the device.
103 * @param endpoint USB endpoint number to be registered.
104 * @param transfer_type Endpoint's transfer type.
105 * @param direction USB communication direction the endpoint is capable of.
106 * @param max_packet_size Maximu size of packets the endpoint accepts.
107 * @param interval Preferred timeout between communication.
108 * @return Error code.
109 */
110static int register_endpoint(
111 ddf_fun_t *fun, usb_endpoint_t endpoint,
112 usb_transfer_type_t transfer_type, usb_direction_t direction,
113 size_t max_packet_size, unsigned packets, unsigned interval)
114{
115 assert(fun);
116 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
117 usb_dev_t *dev = ddf_fun_data_get(fun);
118 assert(hcd);
119 assert(dev);
120 const size_t size = max_packet_size;
121 const usb_target_t target =
122 {{.address = dev->address, .endpoint = endpoint}};
123
124 usb_log_debug("Register endpoint %d:%d %s-%s %zuB %ums.\n",
125 dev->address, endpoint, usb_str_transfer_type(transfer_type),
126 usb_str_direction(direction), max_packet_size, interval);
127
128 return hcd_add_ep(hcd, target, direction, transfer_type,
129 max_packet_size, packets, size, dev->tt_address, dev->port);
130}
131
132/** Unregister endpoint interface function.
133 * @param fun DDF function.
134 * @param address USB address of the endpoint.
135 * @param endpoint USB endpoint number.
136 * @param direction Communication direction of the enpdoint to unregister.
137 * @return Error code.
138 */
139static int unregister_endpoint(
140 ddf_fun_t *fun, usb_endpoint_t endpoint, usb_direction_t direction)
141{
142 assert(fun);
143 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
144 usb_dev_t *dev = ddf_fun_data_get(fun);
145 assert(hcd);
146 assert(dev);
147 const usb_target_t target =
148 {{.address = dev->address, .endpoint = endpoint}};
149 usb_log_debug("Unregister endpoint %d:%d %s.\n",
150 dev->address, endpoint, usb_str_direction(direction));
151 return hcd_remove_ep(hcd, target, direction);
152}
153
154static int reserve_default_address(ddf_fun_t *fun, usb_speed_t speed)
155{
156 assert(fun);
157 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
158 usb_dev_t *dev = ddf_fun_data_get(fun);
159 assert(hcd);
160 assert(dev);
161
162 usb_log_debug("Device %d requested default address at %s speed\n",
163 dev->address, usb_str_speed(speed));
164 return hcd_reserve_default_address(hcd, speed);
165}
166
167static int release_default_address(ddf_fun_t *fun)
168{
169 assert(fun);
170 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
171 usb_dev_t *dev = ddf_fun_data_get(fun);
172 assert(hcd);
173 assert(dev);
174
175 usb_log_debug("Device %d released default address\n", dev->address);
176 return hcd_release_default_address(hcd);
177}
178
179static int device_enumerate(ddf_fun_t *fun, unsigned port)
180{
181 assert(fun);
182 ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
183 usb_dev_t *dev = ddf_fun_data_get(fun);
184 assert(ddf_dev);
185 assert(dev);
186 usb_log_debug("Hub %d reported a new USB device on port: %u\n",
187 dev->address, port);
188 return hcd_ddf_new_device(ddf_dev, dev, port);
189}
190
191static int device_remove(ddf_fun_t *fun, unsigned port)
192{
193 assert(fun);
194 ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
195 usb_dev_t *dev = ddf_fun_data_get(fun);
196 assert(ddf_dev);
197 assert(dev);
198 usb_log_debug("Hub `%s' reported removal of device on port %u\n",
199 ddf_fun_get_name(fun), port);
200 return hcd_ddf_remove_device(ddf_dev, dev, port);
201}
202
203/** Gets handle of the respective device.
204 *
205 * @param[in] fun Device function.
206 * @param[out] handle Place to write the handle.
207 * @return Error code.
208 */
209static int get_my_device_handle(ddf_fun_t *fun, devman_handle_t *handle)
210{
211 assert(fun);
212 if (handle)
213 *handle = ddf_fun_get_handle(fun);
214 return EOK;
215}
216
217/** Inbound communication interface function.
218 * @param fun DDF function.
219 * @param target Communication target.
220 * @param setup_data Data to use in setup stage (control transfers).
221 * @param data Pointer to data buffer.
222 * @param size Size of the data buffer.
223 * @param callback Function to call on communication end.
224 * @param arg Argument passed to the callback function.
225 * @return Error code.
226 */
227static int dev_read(ddf_fun_t *fun, usb_endpoint_t endpoint,
228 uint64_t setup_data, uint8_t *data, size_t size,
229 usbhc_iface_transfer_in_callback_t callback, void *arg)
230{
231 assert(fun);
232 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
233 assert(usb_dev);
234 const usb_target_t target = {{
235 .address = usb_dev->address,
236 .endpoint = endpoint,
237 }};
238 return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)), target,
239 USB_DIRECTION_IN, data, size, setup_data, callback, NULL, arg,
240 "READ");
241}
242
243/** Outbound communication interface function.
244 * @param fun DDF function.
245 * @param target Communication target.
246 * @param setup_data Data to use in setup stage (control transfers).
247 * @param data Pointer to data buffer.
248 * @param size Size of the data buffer.
249 * @param callback Function to call on communication end.
250 * @param arg Argument passed to the callback function.
251 * @return Error code.
252 */
253static int dev_write(ddf_fun_t *fun, usb_endpoint_t endpoint,
254 uint64_t setup_data, const uint8_t *data, size_t size,
255 usbhc_iface_transfer_out_callback_t callback, void *arg)
256{
257 assert(fun);
258 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
259 assert(usb_dev);
260 const usb_target_t target = {{
261 .address = usb_dev->address,
262 .endpoint = endpoint,
263 }};
264 return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)),
265 target, USB_DIRECTION_OUT, (uint8_t*)data, size, setup_data, NULL,
266 callback, arg, "WRITE");
267}
268
269/** USB device interface */
270static usb_iface_t usb_iface = {
271 .get_my_device_handle = get_my_device_handle,
272
273 .reserve_default_address = reserve_default_address,
274 .release_default_address = release_default_address,
275
276 .device_enumerate = device_enumerate,
277 .device_remove = device_remove,
278
279 .register_endpoint = register_endpoint,
280 .unregister_endpoint = unregister_endpoint,
281
282 .read = dev_read,
283 .write = dev_write,
284};
285
286/** Standard USB device interface) */
287static ddf_dev_ops_t usb_ops = {
288 .interfaces[USB_DEV_IFACE] = &usb_iface,
289};
290
291
292/* DDF HELPERS */
293
294#define GET_DEVICE_DESC(size) \
295{ \
296 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
297 | (USB_REQUEST_TYPE_STANDARD << 5) \
298 | USB_REQUEST_RECIPIENT_DEVICE, \
299 .request = USB_DEVREQ_GET_DESCRIPTOR, \
300 .value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
301 .index = uint16_host2usb(0), \
302 .length = uint16_host2usb(size), \
303};
304
305#define SET_ADDRESS(address) \
306{ \
307 .request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
308 | (USB_REQUEST_TYPE_STANDARD << 5) \
309 | USB_REQUEST_RECIPIENT_DEVICE, \
310 .request = USB_DEVREQ_SET_ADDRESS, \
311 .value = uint16_host2usb(address), \
312 .index = uint16_host2usb(0), \
313 .length = uint16_host2usb(0), \
314};
315
316static int hcd_ddf_add_device(ddf_dev_t *parent, usb_dev_t *hub_dev,
317 unsigned port, usb_address_t address, usb_speed_t speed, const char *name,
318 const match_id_list_t *mids)
319{
320 assert(parent);
321
322 char default_name[10] = { 0 }; /* usbxyz-ss */
323 if (!name) {
324 snprintf(default_name, sizeof(default_name) - 1,
325 "usb%u-%cs", address, usb_str_speed(speed)[0]);
326 name = default_name;
327 }
328
329 ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
330 if (!fun)
331 return ENOMEM;
332 usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
333 if (!info) {
334 ddf_fun_destroy(fun);
335 return ENOMEM;
336 }
337 info->address = address;
338 info->speed = speed;
339 info->fun = fun;
340 info->port = port;
341 info->tt_address = hub_dev ? hub_dev->tt_address : -1;
342 link_initialize(&info->link);
343 list_initialize(&info->devices);
344 fibril_mutex_initialize(&info->guard);
345
346 if (hub_dev && hub_dev->speed == USB_SPEED_HIGH && usb_speed_is_11(speed))
347 info->tt_address = hub_dev->address;
348
349 ddf_fun_set_ops(fun, &usb_ops);
350 list_foreach(mids->ids, link, const match_id_t, mid) {
351 ddf_fun_add_match_id(fun, mid->id, mid->score);
352 }
353
354 int ret = ddf_fun_bind(fun);
355 if (ret != EOK) {
356 ddf_fun_destroy(fun);
357 return ret;
358 }
359
360 if (hub_dev) {
361 fibril_mutex_lock(&hub_dev->guard);
362 list_append(&info->link, &hub_dev->devices);
363 fibril_mutex_unlock(&hub_dev->guard);
364 } else {
365 hc_dev_t *hc_dev = dev_to_hc_dev(parent);
366 assert(hc_dev->root_hub == NULL);
367 hc_dev->root_hub = info;
368 }
369 return EOK;
370}
371
372#define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
373do { \
374 match_id_t *mid = malloc(sizeof(match_id_t)); \
375 if (!mid) { \
376 clean_match_ids(list); \
377 return ENOMEM; \
378 } \
379 char *id = NULL; \
380 int ret = asprintf(&id, str, ##__VA_ARGS__); \
381 if (ret < 0) { \
382 clean_match_ids(list); \
383 free(mid); \
384 return ENOMEM; \
385 } \
386 mid->score = sc; \
387 mid->id = id; \
388 add_match_id(list, mid); \
389} while (0)
390
391/* This is a copy of lib/usbdev/src/recognise.c */
392static int create_match_ids(match_id_list_t *l,
393 usb_standard_device_descriptor_t *d)
394{
395 assert(l);
396 assert(d);
397
398 if (d->vendor_id != 0) {
399 /* First, with release number. */
400 ADD_MATCHID_OR_RETURN(l, 100,
401 "usb&vendor=%#04x&product=%#04x&release=%x.%x",
402 d->vendor_id, d->product_id, (d->device_version >> 8),
403 (d->device_version & 0xff));
404
405 /* Next, without release number. */
406 ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
407 d->vendor_id, d->product_id);
408 }
409
410 /* Class match id */
411 ADD_MATCHID_OR_RETURN(l, 50, "usb&class=%s",
412 usb_str_class(d->device_class));
413
414 /* As a last resort, try fallback driver. */
415 ADD_MATCHID_OR_RETURN(l, 10, "usb&fallback");
416
417 return EOK;
418
419}
420
421static int hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub,
422 unsigned port)
423{
424 assert(device);
425
426 hcd_t *hcd = dev_to_hcd(device);
427 assert(hcd);
428
429 hc_dev_t *hc_dev = dev_to_hc_dev(device);
430 assert(hc_dev);
431
432 fibril_mutex_lock(&hub->guard);
433
434 usb_dev_t *victim = NULL;
435
436 list_foreach(hub->devices, link, usb_dev_t, it) {
437 if (it->port == port) {
438 victim = it;
439 break;
440 }
441 }
442 if (victim) {
443 assert(victim->port == port);
444 list_remove(&victim->link);
445 fibril_mutex_unlock(&hub->guard);
446 const int ret = ddf_fun_unbind(victim->fun);
447 if (ret == EOK) {
448 usb_address_t address = victim->address;
449 ddf_fun_destroy(victim->fun);
450 hcd_release_address(hcd, address);
451 } else {
452 usb_log_warning("Failed to unbind device `%s': %s\n",
453 ddf_fun_get_name(victim->fun), str_error(ret));
454 }
455 return EOK;
456 }
457 fibril_mutex_unlock(&hub->guard);
458 return ENOENT;
459}
460
461static int hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port)
462{
463 assert(device);
464
465 hcd_t *hcd = dev_to_hcd(device);
466 assert(hcd);
467
468 usb_speed_t speed = USB_SPEED_MAX;
469
470 /* This checks whether the default address is reserved and gets speed */
471 int ret = usb_bus_get_speed(&hcd->bus, USB_ADDRESS_DEFAULT, &speed);
472 if (ret != EOK) {
473 usb_log_error("Failed to verify speed: %s.", str_error(ret));
474 return ret;
475 }
476
477 usb_log_debug("Found new %s speed USB device.", usb_str_speed(speed));
478
479 static const usb_target_t default_target = {{
480 .address = USB_ADDRESS_DEFAULT,
481 .endpoint = 0,
482 }};
483
484 const usb_address_t address = hcd_request_address(hcd, speed);
485 if (address < 0) {
486 usb_log_error("Failed to reserve new address: %s.",
487 str_error(address));
488 return address;
489 }
490
491 usb_log_debug("Reserved new address: %d\n", address);
492
493 const usb_target_t target = {{
494 .address = address,
495 .endpoint = 0,
496 }};
497
498 const usb_address_t tt_address = hub ? hub->tt_address : -1;
499
500 /* Add default pipe on default address */
501 usb_log_debug("Device(%d): Adding default target(0:0)\n", address);
502 ret = hcd_add_ep(hcd,
503 default_target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
504 CTRL_PIPE_MIN_PACKET_SIZE, CTRL_PIPE_MIN_PACKET_SIZE, 1,
505 tt_address, port);
506 if (ret != EOK) {
507 usb_log_error("Device(%d): Failed to add default target: %s.",
508 address, str_error(ret));
509 hcd_release_address(hcd, address);
510 return ret;
511 }
512
513 /* Get max packet size for default pipe */
514 usb_standard_device_descriptor_t desc = { 0 };
515 const usb_device_request_setup_packet_t get_device_desc_8 =
516 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
517
518 // TODO CALLBACKS
519 usb_log_debug("Device(%d): Requesting first 8B of device descriptor.",
520 address);
521 ssize_t got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
522 &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
523 "read first 8 bytes of dev descriptor");
524
525 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
526 ret = got < 0 ? got : EOVERFLOW;
527 usb_log_error("Device(%d): Failed to get 8B of dev descr: %s.",
528 address, str_error(ret));
529 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
530 hcd_release_address(hcd, address);
531 return ret;
532 }
533
534 /* Register EP on the new address */
535 usb_log_debug("Device(%d): Registering control EP.", address);
536 ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
537 ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
538 ED_MPS_TRANS_OPPORTUNITIES_GET(uint16_usb2host(desc.max_packet_size)),
539 ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
540 tt_address, port);
541 if (ret != EOK) {
542 usb_log_error("Device(%d): Failed to register EP0: %s",
543 address, str_error(ret));
544 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
545 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
546 hcd_release_address(hcd, address);
547 return ret;
548 }
549
550 /* Set new address */
551 const usb_device_request_setup_packet_t set_address =
552 SET_ADDRESS(target.address);
553
554 usb_log_debug("Device(%d): Setting USB address.", address);
555 got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
556 NULL, 0, *(uint64_t *)&set_address, "set address");
557
558 usb_log_debug("Device(%d): Removing default (0:0) EP.", address);
559 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
560
561 if (got != 0) {
562 usb_log_error("Device(%d): Failed to set new address: %s.",
563 address, str_error(got));
564 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
565 hcd_release_address(hcd, address);
566 return got;
567 }
568
569 /* Get std device descriptor */
570 const usb_device_request_setup_packet_t get_device_desc =
571 GET_DEVICE_DESC(sizeof(desc));
572
573 usb_log_debug("Device(%d): Requesting full device descriptor.",
574 address);
575 got = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
576 &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
577 "read device descriptor");
578 if (ret != EOK) {
579 usb_log_error("Device(%d): Failed to set get dev descriptor: %s",
580 address, str_error(ret));
581 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
582 hcd_release_address(hcd, target.address);
583 return ret;
584 }
585
586 /* Create match ids from the device descriptor */
587 match_id_list_t mids;
588 init_match_ids(&mids);
589
590 usb_log_debug("Device(%d): Creating match IDs.", address);
591 ret = create_match_ids(&mids, &desc);
592 if (ret != EOK) {
593 usb_log_error("Device(%d): Failed to create match ids: %s",
594 address, str_error(ret));
595 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
596 hcd_release_address(hcd, target.address);
597 return ret;
598 }
599
600 /* Register device */
601 usb_log_debug("Device(%d): Registering DDF device.", address);
602 ret = hcd_ddf_add_device(device, hub, port, address, speed, NULL, &mids);
603 clean_match_ids(&mids);
604 if (ret != EOK) {
605 usb_log_error("Device(%d): Failed to register: %s.",
606 address, str_error(ret));
607 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
608 hcd_release_address(hcd, target.address);
609 }
610
611 return ret;
612}
613
614/** Announce root hub to the DDF
615 *
616 * @param[in] device Host controller ddf device
617 * @return Error code
618 */
619int hcd_ddf_setup_root_hub(ddf_dev_t *device)
620{
621 assert(device);
622 hcd_t *hcd = dev_to_hcd(device);
623 assert(hcd);
624
625 hcd_reserve_default_address(hcd, hcd->bus.max_speed);
626 const int ret = hcd_ddf_new_device(device, NULL, 0);
627 hcd_release_default_address(hcd);
628 return ret;
629}
630
631/** Initialize hc structures.
632 *
633 * @param[in] device DDF instance of the device to use.
634 * @param[in] max_speed Maximum supported USB speed.
635 * @param[in] bw available bandwidth.
636 * @param[in] bw_count Function to compute required ep bandwidth.
637 *
638 * @return Error code.
639 * This function does all the ddf work for hc driver.
640 */
641int hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
642 size_t bw, bw_count_func_t bw_count)
643{
644 assert(device);
645
646 hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
647 if (instance == NULL) {
648 usb_log_error("Failed to allocate HCD ddf structure.\n");
649 return ENOMEM;
650 }
651 instance->root_hub = NULL;
652 hcd_init(&instance->hcd, max_speed, bw, bw_count);
653
654 int ret = ENOMEM;
655 instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
656 if (!instance->ctl_fun) {
657 usb_log_error("Failed to create HCD ddf fun.\n");
658 goto err_destroy_fun;
659 }
660
661 ret = ddf_fun_bind(instance->ctl_fun);
662 if (ret != EOK) {
663 usb_log_error("Failed to bind ctl_fun: %s.\n", str_error(ret));
664 goto err_destroy_fun;
665 }
666
667 ret = ddf_fun_add_to_category(instance->ctl_fun, USB_HC_CATEGORY);
668 if (ret != EOK) {
669 usb_log_error("Failed to add fun to category: %s.\n",
670 str_error(ret));
671 ddf_fun_unbind(instance->ctl_fun);
672 goto err_destroy_fun;
673 }
674
675 /* HC should be ok at this point (except it can't do anything) */
676 return EOK;
677
678err_destroy_fun:
679 ddf_fun_destroy(instance->ctl_fun);
680 instance->ctl_fun = NULL;
681 return ret;
682}
683
684void hcd_ddf_clean_hc(ddf_dev_t *device)
685{
686 assert(device);
687 hc_dev_t *hc = dev_to_hc_dev(device);
688 assert(hc);
689 const int ret = ddf_fun_unbind(hc->ctl_fun);
690 if (ret == EOK)
691 ddf_fun_destroy(hc->ctl_fun);
692}
693
694//TODO: Move this to generic ddf?
695/** Call the parent driver with a request to enable interrupts
696 *
697 * @param[in] device Device asking for interrupts
698 * @return Error code.
699 */
700int hcd_ddf_enable_interrupts(ddf_dev_t *device)
701{
702 assert(device);
703 async_sess_t *parent_sess =
704 devman_parent_device_connect(ddf_dev_get_handle(device),
705 IPC_FLAG_BLOCKING);
706 const bool enabled = hw_res_enable_interrupt(parent_sess);
707 async_hangup(parent_sess);
708
709 return enabled ? EOK : EIO;
710}
711
712//TODO: Move this to generic ddf?
713int hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res)
714{
715 assert(device);
716 assert(hw_res);
717
718 async_sess_t *parent_sess =
719 devman_parent_device_connect(ddf_dev_get_handle(device),
720 IPC_FLAG_BLOCKING);
721 hw_res_list_parsed_init(hw_res);
722 const int ret = hw_res_get_list_parsed(parent_sess, hw_res, 0);
723 async_hangup(parent_sess);
724 if (ret != EOK)
725 hw_res_list_parsed_clean(hw_res);
726 return ret;
727}
728
729// TODO: move this someplace else
730static inline void irq_code_clean(irq_code_t *code)
731{
732 if (code) {
733 free(code->ranges);
734 free(code->cmds);
735 code->ranges = NULL;
736 code->cmds = NULL;
737 code->rangecount = 0;
738 code->cmdcount = 0;
739 }
740}
741
742/** Register interrupt handler
743 *
744 * @param[in] device Host controller DDF device
745 * @param[in] regs Register range
746 * @param[in] irq Interrupt number
747 * @paran[in] handler Interrupt handler
748 * @param[in] gen_irq_code IRQ code generator.
749 *
750 * @return EOK on success or negative error code
751 */
752int hcd_ddf_setup_interrupts(ddf_dev_t *device,
753 const hw_res_list_parsed_t *hw_res,
754 interrupt_handler_t handler,
755 irq_code_gen_t gen_irq_code)
756{
757 assert(device);
758
759 hcd_t *hcd = dev_to_hcd(device);
760
761 if (!handler || !gen_irq_code)
762 return ENOTSUP;
763
764 irq_code_t irq_code = {0};
765
766 const int irq = gen_irq_code(&irq_code, hcd, hw_res);
767 if (irq < 0) {
768 usb_log_error("Failed to generate IRQ code: %s.\n",
769 str_error(irq));
770 return irq;
771 }
772
773 /* Register handler to avoid interrupt lockup */
774 int ret = register_interrupt_handler(device, irq, handler, &irq_code);
775 irq_code_clean(&irq_code);
776 if (ret != EOK) {
777 usb_log_error("Failed to register interrupt handler: %s.\n",
778 str_error(ret));
779 return ret;
780 }
781
782 /* Enable interrupts */
783 ret = hcd_ddf_enable_interrupts(device);
784 if (ret != EOK) {
785 usb_log_error("Failed to enable interrupts: %s.\n",
786 str_error(ret));
787 unregister_interrupt_handler(device, irq);
788 return ret;
789 }
790 assert(irq > 0);
791 return irq;
792}
793
794/** IRQ handling callback, forward status from call to diver structure.
795 *
796 * @param[in] dev DDF instance of the device to use.
797 * @param[in] iid (Unused).
798 * @param[in] call Pointer to the call from kernel.
799 */
800void ddf_hcd_gen_irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
801{
802 assert(dev);
803 hcd_t *hcd = dev_to_hcd(dev);
804 if (!hcd || !hcd->ops.irq_hook) {
805 usb_log_error("Interrupt on not yet initialized device.\n");
806 return;
807 }
808 const uint32_t status = IPC_GET_ARG1(*call);
809 hcd->ops.irq_hook(hcd, status);
810}
811
812static int interrupt_polling(void *arg)
813{
814 hcd_t *hcd = arg;
815 assert(hcd);
816 if (!hcd->ops.status_hook || !hcd->ops.irq_hook)
817 return ENOTSUP;
818 uint32_t status = 0;
819 while (hcd->ops.status_hook(hcd, &status) == EOK) {
820 hcd->ops.irq_hook(hcd, status);
821 status = 0;
822 /* We should wait 1 frame - 1ms here, but this polling is a
823 * lame crutch anyway so don't hog the system. 10ms is still
824 * good enough for emergency mode */
825 async_usleep(10000);
826 }
827 return EOK;
828}
829
830/** Initialize hc and rh DDF structures and their respective drivers.
831 *
832 * @param device DDF instance of the device to use
833 * @param speed Maximum supported speed
834 * @param bw Available bandwidth (arbitrary units)
835 * @param bw_count Bandwidth computing function
836 * @param irq_handler IRQ handling function
837 * @param gen_irq_code Function to generate IRQ pseudocode
838 * (it needs to return used irq number)
839 * @param driver_init Function to initialize HC driver
840 * @param driver_fini Function to cleanup HC driver
841 * @return Error code
842 *
843 * This function does all the preparatory work for hc and rh drivers:
844 * - gets device's hw resources
845 * - attempts to enable interrupts
846 * - registers interrupt handler
847 * - calls driver specific initialization
848 * - registers root hub
849 */
850int hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver)
851{
852 assert(driver);
853 static const struct { size_t bw; bw_count_func_t bw_count; }bw[] = {
854 [USB_SPEED_FULL] = { .bw = BANDWIDTH_AVAILABLE_USB11,
855 .bw_count = bandwidth_count_usb11 },
856 [USB_SPEED_HIGH] = { .bw = BANDWIDTH_AVAILABLE_USB11,
857 .bw_count = bandwidth_count_usb11 },
858 [USB_SPEED_SUPER] = { .bw = BANDWIDTH_AVAILABLE_USB11,
859 .bw_count = bandwidth_count_usb11 },
860 };
861
862 int ret = EOK;
863 const usb_speed_t speed = driver->hc_speed;
864 if (speed >= ARRAY_SIZE(bw) || bw[speed].bw == 0) {
865 usb_log_error("Driver `%s' reported unsupported speed: %s",
866 driver->name, usb_str_speed(speed));
867 return ENOTSUP;
868 }
869
870 hw_res_list_parsed_t hw_res;
871 ret = hcd_ddf_get_registers(device, &hw_res);
872 if (ret != EOK) {
873 usb_log_error("Failed to get register memory addresses "
874 "for `%s': %s.\n", ddf_dev_get_name(device),
875 str_error(ret));
876 return ret;
877 }
878
879 ret = hcd_ddf_setup_hc(device, speed, bw[speed].bw, bw[speed].bw_count);
880 if (ret != EOK) {
881 usb_log_error("Failed to setup generic HCD.\n");
882 goto err_hw_res;
883 }
884
885 hcd_t *hcd = dev_to_hcd(device);
886
887 if (driver->init)
888 ret = driver->init(hcd, &hw_res);
889 if (ret != EOK) {
890 usb_log_error("Failed to init HCD.\n");
891 goto err_hcd;
892 }
893
894 /* Setup interrupts */
895 interrupt_handler_t *irq_handler =
896 driver->irq_handler ? driver->irq_handler : ddf_hcd_gen_irq_handler;
897 const int irq = hcd_ddf_setup_interrupts(device, &hw_res, irq_handler, driver->irq_code_gen);
898 if (!(irq < 0)) {
899 usb_log_debug("Hw interrupts enabled.\n");
900 }
901
902 /* Claim the device from BIOS */
903 if (driver->claim)
904 ret = driver->claim(hcd, device);
905 if (ret != EOK) {
906 usb_log_error("Failed to claim `%s' for driver `%s': %s",
907 ddf_dev_get_name(device), driver->name, str_error(ret));
908 goto err_irq;
909 }
910
911 /* Start hw driver */
912 if (driver->start)
913 ret = driver->start(hcd, !(irq < 0));
914 if (ret != EOK) {
915 usb_log_error("Failed to start HCD: %s.\n", str_error(ret));
916 goto err_irq;
917 }
918
919 /* Need working irq replacement to setup root hub */
920 if ((irq < 0) && hcd->ops.status_hook) {
921 hcd->polling_fibril = fibril_create(interrupt_polling, hcd);
922 if (hcd->polling_fibril == 0) {
923 usb_log_error("Failed to create polling fibril\n");
924 ret = ENOMEM;
925 goto err_started;
926 }
927 fibril_add_ready(hcd->polling_fibril);
928 usb_log_warning("Failed to enable interrupts: %s."
929 " Falling back to polling.\n", str_error(irq));
930 }
931
932 /*
933 * Creating root hub registers a new USB device so HC
934 * needs to be ready at this time.
935 */
936 ret = hcd_ddf_setup_root_hub(device);
937 if (ret != EOK) {
938 usb_log_error("Failed to setup HC root hub: %s.\n",
939 str_error(ret));
940 goto err_polling;
941 }
942
943 usb_log_info("Controlling new `%s' device `%s'.\n",
944 driver->name, ddf_dev_get_name(device));
945 return EOK;
946
947err_polling:
948 // TODO: Stop the polling fibril (refactor the interrupt_polling func)
949 //
950err_started:
951 if (driver->stop)
952 driver->stop(hcd);
953err_irq:
954 unregister_interrupt_handler(device, irq);
955 if (driver->fini)
956 driver->fini(hcd);
957err_hcd:
958 hcd_ddf_clean_hc(device);
959err_hw_res:
960 hw_res_list_parsed_clean(&hw_res);
961 return ret;
962}
963/**
964 * @}
965 */
Note: See TracBrowser for help on using the repository browser.