Changeset df6ded8 in mainline for uspace/lib/usbhost/src


Ignore:
Timestamp:
2018-02-28T16:37:50Z (8 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
1b20da0
Parents:
f5e5f73 (diff), b2dca8de (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Jakub Jermar <jakub@…> (2018-02-28 16:06:42)
git-committer:
Jakub Jermar <jakub@…> (2018-02-28 16:37:50)
Message:

Merge github.com:helenos-xhci-team/helenos

This commit merges support for USB 3 and generally refactors, fixes,
extends and cleans up the existing USB framework.

Notable additions and features:

  • new host controller driver has been implemented to control various xHC models (among others, NEC Renesas uPD720200)
  • isochronous data transfer mode
  • support for explicit USB device removal
  • USB tablet driver
Location:
uspace/lib/usbhost/src
Files:
4 added
1 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/src/ddf_helpers.c

    rf5e5f73 rdf6ded8  
    11/*
    22 * Copyright (c) 2013 Jan Vesely
     3 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch, Petr Manek
    34 * All rights reserved.
    45 *
     
    3132 */
    3233/** @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>
     34 * Helpers to work with the DDF interface.
     35 */
    4136
    4237#include <adt/list.h>
     
    4742#include <device/hw_res_parsed.h>
    4843#include <errno.h>
    49 #include <fibril_synch.h>
    50 #include <macros.h>
    51 #include <stdio.h>
    52 #include <stdlib.h>
    5344#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>
    5450#include <usb_iface.h>
     51#include <usbhc_iface.h>
     52
     53#include "bus.h"
     54#include "endpoint.h"
    5555
    5656#include "ddf_helpers.h"
    5757
    58 #define CTRL_PIPE_MIN_PACKET_SIZE 8
    59 
    60 typedef struct usb_dev {
    61         link_t link;
    62         list_t devices;
    63         fibril_mutex_t guard;
    64         ddf_fun_t *fun;
    65         usb_address_t address;
    66         usb_speed_t speed;
    67         usb_address_t tt_address;
    68         unsigned port;
    69 } usb_dev_t;
    70 
    71 typedef struct hc_dev {
    72         ddf_fun_t *ctl_fun;
    73         hcd_t hcd;
    74         usb_dev_t *root_hub;
    75 } hc_dev_t;
    76 
    77 static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
    78 {
    79         return ddf_dev_data_get(dev);
    80 }
    81 
    82 hcd_t *dev_to_hcd(ddf_dev_t *dev)
    83 {
    84         hc_dev_t *hc_dev = dev_to_hc_dev(dev);
    85         if (!hc_dev) {
    86                 usb_log_error("Invalid HCD device.\n");
    87                 return NULL;
    88         }
    89         return &hc_dev->hcd;
    90 }
    91 
    92 
    93 static errno_t hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
    94 static errno_t hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
    95 
    96 
    97 /* DDF INTERFACE */
    98 
    99 /** Register endpoint interface function.
    100  * @param fun DDF function.
    101  * @param address USB address of the device.
    102  * @param endpoint USB endpoint number to be registered.
    103  * @param transfer_type Endpoint's transfer type.
    104  * @param direction USB communication direction the endpoint is capable of.
    105  * @param max_packet_size Maximu size of packets the endpoint accepts.
    106  * @param interval Preferred timeout between communication.
     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.
    10765 * @return Error code.
    10866 */
    109 static errno_t register_endpoint(
    110     ddf_fun_t *fun, usb_endpoint_t endpoint,
    111     usb_transfer_type_t transfer_type, usb_direction_t direction,
    112     size_t max_packet_size, unsigned packets, unsigned interval)
    113 {
    114         assert(fun);
    115         hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
    116         usb_dev_t *dev = ddf_fun_data_get(fun);
     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);
    11773        assert(hcd);
     74        assert(hcd->bus);
    11875        assert(dev);
    119         const size_t size = max_packet_size;
    120         const usb_target_t target =
    121             {{.address = dev->address, .endpoint = endpoint}};
    122 
    123         usb_log_debug("Register endpoint %d:%d %s-%s %zuB %ums.\n",
    124             dev->address, endpoint, usb_str_transfer_type(transfer_type),
    125             usb_str_direction(direction), max_packet_size, interval);
    126 
    127         return hcd_add_ep(hcd, target, direction, transfer_type,
    128             max_packet_size, packets, size, dev->tt_address, dev->port);
    129 }
    130 
    131 /** Unregister endpoint interface function.
    132  * @param fun DDF function.
    133  * @param address USB address of the endpoint.
    134  * @param endpoint USB endpoint number.
    135  * @param direction Communication direction of the enpdoint to unregister.
     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.
    136241 * @return Error code.
    137242 */
    138 static errno_t unregister_endpoint(
    139     ddf_fun_t *fun, usb_endpoint_t endpoint, usb_direction_t direction)
    140 {
    141         assert(fun);
    142         hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
    143         usb_dev_t *dev = ddf_fun_data_get(fun);
    144         assert(hcd);
     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);
    145247        assert(dev);
    146         const usb_target_t target =
    147             {{.address = dev->address, .endpoint = endpoint}};
    148         usb_log_debug("Unregister endpoint %d:%d %s.\n",
    149             dev->address, endpoint, usb_str_direction(direction));
    150         return hcd_remove_ep(hcd, target, direction);
    151 }
    152 
    153 static errno_t reserve_default_address(ddf_fun_t *fun, usb_speed_t speed)
    154 {
    155         assert(fun);
    156         hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
    157         usb_dev_t *dev = ddf_fun_data_get(fun);
    158         assert(hcd);
    159         assert(dev);
    160 
    161         usb_log_debug("Device %d requested default address at %s speed\n",
    162             dev->address, usb_str_speed(speed));
    163         return hcd_reserve_default_address(hcd, speed);
    164 }
    165 
    166 static errno_t release_default_address(ddf_fun_t *fun)
    167 {
    168         assert(fun);
    169         hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
    170         usb_dev_t *dev = ddf_fun_data_get(fun);
    171         assert(hcd);
    172         assert(dev);
    173 
    174         usb_log_debug("Device %d released default address\n", dev->address);
    175         return hcd_release_default_address(hcd);
    176 }
    177 
    178 static errno_t device_enumerate(ddf_fun_t *fun, unsigned port)
    179 {
    180         assert(fun);
    181         ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
    182         usb_dev_t *dev = ddf_fun_data_get(fun);
    183         assert(ddf_dev);
    184         assert(dev);
    185         usb_log_debug("Hub %d reported a new USB device on port: %u\n",
    186             dev->address, port);
    187         return hcd_ddf_new_device(ddf_dev, dev, port);
    188 }
    189 
    190 static errno_t device_remove(ddf_fun_t *fun, unsigned port)
    191 {
    192         assert(fun);
    193         ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
    194         usb_dev_t *dev = ddf_fun_data_get(fun);
    195         assert(ddf_dev);
    196         assert(dev);
    197         usb_log_debug("Hub `%s' reported removal of device on port %u\n",
    198             ddf_fun_get_name(fun), port);
    199         return hcd_ddf_remove_device(ddf_dev, dev, port);
    200 }
    201 
    202 /** Gets handle of the respective device.
    203  *
    204  * @param[in] fun Device function.
    205  * @param[out] handle Place to write the handle.
    206  * @return Error code.
    207  */
    208 static errno_t get_my_device_handle(ddf_fun_t *fun, devman_handle_t *handle)
    209 {
    210         assert(fun);
    211         if (handle)
    212                 *handle = ddf_fun_get_handle(fun);
    213         return EOK;
    214 }
    215 
    216 /** Inbound communication interface function.
     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 *
    217265 * @param fun DDF function.
    218266 * @param target Communication target.
     267 * @param dir Communication direction.
    219268 * @param setup_data Data to use in setup stage (control transfers).
    220269 * @param data Pointer to data buffer.
     
    224273 * @return Error code.
    225274 */
    226 static errno_t dev_read(ddf_fun_t *fun, usb_endpoint_t endpoint,
    227     uint64_t setup_data, uint8_t *data, size_t size,
    228     usbhc_iface_transfer_in_callback_t callback, void *arg)
    229 {
    230         assert(fun);
    231         usb_dev_t *usb_dev = ddf_fun_data_get(fun);
    232         assert(usb_dev);
     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
    233283        const usb_target_t target = {{
    234             .address =  usb_dev->address,
    235             .endpoint = endpoint,
     284                .address = dev->address,
     285                .endpoint = ifreq->endpoint,
     286                .stream = ifreq->stream,
    236287        }};
    237         return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)), target,
    238             USB_DIRECTION_IN, data, size, setup_data, callback, NULL, arg,
    239             "READ");
    240 }
    241 
    242 /** Outbound communication interface function.
    243  * @param fun DDF function.
    244  * @param target Communication target.
    245  * @param setup_data Data to use in setup stage (control transfers).
    246  * @param data Pointer to data buffer.
    247  * @param size Size of the data buffer.
    248  * @param callback Function to call on communication end.
    249  * @param arg Argument passed to the callback function.
    250  * @return Error code.
    251  */
    252 static errno_t dev_write(ddf_fun_t *fun, usb_endpoint_t endpoint,
    253     uint64_t setup_data, const uint8_t *data, size_t size,
    254     usbhc_iface_transfer_out_callback_t callback, void *arg)
    255 {
    256         assert(fun);
    257         usb_dev_t *usb_dev = ddf_fun_data_get(fun);
    258         assert(usb_dev);
    259         const usb_target_t target = {{
    260             .address =  usb_dev->address,
    261             .endpoint = endpoint,
    262         }};
    263         return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)),
    264             target, USB_DIRECTION_OUT, (uint8_t*)data, size, setup_data, NULL,
    265             callback, arg, "WRITE");
     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);
    266314}
    267315
    268316/** USB device interface */
    269317static usb_iface_t usb_iface = {
    270         .get_my_device_handle = get_my_device_handle,
    271 
    272         .reserve_default_address = reserve_default_address,
    273         .release_default_address = release_default_address,
     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,
    274324
    275325        .device_enumerate = device_enumerate,
     
    279329        .unregister_endpoint = unregister_endpoint,
    280330
    281         .read = dev_read,
    282         .write = dev_write,
     331        .transfer = transfer,
    283332};
    284333
     
    286335static ddf_dev_ops_t usb_ops = {
    287336        .interfaces[USB_DEV_IFACE] = &usb_iface,
     337        .interfaces[USBHC_DEV_IFACE] = &usbhc_iface,
    288338};
    289339
    290340
    291341/* DDF HELPERS */
    292 
    293 #define GET_DEVICE_DESC(size) \
    294 { \
    295         .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
    296             | (USB_REQUEST_TYPE_STANDARD << 5) \
    297             | USB_REQUEST_RECIPIENT_DEVICE, \
    298         .request = USB_DEVREQ_GET_DESCRIPTOR, \
    299         .value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
    300         .index = uint16_host2usb(0), \
    301         .length = uint16_host2usb(size), \
    302 };
    303 
    304 #define SET_ADDRESS(address) \
    305 { \
    306         .request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
    307             | (USB_REQUEST_TYPE_STANDARD << 5) \
    308             | USB_REQUEST_RECIPIENT_DEVICE, \
    309         .request = USB_DEVREQ_SET_ADDRESS, \
    310         .value = uint16_host2usb(address), \
    311         .index = uint16_host2usb(0), \
    312         .length = uint16_host2usb(0), \
    313 };
    314 
    315 static errno_t hcd_ddf_add_device(ddf_dev_t *parent, usb_dev_t *hub_dev,
    316     unsigned port, usb_address_t address, usb_speed_t speed, const char *name,
    317     const match_id_list_t *mids)
    318 {
    319         assert(parent);
    320 
    321         char default_name[10] = { 0 }; /* usbxyz-ss */
    322         if (!name) {
    323                 snprintf(default_name, sizeof(default_name) - 1,
    324                     "usb%u-%cs", address, usb_str_speed(speed)[0]);
    325                 name = default_name;
    326         }
    327 
    328         ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
    329         if (!fun)
    330                 return ENOMEM;
    331         usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
    332         if (!info) {
    333                 ddf_fun_destroy(fun);
    334                 return ENOMEM;
    335         }
    336         info->address = address;
    337         info->speed = speed;
    338         info->fun = fun;
    339         info->port = port;
    340         info->tt_address = hub_dev ? hub_dev->tt_address : -1;
    341         link_initialize(&info->link);
    342         list_initialize(&info->devices);
    343         fibril_mutex_initialize(&info->guard);
    344 
    345         if (hub_dev && hub_dev->speed == USB_SPEED_HIGH && usb_speed_is_11(speed))
    346                 info->tt_address = hub_dev->address;
    347 
    348         ddf_fun_set_ops(fun, &usb_ops);
    349         list_foreach(mids->ids, link, const match_id_t, mid) {
    350                 ddf_fun_add_match_id(fun, mid->id, mid->score);
    351         }
    352 
    353         errno_t ret = ddf_fun_bind(fun);
    354         if (ret != EOK) {
    355                 ddf_fun_destroy(fun);
    356                 return ret;
    357         }
    358 
    359         if (hub_dev) {
    360                 fibril_mutex_lock(&hub_dev->guard);
    361                 list_append(&info->link, &hub_dev->devices);
    362                 fibril_mutex_unlock(&hub_dev->guard);
    363         } else {
    364                 hc_dev_t *hc_dev = dev_to_hc_dev(parent);
    365                 assert(hc_dev->root_hub == NULL);
    366                 hc_dev->root_hub = info;
    367         }
    368         return EOK;
    369 }
    370342
    371343#define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
     
    394366        assert(l);
    395367        assert(d);
    396        
     368
    397369        if (d->vendor_id != 0) {
    398370                /* First, with release number. */
     
    401373                    d->vendor_id, d->product_id, (d->device_version >> 8),
    402374                    (d->device_version & 0xff));
    403        
     375
    404376                /* Next, without release number. */
    405377                ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
     
    415387
    416388        return EOK;
    417 
    418 }
    419 
    420 static errno_t hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub,
    421     unsigned port)
    422 {
    423         assert(device);
    424 
    425         hcd_t *hcd = dev_to_hcd(device);
    426         assert(hcd);
    427 
    428         hc_dev_t *hc_dev = dev_to_hc_dev(device);
    429         assert(hc_dev);
    430 
    431         fibril_mutex_lock(&hub->guard);
    432 
    433         usb_dev_t *victim = NULL;
    434 
    435         list_foreach(hub->devices, link, usb_dev_t, it) {
    436                 if (it->port == port) {
    437                         victim = it;
    438                         break;
    439                 }
    440         }
    441         if (victim) {
    442                 assert(victim->port == port);
    443                 list_remove(&victim->link);
    444                 fibril_mutex_unlock(&hub->guard);
    445                 const errno_t ret = ddf_fun_unbind(victim->fun);
    446                 if (ret == EOK) {
    447                         usb_address_t address = victim->address;
    448                         ddf_fun_destroy(victim->fun);
    449                         hcd_release_address(hcd, address);
    450                 } else {
    451                         usb_log_warning("Failed to unbind device `%s': %s\n",
    452                             ddf_fun_get_name(victim->fun), str_error(ret));
    453                 }
    454                 return EOK;
    455         }
    456         fibril_mutex_unlock(&hub->guard);
    457         return ENOENT;
    458 }
    459 
    460 static errno_t hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port)
    461 {
    462         assert(device);
    463 
    464         hcd_t *hcd = dev_to_hcd(device);
    465         assert(hcd);
    466 
    467         usb_speed_t speed = USB_SPEED_MAX;
    468 
    469         /* This checks whether the default address is reserved and gets speed */
    470         errno_t ret = usb_bus_get_speed(&hcd->bus, USB_ADDRESS_DEFAULT, &speed);
    471         if (ret != EOK) {
    472                 usb_log_error("Failed to verify speed: %s.", str_error(ret));
    473                 return ret;
    474         }
    475 
    476         usb_log_debug("Found new %s speed USB device.", usb_str_speed(speed));
    477 
    478         static const usb_target_t default_target = {{
    479                 .address = USB_ADDRESS_DEFAULT,
    480                 .endpoint = 0,
    481         }};
    482 
    483         usb_address_t address;
    484         ret = hcd_request_address(hcd, speed, &address);
    485         if (ret != EOK) {
    486                 usb_log_error("Failed to reserve new address: %s.",
    487                     str_error(ret));
    488                 return ret;
    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         size_t got;
    522         ret = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
    523             &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
    524             "read first 8 bytes of dev descriptor", &got);
    525 
    526         if (ret == EOK && got != CTRL_PIPE_MIN_PACKET_SIZE) {
    527                 ret = EOVERFLOW;
    528         }
    529 
    530         if (ret != EOK) {
    531                 usb_log_error("Device(%d): Failed to get 8B of dev descr: %s.",
    532                     address, str_error(ret));
    533                 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
    534                 hcd_release_address(hcd, address);
    535                 return ret;
    536         }
    537 
    538         /* Register EP on the new address */
    539         usb_log_debug("Device(%d): Registering control EP.", address);
    540         ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
    541             ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
    542             ED_MPS_TRANS_OPPORTUNITIES_GET(uint16_usb2host(desc.max_packet_size)),
    543             ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
    544             tt_address, port);
    545         if (ret != EOK) {
    546                 usb_log_error("Device(%d): Failed to register EP0: %s",
    547                     address, str_error(ret));
    548                 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
    549                 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
    550                 hcd_release_address(hcd, address);
    551                 return ret;
    552         }
    553 
    554         /* Set new address */
    555         const usb_device_request_setup_packet_t set_address =
    556             SET_ADDRESS(target.address);
    557 
    558         usb_log_debug("Device(%d): Setting USB address.", address);
    559         ret = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
    560             NULL, 0, *(uint64_t *)&set_address, "set address", &got);
    561 
    562         usb_log_debug("Device(%d): Removing default (0:0) EP.", address);
    563         hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
    564 
    565         if (ret != EOK) {
    566                 usb_log_error("Device(%d): Failed to set new address: %s.",
    567                     address, str_error(ret));
    568                 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
    569                 hcd_release_address(hcd, address);
    570                 return ret;
    571         }
    572 
    573         /* Get std device descriptor */
    574         const usb_device_request_setup_packet_t get_device_desc =
    575             GET_DEVICE_DESC(sizeof(desc));
    576 
    577         usb_log_debug("Device(%d): Requesting full device descriptor.",
    578             address);
    579         ret = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
    580             &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
    581             "read device descriptor", &got);
    582         if (ret != EOK) {
    583                 usb_log_error("Device(%d): Failed to set get dev descriptor: %s",
    584                     address, str_error(ret));
    585                 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
    586                 hcd_release_address(hcd, target.address);
    587                 return ret;
    588         }
     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);
    589426
    590427        /* Create match ids from the device descriptor */
    591         match_id_list_t mids;
    592         init_match_ids(&mids);
    593 
    594         usb_log_debug("Device(%d): Creating match IDs.", address);
    595         ret = create_match_ids(&mids, &desc);
    596         if (ret != EOK) {
    597                 usb_log_error("Device(%d): Failed to create match ids: %s",
    598                     address, str_error(ret));
    599                 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
    600                 hcd_release_address(hcd, target.address);
    601                 return ret;
    602         }
    603 
    604         /* Register device */
    605         usb_log_debug("Device(%d): Registering DDF device.", address);
    606         ret = hcd_ddf_add_device(device, hub, port, address, speed, NULL, &mids);
    607         clean_match_ids(&mids);
    608         if (ret != EOK) {
    609                 usb_log_error("Device(%d): Failed to register: %s.",
    610                     address, str_error(ret));
    611                 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
    612                 hcd_release_address(hcd, target.address);
    613         }
    614 
    615         return ret;
    616 }
    617 
    618 /** Announce root hub to the DDF
    619  *
    620  * @param[in] device Host controller ddf device
    621  * @return Error code
    622  */
    623 errno_t hcd_ddf_setup_root_hub(ddf_dev_t *device)
    624 {
    625         assert(device);
    626         hcd_t *hcd = dev_to_hcd(device);
    627         assert(hcd);
    628 
    629         hcd_reserve_default_address(hcd, hcd->bus.max_speed);
    630         const errno_t ret = hcd_ddf_new_device(device, NULL, 0);
    631         hcd_release_default_address(hcd);
    632         return ret;
     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;
    633438}
    634439
     
    643448 * This function does all the ddf work for hc driver.
    644449 */
    645 errno_t hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
    646     size_t bw, bw_count_func_t bw_count)
     450errno_t hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
    647451{
    648452        assert(device);
    649453
    650         hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
     454        hc_device_t *instance = ddf_dev_data_alloc(device, size);
    651455        if (instance == NULL) {
    652                 usb_log_error("Failed to allocate HCD ddf structure.\n");
     456                usb_log_error("Failed to allocate HCD ddf structure.");
    653457                return ENOMEM;
    654458        }
    655         instance->root_hub = NULL;
    656         hcd_init(&instance->hcd, max_speed, bw, bw_count);
     459        instance->ddf_dev = device;
    657460
    658461        errno_t ret = ENOMEM;
    659462        instance->ctl_fun = ddf_fun_create(device, fun_exposed, "ctl");
    660463        if (!instance->ctl_fun) {
    661                 usb_log_error("Failed to create HCD ddf fun.\n");
     464                usb_log_error("Failed to create HCD ddf fun.");
    662465                goto err_destroy_fun;
    663466        }
     
    665468        ret = ddf_fun_bind(instance->ctl_fun);
    666469        if (ret != EOK) {
    667                 usb_log_error("Failed to bind ctl_fun: %s.\n", str_error(ret));
     470                usb_log_error("Failed to bind ctl_fun: %s.", str_error(ret));
    668471                goto err_destroy_fun;
    669472        }
     
    671474        ret = ddf_fun_add_to_category(instance->ctl_fun, USB_HC_CATEGORY);
    672475        if (ret != EOK) {
    673                 usb_log_error("Failed to add fun to category: %s.\n",
     476                usb_log_error("Failed to add fun to category: %s.",
    674477                    str_error(ret));
    675478                ddf_fun_unbind(instance->ctl_fun);
     
    686489}
    687490
    688 void hcd_ddf_clean_hc(ddf_dev_t *device)
    689 {
    690         assert(device);
    691         hc_dev_t *hc = dev_to_hc_dev(device);
    692         assert(hc);
    693         const errno_t ret = ddf_fun_unbind(hc->ctl_fun);
    694         if (ret == EOK)
    695                 ddf_fun_destroy(hc->ctl_fun);
    696 }
    697 
    698 //TODO: Cache parent session in HCD
     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
    699497/** Call the parent driver with a request to enable interrupt
    700498 *
     
    703501 * @return Error code.
    704502 */
    705 errno_t hcd_ddf_enable_interrupt(ddf_dev_t *device, int inum)
    706 {
    707         async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
     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);
    708506        if (parent_sess == NULL)
    709507                return EIO;
     
    712510}
    713511
    714 //TODO: Cache parent session in HCD
    715 errno_t hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res)
    716 {
    717         async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
     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);
    718515        if (parent_sess == NULL)
    719516                return EIO;
     
    726523}
    727524
    728 // TODO: move this someplace else
    729 static inline void irq_code_clean(irq_code_t *code)
    730 {
    731         if (code) {
    732                 free(code->ranges);
    733                 free(code->cmds);
    734                 code->ranges = NULL;
    735                 code->cmds = NULL;
    736                 code->rangecount = 0;
    737                 code->cmdcount = 0;
    738         }
    739 }
    740 
    741 /** Register interrupt handler
    742  *
    743  * @param[in] device Host controller DDF device
    744  * @param[in] regs Register range
    745  * @param[in] irq Interrupt number
    746  * @paran[in] handler Interrupt handler
    747  * @param[in] gen_irq_code IRQ code generator.
    748  *
    749  * @param[out] handle  IRQ capability handle on success.
    750  *
    751  * @return Error code.
    752  */
    753 errno_t hcd_ddf_setup_interrupts(ddf_dev_t *device,
    754     const hw_res_list_parsed_t *hw_res,
    755     interrupt_handler_t handler,
    756     errno_t (*gen_irq_code)(irq_code_t *, const hw_res_list_parsed_t *, int *),
    757     cap_handle_t *handle)
    758 {
    759 
    760         assert(device);
    761         if (!handler || !gen_irq_code)
    762                 return ENOTSUP;
    763 
    764         irq_code_t irq_code = {0};
    765 
    766         int irq;
    767         errno_t ret = gen_irq_code(&irq_code, hw_res, &irq);
    768         if (ret != EOK) {
    769                 usb_log_error("Failed to generate IRQ code: %s.\n",
    770                     str_error(ret));
    771                 return ret;
    772         }
    773 
    774         /* Register handler to avoid interrupt lockup */
    775         ret = register_interrupt_handler(device, irq, handler,
    776             &irq_code, handle);
    777         irq_code_clean(&irq_code);
    778         if (ret != EOK) {
    779                 usb_log_error("Failed to register interrupt handler: %s.\n",
    780                     str_error(ret));
    781                 return ret;
    782         }
    783 
    784         /* Enable interrupts */
    785         ret = hcd_ddf_enable_interrupt(device, irq);
    786         if (ret != EOK) {
    787                 usb_log_error("Failed to register interrupt handler: %s.\n",
    788                     str_error(ret));
    789                 unregister_interrupt_handler(device, *handle);
    790         }
    791         return ret;
    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] call Pointer to the call from kernel.
    798  */
    799 void ddf_hcd_gen_irq_handler(ipc_call_t *call, ddf_dev_t *dev)
    800 {
    801         assert(dev);
    802         hcd_t *hcd = dev_to_hcd(dev);
    803         if (!hcd || !hcd->ops.irq_hook) {
    804                 usb_log_error("Interrupt on not yet initialized device.\n");
    805                 return;
    806         }
    807         const uint32_t status = IPC_GET_ARG1(*call);
    808         hcd->ops.irq_hook(hcd, status);
    809 }
    810 
    811 static errno_t interrupt_polling(void *arg)
    812 {
    813         hcd_t *hcd = arg;
    814         assert(hcd);
    815         if (!hcd->ops.status_hook || !hcd->ops.irq_hook)
    816                 return ENOTSUP;
    817         uint32_t status = 0;
    818         while (hcd->ops.status_hook(hcd, &status) == EOK) {
    819                 hcd->ops.irq_hook(hcd, status);
    820                 status = 0;
    821                 /* We should wait 1 frame - 1ms here, but this polling is a
    822                  * lame crutch anyway so don't hog the system. 10ms is still
    823                  * good enough for emergency mode */
    824                 async_usleep(10000);
    825         }
    826         return EOK;
    827 }
    828 
    829 /** Initialize hc and rh DDF structures and their respective drivers.
    830  *
    831  * @param device DDF instance of the device to use
    832  * @param speed Maximum supported speed
    833  * @param bw Available bandwidth (arbitrary units)
    834  * @param bw_count Bandwidth computing function
    835  * @param irq_handler IRQ handling function
    836  * @param gen_irq_code Function to generate IRQ pseudocode
    837  *                     (it needs to return used irq number)
    838  * @param driver_init Function to initialize HC driver
    839  * @param driver_fini Function to cleanup HC driver
    840  * @return Error code
    841  *
    842  * This function does all the preparatory work for hc and rh drivers:
    843  *  - gets device's hw resources
    844  *  - attempts to enable interrupts
    845  *  - registers interrupt handler
    846  *  - calls driver specific initialization
    847  *  - registers root hub
    848  */
    849 errno_t hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver)
    850 {
    851         assert(driver);
    852         static const struct { size_t bw; bw_count_func_t bw_count; }bw[] = {
    853             [USB_SPEED_FULL] = { .bw = BANDWIDTH_AVAILABLE_USB11,
    854                                  .bw_count = bandwidth_count_usb11 },
    855             [USB_SPEED_HIGH] = { .bw = BANDWIDTH_AVAILABLE_USB11,
    856                                  .bw_count = bandwidth_count_usb11 },
    857         };
    858 
    859         errno_t ret = EOK;
    860         const usb_speed_t speed = driver->hc_speed;
    861         if (speed >= ARRAY_SIZE(bw) || bw[speed].bw == 0) {
    862                 usb_log_error("Driver `%s' reported unsupported speed: %s",
    863                     driver->name, usb_str_speed(speed));
    864                 return ENOTSUP;
    865         }
    866 
    867         hw_res_list_parsed_t hw_res;
    868         ret = hcd_ddf_get_registers(device, &hw_res);
    869         if (ret != EOK) {
    870                 usb_log_error("Failed to get register memory addresses "
    871                     "for `%s': %s.\n", ddf_dev_get_name(device),
    872                     str_error(ret));
    873                 return ret;
    874         }
    875 
    876         ret = hcd_ddf_setup_hc(device, speed, bw[speed].bw, bw[speed].bw_count);
    877         if (ret != EOK) {
    878                 usb_log_error("Failed to setup generic HCD.\n");
    879                 hw_res_list_parsed_clean(&hw_res);
    880                 return ret;
    881         }
    882 
    883         interrupt_handler_t *irq_handler =
    884             driver->irq_handler ? driver->irq_handler : ddf_hcd_gen_irq_handler;
    885         int irq_cap;
    886         errno_t irq_ret = hcd_ddf_setup_interrupts(device, &hw_res,
    887             irq_handler, driver->irq_code_gen, &irq_cap);
    888         bool irqs_enabled = (irq_ret == EOK);
    889         if (irqs_enabled) {
    890                 usb_log_debug("Hw interrupts enabled.\n");
    891         }
    892 
    893         if (driver->claim) {
    894                 ret = driver->claim(device);
    895                 if (ret != EOK) {
    896                         usb_log_error("Failed to claim `%s' for driver `%s'",
    897                             ddf_dev_get_name(device), driver->name);
    898                         return ret;
    899                 }
    900         }
    901 
    902 
    903         /* Init hw driver */
    904         hcd_t *hcd = dev_to_hcd(device);
    905         ret = driver->init(hcd, &hw_res, irqs_enabled);
    906         hw_res_list_parsed_clean(&hw_res);
    907         if (ret != EOK) {
    908                 usb_log_error("Failed to init HCD: %s.\n", str_error(ret));
    909                 goto irq_unregister;
    910         }
    911 
    912         /* Need working irq replacement to setup root hub */
    913         if (!irqs_enabled && hcd->ops.status_hook) {
    914                 hcd->polling_fibril = fibril_create(interrupt_polling, hcd);
    915                 if (hcd->polling_fibril == 0) {
    916                         usb_log_error("Failed to create polling fibril\n");
    917                         ret = ENOMEM;
    918                         goto irq_unregister;
    919                 }
    920                 fibril_add_ready(hcd->polling_fibril);
    921                 usb_log_warning("Failed to enable interrupts: %s."
    922                     " Falling back to polling.\n", str_error(irq_ret));
    923         }
    924 
    925         /*
    926          * Creating root hub registers a new USB device so HC
    927          * needs to be ready at this time.
    928          */
    929         ret = hcd_ddf_setup_root_hub(device);
    930         if (ret != EOK) {
    931                 usb_log_error("Failed to setup HC root hub: %s.\n",
    932                     str_error(ret));
    933                 driver->fini(dev_to_hcd(device));
    934 irq_unregister:
    935                 /* Unregistering non-existent should be ok */
    936                 unregister_interrupt_handler(device, irq_cap);
    937                 hcd_ddf_clean_hc(device);
    938                 return ret;
    939         }
    940 
    941         usb_log_info("Controlling new `%s' device `%s'.\n",
    942             driver->name, ddf_dev_get_name(device));
    943         return EOK;
    944 }
    945525/**
    946526 * @}
  • uspace/lib/usbhost/src/endpoint.c

    rf5e5f73 rdf6ded8  
    11/*
    22 * Copyright (c) 2011 Jan Vesely
     3 * Copyright (c) 2018 Ondrej Hlavaty
    34 * All rights reserved.
    45 *
     
    3435 */
    3536
    36 #include <usb/host/endpoint.h>
    37 
    3837#include <assert.h>
     38#include <atomic.h>
     39#include <mem.h>
    3940#include <stdlib.h>
    40 #include <atomic.h>
    41 
    42 /** Allocate ad initialize endpoint_t structure.
    43  * @param address USB address.
    44  * @param endpoint USB endpoint number.
    45  * @param direction Communication direction.
    46  * @param type USB transfer type.
    47  * @param speed Communication speed.
    48  * @param max_packet_size Maximum size of data packets.
    49  * @param bw Required bandwidth.
    50  * @return Pointer to initialized endpoint_t structure, NULL on failure.
    51  */
    52 endpoint_t * endpoint_create(usb_address_t address, usb_endpoint_t endpoint,
    53     usb_direction_t direction, usb_transfer_type_t type, usb_speed_t speed,
    54     size_t max_packet_size, unsigned packets, size_t bw,
    55     usb_address_t tt_address, unsigned tt_p)
    56 {
    57         endpoint_t *instance = malloc(sizeof(endpoint_t));
    58         if (instance) {
    59                 atomic_set(&instance->refcnt, 0);
    60                 instance->address = address;
    61                 instance->endpoint = endpoint;
    62                 instance->direction = direction;
    63                 instance->transfer_type = type;
    64                 instance->speed = speed;
    65                 instance->max_packet_size = max_packet_size;
    66                 instance->packets = packets;
    67                 instance->bandwidth = bw;
    68                 instance->toggle = 0;
    69                 instance->active = false;
    70                 instance->tt.address = tt_address;
    71                 instance->tt.port = tt_p;
    72                 instance->hc_data.data = NULL;
    73                 instance->hc_data.toggle_get = NULL;
    74                 instance->hc_data.toggle_set = NULL;
    75                 link_initialize(&instance->link);
    76                 fibril_mutex_initialize(&instance->guard);
    77                 fibril_condvar_initialize(&instance->avail);
    78         }
    79         return instance;
    80 }
    81 
    82 /** Properly dispose of endpoint_t structure.
    83  * @param instance endpoint_t structure.
    84  */
    85 void endpoint_destroy(endpoint_t *instance)
    86 {
    87         assert(instance);
    88         assert(!instance->active);
    89         assert(instance->hc_data.data == NULL);
    90         free(instance);
    91 }
    92 
    93 void endpoint_add_ref(endpoint_t *instance)
    94 {
    95         atomic_inc(&instance->refcnt);
    96 }
    97 
    98 void endpoint_del_ref(endpoint_t *instance)
    99 {
    100         if (atomic_predec(&instance->refcnt) == 0)
    101                 endpoint_destroy(instance);
    102 }
    103 
    104 /** Set device specific data and hooks.
    105  * @param instance endpoint_t structure.
    106  * @param data device specific data.
    107  * @param toggle_get Hook to call when retrieving value of toggle bit.
    108  * @param toggle_set Hook to call when setting the value of toggle bit.
    109  */
    110 void endpoint_set_hc_data(endpoint_t *instance,
    111     void *data, int (*toggle_get)(void *), void (*toggle_set)(void *, int))
    112 {
    113         assert(instance);
    114         fibril_mutex_lock(&instance->guard);
    115         instance->hc_data.data = data;
    116         instance->hc_data.toggle_get = toggle_get;
    117         instance->hc_data.toggle_set = toggle_set;
    118         fibril_mutex_unlock(&instance->guard);
    119 }
    120 
    121 /** Clear device specific data and hooks.
    122  * @param instance endpoint_t structure.
    123  * @note This function does not free memory pointed to by data pointer.
    124  */
    125 void endpoint_clear_hc_data(endpoint_t *instance)
    126 {
    127         assert(instance);
    128         endpoint_set_hc_data(instance, NULL, NULL, NULL);
    129 }
    130 
    131 /** Mark the endpoint as active and block access for further fibrils.
    132  * @param instance endpoint_t structure.
    133  */
    134 void endpoint_use(endpoint_t *instance)
    135 {
    136         assert(instance);
    137         /* Add reference for active endpoint. */
    138         endpoint_add_ref(instance);
    139         fibril_mutex_lock(&instance->guard);
    140         while (instance->active)
    141                 fibril_condvar_wait(&instance->avail, &instance->guard);
    142         instance->active = true;
    143         fibril_mutex_unlock(&instance->guard);
    144 }
    145 
    146 /** Mark the endpoint as inactive and allow access for further fibrils.
    147  * @param instance endpoint_t structure.
    148  */
    149 void endpoint_release(endpoint_t *instance)
    150 {
    151         assert(instance);
    152         fibril_mutex_lock(&instance->guard);
    153         instance->active = false;
    154         fibril_mutex_unlock(&instance->guard);
    155         fibril_condvar_signal(&instance->avail);
    156         /* Drop reference for active endpoint. */
    157         endpoint_del_ref(instance);
    158 }
    159 
    160 /** Get the value of toggle bit.
    161  * @param instance endpoint_t structure.
    162  * @note Will use provided hook.
    163  */
    164 int endpoint_toggle_get(endpoint_t *instance)
    165 {
    166         assert(instance);
    167         fibril_mutex_lock(&instance->guard);
    168         if (instance->hc_data.toggle_get)
    169                 instance->toggle =
    170                     instance->hc_data.toggle_get(instance->hc_data.data);
    171         const int ret = instance->toggle;
    172         fibril_mutex_unlock(&instance->guard);
     41#include <str_error.h>
     42#include <usb/debug.h>
     43#include <usb/descriptor.h>
     44#include <usb/host/hcd.h>
     45#include <usb/host/utility.h>
     46
     47#include "usb_transfer_batch.h"
     48#include "bus.h"
     49
     50#include "endpoint.h"
     51
     52/**
     53 * Initialize provided endpoint structure.
     54 */
     55void endpoint_init(endpoint_t *ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
     56{
     57        memset(ep, 0, sizeof(endpoint_t));
     58
     59        assert(dev);
     60        ep->device = dev;
     61
     62        atomic_set(&ep->refcnt, 0);
     63        fibril_condvar_initialize(&ep->avail);
     64
     65        ep->endpoint = USB_ED_GET_EP(desc->endpoint);
     66        ep->direction = USB_ED_GET_DIR(desc->endpoint);
     67        ep->transfer_type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
     68        ep->max_packet_size = USB_ED_GET_MPS(desc->endpoint);
     69        ep->packets_per_uframe = USB_ED_GET_ADD_OPPS(desc->endpoint) + 1;
     70
     71        /** Direction both is our construct never present in descriptors */
     72        if (ep->transfer_type == USB_TRANSFER_CONTROL)
     73                ep->direction = USB_DIRECTION_BOTH;
     74
     75        ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
     76        ep->transfer_buffer_policy = DMA_POLICY_STRICT;
     77        ep->required_transfer_buffer_policy = DMA_POLICY_STRICT;
     78}
     79
     80/**
     81 * Get the bus endpoint belongs to.
     82 */
     83static inline const bus_ops_t *get_bus_ops(endpoint_t *ep)
     84{
     85        return ep->device->bus->ops;
     86}
     87
     88/**
     89 * Increase the reference count on endpoint.
     90 */
     91void endpoint_add_ref(endpoint_t *ep)
     92{
     93        atomic_inc(&ep->refcnt);
     94}
     95
     96/**
     97 * Call the desctruction callback. Default behavior is to free the memory directly.
     98 */
     99static inline void endpoint_destroy(endpoint_t *ep)
     100{
     101        const bus_ops_t *ops = get_bus_ops(ep);
     102        if (ops->endpoint_destroy) {
     103                ops->endpoint_destroy(ep);
     104        } else {
     105                assert(ep->active_batch == NULL);
     106
     107                /* Assume mostly the eps will be allocated by malloc. */
     108                free(ep);
     109        }
     110}
     111
     112/**
     113 * Decrease the reference count.
     114 */
     115void endpoint_del_ref(endpoint_t *ep)
     116{
     117        if (atomic_predec(&ep->refcnt) == 0) {
     118                endpoint_destroy(ep);
     119        }
     120}
     121
     122/**
     123 * Mark the endpoint as online. Supply a guard to be used for this endpoint
     124 * synchronization.
     125 */
     126void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
     127{
     128        ep->guard = guard;
     129        ep->online = true;
     130}
     131
     132/**
     133 * Mark the endpoint as offline. All other fibrils waiting to activate this
     134 * endpoint will be interrupted.
     135 */
     136void endpoint_set_offline_locked(endpoint_t *ep)
     137{
     138        assert(ep);
     139        assert(fibril_mutex_is_locked(ep->guard));
     140
     141        ep->online = false;
     142        fibril_condvar_broadcast(&ep->avail);
     143}
     144
     145/**
     146 * Wait until a transfer finishes. Can be used even when the endpoint is
     147 * offline (and is interrupted by the endpoint going offline).
     148 */
     149void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
     150{
     151        assert(ep);
     152        assert(fibril_mutex_is_locked(ep->guard));
     153
     154        if (ep->active_batch == NULL)
     155                return;
     156
     157        fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
     158}
     159
     160/**
     161 * Mark the endpoint as active and block access for further fibrils. If the
     162 * endpoint is already active, it will block on ep->avail condvar.
     163 *
     164 * Call only under endpoint guard. After you activate the endpoint and release
     165 * the guard, you must assume that particular transfer is already
     166 * finished/aborted.
     167 *
     168 * Activation and deactivation is not done by the library to maximize
     169 * performance. The HC might want to prepare some memory buffers prior to
     170 * interfering with other world.
     171 *
     172 * @param batch Transfer batch this endpoint is blocked by.
     173 */
     174int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
     175{
     176        assert(ep);
     177        assert(batch);
     178        assert(batch->ep == ep);
     179        assert(ep->guard);
     180        assert(fibril_mutex_is_locked(ep->guard));
     181
     182        while (ep->online && ep->active_batch != NULL)
     183                fibril_condvar_wait(&ep->avail, ep->guard);
     184
     185        if (!ep->online)
     186                return EINTR;
     187
     188        assert(ep->active_batch == NULL);
     189        ep->active_batch = batch;
     190        return EOK;
     191}
     192
     193/**
     194 * Mark the endpoint as inactive and allow access for further fibrils.
     195 */
     196void endpoint_deactivate_locked(endpoint_t *ep)
     197{
     198        assert(ep);
     199        assert(fibril_mutex_is_locked(ep->guard));
     200
     201        ep->active_batch = NULL;
     202        fibril_condvar_signal(&ep->avail);
     203}
     204
     205/**
     206 * Initiate a transfer on an endpoint. Creates a transfer batch, checks the
     207 * bandwidth requirements and schedules the batch.
     208 *
     209 * @param endpoint Endpoint for which to send the batch
     210 */
     211errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req)
     212{
     213        assert(ep);
     214        assert(req);
     215
     216        if (ep->transfer_type == USB_TRANSFER_CONTROL) {
     217                usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name,
     218                    req->target.address, req->target.endpoint,
     219                    req->size, ep->max_packet_size,
     220                    req->setup);
     221        } else {
     222                usb_log_debug("%s %d:%d %zu/%zuB", req->name,
     223                    req->target.address, req->target.endpoint,
     224                    req->size, ep->max_packet_size);
     225        }
     226
     227        device_t * const device = ep->device;
     228        if (!device) {
     229                usb_log_warning("Endpoint detached");
     230                return EAGAIN;
     231        }
     232
     233        const bus_ops_t *ops = device->bus->ops;
     234        if (!ops->batch_schedule) {
     235                usb_log_error("HCD does not implement scheduler.");
     236                return ENOTSUP;
     237        }
     238
     239        size_t size = req->size;
     240        /*
     241         * Limit transfers with reserved bandwidth to the amount reserved.
     242         * OUT transfers are rejected, IN can be just trimmed in advance.
     243         */
     244        if (size > ep->max_transfer_size &&
     245            (ep->transfer_type == USB_TRANSFER_INTERRUPT
     246             || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) {
     247                if (req->dir == USB_DIRECTION_OUT)
     248                        return ENOSPC;
     249                else
     250                        size = ep->max_transfer_size;
     251        }
     252
     253        /* Offline devices don't schedule transfers other than on EP0. */
     254        if (!device->online && ep->endpoint > 0)
     255                return EAGAIN;
     256
     257        usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
     258        if (!batch) {
     259                usb_log_error("Failed to create transfer batch.");
     260                return ENOMEM;
     261        }
     262
     263        batch->target = req->target;
     264        batch->setup.packed = req->setup;
     265        batch->dir = req->dir;
     266        batch->size = size;
     267        batch->offset = req->offset;
     268        batch->dma_buffer = req->buffer;
     269
     270        dma_buffer_acquire(&batch->dma_buffer);
     271
     272        if (batch->offset != 0) {
     273                usb_log_debug("A transfer with nonzero offset requested.");
     274                usb_transfer_batch_bounce(batch);
     275        }
     276
     277        if (usb_transfer_batch_bounce_required(batch))
     278                usb_transfer_batch_bounce(batch);
     279
     280        batch->on_complete = req->on_complete;
     281        batch->on_complete_data = req->arg;
     282
     283        const int ret = ops->batch_schedule(batch);
     284        if (ret != EOK) {
     285                usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
     286                usb_transfer_batch_destroy(batch);
     287        }
     288
    173289        return ret;
    174290}
    175291
    176 /** Set the value of toggle bit.
    177  * @param instance endpoint_t structure.
    178  * @note Will use provided hook.
    179  */
    180 void endpoint_toggle_set(endpoint_t *instance, int toggle)
    181 {
    182         assert(instance);
    183         assert(toggle == 0 || toggle == 1);
    184         fibril_mutex_lock(&instance->guard);
    185         instance->toggle = toggle;
    186         if (instance->hc_data.toggle_set)
    187                 instance->hc_data.toggle_set(instance->hc_data.data, toggle);
    188         fibril_mutex_unlock(&instance->guard);
    189 }
    190 
    191292/**
    192293 * @}
  • uspace/lib/usbhost/src/hcd.c

    rf5e5f73 rdf6ded8  
    11/*
    22 * Copyright (c) 2011 Jan Vesely
     3 * Copyright (c) 2018 Ondrej Hlavaty
    34 * All rights reserved.
    45 *
     
    3233/** @file
    3334 *
    34  */
    35 
    36 #include <usb/debug.h>
    37 #include <usb/request.h>
     35 * Host controller driver framework. Encapsulates DDF device of HC to an
     36 * hc_device_t, which is passed to driver implementing hc_driver_t.
     37 */
    3838
    3939#include <assert.h>
    4040#include <async.h>
     41#include <ddf/interrupt.h>
    4142#include <errno.h>
     43#include <macros.h>
     44#include <str_error.h>
     45#include <usb/debug.h>
     46#include <usb/descriptor.h>
     47#include <usb/request.h>
    4248#include <usb_iface.h>
    4349
     50#include "bus.h"
     51#include "ddf_helpers.h"
     52#include "endpoint.h"
     53#include "usb_transfer_batch.h"
     54
    4455#include "hcd.h"
    4556
    46 /** Calls ep_add_hook upon endpoint registration.
    47  * @param ep Endpoint to be registered.
    48  * @param arg hcd_t in disguise.
    49  * @return Error code.
    50  */
    51 static errno_t register_helper(endpoint_t *ep, void *arg)
    52 {
    53         hcd_t *hcd = arg;
    54         assert(ep);
     57int hc_dev_add(ddf_dev_t *);
     58int hc_dev_remove(ddf_dev_t *);
     59int hc_dev_gone(ddf_dev_t *);
     60int hc_fun_online(ddf_fun_t *);
     61int hc_fun_offline(ddf_fun_t *);
     62
     63static driver_ops_t hc_driver_ops = {
     64        .dev_add = hc_dev_add,
     65        .dev_remove = hc_dev_remove,
     66        .dev_gone = hc_dev_gone,
     67        .fun_online = hc_fun_online,
     68        .fun_offline = hc_fun_offline,
     69};
     70
     71static const hc_driver_t *hc_driver;
     72
     73/**
     74 * The main HC driver routine.
     75 */
     76int hc_driver_main(const hc_driver_t *driver)
     77{
     78        driver_t ddf_driver = {
     79                .name = driver->name,
     80                .driver_ops = &hc_driver_ops,
     81        };
     82
     83        /* Remember ops to call. */
     84        hc_driver = driver;
     85
     86        return ddf_driver_main(&ddf_driver);
     87}
     88
     89/**
     90 * IRQ handling callback. Call the bus operation.
     91 *
     92 * Currently, there is a bus ops lookup to find the interrupt handler. So far,
     93 * the mechanism is too flexible, as it allows different instances of HC to
     94 * have different IRQ handlers, disallowing us to optimize the lookup here.
     95 * TODO: Make the bus mechanism less flexible in irq handling and remove the
     96 * lookup.
     97 */
     98static void irq_handler(ipc_call_t *call, ddf_dev_t *dev)
     99{
     100        assert(dev);
     101        hc_device_t *hcd = dev_to_hcd(dev);
     102
     103        const uint32_t status = IPC_GET_ARG1(*call);
     104        hcd->bus->ops->interrupt(hcd->bus, status);
     105}
     106
     107/**
     108 * Worker for the HW interrupt replacement fibril.
     109 */
     110static errno_t interrupt_polling(void *arg)
     111{
     112        bus_t *bus = arg;
     113        assert(bus);
     114
     115        if (!bus->ops->interrupt || !bus->ops->status)
     116                return ENOTSUP;
     117
     118        uint32_t status = 0;
     119        while (bus->ops->status(bus, &status) == EOK) {
     120                bus->ops->interrupt(bus, status);
     121                status = 0;
     122                /* We should wait 1 frame - 1ms here, but this polling is a
     123                 * lame crutch anyway so don't hog the system. 10ms is still
     124                 * good enough for emergency mode */
     125                async_usleep(10000);
     126        }
     127        return EOK;
     128}
     129
     130/**
     131 * Clean the IRQ code bottom-half.
     132 */
     133static inline void irq_code_clean(irq_code_t *code)
     134{
     135        if (code) {
     136                free(code->ranges);
     137                free(code->cmds);
     138                code->ranges = NULL;
     139                code->cmds = NULL;
     140                code->rangecount = 0;
     141                code->cmdcount = 0;
     142        }
     143}
     144
     145/**
     146 * Register an interrupt handler. If there is a callback to setup the bottom half,
     147 * invoke it and register it. Register for notifications.
     148 *
     149 * If this method fails, a polling fibril is started instead.
     150 *
     151 * @param[in] hcd Host controller device.
     152 * @param[in] hw_res Resources to be used.
     153 *
     154 * @return IRQ capability handle on success.
     155 * @return Negative error code.
     156 */
     157static errno_t hcd_ddf_setup_interrupts(hc_device_t *hcd,
     158    const hw_res_list_parsed_t *hw_res)
     159{
    55160        assert(hcd);
    56         if (hcd->ops.ep_add_hook)
    57                 return hcd->ops.ep_add_hook(hcd, ep);
     161        irq_code_t irq_code = { 0 };
     162
     163        if (!hc_driver->irq_code_gen)
     164                return ENOTSUP;
     165
     166        int irq;
     167        errno_t ret;
     168        ret = hc_driver->irq_code_gen(&irq_code, hcd, hw_res, &irq);
     169        if (ret != EOK) {
     170                usb_log_error("Failed to generate IRQ code: %s.",
     171                    str_error(irq));
     172                return irq;
     173        }
     174
     175        /* Register handler to avoid interrupt lockup */
     176        int irq_cap;
     177        ret = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler,
     178            &irq_code, &irq_cap);
     179        irq_code_clean(&irq_code);
     180        if (ret != EOK) {
     181                usb_log_error("Failed to register interrupt handler: %s.",
     182                    str_error(irq_cap));
     183                return irq_cap;
     184        }
     185
     186        /* Enable interrupts */
     187        ret = hcd_ddf_enable_interrupt(hcd, irq);
     188        if (ret != EOK) {
     189                usb_log_error("Failed to enable interrupts: %s.",
     190                    str_error(ret));
     191                unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
     192                return ret;
     193        }
     194        return irq_cap;
     195}
     196
     197/**
     198 * Initialize HC in memory of the driver.
     199 *
     200 * This function does all the preparatory work for hc and rh drivers:
     201 *  - gets device's hw resources
     202 *  - attempts to enable interrupts
     203 *  - registers interrupt handler
     204 *  - calls driver specific initialization
     205 *  - registers root hub
     206 *
     207 * @param device DDF instance of the device to use
     208 * @return Error code
     209 */
     210errno_t hc_dev_add(ddf_dev_t *device)
     211{
     212        errno_t ret = EOK;
     213        assert(device);
     214
     215        if (!hc_driver->hc_add) {
     216                usb_log_error("Driver '%s' does not support adding devices.",
     217                    hc_driver->name);
     218                return ENOTSUP;
     219        }
     220
     221        ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
     222        if (ret != EOK) {
     223                usb_log_error("Failed to setup HC device.");
     224                return ret;
     225        }
     226
     227        hc_device_t *hcd = dev_to_hcd(device);
     228
     229        hw_res_list_parsed_t hw_res;
     230        ret = hcd_ddf_get_registers(hcd, &hw_res);
     231        if (ret != EOK) {
     232                usb_log_error("Failed to get register memory addresses "
     233                    "for `%s': %s.", ddf_dev_get_name(device),
     234                    str_error(ret));
     235                goto err_hcd;
     236        }
     237
     238        ret = hc_driver->hc_add(hcd, &hw_res);
     239        if (ret != EOK) {
     240                usb_log_error("Failed to init HCD.");
     241                goto err_hw_res;
     242        }
     243
     244        assert(hcd->bus);
     245
     246        /* Setup interrupts  */
     247        hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
     248        if (hcd->irq_cap >= 0) {
     249                usb_log_debug("Hw interrupts enabled.");
     250        }
     251
     252        /* Claim the device from BIOS */
     253        if (hc_driver->claim)
     254                ret = hc_driver->claim(hcd);
     255        if (ret != EOK) {
     256                usb_log_error("Failed to claim `%s' for `%s': %s",
     257                    ddf_dev_get_name(device), hc_driver->name, str_error(ret));
     258                goto err_irq;
     259        }
     260
     261        /* Start hw */
     262        if (hc_driver->start)
     263                ret = hc_driver->start(hcd);
     264        if (ret != EOK) {
     265                usb_log_error("Failed to start HCD: %s.", str_error(ret));
     266                goto err_irq;
     267        }
     268
     269        const bus_ops_t *ops = hcd->bus->ops;
     270
     271        /* Need working irq replacement to setup root hub */
     272        if (hcd->irq_cap < 0 && ops->status) {
     273                hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
     274                if (!hcd->polling_fibril) {
     275                        usb_log_error("Failed to create polling fibril");
     276                        ret = ENOMEM;
     277                        goto err_started;
     278                }
     279                fibril_add_ready(hcd->polling_fibril);
     280                usb_log_warning("Failed to enable interrupts: %s."
     281                    " Falling back to polling.", str_error(hcd->irq_cap));
     282        }
     283
     284        /*
     285         * Creating root hub registers a new USB device so HC
     286         * needs to be ready at this time.
     287         */
     288        if (hc_driver->setup_root_hub)
     289                ret = hc_driver->setup_root_hub(hcd);
     290        if (ret != EOK) {
     291                usb_log_error("Failed to setup HC root hub: %s.",
     292                    str_error(ret));
     293                goto err_polling;
     294        }
     295
     296        usb_log_info("Controlling new `%s' device `%s'.",
     297            hc_driver->name, ddf_dev_get_name(device));
    58298        return EOK;
    59 }
    60 
    61 /** Calls ep_remove_hook upon endpoint removal.
    62  * @param ep Endpoint to be unregistered.
    63  * @param arg hcd_t in disguise.
    64  */
    65 static void unregister_helper(endpoint_t *ep, void *arg)
    66 {
    67         hcd_t *hcd = arg;
    68         assert(ep);
    69         assert(hcd);
    70         if (hcd->ops.ep_remove_hook)
    71                 hcd->ops.ep_remove_hook(hcd, ep);
    72 }
    73 
    74 /** Calls ep_remove_hook upon endpoint removal. Prints warning.
    75  *  * @param ep Endpoint to be unregistered.
    76  *   * @param arg hcd_t in disguise.
    77  *    */
    78 static void unregister_helper_warn(endpoint_t *ep, void *arg)
    79 {
    80         assert(ep);
    81         usb_log_warning("Endpoint %d:%d %s was left behind, removing.\n",
    82             ep->address, ep->endpoint, usb_str_direction(ep->direction));
    83         unregister_helper(ep, arg);
    84 }
    85 
    86 
    87 /** Initialize hcd_t structure.
    88  * Initializes device and endpoint managers. Sets data and hook pointer to NULL.
    89  *
    90  * @param hcd hcd_t structure to initialize, non-null.
    91  * @param max_speed Maximum supported USB speed (full, high).
    92  * @param bandwidth Available bandwidth, passed to endpoint manager.
    93  * @param bw_count Bandwidth compute function, passed to endpoint manager.
    94  */
    95 void hcd_init(hcd_t *hcd, usb_speed_t max_speed, size_t bandwidth,
    96     bw_count_func_t bw_count)
    97 {
    98         assert(hcd);
    99         usb_bus_init(&hcd->bus, bandwidth, bw_count, max_speed);
    100 
    101         hcd_set_implementation(hcd, NULL, NULL);
    102 }
    103 
    104 errno_t hcd_request_address(hcd_t *hcd, usb_speed_t speed, usb_address_t *address)
    105 {
    106         assert(hcd);
    107         return usb_bus_request_address(&hcd->bus, address, false, speed);
    108 }
    109 
    110 errno_t hcd_release_address(hcd_t *hcd, usb_address_t address)
    111 {
    112         assert(hcd);
    113         return usb_bus_remove_address(&hcd->bus, address,
    114             unregister_helper_warn, hcd);
    115 }
    116 
    117 errno_t hcd_reserve_default_address(hcd_t *hcd, usb_speed_t speed)
    118 {
    119         assert(hcd);
    120         usb_address_t address = 0;
    121         return usb_bus_request_address(&hcd->bus, &address, true, speed);
    122 }
    123 
    124 errno_t hcd_add_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir,
    125     usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
    126     size_t size, usb_address_t tt_address, unsigned tt_port)
    127 {
    128         assert(hcd);
    129         return usb_bus_add_ep(&hcd->bus, target.address,
    130             target.endpoint, dir, type, max_packet_size, packets, size,
    131             register_helper, hcd, tt_address, tt_port);
    132 }
    133 
    134 errno_t hcd_remove_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir)
    135 {
    136         assert(hcd);
    137         return usb_bus_remove_ep(&hcd->bus, target.address,
    138             target.endpoint, dir, unregister_helper, hcd);
    139 }
    140 
    141 
    142 typedef struct {
    143         void *original_data;
    144         usbhc_iface_transfer_out_callback_t original_callback;
    145         usb_target_t target;
    146         hcd_t *hcd;
    147 } toggle_t;
    148 
    149 static void toggle_reset_callback(errno_t retval, void *arg)
    150 {
    151         assert(arg);
    152         toggle_t *toggle = arg;
    153         if (retval == EOK) {
    154                 usb_log_debug2("Reseting toggle on %d:%d.\n",
    155                     toggle->target.address, toggle->target.endpoint);
    156                 usb_bus_reset_toggle(&toggle->hcd->bus,
    157                     toggle->target, toggle->target.endpoint == 0);
    158         }
    159 
    160         toggle->original_callback(retval, toggle->original_data);
    161 }
    162 
    163 /** Prepare generic usb_transfer_batch and schedule it.
    164  * @param hcd Host controller driver.
    165  * @param fun DDF fun
    166  * @param target address and endpoint number.
    167  * @param setup_data Data to use in setup stage (Control communication type)
    168  * @param in Callback for device to host communication.
    169  * @param out Callback for host to device communication.
    170  * @param arg Callback parameter.
    171  * @param name Communication identifier (for nicer output).
    172  * @return Error code.
    173  */
    174 errno_t hcd_send_batch(
    175     hcd_t *hcd, usb_target_t target, usb_direction_t direction,
    176     void *data, size_t size, uint64_t setup_data,
    177     usbhc_iface_transfer_in_callback_t in,
    178     usbhc_iface_transfer_out_callback_t out, void *arg, const char* name)
    179 {
    180         assert(hcd);
    181 
    182         endpoint_t *ep = usb_bus_find_ep(&hcd->bus,
    183             target.address, target.endpoint, direction);
    184         if (ep == NULL) {
    185                 usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
    186                     target.address, target.endpoint, name);
    187                 return ENOENT;
    188         }
    189 
    190         usb_log_debug2("%s %d:%d %zu(%zu).\n",
    191             name, target.address, target.endpoint, size, ep->max_packet_size);
    192 
    193         const size_t bw = bandwidth_count_usb11(
    194             ep->speed, ep->transfer_type, size, ep->max_packet_size);
    195         /* Check if we have enough bandwidth reserved */
    196         if (ep->bandwidth < bw) {
    197                 usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
    198                     "but only %zu is reserved.\n",
    199                     ep->address, ep->endpoint, name, bw, ep->bandwidth);
    200                 return ENOSPC;
    201         }
    202         if (!hcd->ops.schedule) {
    203                 usb_log_error("HCD does not implement scheduler.\n");
    204                 return ENOTSUP;
    205         }
    206 
    207         /* Check for commands that reset toggle bit */
    208         if (ep->transfer_type == USB_TRANSFER_CONTROL) {
    209                 const int reset_toggle = usb_request_needs_toggle_reset(
    210                     (usb_device_request_setup_packet_t *) &setup_data);
    211                 if (reset_toggle >= 0) {
    212                         assert(out);
    213                         toggle_t *toggle = malloc(sizeof(toggle_t));
    214                         if (!toggle)
    215                                 return ENOMEM;
    216                         toggle->target.address = target.address;
    217                         toggle->target.endpoint = reset_toggle;
    218                         toggle->original_callback = out;
    219                         toggle->original_data = arg;
    220                         toggle->hcd = hcd;
    221 
    222                         arg = toggle;
    223                         out = toggle_reset_callback;
    224                 }
    225         }
    226 
    227         usb_transfer_batch_t *batch = usb_transfer_batch_create(
    228             ep, data, size, setup_data, in, out, arg);
    229         if (!batch) {
    230                 usb_log_error("Failed to create transfer batch.\n");
    231                 return ENOMEM;
    232         }
    233 
    234         const errno_t ret = hcd->ops.schedule(hcd, batch);
    235         if (ret != EOK)
    236                 usb_transfer_batch_destroy(batch);
    237 
    238         /* Drop our own reference to ep. */
    239         endpoint_del_ref(ep);
    240 
     299
     300err_polling:
     301        // TODO: Stop the polling fibril (refactor the interrupt_polling func)
     302        //
     303err_started:
     304        if (hc_driver->stop)
     305                hc_driver->stop(hcd);
     306err_irq:
     307        unregister_interrupt_handler(device, hcd->irq_cap);
     308        if (hc_driver->hc_remove)
     309                hc_driver->hc_remove(hcd);
     310err_hw_res:
     311        hw_res_list_parsed_clean(&hw_res);
     312err_hcd:
     313        hcd_ddf_clean_hc(hcd);
    241314        return ret;
    242315}
    243316
    244 typedef struct {
    245         volatile unsigned done;
    246         errno_t ret;
    247         size_t size;
    248 } sync_data_t;
    249 
    250 static void transfer_in_cb(errno_t ret, size_t size, void* data)
    251 {
    252         sync_data_t *d = data;
    253         assert(d);
    254         d->ret = ret;
    255         d->done = 1;
    256         d->size = size;
    257 }
    258 
    259 static void transfer_out_cb(errno_t ret, void* data)
    260 {
    261         sync_data_t *d = data;
    262         assert(data);
    263         d->ret = ret;
    264         d->done = 1;
    265 }
    266 
    267 /** this is really ugly version of sync usb communication */
    268 errno_t hcd_send_batch_sync(
    269     hcd_t *hcd, usb_target_t target, usb_direction_t dir,
    270     void *data, size_t size, uint64_t setup_data, const char* name, size_t *out_size)
    271 {
    272         assert(hcd);
    273         sync_data_t sd = { .done = 0, .ret = EBUSY, .size = size };
    274 
    275         const errno_t ret = hcd_send_batch(hcd, target, dir, data, size, setup_data,
    276             dir == USB_DIRECTION_IN ? transfer_in_cb : NULL,
    277             dir == USB_DIRECTION_OUT ? transfer_out_cb : NULL, &sd, name);
    278         if (ret != EOK)
    279                 return ret;
    280 
    281         while (!sd.done) {
    282                 async_usleep(1000);
    283         }
    284 
    285         if (sd.ret == EOK)
    286                 *out_size = sd.size;
    287         return sd.ret;
     317errno_t hc_dev_remove(ddf_dev_t *dev)
     318{
     319        errno_t err;
     320        hc_device_t *hcd = dev_to_hcd(dev);
     321
     322        if (hc_driver->stop)
     323                if ((err = hc_driver->stop(hcd)))
     324                        return err;
     325
     326        unregister_interrupt_handler(dev, hcd->irq_cap);
     327
     328        if (hc_driver->hc_remove)
     329                if ((err = hc_driver->hc_remove(hcd)))
     330                        return err;
     331
     332        hcd_ddf_clean_hc(hcd);
     333
     334        // TODO probably not complete
     335
     336        return EOK;
     337}
     338
     339errno_t hc_dev_gone(ddf_dev_t *dev)
     340{
     341        errno_t err = ENOTSUP;
     342        hc_device_t *hcd = dev_to_hcd(dev);
     343
     344        if (hc_driver->hc_gone)
     345                err = hc_driver->hc_gone(hcd);
     346
     347        hcd_ddf_clean_hc(hcd);
     348
     349        return err;
     350}
     351
     352errno_t hc_fun_online(ddf_fun_t *fun)
     353{
     354        assert(fun);
     355
     356        device_t *dev = ddf_fun_data_get(fun);
     357        assert(dev);
     358
     359        usb_log_info("Device(%d): Requested to be brought online.", dev->address);
     360        return bus_device_online(dev);
     361}
     362
     363int hc_fun_offline(ddf_fun_t *fun)
     364{
     365        assert(fun);
     366
     367        device_t *dev = ddf_fun_data_get(fun);
     368        assert(dev);
     369
     370        usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
     371        return bus_device_offline(dev);
    288372}
    289373
  • uspace/lib/usbhost/src/usb_transfer_batch.c

    rf5e5f73 rdf6ded8  
    11/*
    22 * Copyright (c) 2011 Jan Vesely
     3 * Copyright (c) 2018 Ondrej Hlavaty
    34 * All rights reserved.
    45 *
     
    3334 */
    3435
    35 #include <usb/host/usb_transfer_batch.h>
    36 #include <usb/debug.h>
    37 
    3836#include <assert.h>
    3937#include <errno.h>
    40 #include <macros.h>
    41 #include <mem.h>
    4238#include <stdlib.h>
    43 #include <usbhc_iface.h>
    44 
    45 /** Allocate and initialize usb_transfer_batch structure.
    46  * @param ep endpoint used by the transfer batch.
    47  * @param buffer data to send/recieve.
    48  * @param buffer_size Size of data buffer.
    49  * @param setup_buffer Data to send in SETUP stage of control transfer.
    50  * @param func_in callback on IN transfer completion.
    51  * @param func_out callback on OUT transfer completion.
    52  * @param fun DDF function (passed to callback function).
    53  * @param arg Argument to pass to the callback function.
    54  * @param private_data driver specific per batch data.
    55  * @param private_data_dtor Function to properly destroy private_data.
    56  * @return Pointer to valid usb_transfer_batch_t structure, NULL on failure.
    57  */
    58 usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep, char *buffer,
    59     size_t buffer_size,
    60     uint64_t setup_buffer,
    61     usbhc_iface_transfer_in_callback_t func_in,
    62     usbhc_iface_transfer_out_callback_t func_out,
    63     void *arg)
    64 {
    65         if (func_in == NULL && func_out == NULL)
    66                 return NULL;
    67         if (func_in != NULL && func_out != NULL)
    68                 return NULL;
    69 
    70         usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
    71         if (instance) {
    72                 instance->ep = ep;
    73                 instance->callback_in = func_in;
    74                 instance->callback_out = func_out;
    75                 instance->arg = arg;
    76                 instance->buffer = buffer;
    77                 instance->buffer_size = buffer_size;
    78                 instance->setup_size = 0;
    79                 instance->transfered_size = 0;
    80                 instance->error = EOK;
    81                 if (ep && ep->transfer_type == USB_TRANSFER_CONTROL) {
    82                         memcpy(instance->setup_buffer, &setup_buffer,
    83                             USB_SETUP_PACKET_SIZE);
    84                         instance->setup_size = USB_SETUP_PACKET_SIZE;
     39#include <str_error.h>
     40#include <usb/debug.h>
     41
     42#include "endpoint.h"
     43#include "bus.h"
     44
     45#include "usb_transfer_batch.h"
     46
     47/**
     48 * Create a batch on a given endpoint.
     49 *
     50 * If the bus callback is not defined, it just creates a default batch.
     51 */
     52usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep)
     53{
     54        assert(ep);
     55
     56        bus_t *bus = endpoint_get_bus(ep);
     57
     58        if (!bus->ops->batch_create) {
     59                usb_transfer_batch_t *batch = calloc(1, sizeof(usb_transfer_batch_t));
     60                if (!batch)
     61                        return NULL;
     62                usb_transfer_batch_init(batch, ep);
     63                return batch;
     64        }
     65
     66        return bus->ops->batch_create(ep);
     67}
     68
     69/**
     70 * Initialize given batch structure.
     71 */
     72void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
     73{
     74        assert(ep);
     75        /* Batch reference */
     76        endpoint_add_ref(ep);
     77        batch->ep = ep;
     78}
     79
     80/**
     81 * Destroy the batch. If there's no bus callback, just free it.
     82 */
     83void usb_transfer_batch_destroy(usb_transfer_batch_t *batch)
     84{
     85        assert(batch);
     86        assert(batch->ep);
     87
     88        bus_t *bus = endpoint_get_bus(batch->ep);
     89        endpoint_t *ep = batch->ep;
     90
     91        if (bus->ops) {
     92                usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " destroying.",
     93                    batch, USB_TRANSFER_BATCH_ARGS(*batch));
     94                bus->ops->batch_destroy(batch);
     95        }
     96        else {
     97                usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.",
     98                    batch, USB_TRANSFER_BATCH_ARGS(*batch));
     99                free(batch);
     100        }
     101
     102        /* Batch reference */
     103        endpoint_del_ref(ep);
     104}
     105
     106bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *batch)
     107{
     108        if (!batch->size)
     109                return false;
     110
     111        unsigned flags = batch->dma_buffer.policy & DMA_POLICY_FLAGS_MASK;
     112        unsigned required_flags =
     113            batch->ep->required_transfer_buffer_policy & DMA_POLICY_FLAGS_MASK;
     114
     115        if (required_flags & ~flags)
     116                return true;
     117
     118        size_t chunk_mask = dma_policy_chunk_mask(batch->dma_buffer.policy);
     119        size_t required_chunk_mask =
     120             dma_policy_chunk_mask(batch->ep->required_transfer_buffer_policy);
     121
     122        /* If the chunks are at least as large as required, we're good */
     123        if ((required_chunk_mask & ~chunk_mask) == 0)
     124                return false;
     125
     126        size_t start_chunk = batch->offset & ~chunk_mask;
     127        size_t end_chunk = (batch->offset + batch->size - 1) & ~chunk_mask;
     128
     129        /* The requested area crosses a chunk boundary */
     130        if (start_chunk != end_chunk)
     131                return true;
     132
     133        return false;
     134}
     135
     136errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *batch)
     137{
     138        assert(batch);
     139        assert(!batch->is_bounced);
     140
     141        dma_buffer_release(&batch->dma_buffer);
     142
     143        batch->original_buffer = batch->dma_buffer.virt + batch->offset;
     144
     145        usb_log_debug("Batch(%p): Buffer cannot be used directly, "
     146            "falling back to bounce buffer!", batch);
     147
     148        const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer,
     149            batch->size, batch->ep->transfer_buffer_policy);
     150        if (err)
     151                return err;
     152
     153        /* Copy the data out */
     154        if (batch->dir == USB_DIRECTION_OUT)
     155                memcpy(batch->dma_buffer.virt,
     156                    batch->original_buffer,
     157                    batch->size);
     158
     159        batch->is_bounced = true;
     160        batch->offset = 0;
     161
     162        return err;
     163}
     164
     165/**
     166 * Finish a transfer batch: call handler, destroy batch, release endpoint.
     167 *
     168 * Call only after the batch have been scheduled && completed!
     169 */
     170void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
     171{
     172        assert(batch);
     173        assert(batch->ep);
     174
     175        usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.",
     176            batch, USB_TRANSFER_BATCH_ARGS(*batch));
     177
     178        if (batch->error == EOK && batch->size > 0) {
     179                if (batch->is_bounced) {
     180                        /* We we're forced to use bounce buffer, copy it back */
     181                        if (batch->dir == USB_DIRECTION_IN)
     182                        memcpy(batch->original_buffer,
     183                            batch->dma_buffer.virt,
     184                            batch->transferred_size);
     185
     186                        dma_buffer_free(&batch->dma_buffer);
    85187                }
    86                 if (instance->ep)
    87                         endpoint_use(instance->ep);
    88         }
    89         return instance;
    90 }
    91 
    92 /** Correctly dispose all used data structures.
    93  *
    94  * @param[in] instance Batch structure to use.
    95  */
    96 void usb_transfer_batch_destroy(usb_transfer_batch_t *instance)
    97 {
    98         if (!instance)
    99                 return;
    100         usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
    101             instance, USB_TRANSFER_BATCH_ARGS(*instance));
    102         if (instance->ep) {
    103                 endpoint_release(instance->ep);
    104         }
    105         free(instance);
    106 }
    107 
    108 /** Prepare data and call the right callback.
    109  *
    110  * @param[in] instance Batch structure to use.
    111  * @param[in] data Data to copy to the output buffer.
    112  * @param[in] size Size of @p data.
    113  * @param[in] error Error value to use.
    114  */
    115 void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
    116     const void *data, size_t size, errno_t error)
    117 {
    118         assert(instance);
    119         usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
    120             instance, USB_TRANSFER_BATCH_ARGS(*instance));
    121 
    122         /* NOTE: Only one of these pointers should be set. */
    123         if (instance->callback_out) {
    124                 instance->callback_out(error, instance->arg);
    125         }
    126 
    127         if (instance->callback_in) {
    128                 /* We care about the data and there are some to copy */
    129                 const size_t safe_size = min(size, instance->buffer_size);
    130                 if (data) {
    131                         memcpy(instance->buffer, data, safe_size);
     188                else {
     189                        dma_buffer_release(&batch->dma_buffer);
    132190                }
    133                 instance->callback_in(error, safe_size, instance->arg);
    134         }
    135 }
     191        }
     192
     193        if (batch->on_complete) {
     194                const int err = batch->on_complete(batch->on_complete_data, batch->error, batch->transferred_size);
     195                if (err)
     196                        usb_log_warning("Batch %p failed to complete: %s",
     197                            batch, str_error(err));
     198        }
     199
     200        usb_transfer_batch_destroy(batch);
     201}
     202
    136203/**
    137204 * @}
Note: See TracChangeset for help on using the changeset viewer.