Changeset 32fb6bce in mainline for uspace/lib/usbhost/src/hcd.c


Ignore:
Timestamp:
2017-12-18T22:50:21Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
7f70d1c
Parents:
1ea0bbf
git-author:
Ondřej Hlavatý <aearsis@…> (2017-12-18 22:04:50)
git-committer:
Ondřej Hlavatý <aearsis@…> (2017-12-18 22:50:21)
Message:

usbhost: refactoring

This commit moves interrupt, status and schedule to bus
operations. Then the purpose of hcd_t is better defined, and split into
hc_driver_t and hc_device_t. hc_driver_t is used to wrap driver
implementation by the library (similar to how usb_driver_t is used to
wrap usb device drivers). hc_device_t is used as a parent for hc_t
inside drivers, and is allocated inside the DDF device node.

To support these changes, some local identifiers were renamed, some
functions were moved and/or renamed and their arguments changed. The
most notable one being hcd_send_batch → bus_device_send_batch.

File:
1 edited

Legend:

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

    r1ea0bbf r32fb6bce  
    3636#include <assert.h>
    3737#include <async.h>
     38#include <ddf/interrupt.h>
    3839#include <errno.h>
    3940#include <macros.h>
     
    4546
    4647#include "bus.h"
     48#include "ddf_helpers.h"
    4749#include "endpoint.h"
    4850#include "usb_transfer_batch.h"
     
    5052#include "hcd.h"
    5153
    52 
    53 /** Initialize hcd_t structure.
    54  * Initializes device and endpoint managers. Sets data and hook pointer to NULL.
    55  *
    56  * @param hcd hcd_t structure to initialize, non-null.
    57  * @param max_speed Maximum supported USB speed (full, high).
    58  * @param bandwidth Available bandwidth, passed to endpoint manager.
    59  * @param bw_count Bandwidth compute function, passed to endpoint manager.
    60  */
    61 void hcd_init(hcd_t *hcd) {
     54int hc_dev_add(ddf_dev_t *);
     55int hc_dev_remove(ddf_dev_t *);
     56int hc_dev_gone(ddf_dev_t *);
     57int hc_fun_online(ddf_fun_t *);
     58int hc_fun_offline(ddf_fun_t *);
     59
     60static driver_ops_t hc_driver_ops = {
     61        .dev_add = hc_dev_add,
     62        .dev_remove = hc_dev_remove,
     63        .dev_gone = hc_dev_gone,
     64        .fun_online = hc_fun_offline,
     65        .fun_offline = hc_fun_offline,
     66};
     67
     68static const hc_driver_t *hc_driver;
     69
     70int hc_driver_main(const hc_driver_t *driver)
     71{
     72        driver_t ddf_driver = {
     73                .name = driver->name,
     74                .driver_ops = &hc_driver_ops,
     75        };
     76
     77        /* Remember ops to call. */
     78        hc_driver = driver;
     79
     80        return ddf_driver_main(&ddf_driver);
     81}
     82
     83/** IRQ handling callback, forward status from call to diver structure.
     84 *
     85 * @param[in] dev DDF instance of the device to use.
     86 * @param[in] iid (Unused).
     87 * @param[in] call Pointer to the call from kernel.
     88 */
     89static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
     90{
     91        assert(dev);
     92        hc_device_t *hcd = dev_to_hcd(dev);
     93
     94        const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, interrupt);
     95        assert(ops);
     96
     97        const uint32_t status = IPC_GET_ARG1(*call);
     98        ops->interrupt(hcd->bus, status);
     99}
     100
     101/** Worker for the HW interrupt replacement fibril.
     102 */
     103static int interrupt_polling(void *arg)
     104{
     105        hc_device_t *hcd = arg;
    62106        assert(hcd);
    63 
    64         hcd_set_implementation(hcd, NULL, NULL, NULL);
     107        bus_t *bus = hcd->bus;
     108
     109        const bus_ops_t *interrupt_ops = BUS_OPS_LOOKUP(bus->ops, interrupt);
     110        const bus_ops_t *status_ops = BUS_OPS_LOOKUP(bus->ops, status);
     111        if (!interrupt_ops || !status_ops)
     112                return ENOTSUP;
     113
     114        uint32_t status = 0;
     115        while (status_ops->status(bus, &status) == EOK) {
     116                interrupt_ops->interrupt(bus, status);
     117                status = 0;
     118                /* We should wait 1 frame - 1ms here, but this polling is a
     119                 * lame crutch anyway so don't hog the system. 10ms is still
     120                 * good enough for emergency mode */
     121                async_usleep(10000);
     122        }
     123        return EOK;
     124}
     125
     126static inline void irq_code_clean(irq_code_t *code)
     127{
     128        if (code) {
     129                free(code->ranges);
     130                free(code->cmds);
     131                code->ranges = NULL;
     132                code->cmds = NULL;
     133                code->rangecount = 0;
     134                code->cmdcount = 0;
     135        }
     136}
     137
     138/** Register interrupt handler
     139 *
     140 * @param[in] device Host controller DDF device
     141 * @param[in] regs Register range
     142 * @param[in] irq Interrupt number
     143 * @paran[in] handler Interrupt handler
     144 * @param[in] gen_irq_code IRQ code generator.
     145 *
     146 * @return IRQ capability handle on success.
     147 * @return Negative error code.
     148 */
     149static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
     150{
     151        assert(hcd);
     152        irq_code_t irq_code = {0};
     153
     154        if (!hc_driver->irq_code_gen)
     155                return ENOTSUP;
     156
     157        const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res);
     158        if (irq < 0) {
     159                usb_log_error("Failed to generate IRQ code: %s.\n",
     160                    str_error(irq));
     161                return irq;
     162        }
     163
     164        /* Register handler to avoid interrupt lockup */
     165        const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code);
     166        irq_code_clean(&irq_code);
     167        if (irq_cap < 0) {
     168                usb_log_error("Failed to register interrupt handler: %s.\n",
     169                    str_error(irq_cap));
     170                return irq_cap;
     171        }
     172
     173        /* Enable interrupts */
     174        int ret = hcd_ddf_enable_interrupt(hcd, irq);
     175        if (ret != EOK) {
     176                usb_log_error("Failed to enable interrupts: %s.\n",
     177                    str_error(ret));
     178                unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
     179                return ret;
     180        }
     181        return irq_cap;
     182}
     183
     184/** Initialize HC in memory of the driver.
     185 *
     186 * @param device DDF instance of the device to use
     187 * @return Error code
     188 *
     189 * This function does all the preparatory work for hc and rh drivers:
     190 *  - gets device's hw resources
     191 *  - attempts to enable interrupts
     192 *  - registers interrupt handler
     193 *  - calls driver specific initialization
     194 *  - registers root hub
     195 */
     196int hc_dev_add(ddf_dev_t *device)
     197{
     198        int ret = EOK;
     199        assert(device);
     200
     201        if (!hc_driver->hc_add) {
     202                usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name);
     203                return ENOTSUP;
     204        }
     205
     206        ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
     207        if (ret != EOK) {
     208                usb_log_error("Failed to setup HC device.\n");
     209                return ret;
     210        }
     211
     212        hc_device_t *hcd = dev_to_hcd(device);
     213
     214        hw_res_list_parsed_t hw_res;
     215        ret = hcd_ddf_get_registers(hcd, &hw_res);
     216        if (ret != EOK) {
     217                usb_log_error("Failed to get register memory addresses "
     218                    "for `%s': %s.\n", ddf_dev_get_name(device),
     219                    str_error(ret));
     220                goto err_hcd;
     221        }
     222
     223        ret = hc_driver->hc_add(hcd, &hw_res);
     224        if (ret != EOK) {
     225                usb_log_error("Failed to init HCD.\n");
     226                goto err_hw_res;
     227        }
     228
     229        assert(hcd->bus);
     230
     231        /* Setup interrupts  */
     232        hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
     233        if (hcd->irq_cap >= 0) {
     234                usb_log_debug("Hw interrupts enabled.\n");
     235        }
     236
     237        /* Claim the device from BIOS */
     238        if (hc_driver->claim)
     239                ret = hc_driver->claim(hcd);
     240        if (ret != EOK) {
     241                usb_log_error("Failed to claim `%s' for `%s': %s",
     242                    ddf_dev_get_name(device), hc_driver->name, str_error(ret));
     243                goto err_irq;
     244        }
     245
     246        /* Start hw */
     247        if (hc_driver->start)
     248                ret = hc_driver->start(hcd);
     249        if (ret != EOK) {
     250                usb_log_error("Failed to start HCD: %s.\n", str_error(ret));
     251                goto err_irq;
     252        }
     253
     254        const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, status);
     255
     256        /* Need working irq replacement to setup root hub */
     257        if (hcd->irq_cap < 0 && ops) {
     258                hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
     259                if (!hcd->polling_fibril) {
     260                        usb_log_error("Failed to create polling fibril\n");
     261                        ret = ENOMEM;
     262                        goto err_started;
     263                }
     264                fibril_add_ready(hcd->polling_fibril);
     265                usb_log_warning("Failed to enable interrupts: %s."
     266                    " Falling back to polling.\n", str_error(hcd->irq_cap));
     267        }
     268
     269        /*
     270         * Creating root hub registers a new USB device so HC
     271         * needs to be ready at this time.
     272         */
     273        if (hc_driver->setup_root_hub)
     274                ret = hc_driver->setup_root_hub(hcd);
     275        if (ret != EOK) {
     276                usb_log_error("Failed to setup HC root hub: %s.\n",
     277                    str_error(ret));
     278                goto err_polling;
     279        }
     280
     281        usb_log_info("Controlling new `%s' device `%s'.\n",
     282           hc_driver->name, ddf_dev_get_name(device));
     283        return EOK;
     284
     285err_polling:
     286        // TODO: Stop the polling fibril (refactor the interrupt_polling func)
     287        //
     288err_started:
     289        if (hc_driver->stop)
     290                hc_driver->stop(hcd);
     291err_irq:
     292        unregister_interrupt_handler(device, hcd->irq_cap);
     293        if (hc_driver->hc_remove)
     294                hc_driver->hc_remove(hcd);
     295err_hw_res:
     296        hw_res_list_parsed_clean(&hw_res);
     297err_hcd:
     298        hcd_ddf_clean_hc(hcd);
     299        return ret;
     300}
     301
     302int hc_dev_remove(ddf_dev_t *dev)
     303{
     304        int err;
     305        hc_device_t *hcd = dev_to_hcd(dev);
     306
     307        if (hc_driver->stop)
     308                if ((err = hc_driver->stop(hcd)))
     309                        return err;
     310
     311        unregister_interrupt_handler(dev, hcd->irq_cap);
     312
     313        if (hc_driver->hc_remove)
     314                if ((err = hc_driver->hc_remove(hcd)))
     315                        return err;
     316
     317        hcd_ddf_clean_hc(hcd);
     318
     319        // TODO probably not complete
     320
     321        return EOK;
     322}
     323
     324int hc_dev_gone(ddf_dev_t *dev)
     325{
     326        int err = ENOTSUP;
     327        hc_device_t *hcd = dev_to_hcd(dev);
     328
     329        if (hc_driver->hc_gone)
     330                err = hc_driver->hc_gone(hcd);
     331
     332        hcd_ddf_clean_hc(hcd);
     333
     334        return err;
     335}
     336
     337int hc_fun_online(ddf_fun_t *fun)
     338{
     339        assert(fun);
     340
     341        device_t *dev = ddf_fun_data_get(fun);
     342        assert(dev);
     343
     344        usb_log_info("Device(%d): Requested to be brought online.", dev->address);
     345        return bus_device_online(dev);
     346}
     347
     348int hc_fun_offline(ddf_fun_t *fun)
     349{
     350        assert(fun);
     351
     352        device_t *dev = ddf_fun_data_get(fun);
     353        assert(dev);
     354
     355        usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
     356        return bus_device_offline(dev);
    65357}
    66358
     
    72364 * @return Max packet size for EP 0
    73365 */
    74 int hcd_get_ep0_max_packet_size(uint16_t *mps, hcd_t *hcd, device_t *dev)
     366int hcd_get_ep0_max_packet_size(uint16_t *mps, bus_t *bus, device_t *dev)
    75367{
    76368        assert(mps);
     
    97389
    98390        usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
    99         ssize_t got = hcd_send_batch_sync(hcd, dev, control_ep, USB_DIRECTION_IN,
     391        ssize_t got = bus_device_send_batch_sync(dev, control_ep, USB_DIRECTION_IN,
    100392            (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
    101393            "read first 8 bytes of dev descriptor");
     
    156448 *
    157449 */
    158 static toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
     450toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
    159451    const usb_device_request_setup_packet_t *request)
    160452{
     
    186478}
    187479
    188 /** Prepare generic usb_transfer_batch and schedule it.
    189  * @param hcd Host controller driver.
    190  * @param target address and endpoint number.
    191  * @param setup_data Data to use in setup stage (Control communication type)
    192  * @param in Callback for device to host communication.
    193  * @param out Callback for host to device communication.
    194  * @param arg Callback parameter.
    195  * @param name Communication identifier (for nicer output).
    196  * @return Error code.
    197  */
    198 int hcd_send_batch(hcd_t *hcd, device_t *device, usb_target_t target,
    199     usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
    200     usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
    201 {
    202         assert(hcd);
    203         assert(device->address == target.address);
    204 
    205         if (!hcd->ops.schedule) {
    206                 usb_log_error("HCD does not implement scheduler.\n");
    207                 return ENOTSUP;
    208         }
    209 
    210         endpoint_t *ep = bus_find_endpoint(device, target, direction);
    211         if (ep == NULL) {
    212                 usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
    213                     device->address, target.endpoint, name);
    214                 return ENOENT;
    215         }
    216 
    217         // TODO cut here aka provide helper to call with instance of endpoint_t in hand
    218         assert(ep->device == device);
    219 
    220         usb_log_debug2("%s %d:%d %zu(%zu).\n",
    221             name, target.address, target.endpoint, size, ep->max_packet_size);
    222 
    223         const size_t bw = endpoint_count_bw(ep, size);
    224         /* Check if we have enough bandwidth reserved */
    225         if (ep->bandwidth < bw) {
    226                 usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
    227                     "but only %zu is reserved.\n",
    228                     device->address, ep->endpoint, name, bw, ep->bandwidth);
    229                 return ENOSPC;
    230         }
    231 
    232         usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
    233         if (!batch) {
    234                 usb_log_error("Failed to create transfer batch.\n");
    235                 return ENOMEM;
    236         }
    237 
    238         batch->target = target;
    239         batch->buffer = data;
    240         batch->buffer_size = size;
    241         batch->setup.packed = setup_data;
    242         batch->dir = direction;
    243         batch->on_complete = on_complete;
    244         batch->on_complete_data = arg;
    245 
    246         /* Check for commands that reset toggle bit */
    247         if (ep->transfer_type == USB_TRANSFER_CONTROL)
    248                 batch->toggle_reset_mode
    249                         = hcd_get_request_toggle_reset_mode(&batch->setup.packet);
    250 
    251         const int ret = hcd->ops.schedule(hcd, batch);
    252         if (ret != EOK) {
    253                 usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
    254                 usb_transfer_batch_destroy(batch);
    255         }
    256 
    257         /* Drop our own reference to ep. */
    258         endpoint_del_ref(ep);
    259 
    260         return ret;
    261 }
    262 
    263 typedef struct {
    264         fibril_mutex_t done_mtx;
    265         fibril_condvar_t done_cv;
    266         unsigned done;
    267 
    268         size_t transfered_size;
    269         int error;
    270 } sync_data_t;
    271 
    272 static int sync_transfer_complete(void *arg, int error, size_t transfered_size)
    273 {
    274         sync_data_t *d = arg;
    275         assert(d);
    276         d->transfered_size = transfered_size;
    277         d->error = error;
    278         fibril_mutex_lock(&d->done_mtx);
    279         d->done = 1;
    280         fibril_condvar_broadcast(&d->done_cv);
    281         fibril_mutex_unlock(&d->done_mtx);
    282         return EOK;
    283 }
    284 
    285 ssize_t hcd_send_batch_sync(hcd_t *hcd, device_t *device, usb_target_t target,
    286     usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
    287     const char *name)
    288 {
    289         assert(hcd);
    290         sync_data_t sd = { .done = 0 };
    291         fibril_mutex_initialize(&sd.done_mtx);
    292         fibril_condvar_initialize(&sd.done_cv);
    293 
    294         const int ret = hcd_send_batch(hcd, device, target, direction,
    295             data, size, setup_data,
    296             sync_transfer_complete, &sd, name);
    297         if (ret != EOK)
    298                 return ret;
    299 
    300         fibril_mutex_lock(&sd.done_mtx);
    301         while (!sd.done)
    302                 fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);
    303         fibril_mutex_unlock(&sd.done_mtx);
    304 
    305         return (sd.error == EOK)
    306                 ? (ssize_t) sd.transfered_size
    307                 : (ssize_t) sd.error;
    308 }
    309 
    310480
    311481/**
Note: See TracChangeset for help on using the changeset viewer.