| 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 |  */
 | 
|---|
| 67 | static 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 |  */
 | 
|---|
| 102 | static 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 |  */
 | 
|---|
| 126 | static 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 |  */
 | 
|---|
| 150 | static 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 |         /*
 | 
|---|
| 185 |          * If the driver didn't name the dev when enumerating,
 | 
|---|
| 186 |          * do it in some generic way.
 | 
|---|
| 187 |          */
 | 
|---|
| 188 |         if (!ddf_fun_get_name(dev->fun)) {
 | 
|---|
| 189 |                 bus_device_set_default_name(dev);
 | 
|---|
| 190 |         }
 | 
|---|
| 191 | 
 | 
|---|
| 192 |         if ((err = ddf_fun_bind(dev->fun))) {
 | 
|---|
| 193 |                 usb_log_error("Device(%d): Failed to register: %s.", dev->address, str_error(err));
 | 
|---|
| 194 |                 goto err_usb_dev;
 | 
|---|
| 195 |         }
 | 
|---|
| 196 | 
 | 
|---|
| 197 |         return EOK;
 | 
|---|
| 198 | 
 | 
|---|
| 199 | err_usb_dev:
 | 
|---|
| 200 |         hcd_ddf_fun_destroy(dev);
 | 
|---|
| 201 |         return err;
 | 
|---|
| 202 | }
 | 
|---|
| 203 | 
 | 
|---|
| 204 | static errno_t device_remove(ddf_fun_t *fun, unsigned port)
 | 
|---|
| 205 | {
 | 
|---|
| 206 |         assert(fun);
 | 
|---|
| 207 |         device_t *hub = ddf_fun_data_get(fun);
 | 
|---|
| 208 |         assert(hub);
 | 
|---|
| 209 |         usb_log_debug("Hub `%s' reported removal of device on port %u",
 | 
|---|
| 210 |             ddf_fun_get_name(fun), port);
 | 
|---|
| 211 | 
 | 
|---|
| 212 |         device_t *victim = NULL;
 | 
|---|
| 213 | 
 | 
|---|
| 214 |         fibril_mutex_lock(&hub->guard);
 | 
|---|
| 215 |         list_foreach(hub->devices, link, device_t, it) {
 | 
|---|
| 216 |                 if (it->port == port) {
 | 
|---|
| 217 |                         victim = it;
 | 
|---|
| 218 |                         break;
 | 
|---|
| 219 |                 }
 | 
|---|
| 220 |         }
 | 
|---|
| 221 |         fibril_mutex_unlock(&hub->guard);
 | 
|---|
| 222 | 
 | 
|---|
| 223 |         if (!victim) {
 | 
|---|
| 224 |                 usb_log_warning("Hub '%s' tried to remove non-existent"
 | 
|---|
| 225 |                     " device.", ddf_fun_get_name(fun));
 | 
|---|
| 226 |                 return ENOENT;
 | 
|---|
| 227 |         }
 | 
|---|
| 228 | 
 | 
|---|
| 229 |         assert(victim->fun);
 | 
|---|
| 230 |         assert(victim->port == port);
 | 
|---|
| 231 |         assert(victim->hub == hub);
 | 
|---|
| 232 | 
 | 
|---|
| 233 |         bus_device_gone(victim);
 | 
|---|
| 234 |         return EOK;
 | 
|---|
| 235 | }
 | 
|---|
| 236 | 
 | 
|---|
| 237 | /**
 | 
|---|
| 238 |  * Gets description of the device that is calling.
 | 
|---|
| 239 |  *
 | 
|---|
| 240 |  * @param[in] fun Device function.
 | 
|---|
| 241 |  * @param[out] desc Device descriptor to be filled.
 | 
|---|
| 242 |  * @return Error code.
 | 
|---|
| 243 |  */
 | 
|---|
| 244 | static errno_t get_device_description(ddf_fun_t *fun, usb_device_desc_t *desc)
 | 
|---|
| 245 | {
 | 
|---|
| 246 |         assert(fun);
 | 
|---|
| 247 |         device_t *dev = ddf_fun_data_get(fun);
 | 
|---|
| 248 |         assert(dev);
 | 
|---|
| 249 | 
 | 
|---|
| 250 |         if (!desc)
 | 
|---|
| 251 |                 return EOK;
 | 
|---|
| 252 | 
 | 
|---|
| 253 |         *desc = (usb_device_desc_t) {
 | 
|---|
| 254 |                 .address = dev->address,
 | 
|---|
| 255 |                 .depth = dev->tier,
 | 
|---|
| 256 |                 .speed = dev->speed,
 | 
|---|
| 257 |                 .handle = ddf_fun_get_handle(fun),
 | 
|---|
| 258 |                 .iface = -1,
 | 
|---|
| 259 |         };
 | 
|---|
| 260 |         return EOK;
 | 
|---|
| 261 | }
 | 
|---|
| 262 | 
 | 
|---|
| 263 | /**
 | 
|---|
| 264 |  * Transfer issuing interface function.
 | 
|---|
| 265 |  *
 | 
|---|
| 266 |  * @param fun DDF function.
 | 
|---|
| 267 |  * @param target Communication target.
 | 
|---|
| 268 |  * @param dir Communication direction.
 | 
|---|
| 269 |  * @param setup_data Data to use in setup stage (control transfers).
 | 
|---|
| 270 |  * @param data Pointer to data buffer.
 | 
|---|
| 271 |  * @param size Size of the data buffer.
 | 
|---|
| 272 |  * @param callback Function to call on communication end.
 | 
|---|
| 273 |  * @param arg Argument passed to the callback function.
 | 
|---|
| 274 |  * @return Error code.
 | 
|---|
| 275 |  */
 | 
|---|
| 276 | static errno_t transfer(ddf_fun_t *fun,
 | 
|---|
| 277 |     const usbhc_iface_transfer_request_t *ifreq,
 | 
|---|
| 278 |     usbhc_iface_transfer_callback_t callback, void *arg)
 | 
|---|
| 279 | {
 | 
|---|
| 280 |         assert(fun);
 | 
|---|
| 281 |         device_t *dev = ddf_fun_data_get(fun);
 | 
|---|
| 282 |         assert(dev);
 | 
|---|
| 283 | 
 | 
|---|
| 284 |         const usb_target_t target = {
 | 
|---|
| 285 |                 {
 | 
|---|
| 286 |                         .address = dev->address,
 | 
|---|
| 287 |                         .endpoint = ifreq->endpoint,
 | 
|---|
| 288 |                         .stream = ifreq->stream,
 | 
|---|
| 289 |                 }
 | 
|---|
| 290 |         };
 | 
|---|
| 291 | 
 | 
|---|
| 292 |         if (!usb_target_is_valid(&target))
 | 
|---|
| 293 |                 return EINVAL;
 | 
|---|
| 294 | 
 | 
|---|
| 295 |         if (ifreq->offset > 0 && ifreq->size == 0)
 | 
|---|
| 296 |                 return EINVAL;
 | 
|---|
| 297 | 
 | 
|---|
| 298 |         if (ifreq->size > 0 && !dma_buffer_is_set(&ifreq->buffer))
 | 
|---|
| 299 |                 return EBADMEM;
 | 
|---|
| 300 | 
 | 
|---|
| 301 |         if (!callback && arg)
 | 
|---|
| 302 |                 return EBADMEM;
 | 
|---|
| 303 | 
 | 
|---|
| 304 |         const transfer_request_t request = {
 | 
|---|
| 305 |                 .target = target,
 | 
|---|
| 306 |                 .dir = ifreq->dir,
 | 
|---|
| 307 |                 .buffer = ifreq->buffer,
 | 
|---|
| 308 |                 .offset = ifreq->offset,
 | 
|---|
| 309 |                 .size = ifreq->size,
 | 
|---|
| 310 |                 .setup = ifreq->setup,
 | 
|---|
| 311 |                 .on_complete = callback,
 | 
|---|
| 312 |                 .arg = arg,
 | 
|---|
| 313 |                 .name = (ifreq->dir == USB_DIRECTION_IN) ? "READ" : "WRITE",
 | 
|---|
| 314 |         };
 | 
|---|
| 315 | 
 | 
|---|
| 316 |         return bus_issue_transfer(dev, &request);
 | 
|---|
| 317 | }
 | 
|---|
| 318 | 
 | 
|---|
| 319 | /** USB device interface */
 | 
|---|
| 320 | static usb_iface_t usb_iface = {
 | 
|---|
| 321 |         .get_my_description = get_device_description,
 | 
|---|
| 322 | };
 | 
|---|
| 323 | 
 | 
|---|
| 324 | /** USB host controller interface */
 | 
|---|
| 325 | static usbhc_iface_t usbhc_iface = {
 | 
|---|
| 326 |         .default_address_reservation = default_address_reservation,
 | 
|---|
| 327 | 
 | 
|---|
| 328 |         .device_enumerate = device_enumerate,
 | 
|---|
| 329 |         .device_remove = device_remove,
 | 
|---|
| 330 | 
 | 
|---|
| 331 |         .register_endpoint = register_endpoint,
 | 
|---|
| 332 |         .unregister_endpoint = unregister_endpoint,
 | 
|---|
| 333 | 
 | 
|---|
| 334 |         .transfer = transfer,
 | 
|---|
| 335 | };
 | 
|---|
| 336 | 
 | 
|---|
| 337 | /** Standard USB device interface) */
 | 
|---|
| 338 | static ddf_dev_ops_t usb_ops = {
 | 
|---|
| 339 |         .interfaces[USB_DEV_IFACE] = &usb_iface,
 | 
|---|
| 340 |         .interfaces[USBHC_DEV_IFACE] = &usbhc_iface,
 | 
|---|
| 341 | };
 | 
|---|
| 342 | 
 | 
|---|
| 343 | /* DDF HELPERS */
 | 
|---|
| 344 | 
 | 
|---|
| 345 | #define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
 | 
|---|
| 346 | do { \
 | 
|---|
| 347 |         match_id_t *mid = malloc(sizeof(match_id_t)); \
 | 
|---|
| 348 |         if (!mid) { \
 | 
|---|
| 349 |                 clean_match_ids(list); \
 | 
|---|
| 350 |                 return ENOMEM; \
 | 
|---|
| 351 |         } \
 | 
|---|
| 352 |         char *id = NULL; \
 | 
|---|
| 353 |         int ret = asprintf(&id, str, ##__VA_ARGS__); \
 | 
|---|
| 354 |         if (ret < 0) { \
 | 
|---|
| 355 |                 clean_match_ids(list); \
 | 
|---|
| 356 |                 free(mid); \
 | 
|---|
| 357 |                 return ENOMEM; \
 | 
|---|
| 358 |         } \
 | 
|---|
| 359 |         mid->score = sc; \
 | 
|---|
| 360 |         mid->id = id; \
 | 
|---|
| 361 |         add_match_id(list, mid); \
 | 
|---|
| 362 | } while (0)
 | 
|---|
| 363 | 
 | 
|---|
| 364 | /* This is a copy of lib/usbdev/src/recognise.c */
 | 
|---|
| 365 | static errno_t create_match_ids(match_id_list_t *l,
 | 
|---|
| 366 |     usb_standard_device_descriptor_t *d)
 | 
|---|
| 367 | {
 | 
|---|
| 368 |         assert(l);
 | 
|---|
| 369 |         assert(d);
 | 
|---|
| 370 | 
 | 
|---|
| 371 |         if (d->vendor_id != 0) {
 | 
|---|
| 372 |                 /* First, with release number. */
 | 
|---|
| 373 |                 ADD_MATCHID_OR_RETURN(l, 100,
 | 
|---|
| 374 |                     "usb&vendor=%#06x&product=%#06x&release=%x.%x",
 | 
|---|
| 375 |                     d->vendor_id, d->product_id, (d->device_version >> 8),
 | 
|---|
| 376 |                     (d->device_version & 0xff));
 | 
|---|
| 377 | 
 | 
|---|
| 378 |                 /* Next, without release number. */
 | 
|---|
| 379 |                 ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#06x&product=%#06x",
 | 
|---|
| 380 |                     d->vendor_id, d->product_id);
 | 
|---|
| 381 |         }
 | 
|---|
| 382 | 
 | 
|---|
| 383 |         /* Class match id */
 | 
|---|
| 384 |         ADD_MATCHID_OR_RETURN(l, 50, "usb&class=%s",
 | 
|---|
| 385 |             usb_str_class(d->device_class));
 | 
|---|
| 386 | 
 | 
|---|
| 387 |         /* As a last resort, try fallback driver. */
 | 
|---|
| 388 |         ADD_MATCHID_OR_RETURN(l, 10, "usb&fallback");
 | 
|---|
| 389 | 
 | 
|---|
| 390 |         return EOK;
 | 
|---|
| 391 | }
 | 
|---|
| 392 | 
 | 
|---|
| 393 | device_t *hcd_ddf_fun_create(hc_device_t *hc, usb_speed_t speed)
 | 
|---|
| 394 | {
 | 
|---|
| 395 |         /* Create DDF function for the new device */
 | 
|---|
| 396 |         ddf_fun_t *fun = ddf_fun_create(hc->ddf_dev, fun_inner, NULL);
 | 
|---|
| 397 |         if (!fun)
 | 
|---|
| 398 |                 return NULL;
 | 
|---|
| 399 | 
 | 
|---|
| 400 |         ddf_fun_set_ops(fun, &usb_ops);
 | 
|---|
| 401 | 
 | 
|---|
| 402 |         /* Create USB device node for the new device */
 | 
|---|
| 403 |         device_t *dev = ddf_fun_data_alloc(fun, hc->bus->device_size);
 | 
|---|
| 404 |         if (!dev) {
 | 
|---|
| 405 |                 ddf_fun_destroy(fun);
 | 
|---|
| 406 |                 return NULL;
 | 
|---|
| 407 |         }
 | 
|---|
| 408 | 
 | 
|---|
| 409 |         bus_device_init(dev, hc->bus);
 | 
|---|
| 410 |         dev->fun = fun;
 | 
|---|
| 411 |         dev->speed = speed;
 | 
|---|
| 412 |         return dev;
 | 
|---|
| 413 | }
 | 
|---|
| 414 | 
 | 
|---|
| 415 | void hcd_ddf_fun_destroy(device_t *dev)
 | 
|---|
| 416 | {
 | 
|---|
| 417 |         assert(dev);
 | 
|---|
| 418 |         assert(dev->fun);
 | 
|---|
| 419 |         ddf_fun_destroy(dev->fun);
 | 
|---|
| 420 | }
 | 
|---|
| 421 | 
 | 
|---|
| 422 | errno_t hcd_ddf_setup_match_ids(device_t *device, usb_standard_device_descriptor_t *desc)
 | 
|---|
| 423 | {
 | 
|---|
| 424 |         errno_t err;
 | 
|---|
| 425 |         match_id_list_t mids;
 | 
|---|
| 426 | 
 | 
|---|
| 427 |         init_match_ids(&mids);
 | 
|---|
| 428 | 
 | 
|---|
| 429 |         /* Create match ids from the device descriptor */
 | 
|---|
| 430 |         usb_log_debug("Device(%d): Creating match IDs.", device->address);
 | 
|---|
| 431 |         if ((err = create_match_ids(&mids, desc))) {
 | 
|---|
| 432 |                 return err;
 | 
|---|
| 433 |         }
 | 
|---|
| 434 | 
 | 
|---|
| 435 |         list_foreach(mids.ids, link, const match_id_t, mid) {
 | 
|---|
| 436 |                 ddf_fun_add_match_id(device->fun, mid->id, mid->score);
 | 
|---|
| 437 |         }
 | 
|---|
| 438 | 
 | 
|---|
| 439 |         return EOK;
 | 
|---|
| 440 | }
 | 
|---|
| 441 | 
 | 
|---|
| 442 | /** Initialize hc structures.
 | 
|---|
| 443 |  *
 | 
|---|
| 444 |  * @param[in] device DDF instance of the device to use.
 | 
|---|
| 445 |  * @param[in] max_speed Maximum supported USB speed.
 | 
|---|
| 446 |  * @param[in] bw available bandwidth.
 | 
|---|
| 447 |  * @param[in] bw_count Function to compute required ep bandwidth.
 | 
|---|
| 448 |  *
 | 
|---|
| 449 |  * @return Error code.
 | 
|---|
| 450 |  * This function does all the ddf work for hc driver.
 | 
|---|
| 451 |  */
 | 
|---|
| 452 | errno_t hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
 | 
|---|
| 453 | {
 | 
|---|
| 454 |         assert(device);
 | 
|---|
| 455 | 
 | 
|---|
| 456 |         hc_device_t *instance = ddf_dev_data_alloc(device, size);
 | 
|---|
| 457 |         if (instance == NULL) {
 | 
|---|
| 458 |                 usb_log_error("Failed to allocate HCD ddf structure.");
 | 
|---|
| 459 |                 return ENOMEM;
 | 
|---|
| 460 |         }
 | 
|---|
| 461 |         instance->ddf_dev = device;
 | 
|---|
| 462 | 
 | 
|---|
| 463 |         errno_t ret = ENOMEM;
 | 
|---|
| 464 |         instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
 | 
|---|
| 465 |         if (!instance->ctl_fun) {
 | 
|---|
| 466 |                 usb_log_error("Failed to create HCD ddf fun.");
 | 
|---|
| 467 |                 goto err_destroy_fun;
 | 
|---|
| 468 |         }
 | 
|---|
| 469 | 
 | 
|---|
| 470 |         ret = ddf_fun_bind(instance->ctl_fun);
 | 
|---|
| 471 |         if (ret != EOK) {
 | 
|---|
| 472 |                 usb_log_error("Failed to bind ctl_fun: %s.", str_error(ret));
 | 
|---|
| 473 |                 goto err_destroy_fun;
 | 
|---|
| 474 |         }
 | 
|---|
| 475 | 
 | 
|---|
| 476 |         ret = ddf_fun_add_to_category(instance->ctl_fun, USB_HC_CATEGORY);
 | 
|---|
| 477 |         if (ret != EOK) {
 | 
|---|
| 478 |                 usb_log_error("Failed to add fun to category: %s.",
 | 
|---|
| 479 |                     str_error(ret));
 | 
|---|
| 480 |                 ddf_fun_unbind(instance->ctl_fun);
 | 
|---|
| 481 |                 goto err_destroy_fun;
 | 
|---|
| 482 |         }
 | 
|---|
| 483 | 
 | 
|---|
| 484 |         /* HC should be ok at this point (except it can't do anything) */
 | 
|---|
| 485 |         return EOK;
 | 
|---|
| 486 | 
 | 
|---|
| 487 | err_destroy_fun:
 | 
|---|
| 488 |         ddf_fun_destroy(instance->ctl_fun);
 | 
|---|
| 489 |         instance->ctl_fun = NULL;
 | 
|---|
| 490 |         return ret;
 | 
|---|
| 491 | }
 | 
|---|
| 492 | 
 | 
|---|
| 493 | void hcd_ddf_clean_hc(hc_device_t *hcd)
 | 
|---|
| 494 | {
 | 
|---|
| 495 |         if (ddf_fun_unbind(hcd->ctl_fun) == EOK)
 | 
|---|
| 496 |                 ddf_fun_destroy(hcd->ctl_fun);
 | 
|---|
| 497 | }
 | 
|---|
| 498 | 
 | 
|---|
| 499 | /** Call the parent driver with a request to enable interrupt
 | 
|---|
| 500 |  *
 | 
|---|
| 501 |  * @param[in] device Device asking for interrupts
 | 
|---|
| 502 |  * @param[in] inum Interrupt number
 | 
|---|
| 503 |  * @return Error code.
 | 
|---|
| 504 |  */
 | 
|---|
| 505 | errno_t hcd_ddf_enable_interrupt(hc_device_t *hcd, int inum)
 | 
|---|
| 506 | {
 | 
|---|
| 507 |         async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 | 
|---|
| 508 |         if (parent_sess == NULL)
 | 
|---|
| 509 |                 return EIO;
 | 
|---|
| 510 | 
 | 
|---|
| 511 |         return hw_res_enable_interrupt(parent_sess, inum);
 | 
|---|
| 512 | }
 | 
|---|
| 513 | 
 | 
|---|
| 514 | errno_t hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res)
 | 
|---|
| 515 | {
 | 
|---|
| 516 |         async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 | 
|---|
| 517 |         if (parent_sess == NULL)
 | 
|---|
| 518 |                 return EIO;
 | 
|---|
| 519 | 
 | 
|---|
| 520 |         hw_res_list_parsed_init(hw_res);
 | 
|---|
| 521 |         const errno_t ret = hw_res_get_list_parsed(parent_sess, hw_res, 0);
 | 
|---|
| 522 |         if (ret != EOK)
 | 
|---|
| 523 |                 hw_res_list_parsed_clean(hw_res);
 | 
|---|
| 524 |         return ret;
 | 
|---|
| 525 | }
 | 
|---|
| 526 | 
 | 
|---|
| 527 | /**
 | 
|---|
| 528 |  * @}
 | 
|---|
| 529 |  */
 | 
|---|