Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/ohci/hc.c

    ra25d893 rb5111c46  
    3434 */
    3535
    36 #include <assert.h>
    37 #include <async.h>
    3836#include <errno.h>
    39 #include <macros.h>
    40 #include <mem.h>
    41 #include <stdlib.h>
     37#include <stdbool.h>
    4238#include <str_error.h>
    43 #include <sys/types.h>
     39#include <adt/list.h>
     40#include <libarch/ddi.h>
    4441
    4542#include <usb/debug.h>
    4643#include <usb/usb.h>
    47 
     44#include <usb/ddfiface.h>
     45
     46#include "hc.h"
    4847#include "ohci_endpoint.h"
    49 #include "ohci_batch.h"
    50 #include "utils/malloc32.h"
    51 
    52 #include "hc.h"
    5348
    5449#define OHCI_USED_INTERRUPTS \
     
    8984};
    9085
     86enum {
     87        /** Number of PIO ranges used in IRQ code */
     88        hc_irq_pio_range_count =
     89            sizeof(ohci_pio_ranges) / sizeof(irq_pio_range_t),
     90
     91        /** Number of commands used in IRQ code */
     92        hc_irq_cmd_count =
     93            sizeof(ohci_irq_commands) / sizeof(irq_cmd_t)
     94};
     95
    9196static void hc_gain_control(hc_t *instance);
    9297static void hc_start(hc_t *instance);
    9398static int hc_init_transfer_lists(hc_t *instance);
    9499static int hc_init_memory(hc_t *instance);
     100static int interrupt_emulator(hc_t *instance);
     101static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
    95102
    96103/** Generate IRQ code.
     
    99106 * @param[out] cmds Commands buffer.
    100107 * @param[in] cmds_size Size of the commands buffer (bytes).
    101  * @param[in] hw_res Device's resources.
     108 * @param[in] regs Device's register range.
    102109 *
    103110 * @return Error code.
    104111 */
    105 int ohci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
    106 {
    107         assert(code);
    108         assert(hw_res);
    109 
    110         if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
    111                 return EINVAL;
    112 
    113         const addr_range_t regs = hw_res->mem_ranges.ranges[0];
    114 
    115         if (RNGSZ(regs) < sizeof(ohci_regs_t))
     112int
     113hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
     114    size_t cmds_size, addr_range_t *regs)
     115{
     116        if ((ranges_size < sizeof(ohci_pio_ranges)) ||
     117            (cmds_size < sizeof(ohci_irq_commands)) ||
     118            (RNGSZ(*regs) < sizeof(ohci_regs_t)))
    116119                return EOVERFLOW;
    117120
    118         code->ranges = malloc(sizeof(ohci_pio_ranges));
    119         if (code->ranges == NULL)
    120                 return ENOMEM;
    121 
    122         code->cmds = malloc(sizeof(ohci_irq_commands));
    123         if (code->cmds == NULL) {
    124                 free(code->ranges);
    125                 return ENOMEM;
    126         }
    127 
    128         code->rangecount = ARRAY_SIZE(ohci_pio_ranges);
    129         code->cmdcount = ARRAY_SIZE(ohci_irq_commands);
    130 
    131         memcpy(code->ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
    132         code->ranges[0].base = RNGABS(regs);
    133 
    134         memcpy(code->cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
    135         ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(regs);
    136         code->cmds[0].addr = (void *) &registers->interrupt_status;
    137         code->cmds[3].addr = (void *) &registers->interrupt_status;
    138         OHCI_WR(code->cmds[1].value, OHCI_USED_INTERRUPTS);
    139 
    140         usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.\n",
    141             RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
    142 
    143         return hw_res->irqs.irqs[0];
     121        memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
     122        ranges[0].base = RNGABS(*regs);
     123
     124        memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
     125        ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(*regs);
     126        cmds[0].addr = (void *) &registers->interrupt_status;
     127        cmds[3].addr = (void *) &registers->interrupt_status;
     128        OHCI_WR(cmds[1].value, OHCI_USED_INTERRUPTS);
     129
     130        return EOK;
     131}
     132
     133/** Register interrupt handler.
     134 *
     135 * @param[in] device Host controller DDF device
     136 * @param[in] regs Register range
     137 * @param[in] irq Interrupt number
     138 * @paran[in] handler Interrupt handler
     139 *
     140 * @return EOK on success or negative error code
     141 */
     142int hc_register_irq_handler(ddf_dev_t *device, addr_range_t *regs, int irq,
     143    interrupt_handler_t handler)
     144{
     145        int rc;
     146
     147        irq_pio_range_t irq_ranges[hc_irq_pio_range_count];
     148        irq_cmd_t irq_cmds[hc_irq_cmd_count];
     149
     150        irq_code_t irq_code = {
     151                .rangecount = hc_irq_pio_range_count,
     152                .ranges = irq_ranges,
     153                .cmdcount = hc_irq_cmd_count,
     154                .cmds = irq_cmds
     155        };
     156
     157        rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
     158            sizeof(irq_cmds), regs);
     159        if (rc != EOK) {
     160                usb_log_error("Failed to generate IRQ code: %s.\n",
     161                    str_error(rc));
     162                return rc;
     163        }
     164
     165        /* Register handler to avoid interrupt lockup */
     166        rc = register_interrupt_handler(device, irq, handler, &irq_code);
     167        if (rc != EOK) {
     168                usb_log_error("Failed to register interrupt handler: %s.\n",
     169                    str_error(rc));
     170                return rc;
     171        }
     172
     173        return EOK;
     174}
     175
     176/** Announce OHCI root hub to the DDF
     177 *
     178 * @param[in] instance OHCI driver intance
     179 * @param[in] hub_fun DDF fuction representing OHCI root hub
     180 * @return Error code
     181 */
     182int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
     183{
     184        bool addr_reqd = false;
     185        bool ep_added = false;
     186        bool fun_bound = false;
     187        int rc;
     188
     189        assert(instance);
     190        assert(hub_fun);
     191
     192        /* Try to get address 1 for root hub. */
     193        instance->rh.address = 1;
     194        rc = usb_device_manager_request_address(
     195            &instance->generic->dev_manager, &instance->rh.address, false,
     196            USB_SPEED_FULL);
     197        if (rc != EOK) {
     198                usb_log_error("Failed to get OHCI root hub address: %s\n",
     199                    str_error(rc));
     200                goto error;
     201        }
     202
     203        addr_reqd = true;
     204
     205        rc = usb_endpoint_manager_add_ep(
     206            &instance->generic->ep_manager, instance->rh.address, 0,
     207            USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL, USB_SPEED_FULL, 64,
     208            0, NULL, NULL);
     209        if (rc != EOK) {
     210                usb_log_error("Failed to register root hub control endpoint: %s.\n",
     211                    str_error(rc));
     212                goto error;
     213        }
     214
     215        ep_added = true;
     216
     217        rc = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100);
     218        if (rc != EOK) {
     219                usb_log_error("Failed to add root hub match-id: %s.\n",
     220                    str_error(rc));
     221                goto error;
     222        }
     223
     224        rc = ddf_fun_bind(hub_fun);
     225        if (rc != EOK) {
     226                usb_log_error("Failed to bind root hub function: %s.\n",
     227                    str_error(rc));
     228                goto error;
     229        }
     230
     231        fun_bound = true;
     232
     233        rc = usb_device_manager_bind_address(&instance->generic->dev_manager,
     234            instance->rh.address, ddf_fun_get_handle(hub_fun));
     235        if (rc != EOK) {
     236                usb_log_warning("Failed to bind root hub address: %s.\n",
     237                    str_error(rc));
     238        }
     239
     240        return EOK;
     241error:
     242        if (fun_bound)
     243                ddf_fun_unbind(hub_fun);
     244        if (ep_added) {
     245                usb_endpoint_manager_remove_ep(
     246                    &instance->generic->ep_manager, instance->rh.address, 0,
     247                    USB_DIRECTION_BOTH, NULL, NULL);
     248        }
     249        if (addr_reqd) {
     250                usb_device_manager_release_address(
     251                    &instance->generic->dev_manager, instance->rh.address);
     252        }
     253        return rc;
    144254}
    145255
     
    147257 *
    148258 * @param[in] instance Memory place for the structure.
    149  * @param[in] regs Device's resources
     259 * @param[in] HC function node
     260 * @param[in] regs Device's I/O registers range.
    150261 * @param[in] interrupts True if w interrupts should be used
    151262 * @return Error code
    152263 */
    153 int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts)
    154 {
    155         assert(instance);
    156         assert(hw_res);
    157         if (hw_res->mem_ranges.count != 1 ||
    158             hw_res->mem_ranges.ranges[0].size < sizeof(ohci_regs_t))
    159             return EINVAL;
    160 
    161         int ret = pio_enable_range(&hw_res->mem_ranges.ranges[0],
    162             (void **) &instance->registers);
    163         if (ret != EOK) {
    164                 usb_log_error("Failed to gain access to registers: %s.\n",
    165                     str_error(ret));
    166                 return ret;
    167         }
    168         usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.\n",
    169             hw_res->mem_ranges.ranges[0].address.absolute,
    170             hw_res->mem_ranges.ranges[0].size);
     264int hc_init(hc_t *instance, ddf_fun_t *fun, addr_range_t *regs, bool interrupts)
     265{
     266        assert(instance);
     267
     268        int rc = pio_enable_range(regs, (void **) &instance->registers);
     269        if (rc != EOK) {
     270                usb_log_error("Failed to gain access to device registers: %s.\n",
     271                    str_error(rc));
     272                return rc;
     273        }
    171274
    172275        list_initialize(&instance->pending_batches);
     276
     277        instance->generic = ddf_fun_data_alloc(fun, sizeof(hcd_t));
     278        if (instance->generic == NULL) {
     279                usb_log_error("Out of memory.\n");
     280                return ENOMEM;
     281        }
     282
     283        hcd_init(instance->generic, USB_SPEED_FULL,
     284            BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
     285        instance->generic->private_data = instance;
     286        instance->generic->schedule = hc_schedule;
     287        instance->generic->ep_add_hook = ohci_endpoint_init;
     288        instance->generic->ep_remove_hook = ohci_endpoint_fini;
     289
     290        rc = hc_init_memory(instance);
     291        if (rc != EOK) {
     292                usb_log_error("Failed to create OHCI memory structures: %s.\n",
     293                    str_error(rc));
     294                return rc;
     295        }
     296
    173297        fibril_mutex_initialize(&instance->guard);
    174         instance->hw_interrupts = interrupts;
    175 
    176         ret = hc_init_memory(instance);
    177         if (ret != EOK) {
    178                 usb_log_error("Failed to create OHCI memory structures: %s.\n",
    179                     str_error(ret));
    180                 //TODO: We should disable pio access here
    181                 return ret;
    182         }
    183298
    184299        hc_gain_control(instance);
    185300
    186         ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
     301        if (!interrupts) {
     302                instance->interrupt_emulator =
     303                    fibril_create((int(*)(void*))interrupt_emulator, instance);
     304                fibril_add_ready(instance->interrupt_emulator);
     305        }
     306
     307        rh_init(&instance->rh, instance->registers);
    187308        hc_start(instance);
    188309
    189310        return EOK;
    190311}
    191 
    192 /** Safely dispose host controller internal structures
    193  *
    194  * @param[in] instance Host controller structure to use.
    195  */
    196 void hc_fini(hc_t *instance)
    197 {
    198         assert(instance);
    199         /* TODO: implement*/
    200 };
    201312
    202313void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
     
    268379}
    269380
    270 int ohci_hc_status(hcd_t *hcd, uint32_t *status)
    271 {
    272         assert(hcd);
    273         assert(status);
    274         hc_t *instance = hcd->driver.data;
    275         assert(instance);
    276 
    277         if (instance->registers){
    278                 *status = OHCI_RD(instance->registers->interrupt_status);
    279                 OHCI_WR(instance->registers->interrupt_status, *status);
    280         }
    281         return EOK;
    282 }
    283 
    284381/** Add USB transfer to the schedule.
    285382 *
    286  * @param[in] hcd HCD driver structure.
     383 * @param[in] instance OHCI hc driver structure.
    287384 * @param[in] batch Batch representing the transfer.
    288385 * @return Error code.
    289386 */
    290 int ohci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     387int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
    291388{
    292389        assert(hcd);
    293         hc_t *instance = hcd->driver.data;
     390        hc_t *instance = hcd->private_data;
    294391        assert(instance);
    295392
    296393        /* Check for root hub communication */
    297         if (batch->ep->address == ohci_rh_get_address(&instance->rh)) {
     394        if (batch->ep->address == instance->rh.address) {
    298395                usb_log_debug("OHCI root hub request.\n");
    299                 return ohci_rh_schedule(&instance->rh, batch);
     396                rh_request(&instance->rh, batch);
     397                return EOK;
    300398        }
    301399        ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
     
    325423/** Interrupt handling routine
    326424 *
    327  * @param[in] hcd HCD driver structure.
     425 * @param[in] instance OHCI hc driver structure.
    328426 * @param[in] status Value of the status register at the time of interrupt.
    329427 */
    330 void ohci_hc_interrupt(hcd_t *hcd, uint32_t status)
    331 {
    332         assert(hcd);
    333         hc_t *instance = hcd->driver.data;
     428void hc_interrupt(hc_t *instance, uint32_t status)
     429{
    334430        status = OHCI_RD(status);
    335431        assert(instance);
     
    338434        usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
    339435        if (status & I_RHSC)
    340                 ohci_rh_interrupt(&instance->rh);
     436                rh_interrupt(&instance->rh);
    341437
    342438        if (status & I_WDH) {
     
    369465        }
    370466
     467}
     468
     469/** Check status register regularly
     470 *
     471 * @param[in] instance OHCI hc driver structure.
     472 * @return Error code
     473 */
     474int interrupt_emulator(hc_t *instance)
     475{
     476        assert(instance);
     477        usb_log_info("Started interrupt emulator.\n");
     478        while (1) {
     479                const uint32_t status = instance->registers->interrupt_status;
     480                instance->registers->interrupt_status = status;
     481                hc_interrupt(instance, status);
     482                async_usleep(10000);
     483        }
     484        return EOK;
    371485}
    372486
     
    394508                    ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
    395509                /* Zero everything but A20State */
    396                 //TODO: should we ack interrupts before doing this?
    397510                OHCI_CLR(*ohci_emulation_reg, ~0x100);
    398511                usb_log_debug(
     
    404517        if (OHCI_RD(instance->registers->control) & C_IR) {
    405518                usb_log_debug("SMM driver: request ownership change.\n");
    406                 //TODO: should we ack interrupts before doing this?
    407519                OHCI_SET(instance->registers->command_status, CS_OCR);
    408520                /* Hope that SMM actually knows its stuff or we can hang here */
    409                 while (OHCI_RD(instance->registers->control) & C_IR) {
     521                while (OHCI_RD(instance->registers->control & C_IR)) {
    410522                        async_usleep(1000);
    411523                }
     
    491603
    492604        /* Enable interrupts */
    493         if (instance->hw_interrupts) {
    494                 OHCI_WR(instance->registers->interrupt_enable,
    495                     OHCI_USED_INTERRUPTS);
    496                 usb_log_debug("Enabled interrupts: %x.\n",
    497                     OHCI_RD(instance->registers->interrupt_enable));
    498                 OHCI_WR(instance->registers->interrupt_enable, I_MI);
    499         }
     605        OHCI_WR(instance->registers->interrupt_enable, OHCI_USED_INTERRUPTS);
     606        usb_log_debug("Enabled interrupts: %x.\n",
     607            OHCI_RD(instance->registers->interrupt_enable));
     608        OHCI_WR(instance->registers->interrupt_enable, I_MI);
    500609
    501610        /* Set periodic start to 90% */
     
    523632do { \
    524633        const char *name = usb_str_transfer_type(type); \
    525         const int ret = endpoint_list_init(&instance->lists[type], name); \
     634        int ret = endpoint_list_init(&instance->lists[type], name); \
    526635        if (ret != EOK) { \
    527636                usb_log_error("Failed to setup %s endpoint list: %s.\n", \
Note: See TracChangeset for help on using the changeset viewer.