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 #include <usb/ddfiface.h>
00042
00043 #include "hc.h"
00044 #include "hcd_endpoint.h"
00045
00046 #define OHCI_USED_INTERRUPTS \
00047 (I_SO | I_WDH | I_UE | I_RHSC)
00048 static int interrupt_emulator(hc_t *instance);
00049 static void hc_gain_control(hc_t *instance);
00050 static int hc_init_transfer_lists(hc_t *instance);
00051 static int hc_init_memory(hc_t *instance);
00052
00059 int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
00060 {
00061 assert(instance);
00062 assert(hub_fun);
00063
00064 const usb_address_t hub_address =
00065 device_keeper_get_free_address(&instance->manager, USB_SPEED_FULL);
00066 if (hub_address <= 0) {
00067 usb_log_error("Failed(%d) to get OHCI root hub address.\n",
00068 hub_address);
00069 return hub_address;
00070 }
00071 instance->rh.address = hub_address;
00072 usb_device_keeper_bind(
00073 &instance->manager, hub_address, hub_fun->handle);
00074
00075 #define CHECK_RET_RELEASE(ret, message...) \
00076 if (ret != EOK) { \
00077 usb_log_error(message); \
00078 hc_remove_endpoint(instance, hub_address, 0, USB_DIRECTION_BOTH); \
00079 usb_device_keeper_release(&instance->manager, hub_address); \
00080 return ret; \
00081 } else (void)0
00082
00083 int ret = hc_add_endpoint(instance, hub_address, 0, USB_SPEED_FULL,
00084 USB_TRANSFER_CONTROL, USB_DIRECTION_BOTH, 64, 0, 0);
00085 CHECK_RET_RELEASE(ret, "Failed(%d) to add OHCI rh endpoint 0.\n", ret);
00086
00087 char *match_str = NULL;
00088
00089 ret = asprintf(&match_str, "usb&class=hub");
00090 ret = ret > 0 ? 0 : ret;
00091 CHECK_RET_RELEASE(ret, "Failed(%d) to create match-id string.\n", ret);
00092
00093 ret = ddf_fun_add_match_id(hub_fun, match_str, 100);
00094 CHECK_RET_RELEASE(ret, "Failed(%d) add root hub match-id.\n", ret);
00095
00096 ret = ddf_fun_bind(hub_fun);
00097 CHECK_RET_RELEASE(ret, "Failed(%d) to bind root hub function.\n", ret);
00098
00099 return EOK;
00100 #undef CHECK_RET_RELEASE
00101 }
00102
00111 int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
00112 {
00113 assert(instance);
00114 int ret = EOK;
00115 #define CHECK_RET_RETURN(ret, message...) \
00116 if (ret != EOK) { \
00117 usb_log_error(message); \
00118 return ret; \
00119 } else (void)0
00120
00121 ret = pio_enable((void*)regs, reg_size, (void**)&instance->registers);
00122 CHECK_RET_RETURN(ret,
00123 "Failed(%d) to gain access to device registers: %s.\n",
00124 ret, str_error(ret));
00125
00126 list_initialize(&instance->pending_batches);
00127 usb_device_keeper_init(&instance->manager);
00128 ret = usb_endpoint_manager_init(&instance->ep_manager,
00129 BANDWIDTH_AVAILABLE_USB11);
00130 CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
00131 str_error(ret));
00132
00133 ret = hc_init_memory(instance);
00134 CHECK_RET_RETURN(ret, "Failed to create OHCI memory structures: %s.\n",
00135 str_error(ret));
00136 #undef CHECK_RET_RETURN
00137
00138 fibril_mutex_initialize(&instance->guard);
00139 hc_gain_control(instance);
00140
00141 rh_init(&instance->rh, instance->registers);
00142
00143 if (!interrupts) {
00144 instance->interrupt_emulator =
00145 fibril_create((int(*)(void*))interrupt_emulator, instance);
00146 fibril_add_ready(instance->interrupt_emulator);
00147 }
00148
00149 return EOK;
00150 }
00151
00165 int hc_add_endpoint(
00166 hc_t *instance, usb_address_t address, usb_endpoint_t endpoint,
00167 usb_speed_t speed, usb_transfer_type_t type, usb_direction_t direction,
00168 size_t mps, size_t size, unsigned interval)
00169 {
00170 endpoint_t *ep = malloc(sizeof(endpoint_t));
00171 if (ep == NULL)
00172 return ENOMEM;
00173 int ret =
00174 endpoint_init(ep, address, endpoint, direction, type, speed, mps);
00175 if (ret != EOK) {
00176 free(ep);
00177 return ret;
00178 }
00179
00180 hcd_endpoint_t *hcd_ep = hcd_endpoint_assign(ep);
00181 if (hcd_ep == NULL) {
00182 endpoint_destroy(ep);
00183 return ENOMEM;
00184 }
00185
00186 ret = usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size);
00187 if (ret != EOK) {
00188 hcd_endpoint_clear(ep);
00189 endpoint_destroy(ep);
00190 return ret;
00191 }
00192
00193
00194 switch (ep->transfer_type) {
00195 case USB_TRANSFER_CONTROL:
00196 instance->registers->control &= ~C_CLE;
00197 endpoint_list_add_ep(
00198 &instance->lists[ep->transfer_type], hcd_ep);
00199 instance->registers->control_current = 0;
00200 instance->registers->control |= C_CLE;
00201 break;
00202 case USB_TRANSFER_BULK:
00203 instance->registers->control &= ~C_BLE;
00204 endpoint_list_add_ep(
00205 &instance->lists[ep->transfer_type], hcd_ep);
00206 instance->registers->control |= C_BLE;
00207 break;
00208 case USB_TRANSFER_ISOCHRONOUS:
00209 case USB_TRANSFER_INTERRUPT:
00210 instance->registers->control &= (~C_PLE & ~C_IE);
00211 endpoint_list_add_ep(
00212 &instance->lists[ep->transfer_type], hcd_ep);
00213 instance->registers->control |= C_PLE | C_IE;
00214 break;
00215 default:
00216 break;
00217 }
00218
00219 return EOK;
00220 }
00221
00230 int hc_remove_endpoint(hc_t *instance, usb_address_t address,
00231 usb_endpoint_t endpoint, usb_direction_t direction)
00232 {
00233 assert(instance);
00234 fibril_mutex_lock(&instance->guard);
00235 endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
00236 address, endpoint, direction, NULL);
00237 if (ep == NULL) {
00238 usb_log_error("Endpoint unregister failed: No such EP.\n");
00239 fibril_mutex_unlock(&instance->guard);
00240 return ENOENT;
00241 }
00242
00243 hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
00244 if (hcd_ep) {
00245
00246 switch (ep->transfer_type) {
00247 case USB_TRANSFER_CONTROL:
00248 instance->registers->control &= ~C_CLE;
00249 endpoint_list_remove_ep(
00250 &instance->lists[ep->transfer_type], hcd_ep);
00251 instance->registers->control_current = 0;
00252 instance->registers->control |= C_CLE;
00253 break;
00254 case USB_TRANSFER_BULK:
00255 instance->registers->control &= ~C_BLE;
00256 endpoint_list_remove_ep(
00257 &instance->lists[ep->transfer_type], hcd_ep);
00258 instance->registers->control |= C_BLE;
00259 break;
00260 case USB_TRANSFER_ISOCHRONOUS:
00261 case USB_TRANSFER_INTERRUPT:
00262 instance->registers->control &= (~C_PLE & ~C_IE);
00263 endpoint_list_remove_ep(
00264 &instance->lists[ep->transfer_type], hcd_ep);
00265 instance->registers->control |= C_PLE | C_IE;
00266 break;
00267 default:
00268 break;
00269 }
00270 hcd_endpoint_clear(ep);
00271 } else {
00272 usb_log_warning("Endpoint without hcd equivalent structure.\n");
00273 }
00274 int ret = usb_endpoint_manager_unregister_ep(&instance->ep_manager,
00275 address, endpoint, direction);
00276 fibril_mutex_unlock(&instance->guard);
00277 return ret;
00278 }
00279
00289 endpoint_t * hc_get_endpoint(hc_t *instance, usb_address_t address,
00290 usb_endpoint_t endpoint, usb_direction_t direction, size_t *bw)
00291 {
00292 assert(instance);
00293 fibril_mutex_lock(&instance->guard);
00294 endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
00295 address, endpoint, direction, bw);
00296 fibril_mutex_unlock(&instance->guard);
00297 return ep;
00298 }
00299
00306 int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
00307 {
00308 assert(instance);
00309 assert(batch);
00310 assert(batch->ep);
00311
00312
00313 if (batch->ep->address == instance->rh.address) {
00314 return rh_request(&instance->rh, batch);
00315 }
00316
00317 fibril_mutex_lock(&instance->guard);
00318 list_append(&batch->link, &instance->pending_batches);
00319 batch_commit(batch);
00320
00321
00322 switch (batch->ep->transfer_type)
00323 {
00324 case USB_TRANSFER_CONTROL:
00325 instance->registers->command_status |= CS_CLF;
00326 break;
00327 case USB_TRANSFER_BULK:
00328 instance->registers->command_status |= CS_BLF;
00329 break;
00330 default:
00331 break;
00332 }
00333 fibril_mutex_unlock(&instance->guard);
00334 return EOK;
00335 }
00336
00342 void hc_interrupt(hc_t *instance, uint32_t status)
00343 {
00344 assert(instance);
00345 if ((status & ~I_SF) == 0)
00346 return;
00347 usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
00348 if (status & I_RHSC)
00349 rh_interrupt(&instance->rh);
00350
00351 if (status & I_WDH) {
00352 fibril_mutex_lock(&instance->guard);
00353 usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).\n", instance->hcca,
00354 instance->registers->hcca,
00355 (void *) addr_to_phys(instance->hcca));
00356 usb_log_debug2("Periodic current: %#" PRIx32 ".\n",
00357 instance->registers->periodic_current);
00358
00359 link_t *current = instance->pending_batches.next;
00360 while (current != &instance->pending_batches) {
00361 link_t *next = current->next;
00362 usb_transfer_batch_t *batch =
00363 usb_transfer_batch_from_link(current);
00364
00365 if (batch_is_complete(batch)) {
00366 list_remove(current);
00367 usb_transfer_batch_finish(batch);
00368 }
00369 current = next;
00370 }
00371 fibril_mutex_unlock(&instance->guard);
00372 }
00373
00374 if (status & I_UE) {
00375 hc_start_hw(instance);
00376 }
00377
00378 }
00379
00385 int interrupt_emulator(hc_t *instance)
00386 {
00387 assert(instance);
00388 usb_log_info("Started interrupt emulator.\n");
00389 while (1) {
00390 const uint32_t status = instance->registers->interrupt_status;
00391 instance->registers->interrupt_status = status;
00392 hc_interrupt(instance, status);
00393 async_usleep(10000);
00394 }
00395 return EOK;
00396 }
00397
00402 void hc_gain_control(hc_t *instance)
00403 {
00404 assert(instance);
00405 usb_log_debug("Requesting OHCI control.\n");
00406
00407 volatile uint32_t *ohci_emulation_reg =
00408 (uint32_t*)((char*)instance->registers + 0x100);
00409 usb_log_debug("OHCI legacy register %p: %x.\n",
00410 ohci_emulation_reg, *ohci_emulation_reg);
00411
00412 *ohci_emulation_reg &= 0x100;
00413 usb_log_debug("OHCI legacy register %p: %x.\n",
00414 ohci_emulation_reg, *ohci_emulation_reg);
00415
00416
00417 if (instance->registers->control & C_IR) {
00418 usb_log_debug("SMM driver: request ownership change.\n");
00419 instance->registers->command_status |= CS_OCR;
00420 while (instance->registers->control & C_IR) {
00421 async_usleep(1000);
00422 }
00423 usb_log_info("SMM driver: Ownership taken.\n");
00424 instance->registers->control &= (C_HCFS_RESET << C_HCFS_SHIFT);
00425 async_usleep(50000);
00426 return;
00427 }
00428
00429 const unsigned hc_status =
00430 (instance->registers->control >> C_HCFS_SHIFT) & C_HCFS_MASK;
00431
00432 if (hc_status != C_HCFS_RESET) {
00433 usb_log_debug("BIOS driver found.\n");
00434 if (hc_status == C_HCFS_OPERATIONAL) {
00435 usb_log_info("BIOS driver: HC operational.\n");
00436 return;
00437 }
00438
00439 instance->registers->control &= (C_HCFS_RESUME << C_HCFS_SHIFT);
00440 async_usleep(20000);
00441 usb_log_info("BIOS driver: HC resumed.\n");
00442 return;
00443 }
00444
00445
00446
00447 usb_log_info("HC found in reset.\n");
00448 async_usleep(50000);
00449 }
00450
00455 void hc_start_hw(hc_t *instance)
00456 {
00457
00458 assert(instance);
00459 usb_log_debug2("Started hc initialization routine.\n");
00460
00461
00462 const uint32_t fm_interval = instance->registers->fm_interval;
00463 usb_log_debug2("Old value of HcFmInterval: %x.\n", fm_interval);
00464
00465
00466 usb_log_debug2("HC reset.\n");
00467 size_t time = 0;
00468 instance->registers->command_status = CS_HCR;
00469 while (instance->registers->command_status & CS_HCR) {
00470 async_usleep(10);
00471 time += 10;
00472 }
00473 usb_log_debug2("HC reset complete in %zu us.\n", time);
00474
00475
00476 instance->registers->fm_interval = fm_interval;
00477 assert((instance->registers->command_status & CS_HCR) == 0);
00478
00479
00480 usb_log_debug2("HC should be in suspend state(%x).\n",
00481 instance->registers->control);
00482
00483
00484 instance->registers->hcca = addr_to_phys(instance->hcca);
00485
00486
00487 instance->registers->bulk_head =
00488 instance->lists[USB_TRANSFER_BULK].list_head_pa;
00489 usb_log_debug2("Bulk HEAD set to: %p (%#" PRIx32 ").\n",
00490 instance->lists[USB_TRANSFER_BULK].list_head,
00491 instance->lists[USB_TRANSFER_BULK].list_head_pa);
00492
00493 instance->registers->control_head =
00494 instance->lists[USB_TRANSFER_CONTROL].list_head_pa;
00495 usb_log_debug2("Control HEAD set to: %p (%#" PRIx32 ").\n",
00496 instance->lists[USB_TRANSFER_CONTROL].list_head,
00497 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
00498
00499
00500 instance->registers->control |= (C_PLE | C_IE | C_CLE | C_BLE);
00501 usb_log_debug2("All queues enabled(%x).\n",
00502 instance->registers->control);
00503
00504
00505 instance->registers->interrupt_enable = OHCI_USED_INTERRUPTS;
00506 usb_log_debug2("Enabled interrupts: %x.\n",
00507 instance->registers->interrupt_enable);
00508 instance->registers->interrupt_enable = I_MI;
00509
00510
00511 uint32_t frame_length = ((fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK);
00512 instance->registers->periodic_start = (frame_length / 10) * 9;
00513 usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).\n",
00514 instance->registers->periodic_start,
00515 instance->registers->periodic_start, frame_length);
00516
00517 instance->registers->control &= (C_HCFS_OPERATIONAL << C_HCFS_SHIFT);
00518 usb_log_info("OHCI HC up and running(%x).\n",
00519 instance->registers->control);
00520 }
00521
00527 int hc_init_transfer_lists(hc_t *instance)
00528 {
00529 assert(instance);
00530 #define SETUP_ENDPOINT_LIST(type) \
00531 do { \
00532 const char *name = usb_str_transfer_type(type); \
00533 int ret = endpoint_list_init(&instance->lists[type], name); \
00534 if (ret != EOK) { \
00535 usb_log_error("Failed(%d) to setup %s endpoint list.\n", \
00536 ret, name); \
00537 endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\
00538 endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
00539 endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
00540 endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
00541 return ret; \
00542 } \
00543 } while (0)
00544
00545 SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
00546 SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
00547 SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
00548 SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
00549 #undef SETUP_ENDPOINT_LIST
00550 endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
00551 &instance->lists[USB_TRANSFER_ISOCHRONOUS]);
00552
00553 return EOK;
00554 }
00555
00561 int hc_init_memory(hc_t *instance)
00562 {
00563 assert(instance);
00564
00565 bzero(&instance->rh, sizeof(instance->rh));
00566
00567 const int ret = hc_init_transfer_lists(instance);
00568 if (ret != EOK) {
00569 return ret;
00570 }
00571
00572
00573 instance->hcca = malloc32(sizeof(hcca_t));
00574 if (instance->hcca == NULL)
00575 return ENOMEM;
00576 bzero(instance->hcca, sizeof(hcca_t));
00577 usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
00578
00579 unsigned i = 0;
00580 for (; i < 32; ++i) {
00581 instance->hcca->int_ep[i] =
00582 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa;
00583 }
00584 usb_log_debug2("Interrupt HEADs set to: %p (%#" PRIx32 ").\n",
00585 instance->lists[USB_TRANSFER_INTERRUPT].list_head,
00586 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
00587
00588
00589 instance->interrupt_code.cmds = instance->interrupt_commands;
00590 instance->interrupt_code.cmdcount = OHCI_NEEDED_IRQ_COMMANDS;
00591 {
00592
00593 instance->interrupt_commands[0].cmd = CMD_MEM_READ_32;
00594 instance->interrupt_commands[0].dstarg = 1;
00595 instance->interrupt_commands[0].addr =
00596 (void*)&instance->registers->interrupt_status;
00597
00598
00599 instance->interrupt_commands[1].cmd = CMD_BTEST;
00600 instance->interrupt_commands[1].value =
00601 OHCI_USED_INTERRUPTS;
00602 instance->interrupt_commands[1].srcarg = 1;
00603 instance->interrupt_commands[1].dstarg = 2;
00604
00605
00606 instance->interrupt_commands[2].cmd = CMD_PREDICATE;
00607 instance->interrupt_commands[2].value = 2;
00608 instance->interrupt_commands[2].srcarg = 2;
00609
00610
00611 instance->interrupt_commands[3].cmd = CMD_MEM_WRITE_A_32;
00612 instance->interrupt_commands[3].srcarg = 1;
00613 instance->interrupt_commands[3].addr =
00614 (void*)&instance->registers->interrupt_status;
00615
00616
00617 instance->interrupt_commands[4].cmd = CMD_ACCEPT;
00618 }
00619
00620 return EOK;
00621 }