hc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Jan Vesely
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00034 #include <errno.h>
00035 #include <str_error.h>
00036 #include <adt/list.h>
00037 #include <libarch/ddi.h>
00038 
00039 #include <usb/debug.h>
00040 #include <usb/usb.h>
00041 
00042 #include "hc.h"
00043 
00044 #define UHCI_INTR_ALLOW_INTERRUPTS \
00045     (UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET)
00046 #define UHCI_STATUS_USED_INTERRUPTS \
00047     (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)
00048 
00049 
00050 static int hc_init_transfer_lists(hc_t *instance);
00051 static int hc_init_mem_structures(hc_t *instance);
00052 static void hc_init_hw(hc_t *instance);
00053 
00054 static int hc_interrupt_emulator(void *arg);
00055 static int hc_debug_checker(void *arg);
00056 /*----------------------------------------------------------------------------*/
00069 int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interrupts)
00070 {
00071         assert(reg_size >= sizeof(regs_t));
00072         int ret;
00073 
00074 #define CHECK_RET_RETURN(ret, message...) \
00075         if (ret != EOK) { \
00076                 usb_log_error(message); \
00077                 return ret; \
00078         } else (void) 0
00079 
00080         instance->hw_interrupts = interrupts;
00081         instance->hw_failures = 0;
00082 
00083         /* allow access to hc control registers */
00084         regs_t *io;
00085         ret = pio_enable(regs, reg_size, (void **)&io);
00086         CHECK_RET_RETURN(ret,
00087             "Failed(%d) to gain access to registers at %p: %s.\n",
00088             ret, io, str_error(ret));
00089         instance->registers = io;
00090         usb_log_debug("Device registers at %p (%zuB) accessible.\n",
00091             io, reg_size);
00092 
00093         ret = hc_init_mem_structures(instance);
00094         CHECK_RET_RETURN(ret,
00095             "Failed(%d) to initialize UHCI memory structures: %s.\n",
00096             ret, str_error(ret));
00097 
00098         hc_init_hw(instance);
00099         if (!interrupts) {
00100                 instance->interrupt_emulator =
00101                     fibril_create(hc_interrupt_emulator, instance);
00102                 fibril_add_ready(instance->interrupt_emulator);
00103         }
00104         (void)hc_debug_checker;
00105 
00106         return EOK;
00107 #undef CHECK_RET_DEST_FUN_RETURN
00108 }
00109 /*----------------------------------------------------------------------------*/
00115 void hc_init_hw(hc_t *instance)
00116 {
00117         assert(instance);
00118         regs_t *registers = instance->registers;
00119 
00120         /* Reset everything, who knows what touched it before us */
00121         pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
00122         async_usleep(10000); /* 10ms according to USB spec */
00123         pio_write_16(&registers->usbcmd, 0);
00124 
00125         /* Reset hc, all states and counters */
00126         pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
00127         do { async_usleep(10); }
00128         while ((pio_read_16(&registers->usbcmd) & UHCI_CMD_HCRESET) != 0);
00129 
00130         /* Set frame to exactly 1ms */
00131         pio_write_8(&registers->sofmod, 64);
00132 
00133         /* Set frame list pointer */
00134         const uint32_t pa = addr_to_phys(instance->frame_list);
00135         pio_write_32(&registers->flbaseadd, pa);
00136 
00137         if (instance->hw_interrupts) {
00138                 /* Enable all interrupts, but resume interrupt */
00139                 pio_write_16(&instance->registers->usbintr,
00140                     UHCI_INTR_ALLOW_INTERRUPTS);
00141         }
00142 
00143         const uint16_t status = pio_read_16(&registers->usbcmd);
00144         if (status != 0)
00145                 usb_log_warning("Previous command value: %x.\n", status);
00146 
00147         /* Start the hc with large(64B) packet FSBR */
00148         pio_write_16(&registers->usbcmd,
00149             UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE);
00150 }
00151 /*----------------------------------------------------------------------------*/
00163 int hc_init_mem_structures(hc_t *instance)
00164 {
00165         assert(instance);
00166 #define CHECK_RET_RETURN(ret, message...) \
00167         if (ret != EOK) { \
00168                 usb_log_error(message); \
00169                 return ret; \
00170         } else (void) 0
00171 
00172         /* Init interrupt code */
00173         instance->interrupt_code.cmds = instance->interrupt_commands;
00174         {
00175                 /* Read status register */
00176                 instance->interrupt_commands[0].cmd = CMD_PIO_READ_16;
00177                 instance->interrupt_commands[0].dstarg = 1;
00178                 instance->interrupt_commands[0].addr =
00179                     &instance->registers->usbsts;
00180 
00181                 /* Test whether we are the interrupt cause */
00182                 instance->interrupt_commands[1].cmd = CMD_BTEST;
00183                 instance->interrupt_commands[1].value =
00184                     UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS;
00185                 instance->interrupt_commands[1].srcarg = 1;
00186                 instance->interrupt_commands[1].dstarg = 2;
00187 
00188                 /* Predicate cleaning and accepting */
00189                 instance->interrupt_commands[2].cmd = CMD_PREDICATE;
00190                 instance->interrupt_commands[2].value = 2;
00191                 instance->interrupt_commands[2].srcarg = 2;
00192 
00193                 /* Write clean status register */
00194                 instance->interrupt_commands[3].cmd = CMD_PIO_WRITE_A_16;
00195                 instance->interrupt_commands[3].srcarg = 1;
00196                 instance->interrupt_commands[3].addr =
00197                     &instance->registers->usbsts;
00198 
00199                 /* Accept interrupt */
00200                 instance->interrupt_commands[4].cmd = CMD_ACCEPT;
00201 
00202                 instance->interrupt_code.cmdcount = UHCI_NEEDED_IRQ_COMMANDS;
00203         }
00204 
00205         /* Init transfer lists */
00206         int ret = hc_init_transfer_lists(instance);
00207         CHECK_RET_RETURN(ret, "Failed to init transfer lists.\n");
00208         usb_log_debug("Initialized transfer lists.\n");
00209 
00210         /* Init USB frame list page*/
00211         instance->frame_list = get_page();
00212         ret = instance->frame_list ? EOK : ENOMEM;
00213         CHECK_RET_RETURN(ret, "Failed to get frame list page.\n");
00214         usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
00215 
00216         /* Set all frames to point to the first queue head */
00217         const uint32_t queue = LINK_POINTER_QH(
00218                 addr_to_phys(instance->transfers_interrupt.queue_head));
00219 
00220         unsigned i = 0;
00221         for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
00222                 instance->frame_list[i] = queue;
00223         }
00224 
00225         /* Init device keeper */
00226         usb_device_keeper_init(&instance->manager);
00227         usb_log_debug("Initialized device manager.\n");
00228 
00229         ret = usb_endpoint_manager_init(&instance->ep_manager,
00230             BANDWIDTH_AVAILABLE_USB11);
00231         CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
00232             str_error(ret));
00233 
00234         return EOK;
00235 #undef CHECK_RET_RETURN
00236 }
00237 /*----------------------------------------------------------------------------*/
00247 int hc_init_transfer_lists(hc_t *instance)
00248 {
00249         assert(instance);
00250 #define SETUP_TRANSFER_LIST(type, name) \
00251 do { \
00252         int ret = transfer_list_init(&instance->transfers_##type, name); \
00253         if (ret != EOK) { \
00254                 usb_log_error("Failed(%d) to setup %s transfer list: %s.\n", \
00255                     ret, name, str_error(ret)); \
00256                 transfer_list_fini(&instance->transfers_bulk_full); \
00257                 transfer_list_fini(&instance->transfers_control_full); \
00258                 transfer_list_fini(&instance->transfers_control_slow); \
00259                 transfer_list_fini(&instance->transfers_interrupt); \
00260                 return ret; \
00261         } \
00262 } while (0)
00263 
00264         SETUP_TRANSFER_LIST(bulk_full, "BULK FULL");
00265         SETUP_TRANSFER_LIST(control_full, "CONTROL FULL");
00266         SETUP_TRANSFER_LIST(control_slow, "CONTROL LOW");
00267         SETUP_TRANSFER_LIST(interrupt, "INTERRUPT");
00268 #undef SETUP_TRANSFER_LIST
00269         /* Connect lists into one schedule */
00270         transfer_list_set_next(&instance->transfers_control_full,
00271                 &instance->transfers_bulk_full);
00272         transfer_list_set_next(&instance->transfers_control_slow,
00273                 &instance->transfers_control_full);
00274         transfer_list_set_next(&instance->transfers_interrupt,
00275                 &instance->transfers_control_slow);
00276 
00277         /*FSBR, This feature is not needed (adds no benefit) and is supposedly
00278          * buggy on certain hw, enable at your own risk. */
00279 #ifdef FSBR
00280         transfer_list_set_next(&instance->transfers_bulk_full,
00281             &instance->transfers_control_full);
00282 #endif
00283 
00284         /* Assign pointers to be used during scheduling */
00285         instance->transfers[USB_SPEED_FULL][USB_TRANSFER_INTERRUPT] =
00286           &instance->transfers_interrupt;
00287         instance->transfers[USB_SPEED_LOW][USB_TRANSFER_INTERRUPT] =
00288           &instance->transfers_interrupt;
00289         instance->transfers[USB_SPEED_FULL][USB_TRANSFER_CONTROL] =
00290           &instance->transfers_control_full;
00291         instance->transfers[USB_SPEED_LOW][USB_TRANSFER_CONTROL] =
00292           &instance->transfers_control_slow;
00293         instance->transfers[USB_SPEED_FULL][USB_TRANSFER_BULK] =
00294           &instance->transfers_bulk_full;
00295 
00296         return EOK;
00297 #undef CHECK_RET_CLEAR_RETURN
00298 }
00299 /*----------------------------------------------------------------------------*/
00308 int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
00309 {
00310         assert(instance);
00311         assert(batch);
00312 
00313         transfer_list_t *list =
00314             instance->transfers[batch->ep->speed][batch->ep->transfer_type];
00315         assert(list);
00316         transfer_list_add_batch(list, batch);
00317 
00318         return EOK;
00319 }
00320 /*----------------------------------------------------------------------------*/
00331 void hc_interrupt(hc_t *instance, uint16_t status)
00332 {
00333         assert(instance);
00334         /* Lower 2 bits are transaction error and transaction complete */
00335         if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
00336                 LIST_INITIALIZE(done);
00337                 transfer_list_remove_finished(
00338                     &instance->transfers_interrupt, &done);
00339                 transfer_list_remove_finished(
00340                     &instance->transfers_control_slow, &done);
00341                 transfer_list_remove_finished(
00342                     &instance->transfers_control_full, &done);
00343                 transfer_list_remove_finished(
00344                     &instance->transfers_bulk_full, &done);
00345 
00346                 while (!list_empty(&done)) {
00347                         link_t *item = done.next;
00348                         list_remove(item);
00349                         usb_transfer_batch_t *batch =
00350                             list_get_instance(item, usb_transfer_batch_t, link);
00351                         usb_transfer_batch_finish(batch);
00352                 }
00353         }
00354         /* Resume interrupts are not supported */
00355         if (status & UHCI_STATUS_RESUME) {
00356                 usb_log_error("Resume interrupt!\n");
00357         }
00358 
00359         /* Bits 4 and 5 indicate hc error */
00360         if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
00361                 usb_log_error("UHCI hardware failure!.\n");
00362                 ++instance->hw_failures;
00363                 transfer_list_abort_all(&instance->transfers_interrupt);
00364                 transfer_list_abort_all(&instance->transfers_control_slow);
00365                 transfer_list_abort_all(&instance->transfers_control_full);
00366                 transfer_list_abort_all(&instance->transfers_bulk_full);
00367 
00368                 if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
00369                         /* reinitialize hw, this triggers virtual disconnect*/
00370                         hc_init_hw(instance);
00371                 } else {
00372                         usb_log_fatal("Too many UHCI hardware failures!.\n");
00373                         hc_fini(instance);
00374                 }
00375         }
00376 }
00377 /*----------------------------------------------------------------------------*/
00383 int hc_interrupt_emulator(void* arg)
00384 {
00385         usb_log_debug("Started interrupt emulator.\n");
00386         hc_t *instance = arg;
00387         assert(instance);
00388 
00389         while (1) {
00390                 /* Read and clear status register */
00391                 uint16_t status = pio_read_16(&instance->registers->usbsts);
00392                 pio_write_16(&instance->registers->usbsts, status);
00393                 if (status != 0)
00394                         usb_log_debug2("UHCI status: %x.\n", status);
00395 // Qemu fails to report stalled communication
00396 // see https://bugs.launchpad.net/qemu/+bug/757654
00397 // This is a simple workaround to force queue processing every time
00398         //      status |= 1;
00399                 hc_interrupt(instance, status);
00400                 async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
00401         }
00402         return EOK;
00403 }
00404 /*---------------------------------------------------------------------------*/
00410 int hc_debug_checker(void *arg)
00411 {
00412         hc_t *instance = arg;
00413         assert(instance);
00414 
00415 #define QH(queue) \
00416         instance->transfers_##queue.queue_head
00417 
00418         while (1) {
00419                 const uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
00420                 const uint16_t sts = pio_read_16(&instance->registers->usbsts);
00421                 const uint16_t intr =
00422                     pio_read_16(&instance->registers->usbintr);
00423 
00424                 if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) {
00425                         usb_log_debug2("Command: %X Status: %X Intr: %x\n",
00426                             cmd, sts, intr);
00427                 }
00428 
00429                 const uintptr_t frame_list =
00430                     pio_read_32(&instance->registers->flbaseadd) & ~0xfff;
00431                 if (frame_list != addr_to_phys(instance->frame_list)) {
00432                         usb_log_debug("Framelist address: %p vs. %p.\n",
00433                             (void *) frame_list,
00434                             (void *) addr_to_phys(instance->frame_list));
00435                 }
00436 
00437                 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
00438 
00439                 uintptr_t expected_pa = instance->frame_list[frnum]
00440                     & LINK_POINTER_ADDRESS_MASK;
00441                 uintptr_t real_pa = addr_to_phys(QH(interrupt));
00442                 if (expected_pa != real_pa) {
00443                         usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.\n",
00444                             (void *) expected_pa, frnum, (void *) real_pa);
00445                 }
00446 
00447                 expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK;
00448                 real_pa = addr_to_phys(QH(control_slow));
00449                 if (expected_pa != real_pa) {
00450                         usb_log_debug("Control Slow QH: %p vs. %p.\n",
00451                             (void *) expected_pa, (void *) real_pa);
00452                 }
00453 
00454                 expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK;
00455                 real_pa = addr_to_phys(QH(control_full));
00456                 if (expected_pa != real_pa) {
00457                         usb_log_debug("Control Full QH: %p vs. %p.\n",
00458                             (void *) expected_pa, (void *) real_pa);
00459                 }
00460 
00461                 expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK;
00462                 real_pa = addr_to_phys(QH(bulk_full));
00463                 if (expected_pa != real_pa ) {
00464                         usb_log_debug("Bulk QH: %p vs. %p.\n",
00465                             (void *) expected_pa, (void *) real_pa);
00466                 }
00467                 async_usleep(UHCI_DEBUGER_TIMEOUT);
00468         }
00469         return EOK;
00470 #undef QH
00471 }

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7