Changeset 00aece0 in mainline for uspace/drv/bus/usb/uhci/hc.c


Ignore:
Timestamp:
2012-02-18T16:47:38Z (13 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
4449c6c
Parents:
bd5f3b7 (diff), f943dd3 (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:

Merge mainline changes.

File:
1 edited

Legend:

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

    rbd5f3b7 r00aece0  
    4141
    4242#include "hc.h"
     43#include "uhci_batch.h"
    4344
    4445#define UHCI_INTR_ALLOW_INTERRUPTS \
     
    4748    (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)
    4849
    49 static const irq_cmd_t uhci_irq_commands[] =
    50 {
     50static const irq_pio_range_t uhci_irq_pio_ranges[] = {
     51        {
     52                .base = 0,      /* filled later */
     53                .size = sizeof(uhci_regs_t)
     54        }
     55};
     56
     57static const irq_cmd_t uhci_irq_commands[] = {
    5158        { .cmd = CMD_PIO_READ_16, .dstarg = 1, .addr = NULL/*filled later*/},
    5259        { .cmd = CMD_BTEST, .srcarg = 1, .dstarg = 2,
     
    5764};
    5865
     66static void hc_init_hw(const hc_t *instance);
     67static int hc_init_mem_structures(hc_t *instance);
    5968static int hc_init_transfer_lists(hc_t *instance);
    60 static int hc_init_mem_structures(hc_t *instance);
    61 static void hc_init_hw(hc_t *instance);
     69static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
    6270
    6371static int hc_interrupt_emulator(void *arg);
     
    6573
    6674/*----------------------------------------------------------------------------*/
     75/** Get number of PIO ranges used in IRQ code.
     76 * @return Number of ranges.
     77 */
     78size_t hc_irq_pio_range_count(void)
     79{
     80        return sizeof(uhci_irq_pio_ranges) / sizeof(irq_pio_range_t);
     81}
     82/*----------------------------------------------------------------------------*/
    6783/** Get number of commands used in IRQ code.
    6884 * @return Number of commands.
     
    7389}
    7490/*----------------------------------------------------------------------------*/
    75 /** Generate IRQ code commands.
    76  * @param[out] cmds Place to store the commands.
    77  * @param[in] cmd_size Size of the place (bytes).
     91/** Generate IRQ code.
     92 * @param[out] ranges PIO ranges buffer.
     93 * @param[in] ranges_size Size of the ranges buffer (bytes).
     94 * @param[out] cmds Commands buffer.
     95 * @param[in] cmds_size Size of the commands buffer (bytes).
    7896 * @param[in] regs Physical address of device's registers.
    7997 * @param[in] reg_size Size of the register area (bytes).
     
    8199 * @return Error code.
    82100 */
    83 int hc_get_irq_commands(
    84     irq_cmd_t cmds[], size_t cmd_size, uintptr_t regs, size_t reg_size)
    85 {
    86         if (cmd_size < sizeof(uhci_irq_commands)
    87             || reg_size < sizeof(uhci_regs_t))
     101int
     102hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
     103    size_t cmds_size, uintptr_t regs, size_t reg_size)
     104{
     105        if ((ranges_size < sizeof(uhci_irq_pio_ranges)) ||
     106            (cmds_size < sizeof(uhci_irq_commands)) ||
     107            (reg_size < sizeof(uhci_regs_t)))
    88108                return EOVERFLOW;
    89109
    90         uhci_regs_t *registers = (uhci_regs_t*)regs;
     110        memcpy(ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges));
     111        ranges[0].base = regs;
    91112
    92113        memcpy(cmds, uhci_irq_commands, sizeof(uhci_irq_commands));
    93 
    94         cmds[0].addr = (void*)&registers->usbsts;
    95         cmds[3].addr = (void*)&registers->usbsts;
    96         return EOK;
     114        uhci_regs_t *registers = (uhci_regs_t *) regs;
     115        cmds[0].addr = &registers->usbsts;
     116        cmds[3].addr = &registers->usbsts;
     117
     118        return EOK;
     119}
     120/*----------------------------------------------------------------------------*/
     121/** Take action based on the interrupt cause.
     122 *
     123 * @param[in] instance UHCI structure to use.
     124 * @param[in] status Value of the status register at the time of interrupt.
     125 *
     126 * Interrupt might indicate:
     127 * - transaction completed, either by triggering IOC, SPD, or an error
     128 * - some kind of device error
     129 * - resume from suspend state (not implemented)
     130 */
     131void hc_interrupt(hc_t *instance, uint16_t status)
     132{
     133        assert(instance);
     134        /* Lower 2 bits are transaction error and transaction complete */
     135        if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
     136                LIST_INITIALIZE(done);
     137                transfer_list_remove_finished(
     138                    &instance->transfers_interrupt, &done);
     139                transfer_list_remove_finished(
     140                    &instance->transfers_control_slow, &done);
     141                transfer_list_remove_finished(
     142                    &instance->transfers_control_full, &done);
     143                transfer_list_remove_finished(
     144                    &instance->transfers_bulk_full, &done);
     145
     146                while (!list_empty(&done)) {
     147                        link_t *item = list_first(&done);
     148                        list_remove(item);
     149                        uhci_transfer_batch_t *batch =
     150                            uhci_transfer_batch_from_link(item);
     151                        uhci_transfer_batch_finish_dispose(batch);
     152                }
     153        }
     154        /* Resume interrupts are not supported */
     155        if (status & UHCI_STATUS_RESUME) {
     156                usb_log_error("Resume interrupt!\n");
     157        }
     158
     159        /* Bits 4 and 5 indicate hc error */
     160        if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
     161                usb_log_error("UHCI hardware failure!.\n");
     162                ++instance->hw_failures;
     163                transfer_list_abort_all(&instance->transfers_interrupt);
     164                transfer_list_abort_all(&instance->transfers_control_slow);
     165                transfer_list_abort_all(&instance->transfers_control_full);
     166                transfer_list_abort_all(&instance->transfers_bulk_full);
     167
     168                if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
     169                        /* reinitialize hw, this triggers virtual disconnect*/
     170                        hc_init_hw(instance);
     171                } else {
     172                        usb_log_fatal("Too many UHCI hardware failures!.\n");
     173                        hc_fini(instance);
     174                }
     175        }
    97176}
    98177/*----------------------------------------------------------------------------*/
     
    137216            str_error(ret));
    138217
     218#undef CHECK_RET_RETURN
     219
     220        hcd_init(&instance->generic, USB_SPEED_FULL,
     221            BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
     222
     223        instance->generic.private_data = instance;
     224        instance->generic.schedule = hc_schedule;
     225        instance->generic.ep_add_hook = NULL;
     226
    139227        hc_init_hw(instance);
    140228        if (!interrupts) {
     
    146234
    147235        return EOK;
    148 #undef CHECK_RET_DEST_FUN_RETURN
    149236}
    150237/*----------------------------------------------------------------------------*/
     
    154241 * For magic values see UHCI Design Guide
    155242 */
    156 void hc_init_hw(hc_t *instance)
     243void hc_init_hw(const hc_t *instance)
    157244{
    158245        assert(instance);
     
    198285 *
    199286 * Structures:
    200  *  - interrupt code (I/O addressses are customized per instance)
    201287 *  - transfer lists (queue heads need to be accessible by the hw)
    202288 *  - frame list page (needs to be one UHCI hw accessible 4K page)
     
    205291{
    206292        assert(instance);
    207 #define CHECK_RET_RETURN(ret, message...) \
    208         if (ret != EOK) { \
    209                 usb_log_error(message); \
    210                 return ret; \
    211         } else (void) 0
     293
     294        /* Init USB frame list page */
     295        instance->frame_list = get_page();
     296        if (!instance->frame_list) {
     297                return ENOMEM;
     298        }
     299        usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
    212300
    213301        /* Init transfer lists */
    214302        int ret = hc_init_transfer_lists(instance);
    215         CHECK_RET_RETURN(ret, "Failed to initialize transfer lists.\n");
     303        if (ret != EOK) {
     304                usb_log_error("Failed to initialize transfer lists.\n");
     305                return_page(instance->frame_list);
     306                return ENOMEM;
     307        }
    216308        usb_log_debug("Initialized transfer lists.\n");
    217309
    218         /* Init device keeper */
    219         usb_device_keeper_init(&instance->manager);
    220         usb_log_debug("Initialized device keeper.\n");
    221 
    222         ret = usb_endpoint_manager_init(&instance->ep_manager,
    223             BANDWIDTH_AVAILABLE_USB11);
    224         CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
    225             str_error(ret));
    226 
    227         /* Init USB frame list page*/
    228         instance->frame_list = get_page();
    229         if (!instance->frame_list) {
    230                 usb_log_error("Failed to get frame list page.\n");
    231                 usb_endpoint_manager_destroy(&instance->ep_manager);
    232                 return ENOMEM;
    233         }
    234         usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
    235310
    236311        /* Set all frames to point to the first queue head */
    237312        const uint32_t queue = LINK_POINTER_QH(
    238313                addr_to_phys(instance->transfers_interrupt.queue_head));
    239         unsigned i = 0;
    240         for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
     314
     315        for (unsigned i = 0; i < UHCI_FRAME_LIST_COUNT; ++i) {
    241316                instance->frame_list[i] = queue;
    242317        }
    243318
    244319        return EOK;
    245 #undef CHECK_RET_RETURN
    246320}
    247321/*----------------------------------------------------------------------------*/
     
    316390 * Checks for bandwidth availability and appends the batch to the proper queue.
    317391 */
    318 int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
    319 {
     392int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
     393{
     394        assert(hcd);
     395        hc_t *instance = hcd->private_data;
    320396        assert(instance);
    321397        assert(batch);
     398        uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
     399        if (!uhci_batch) {
     400                usb_log_error("Failed to create UHCI transfer structures.\n");
     401                return ENOMEM;
     402        }
    322403
    323404        transfer_list_t *list =
    324405            instance->transfers[batch->ep->speed][batch->ep->transfer_type];
    325406        assert(list);
    326         transfer_list_add_batch(list, batch);
    327 
    328         return EOK;
    329 }
    330 /*----------------------------------------------------------------------------*/
    331 /** Take action based on the interrupt cause.
    332  *
    333  * @param[in] instance UHCI structure to use.
    334  * @param[in] status Value of the status register at the time of interrupt.
    335  *
    336  * Interrupt might indicate:
    337  * - transaction completed, either by triggering IOC, SPD, or an error
    338  * - some kind of device error
    339  * - resume from suspend state (not implemented)
    340  */
    341 void hc_interrupt(hc_t *instance, uint16_t status)
    342 {
    343         assert(instance);
    344         /* Lower 2 bits are transaction error and transaction complete */
    345         if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
    346                 LIST_INITIALIZE(done);
    347                 transfer_list_remove_finished(
    348                     &instance->transfers_interrupt, &done);
    349                 transfer_list_remove_finished(
    350                     &instance->transfers_control_slow, &done);
    351                 transfer_list_remove_finished(
    352                     &instance->transfers_control_full, &done);
    353                 transfer_list_remove_finished(
    354                     &instance->transfers_bulk_full, &done);
    355 
    356                 while (!list_empty(&done)) {
    357                         link_t *item = list_first(&done);
    358                         list_remove(item);
    359                         usb_transfer_batch_t *batch =
    360                             list_get_instance(item, usb_transfer_batch_t, link);
    361                         usb_transfer_batch_finish(batch);
    362                 }
    363         }
    364         /* Resume interrupts are not supported */
    365         if (status & UHCI_STATUS_RESUME) {
    366                 usb_log_error("Resume interrupt!\n");
    367         }
    368 
    369         /* Bits 4 and 5 indicate hc error */
    370         if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
    371                 usb_log_error("UHCI hardware failure!.\n");
    372                 ++instance->hw_failures;
    373                 transfer_list_abort_all(&instance->transfers_interrupt);
    374                 transfer_list_abort_all(&instance->transfers_control_slow);
    375                 transfer_list_abort_all(&instance->transfers_control_full);
    376                 transfer_list_abort_all(&instance->transfers_bulk_full);
    377 
    378                 if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
    379                         /* reinitialize hw, this triggers virtual disconnect*/
    380                         hc_init_hw(instance);
    381                 } else {
    382                         usb_log_fatal("Too many UHCI hardware failures!.\n");
    383                         hc_fini(instance);
    384                 }
    385         }
     407        transfer_list_add_batch(list, uhci_batch);
     408
     409        return EOK;
    386410}
    387411/*----------------------------------------------------------------------------*/
     
    403427                if (status != 0)
    404428                        usb_log_debug2("UHCI status: %x.\n", status);
    405 // Qemu fails to report stalled communication
    406 // see https://bugs.launchpad.net/qemu/+bug/757654
    407 // This is a simple workaround to force queue processing every time
    408         //      status |= 1;
    409429                hc_interrupt(instance, status);
    410430                async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
     
    412432        return EOK;
    413433}
    414 /*---------------------------------------------------------------------------*/
     434/*----------------------------------------------------------------------------*/
    415435/** Debug function, checks consistency of memory structures.
    416436 *
Note: See TracChangeset for help on using the changeset viewer.