Changeset e50cd7f in mainline for uspace/drv/ohci/hc.c


Ignore:
Timestamp:
2011-04-17T19:17:55Z (14 years ago)
Author:
Matej Klonfar <maklf@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
63517c2, cfbbe1d3
Parents:
ef354b6 (diff), 8595577b (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.
Message:

new report structure fixes

File:
1 edited

Legend:

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

    ref354b6 re50cd7f  
    4343
    4444#include "hc.h"
     45#include "hcd_endpoint.h"
    4546
    4647static int interrupt_emulator(hc_t *instance);
    4748static void hc_gain_control(hc_t *instance);
    4849static void hc_init_hw(hc_t *instance);
     50static int hc_init_transfer_lists(hc_t *instance);
     51static int hc_init_memory(hc_t *instance);
    4952/*----------------------------------------------------------------------------*/
    5053int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
     
    5255        assert(instance);
    5356        assert(hub_fun);
     57
     58        int ret;
    5459
    5560        usb_address_t hub_address =
    5661            device_keeper_get_free_address(&instance->manager, USB_SPEED_FULL);
     62        if (hub_address <= 0) {
     63                usb_log_error("Failed to get OHCI root hub address.\n");
     64                return hub_address;
     65        }
    5766        instance->rh.address = hub_address;
    5867        usb_device_keeper_bind(
    5968            &instance->manager, hub_address, hub_fun->handle);
    6069
     70        ret = hc_add_endpoint(instance, hub_address, 0, USB_SPEED_FULL,
     71            USB_TRANSFER_CONTROL, USB_DIRECTION_BOTH, 64, 0, 0);
     72        if (ret != EOK) {
     73                usb_log_error("Failed to add OHCI rh endpoint 0.\n");
     74                usb_device_keeper_release(&instance->manager, hub_address);
     75                return ret;
     76        }
     77
    6178        char *match_str = NULL;
    62         int ret = asprintf(&match_str, "usb&class=hub");
    63         ret = (match_str == NULL) ? ret : EOK;
     79        /* DDF needs heap allocated string */
     80        ret = asprintf(&match_str, "usb&class=hub");
    6481        if (ret < 0) {
    65                 usb_log_error("Failed to create root hub match-id string.\n");
     82                usb_log_error(
     83                    "Failed(%d) to create root hub match-id string.\n", ret);
     84                usb_device_keeper_release(&instance->manager, hub_address);
    6685                return ret;
    6786        }
     
    6988        ret = ddf_fun_add_match_id(hub_fun, match_str, 100);
    7089        if (ret != EOK) {
    71                 usb_log_error("Failed add create root hub match-id.\n");
     90                usb_log_error("Failed add root hub match-id.\n");
    7291        }
    7392        return ret;
     
    90109            ret, str_error(ret));
    91110
    92         instance->ddf_instance = fun;
    93111        usb_device_keeper_init(&instance->manager);
    94112        ret = usb_endpoint_manager_init(&instance->ep_manager,
     
    97115            ret, str_error(ret));
    98116
     117        hc_gain_control(instance);
     118        ret = hc_init_memory(instance);
     119        CHECK_RET_RETURN(ret, "Failed to create OHCI memory structures:%s.\n",
     120            ret, str_error(ret));
     121        hc_init_hw(instance);
     122        fibril_mutex_initialize(&instance->guard);
     123
     124        rh_init(&instance->rh, instance->registers);
     125
    99126        if (!interrupts) {
    100127                instance->interrupt_emulator =
     
    103130        }
    104131
    105         hc_gain_control(instance);
    106 
    107         rh_init(&instance->rh, dev, instance->registers);
    108 
    109         hc_init_hw(instance);
    110 
    111         /* TODO: implement */
    112         return EOK;
     132        list_initialize(&instance->pending_batches);
     133#undef CHECK_RET_RETURN
     134        return EOK;
     135}
     136/*----------------------------------------------------------------------------*/
     137int hc_add_endpoint(
     138    hc_t *instance, usb_address_t address, usb_endpoint_t endpoint,
     139    usb_speed_t speed, usb_transfer_type_t type, usb_direction_t direction,
     140    size_t mps, size_t size, unsigned interval)
     141{
     142        endpoint_t *ep = malloc(sizeof(endpoint_t));
     143        if (ep == NULL)
     144                return ENOMEM;
     145        int ret =
     146            endpoint_init(ep, address, endpoint, direction, type, speed, mps);
     147        if (ret != EOK) {
     148                free(ep);
     149                return ret;
     150        }
     151
     152        hcd_endpoint_t *hcd_ep = hcd_endpoint_assign(ep);
     153        if (hcd_ep == NULL) {
     154                endpoint_destroy(ep);
     155                return ENOMEM;
     156        }
     157
     158        ret = usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size);
     159        if (ret != EOK) {
     160                hcd_endpoint_clear(ep);
     161                endpoint_destroy(ep);
     162                return ret;
     163        }
     164
     165        /* Enqueue hcd_ep */
     166        switch (ep->transfer_type) {
     167        case USB_TRANSFER_CONTROL:
     168                instance->registers->control &= ~C_CLE;
     169                endpoint_list_add_ep(
     170                    &instance->lists[ep->transfer_type], hcd_ep);
     171                instance->registers->control_current = 0;
     172                instance->registers->control |= C_CLE;
     173                break;
     174        case USB_TRANSFER_BULK:
     175                instance->registers->control &= ~C_BLE;
     176                endpoint_list_add_ep(
     177                    &instance->lists[ep->transfer_type], hcd_ep);
     178                instance->registers->control |= C_BLE;
     179                break;
     180        case USB_TRANSFER_ISOCHRONOUS:
     181        case USB_TRANSFER_INTERRUPT:
     182                instance->registers->control &= (~C_PLE & ~C_IE);
     183                endpoint_list_add_ep(
     184                    &instance->lists[ep->transfer_type], hcd_ep);
     185                instance->registers->control |= C_PLE | C_IE;
     186                break;
     187        default:
     188                break;
     189        }
     190
     191        return EOK;
     192}
     193/*----------------------------------------------------------------------------*/
     194int hc_remove_endpoint(hc_t *instance, usb_address_t address,
     195    usb_endpoint_t endpoint, usb_direction_t direction)
     196{
     197        assert(instance);
     198        fibril_mutex_lock(&instance->guard);
     199        endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
     200            address, endpoint, direction, NULL);
     201        if (ep == NULL) {
     202                usb_log_error("Endpoint unregister failed: No such EP.\n");
     203                fibril_mutex_unlock(&instance->guard);
     204                return ENOENT;
     205        }
     206
     207        hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
     208        if (hcd_ep) {
     209                /* Dequeue hcd_ep */
     210                switch (ep->transfer_type) {
     211                case USB_TRANSFER_CONTROL:
     212                        instance->registers->control &= ~C_CLE;
     213                        endpoint_list_remove_ep(
     214                            &instance->lists[ep->transfer_type], hcd_ep);
     215                        instance->registers->control_current = 0;
     216                        instance->registers->control |= C_CLE;
     217                        break;
     218                case USB_TRANSFER_BULK:
     219                        instance->registers->control &= ~C_BLE;
     220                        endpoint_list_remove_ep(
     221                            &instance->lists[ep->transfer_type], hcd_ep);
     222                        instance->registers->control |= C_BLE;
     223                        break;
     224                case USB_TRANSFER_ISOCHRONOUS:
     225                case USB_TRANSFER_INTERRUPT:
     226                        instance->registers->control &= (~C_PLE & ~C_IE);
     227                        endpoint_list_remove_ep(
     228                            &instance->lists[ep->transfer_type], hcd_ep);
     229                        instance->registers->control |= C_PLE | C_IE;
     230                        break;
     231                default:
     232                        break;
     233                }
     234                hcd_endpoint_clear(ep);
     235        } else {
     236                usb_log_warning("Endpoint without hcd equivalent structure.\n");
     237        }
     238        int ret = usb_endpoint_manager_unregister_ep(&instance->ep_manager,
     239            address, endpoint, direction);
     240        fibril_mutex_unlock(&instance->guard);
     241        return ret;
     242}
     243/*----------------------------------------------------------------------------*/
     244endpoint_t * hc_get_endpoint(hc_t *instance, usb_address_t address,
     245    usb_endpoint_t endpoint, usb_direction_t direction, size_t *bw)
     246{
     247        assert(instance);
     248        fibril_mutex_lock(&instance->guard);
     249        endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
     250            address, endpoint, direction, bw);
     251        fibril_mutex_unlock(&instance->guard);
     252        return ep;
    113253}
    114254/*----------------------------------------------------------------------------*/
     
    117257        assert(instance);
    118258        assert(batch);
    119         if (batch->target.address == instance->rh.address) {
     259        assert(batch->ep);
     260
     261        /* check for root hub communication */
     262        if (batch->ep->address == instance->rh.address) {
    120263                return rh_request(&instance->rh, batch);
    121264        }
    122         /* TODO: implement */
    123         return ENOTSUP;
     265
     266        fibril_mutex_lock(&instance->guard);
     267        list_append(&batch->link, &instance->pending_batches);
     268        batch_commit(batch);
     269        switch (batch->ep->transfer_type) {
     270        case USB_TRANSFER_CONTROL:
     271                instance->registers->command_status |= CS_CLF;
     272                break;
     273        case USB_TRANSFER_BULK:
     274                instance->registers->command_status |= CS_BLF;
     275                break;
     276        default:
     277                break;
     278        }
     279
     280        fibril_mutex_unlock(&instance->guard);
     281        return EOK;
    124282}
    125283/*----------------------------------------------------------------------------*/
     
    127285{
    128286        assert(instance);
    129         if (status == 0)
     287        if ((status & ~IS_SF) == 0) /* ignore sof status */
    130288                return;
    131289        if (status & IS_RHSC)
    132290                rh_interrupt(&instance->rh);
    133291
    134         usb_log_info("OHCI interrupt: %x.\n", status);
    135 
    136         /* TODO: Check for further interrupt causes */
    137         /* TODO: implement */
     292        usb_log_debug("OHCI interrupt: %x.\n", status);
     293
     294        if (status & IS_WDH) {
     295                fibril_mutex_lock(&instance->guard);
     296                usb_log_debug2("HCCA: %p-%p(%p).\n", instance->hcca,
     297                    instance->registers->hcca, addr_to_phys(instance->hcca));
     298                usb_log_debug2("Periodic current: %p.\n",
     299                    instance->registers->periodic_current);
     300
     301                link_t *current = instance->pending_batches.next;
     302                while (current != &instance->pending_batches) {
     303                        link_t *next = current->next;
     304                        usb_transfer_batch_t *batch =
     305                            usb_transfer_batch_from_link(current);
     306
     307                        if (batch_is_complete(batch)) {
     308                                list_remove(current);
     309                                usb_transfer_batch_finish(batch);
     310                        }
     311                        current = next;
     312                }
     313                fibril_mutex_unlock(&instance->guard);
     314        }
    138315}
    139316/*----------------------------------------------------------------------------*/
     
    146323                instance->registers->interrupt_status = status;
    147324                hc_interrupt(instance, status);
    148                 async_usleep(1000);
     325                async_usleep(50000);
    149326        }
    150327        return EOK;
     
    154331{
    155332        assert(instance);
     333        /* Turn off legacy emulation */
     334        volatile uint32_t *ohci_emulation_reg =
     335            (uint32_t*)((char*)instance->registers + 0x100);
     336        usb_log_debug("OHCI legacy register %p: %x.\n",
     337                ohci_emulation_reg, *ohci_emulation_reg);
     338        *ohci_emulation_reg = 0;
     339
    156340        /* Interrupt routing enabled => smm driver is active */
    157341        if (instance->registers->control & C_IR) {
    158                 usb_log_info("Found SMM driver requesting ownership change.\n");
     342                usb_log_debug("SMM driver: request ownership change.\n");
    159343                instance->registers->command_status |= CS_OCR;
    160344                while (instance->registers->control & C_IR) {
    161345                        async_usleep(1000);
    162346                }
    163                 usb_log_info("Ownership taken from SMM driver.\n");
     347                usb_log_info("SMM driver: Ownership taken.\n");
    164348                return;
    165349        }
     
    169353        /* Interrupt routing disabled && status != USB_RESET => BIOS active */
    170354        if (hc_status != C_HCFS_RESET) {
    171                 usb_log_info("Found BIOS driver.\n");
     355                usb_log_debug("BIOS driver found.\n");
    172356                if (hc_status == C_HCFS_OPERATIONAL) {
    173                         usb_log_info("HC operational(BIOS).\n");
     357                        usb_log_info("BIOS driver: HC operational.\n");
    174358                        return;
    175359                }
     
    177361                instance->registers->control &= (C_HCFS_RESUME << C_HCFS_SHIFT);
    178362                async_usleep(20000);
     363                usb_log_info("BIOS driver: HC resumed.\n");
    179364                return;
    180365        }
     
    182367        /* HC is in reset (hw startup) => no other driver
    183368         * maintain reset for at least the time specified in USB spec (50 ms)*/
     369        usb_log_info("HC found in reset.\n");
    184370        async_usleep(50000);
    185 
    186         /* turn off legacy emulation */
    187         volatile uint32_t *ohci_emulation_reg =
    188             (uint32_t*)((char*)instance->registers + 0x100);
    189         usb_log_info("OHCI legacy register status %p: %x.\n",
    190                 ohci_emulation_reg, *ohci_emulation_reg);
    191         *ohci_emulation_reg = 0;
    192 
    193371}
    194372/*----------------------------------------------------------------------------*/
    195373void hc_init_hw(hc_t *instance)
    196374{
    197         assert(instance);
     375        /* OHCI guide page 42 */
     376        assert(instance);
     377        usb_log_debug2("Started hc initialization routine.\n");
     378
     379        /* Save contents of fm_interval register */
    198380        const uint32_t fm_interval = instance->registers->fm_interval;
     381        usb_log_debug2("Old value of HcFmInterval: %x.\n", fm_interval);
     382
     383        /* Reset hc */
     384        usb_log_debug2("HC reset.\n");
     385        size_t time = 0;
    199386        instance->registers->command_status = CS_HCR;
    200         async_usleep(10);
     387        while (instance->registers->command_status & CS_HCR) {
     388                async_usleep(10);
     389                time += 10;
     390        }
     391        usb_log_debug2("HC reset complete in %zu us.\n", time);
     392
     393        /* Restore fm_interval */
    201394        instance->registers->fm_interval = fm_interval;
    202395        assert((instance->registers->command_status & CS_HCR) == 0);
     396
    203397        /* hc is now in suspend state */
    204         /* TODO: init HCCA block */
    205         /* TODO: init queues */
    206         /* TODO: enable queues */
    207         /* TODO: enable interrupts */
    208         /* TODO: set periodic start to 90% */
     398        usb_log_debug2("HC should be in suspend state(%x).\n",
     399            instance->registers->control);
     400
     401        /* Use HCCA */
     402        instance->registers->hcca = addr_to_phys(instance->hcca);
     403
     404        /* Use queues */
     405        instance->registers->bulk_head =
     406            instance->lists[USB_TRANSFER_BULK].list_head_pa;
     407        usb_log_debug2("Bulk HEAD set to: %p(%p).\n",
     408            instance->lists[USB_TRANSFER_BULK].list_head,
     409            instance->lists[USB_TRANSFER_BULK].list_head_pa);
     410
     411        instance->registers->control_head =
     412            instance->lists[USB_TRANSFER_CONTROL].list_head_pa;
     413        usb_log_debug2("Control HEAD set to: %p(%p).\n",
     414            instance->lists[USB_TRANSFER_CONTROL].list_head,
     415            instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
     416
     417        /* Enable queues */
     418        instance->registers->control |= (C_PLE | C_IE | C_CLE | C_BLE);
     419        usb_log_debug2("All queues enabled(%x).\n",
     420            instance->registers->control);
     421
     422        /* Disable interrupts */
     423        instance->registers->interrupt_disable = I_SF | I_OC;
     424        usb_log_debug2("Disabling interrupts: %x.\n",
     425            instance->registers->interrupt_disable);
     426        instance->registers->interrupt_disable = I_MI;
     427        usb_log_debug2("Enabled interrupts: %x.\n",
     428            instance->registers->interrupt_enable);
     429
     430        /* Set periodic start to 90% */
     431        uint32_t frame_length = ((fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK);
     432        instance->registers->periodic_start = (frame_length / 10) * 9;
     433        usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).\n",
     434            instance->registers->periodic_start,
     435            instance->registers->periodic_start, frame_length);
    209436
    210437        instance->registers->control &= (C_HCFS_OPERATIONAL << C_HCFS_SHIFT);
    211         usb_log_info("OHCI HC up and running.\n");
     438        usb_log_info("OHCI HC up and running(%x).\n",
     439            instance->registers->control);
     440}
     441/*----------------------------------------------------------------------------*/
     442int hc_init_transfer_lists(hc_t *instance)
     443{
     444        assert(instance);
     445
     446#define SETUP_ENDPOINT_LIST(type) \
     447do { \
     448        const char *name = usb_str_transfer_type(type); \
     449        int ret = endpoint_list_init(&instance->lists[type], name); \
     450        if (ret != EOK) { \
     451                usb_log_error("Failed(%d) to setup %s endpoint list.\n", \
     452                    ret, name); \
     453                endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]); \
     454                endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
     455                endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
     456                endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
     457        } \
     458} while (0)
     459
     460        SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
     461        SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
     462        SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
     463        SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
     464#undef SETUP_ENDPOINT_LIST
     465        endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
     466            &instance->lists[USB_TRANSFER_ISOCHRONOUS]);
     467
     468        return EOK;
     469}
     470/*----------------------------------------------------------------------------*/
     471int hc_init_memory(hc_t *instance)
     472{
     473        assert(instance);
     474        /* Init queues */
     475        hc_init_transfer_lists(instance);
     476
     477        /*Init HCCA */
     478        instance->hcca = malloc32(sizeof(hcca_t));
     479        if (instance->hcca == NULL)
     480                return ENOMEM;
     481        bzero(instance->hcca, sizeof(hcca_t));
     482        usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
     483
     484        unsigned i = 0;
     485        for (; i < 32; ++i) {
     486                instance->hcca->int_ep[i] =
     487                    instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa;
     488        }
     489        usb_log_debug2("Interrupt HEADs set to: %p(%p).\n",
     490            instance->lists[USB_TRANSFER_INTERRUPT].list_head,
     491            instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
     492
     493        return EOK;
    212494}
    213495/**
Note: See TracChangeset for help on using the changeset viewer.