Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/uhci/uhci.c

    re991937 r7de1988c  
    3434 */
    3535
     36/* XXX Fix this */
     37#define _DDF_DATA_IMPLANT
     38
    3639#include <errno.h>
     40#include <stdbool.h>
    3741#include <str_error.h>
    3842#include <ddf/interrupt.h>
     43#include <usb_iface.h>
     44#include <usb/ddfiface.h>
    3945#include <usb/debug.h>
    40 #include <usb/host/hcd.h>
    41 #include <usb/host/ddf_helpers.h>
    4246
    4347#include "uhci.h"
     
    4549#include "res.h"
    4650#include "hc.h"
    47 
     51#include "root_hub.h"
     52
     53/** Structure representing both functions of UHCI hc, USB host controller
     54 * and USB root hub */
     55typedef struct uhci {
     56        /** Pointer to DDF representation of UHCI host controller */
     57        ddf_fun_t *hc_fun;
     58        /** Pointer to DDF representation of UHCI root hub */
     59        ddf_fun_t *rh_fun;
     60
     61        /** Internal driver's representation of UHCI host controller */
     62        hc_t hc;
     63        /** Internal driver's representation of UHCI root hub */
     64        rh_t rh;
     65} uhci_t;
     66
     67static inline uhci_t *dev_to_uhci(ddf_dev_t *dev)
     68{
     69        return ddf_dev_data_get(dev);
     70}
    4871
    4972/** IRQ handling callback, forward status from call to diver structure.
     
    5679{
    5780        assert(dev);
    58         hcd_t *hcd = dev_to_hcd(dev);
    59         if (!hcd || !hcd->private_data) {
     81        uhci_t *uhci = dev_to_uhci(dev);
     82        if (!uhci) {
    6083                usb_log_error("Interrupt on not yet initialized device.\n");
    6184                return;
    6285        }
    6386        const uint16_t status = IPC_GET_ARG1(*call);
    64         hc_interrupt(hcd->private_data, status);
    65 }
     87        hc_interrupt(&uhci->hc, status);
     88}
     89
     90/** Operations supported by the HC driver */
     91static ddf_dev_ops_t hc_ops = {
     92        .interfaces[USBHC_DEV_IFACE] = &hcd_iface, /* see iface.h/c */
     93};
     94
     95/** Gets handle of the respective hc.
     96 *
     97 * @param[in] fun DDF function of uhci device.
     98 * @param[out] handle Host cotnroller handle.
     99 * @return Error code.
     100 */
     101static int usb_iface_get_hc_handle(ddf_fun_t *fun, devman_handle_t *handle)
     102{
     103        ddf_fun_t *hc_fun = dev_to_uhci(ddf_fun_get_dev(fun))->hc_fun;
     104        assert(hc_fun);
     105
     106        if (handle != NULL)
     107                *handle = ddf_fun_get_handle(hc_fun);
     108        return EOK;
     109}
     110
     111/** USB interface implementation used by RH */
     112static usb_iface_t usb_iface = {
     113        .get_hc_handle = usb_iface_get_hc_handle,
     114};
     115
     116/** Get root hub hw resources (I/O registers).
     117 *
     118 * @param[in] fun Root hub function.
     119 * @return Pointer to the resource list used by the root hub.
     120 */
     121static hw_resource_list_t *get_resource_list(ddf_fun_t *fun)
     122{
     123        rh_t *rh = ddf_fun_data_get(fun);
     124        assert(rh);
     125        return &rh->resource_list;
     126}
     127
     128/** Interface to provide the root hub driver with hw info */
     129static hw_res_ops_t hw_res_iface = {
     130        .get_resource_list = get_resource_list,
     131        .enable_interrupt = NULL,
     132};
     133
     134/** RH function support for uhci_rhd */
     135static ddf_dev_ops_t rh_ops = {
     136        .interfaces[USB_DEV_IFACE] = &usb_iface,
     137        .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface
     138};
    66139
    67140/** Initialize hc and rh DDF structures and their respective drivers.
     
    77150int device_setup_uhci(ddf_dev_t *device)
    78151{
     152        bool ih_registered = false;
     153        bool hc_inited = false;
     154        bool fun_bound = false;
     155        int rc;
     156
    79157        if (!device)
    80158                return EBADMEM;
    81159
    82 #define CHECK_RET_RETURN(ret, message...) \
    83 if (ret != EOK) { \
    84         usb_log_error(message); \
    85         return ret; \
    86 } else (void)0
    87 
    88         uintptr_t reg_base = 0;
    89         size_t reg_size = 0;
     160        uhci_t *instance = ddf_dev_data_alloc(device, sizeof(uhci_t));
     161        if (instance == NULL) {
     162                usb_log_error("Failed to allocate OHCI driver.\n");
     163                return ENOMEM;
     164        }
     165
     166        instance->hc_fun = ddf_fun_create(device, fun_exposed, "uhci_hc");
     167        if (instance->hc_fun == NULL) {
     168                usb_log_error("Failed to create UHCI HC function.\n");
     169                rc = ENOMEM;
     170                goto error;
     171        }
     172
     173        ddf_fun_set_ops(instance->hc_fun, &hc_ops);
     174        ddf_fun_data_implant(instance->hc_fun, &instance->hc.generic);
     175
     176        instance->rh_fun = ddf_fun_create(device, fun_inner, "uhci_rh");
     177        if (instance->rh_fun == NULL) {
     178                usb_log_error("Failed to create UHCI RH function.\n");
     179                rc = ENOMEM;
     180                goto error;
     181        }
     182
     183        ddf_fun_set_ops(instance->rh_fun, &rh_ops);
     184        ddf_fun_data_implant(instance->rh_fun, &instance->rh);
     185
     186        addr_range_t regs;
    90187        int irq = 0;
    91188
    92         int ret = get_my_registers(device, &reg_base, &reg_size, &irq);
    93         CHECK_RET_RETURN(ret, "Failed to get I/O region for %" PRIun ": %s.\n",
    94             ddf_dev_get_handle(device), str_error(ret));
    95         usb_log_debug("I/O regs at 0x%p (size %zu), IRQ %d.\n",
    96             (void *) reg_base, reg_size, irq);
    97 
    98         const size_t ranges_count = hc_irq_pio_range_count();
    99         const size_t cmds_count = hc_irq_cmd_count();
    100         irq_pio_range_t irq_ranges[ranges_count];
    101         irq_cmd_t irq_cmds[cmds_count];
    102         ret = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
    103             sizeof(irq_cmds), reg_base, reg_size);
    104         CHECK_RET_RETURN(ret, "Failed to generate IRQ commands: %s.\n",
    105             str_error(ret));
    106 
    107         irq_code_t irq_code = {
    108                 .rangecount = ranges_count,
    109                 .ranges = irq_ranges,
    110                 .cmdcount = cmds_count,
    111                 .cmds = irq_cmds
    112         };
    113 
    114         /* Register handler to avoid interrupt lockup */
    115         ret = register_interrupt_handler(device, irq, irq_handler, &irq_code);
    116         CHECK_RET_RETURN(ret, "Failed to register interrupt handler: %s.\n",
    117             str_error(ret));
    118        
    119         ret = disable_legacy(device);
    120         CHECK_RET_RETURN(ret, "Failed to disable legacy USB: %s.\n",
    121             str_error(ret));
     189        rc = get_my_registers(device, &regs, &irq);
     190        if (rc != EOK) {
     191                usb_log_error("Failed to get I/O addresses for %" PRIun ": %s.\n",
     192                    ddf_dev_get_handle(device), str_error(rc));
     193                goto error;
     194        }
     195        usb_log_debug("I/O regs at %p (size %zu), IRQ %d.\n",
     196            RNGABSPTR(regs), RNGSZ(regs), irq);
     197
     198        rc = disable_legacy(device);
     199        if (rc != EOK) {
     200                usb_log_error("Failed to disable legacy USB: %s.\n",
     201                    str_error(rc));
     202                goto error;
     203        }
     204
     205        rc = hc_register_irq_handler(device, &regs, irq, irq_handler);
     206        if (rc != EOK) {
     207                usb_log_error("Failed to register interrupt handler: %s.\n",
     208                    str_error(rc));
     209                goto error;
     210        }
     211
     212        ih_registered = true;
    122213
    123214        bool interrupts = false;
    124         ret = enable_interrupts(device);
    125         if (ret != EOK) {
     215        rc = enable_interrupts(device);
     216        if (rc != EOK) {
    126217                usb_log_warning("Failed to enable interrupts: %s."
    127                     " Falling back to polling.\n", str_error(ret));
     218                    " Falling back to polling.\n", str_error(rc));
    128219        } else {
    129220                usb_log_debug("Hw interrupts enabled.\n");
     
    131222        }
    132223
    133         ret = hcd_ddf_setup_hc(device, USB_SPEED_FULL,
    134             BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
    135         CHECK_RET_RETURN(ret, "Failed to setup UHCI HCD.\n");
    136        
    137         hc_t *hc = malloc(sizeof(hc_t));
    138         ret = hc ? EOK : ENOMEM;
    139         CHECK_RET_RETURN(ret, "Failed to allocate UHCI HC structure.\n");
    140 
    141         ret = hc_init(hc, (void*)reg_base, reg_size, interrupts);
    142         CHECK_RET_RETURN(ret,
    143             "Failed to init uhci_hcd: %s.\n", str_error(ret));
    144 
    145         hcd_set_implementation(dev_to_hcd(device), hc, hc_schedule, NULL, NULL);
    146 
    147         /*
    148          * Creating root hub registers a new USB device so HC
    149          * needs to be ready at this time.
    150          */
    151         ret = hcd_ddf_setup_root_hub(device);
    152         if (ret != EOK) {
    153                 // TODO: Undo hcd_setup_device
    154                 hc_fini(hc);
    155                 CHECK_RET_RETURN(ret, "Failed to setup UHCI root hub: %s.\n",
    156                     str_error(ret));
    157                 return ret;
     224        rc = hc_init(&instance->hc, &regs, interrupts);
     225        if (rc != EOK) {
     226                usb_log_error("Failed to init uhci_hcd: %s.\n", str_error(rc));
     227                goto error;
     228        }
     229
     230        hc_inited = true;
     231
     232        rc = ddf_fun_bind(instance->hc_fun);
     233        if (rc != EOK) {
     234                usb_log_error("Failed to bind UHCI device function: %s.\n",
     235                    str_error(rc));
     236                goto error;
     237        }
     238
     239        fun_bound = true;
     240
     241        rc = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
     242        if (rc != EOK) {
     243                usb_log_error("Failed to add UHCI to HC class: %s.\n",
     244                    str_error(rc));
     245                goto error;
     246        }
     247
     248        rc = rh_init(&instance->rh, instance->rh_fun,
     249            (uintptr_t)instance->hc.registers + 0x10, 4);
     250        if (rc != EOK) {
     251                usb_log_error("Failed to setup UHCI root hub: %s.\n",
     252                    str_error(rc));
     253                goto error;
     254        }
     255
     256        rc = ddf_fun_bind(instance->rh_fun);
     257        if (rc != EOK) {
     258                usb_log_error("Failed to register UHCI root hub: %s.\n",
     259                    str_error(rc));
     260                goto error;
    158261        }
    159262
    160263        return EOK;
     264
     265error:
     266        if (fun_bound)
     267                ddf_fun_unbind(instance->hc_fun);
     268        if (hc_inited)
     269                hc_fini(&instance->hc);
     270        if (ih_registered)
     271                unregister_interrupt_handler(device, irq);
     272        if (instance->hc_fun != NULL)
     273                ddf_fun_destroy(instance->hc_fun);
     274        if (instance->rh_fun != NULL) {
     275                ddf_fun_destroy(instance->rh_fun);
     276        }
     277        return rc;
    161278}
    162279/**
Note: See TracChangeset for help on using the changeset viewer.