00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
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
00121 pio_write_16(®isters->usbcmd, UHCI_CMD_GLOBAL_RESET);
00122 async_usleep(10000);
00123 pio_write_16(®isters->usbcmd, 0);
00124
00125
00126 pio_write_16(®isters->usbcmd, UHCI_CMD_HCRESET);
00127 do { async_usleep(10); }
00128 while ((pio_read_16(®isters->usbcmd) & UHCI_CMD_HCRESET) != 0);
00129
00130
00131 pio_write_8(®isters->sofmod, 64);
00132
00133
00134 const uint32_t pa = addr_to_phys(instance->frame_list);
00135 pio_write_32(®isters->flbaseadd, pa);
00136
00137 if (instance->hw_interrupts) {
00138
00139 pio_write_16(&instance->registers->usbintr,
00140 UHCI_INTR_ALLOW_INTERRUPTS);
00141 }
00142
00143 const uint16_t status = pio_read_16(®isters->usbcmd);
00144 if (status != 0)
00145 usb_log_warning("Previous command value: %x.\n", status);
00146
00147
00148 pio_write_16(®isters->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
00173 instance->interrupt_code.cmds = instance->interrupt_commands;
00174 {
00175
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
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
00189 instance->interrupt_commands[2].cmd = CMD_PREDICATE;
00190 instance->interrupt_commands[2].value = 2;
00191 instance->interrupt_commands[2].srcarg = 2;
00192
00193
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
00200 instance->interrupt_commands[4].cmd = CMD_ACCEPT;
00201
00202 instance->interrupt_code.cmdcount = UHCI_NEEDED_IRQ_COMMANDS;
00203 }
00204
00205
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
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
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
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
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
00278
00279 #ifdef FSBR
00280 transfer_list_set_next(&instance->transfers_bulk_full,
00281 &instance->transfers_control_full);
00282 #endif
00283
00284
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
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
00355 if (status & UHCI_STATUS_RESUME) {
00356 usb_log_error("Resume interrupt!\n");
00357 }
00358
00359
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
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
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
00396
00397
00398
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 }