Ignore:
File:
1 edited

Legend:

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

    r7de1988c re991937  
    3434 */
    3535
    36 /* XXX Fix this */
    37 #define _DDF_DATA_IMPLANT
    38 
    3936#include <errno.h>
    40 #include <stdbool.h>
    4137#include <str_error.h>
    4238#include <ddf/interrupt.h>
    43 #include <usb_iface.h>
    44 #include <usb/ddfiface.h>
    4539#include <usb/debug.h>
     40#include <usb/host/hcd.h>
     41#include <usb/host/ddf_helpers.h>
    4642
    4743#include "uhci.h"
     
    4945#include "res.h"
    5046#include "hc.h"
    51 #include "root_hub.h"
    5247
    53 /** Structure representing both functions of UHCI hc, USB host controller
    54  * and USB root hub */
    55 typedef 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 
    67 static inline uhci_t *dev_to_uhci(ddf_dev_t *dev)
    68 {
    69         return ddf_dev_data_get(dev);
    70 }
    7148
    7249/** IRQ handling callback, forward status from call to diver structure.
     
    7956{
    8057        assert(dev);
    81         uhci_t *uhci = dev_to_uhci(dev);
    82         if (!uhci) {
     58        hcd_t *hcd = dev_to_hcd(dev);
     59        if (!hcd || !hcd->private_data) {
    8360                usb_log_error("Interrupt on not yet initialized device.\n");
    8461                return;
    8562        }
    8663        const uint16_t status = IPC_GET_ARG1(*call);
    87         hc_interrupt(&uhci->hc, status);
     64        hc_interrupt(hcd->private_data, status);
    8865}
    89 
    90 /** Operations supported by the HC driver */
    91 static 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  */
    101 static 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 */
    112 static 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  */
    121 static 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 */
    129 static 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 */
    135 static ddf_dev_ops_t rh_ops = {
    136         .interfaces[USB_DEV_IFACE] = &usb_iface,
    137         .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface
    138 };
    13966
    14067/** Initialize hc and rh DDF structures and their respective drivers.
     
    15077int device_setup_uhci(ddf_dev_t *device)
    15178{
    152         bool ih_registered = false;
    153         bool hc_inited = false;
    154         bool fun_bound = false;
    155         int rc;
    156 
    15779        if (!device)
    15880                return EBADMEM;
    15981
    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         }
     82#define CHECK_RET_RETURN(ret, message...) \
     83if (ret != EOK) { \
     84        usb_log_error(message); \
     85        return ret; \
     86} else (void)0
    16587
    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;
     88        uintptr_t reg_base = 0;
     89        size_t reg_size = 0;
    18790        int irq = 0;
    18891
    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);
     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);
    19797
    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         }
     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));
    204106
    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         }
     107        irq_code_t irq_code = {
     108                .rangecount = ranges_count,
     109                .ranges = irq_ranges,
     110                .cmdcount = cmds_count,
     111                .cmds = irq_cmds
     112        };
    211113
    212         ih_registered = true;
     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));
    213122
    214123        bool interrupts = false;
    215         rc = enable_interrupts(device);
    216         if (rc != EOK) {
     124        ret = enable_interrupts(device);
     125        if (ret != EOK) {
    217126                usb_log_warning("Failed to enable interrupts: %s."
    218                     " Falling back to polling.\n", str_error(rc));
     127                    " Falling back to polling.\n", str_error(ret));
    219128        } else {
    220129                usb_log_debug("Hw interrupts enabled.\n");
     
    222131        }
    223132
    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         }
     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");
    229140
    230         hc_inited = true;
     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));
    231144
    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         }
     145        hcd_set_implementation(dev_to_hcd(device), hc, hc_schedule, NULL, NULL);
    238146
    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;
     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;
    261158        }
    262159
    263160        return EOK;
    264 
    265 error:
    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;
    278161}
    279162/**
Note: See TracChangeset for help on using the changeset viewer.