static int usb_generic_hid_get_event(ddf_fun_t *fun, uint8_t *buffer, size_t size, size_t *act_size, int *event_nr, unsigned int flags) { usb_log_debug2("Generic HID: Get event.\n"); if (fun == NULL || fun->driver_data == NULL || buffer == NULL || act_size == NULL || event_nr == NULL) { usb_log_debug("No function"); return EINVAL; } const usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)fun->driver_data; if (hid_dev->input_report_size > size) { usb_log_debug("input_report_size > size (%zu, %zu)\n", hid_dev->input_report_size, size); return EINVAL; // TODO: other error code } /*! @todo This should probably be somehow atomic. */ memcpy(buffer, hid_dev->input_report, hid_dev->input_report_size); *act_size = hid_dev->input_report_size; *event_nr = usb_hid_report_number(hid_dev); usb_log_debug2("OK\n"); return EOK; }
static size_t usb_generic_hid_get_event_length(ddf_fun_t *fun) { usb_log_debug2("Generic HID: Get event length (fun: %p, " "fun->driver_data: %p.\n", fun, fun->driver_data); if (fun == NULL || fun->driver_data == NULL) { return 0; } const usb_hid_dev_t *hid_dev = fun->driver_data; usb_log_debug2("hid_dev: %p, Max input report size (%zu).\n", hid_dev, hid_dev->max_input_report_size); return hid_dev->max_input_report_size; }
/** Add endpoint to the list and queue. * * @param[in] instance List to use. * @param[in] endpoint Endpoint to add. * * The endpoint is added to the end of the list and queue. */ void endpoint_list_add_ep(endpoint_list_t *instance, ohci_endpoint_t *ep) { assert(instance); assert(ep); usb_log_debug2("Queue %s: Adding endpoint(%p).\n", instance->name, ep); fibril_mutex_lock(&instance->guard); ed_t *last_ed = NULL; /* Add to the hardware queue. */ if (list_empty(&instance->endpoint_list)) { /* There are no active EDs */ last_ed = instance->list_head; } else { /* There are active EDs, get the last one */ ohci_endpoint_t *last = list_get_instance( list_last(&instance->endpoint_list), ohci_endpoint_t, link); last_ed = last->ed; } /* Keep link */ ep->ed->next = last_ed->next; /* Make sure ED is written to the memory */ write_barrier(); /* Add ed to the hw queue */ ed_append_ed(last_ed, ep->ed); /* Make sure ED is updated */ write_barrier(); /* Add to the sw list */ list_append(&ep->link, &instance->endpoint_list); ohci_endpoint_t *first = list_get_instance( list_first(&instance->endpoint_list), ohci_endpoint_t, link); usb_log_debug("HCD EP(%p) added to list %s, first is %p(%p).\n", ep, instance->name, first, first->ed); if (last_ed == instance->list_head) { usb_log_debug2("%s head ED(%p-0x%0" PRIx32 "): %x:%x:%x:%x.\n", instance->name, last_ed, instance->list_head_pa, last_ed->status, last_ed->td_tail, last_ed->td_head, last_ed->next); } fibril_mutex_unlock(&instance->guard); }
static void toggle_reset_callback(int retval, void *arg) { assert(arg); toggle_t *toggle = arg; if (retval == EOK) { usb_log_debug2("Reseting toggle on %d:%d.\n", toggle->target.address, toggle->target.endpoint); usb_bus_reset_toggle(&toggle->hcd->bus, toggle->target, toggle->target.endpoint == 0); } toggle->original_callback(retval, toggle->original_data); }
static size_t usb_generic_get_report_descriptor_length(ddf_fun_t *fun) { usb_log_debug("Generic HID: Get report descriptor length.\n"); if (fun == NULL || fun->driver_data == NULL) { usb_log_debug("No function"); return EINVAL; } const usb_hid_dev_t *hid_dev = fun->driver_data; usb_log_debug2("hid_dev->report_desc_size = %zu\n", hid_dev->report_desc_size); return hid_dev->report_desc_size; }
void usb_generic_hid_deinit(usb_hid_dev_t *hid_dev, void *data) { ddf_fun_t *fun = data; if (fun == NULL) return; if (ddf_fun_unbind(fun) != EOK) { usb_log_error("Failed to unbind generic hid fun.\n"); return; } usb_log_debug2("%s unbound.\n", fun->name); /* We did not allocate this, so leave this alone * the device would take care of it */ fun->driver_data = NULL; ddf_fun_destroy(fun); }
/** Initialize a new ddf driver instance for uhci hc and hub. * * @param[in] device DDF instance of the device to initialize. * @return Error code. */ int uhci_dev_add(ddf_dev_t *device) { usb_log_debug2("uhci_dev_add() called\n"); assert(device); const int ret = device_setup_uhci(device); if (ret != EOK) { usb_log_error("Failed to initialize UHCI driver: %s.\n", str_error(ret)); } else { usb_log_info("Controlling new UHCI device '%s'.\n", ddf_dev_get_name(device)); } return ret; }
/** Destroy pipes previously created by usb_device_create_pipes. * * @param[in] usb_dev USB device. * */ void usb_device_destroy_pipes(usb_device_t *usb_dev) { assert(usb_dev); assert(usb_dev->pipes || usb_dev->pipes_count == 0); /* Destroy the pipes. */ for (size_t i = 0; i < usb_dev->pipes_count; ++i) { usb_log_debug2("Unregistering pipe %zu: %spresent.\n", i, usb_dev->pipes[i].present ? "" : "not "); if (usb_dev->pipes[i].present) usb_pipe_unregister(&usb_dev->pipes[i].pipe); } free(usb_dev->pipes); usb_dev->pipes = NULL; usb_dev->pipes_count = 0; }
/** Polling function, emulates interrupts. * * @param[in] arg UHCI hc structure to use. * @return EOK (should never return) */ int hc_interrupt_emulator(void* arg) { usb_log_debug("Started interrupt emulator.\n"); hc_t *instance = arg; assert(instance); while (1) { /* Read and clear status register */ uint16_t status = pio_read_16(&instance->registers->usbsts); pio_write_16(&instance->registers->usbsts, status); if (status != 0) usb_log_debug2("UHCI status: %x.\n", status); hc_interrupt(instance, status); async_usleep(UHCI_INT_EMULATOR_TIMEOUT); } return EOK; }
/** Initialize transfer list structures. * * @param[in] instance Memory place to use. * @param[in] name Name of the new list. * @return Error code * * Allocates memory for internal ed_t structure. */ int endpoint_list_init(endpoint_list_t *instance, const char *name) { assert(instance); instance->name = name; instance->list_head = malloc32(sizeof(ed_t)); if (!instance->list_head) { usb_log_error("Failed to allocate list head.\n"); return ENOMEM; } instance->list_head_pa = addr_to_phys(instance->list_head); usb_log_debug2("Transfer list %s setup with ED: %p(0x%0" PRIx32 ")).\n", name, instance->list_head, instance->list_head_pa); ed_init(instance->list_head, NULL, NULL); list_initialize(&instance->endpoint_list); fibril_mutex_initialize(&instance->guard); return EOK; }
/** * Callback for removing a device from the driver. * * @param dev Structure representing the device. * @return Error code. */ static int usb_hid_device_gone(usb_device_t *dev) { assert(dev); usb_hid_dev_t *hid_dev = usb_device_data_get(dev); assert(hid_dev); unsigned tries = 100; /* Wait for fail. */ while (hid_dev->running && tries--) { async_usleep(100000); } if (hid_dev->running) { usb_log_error("Can't remove hid, still running.\n"); return EBUSY; } usb_hid_deinit(hid_dev); usb_log_debug2("%s destruction complete.\n", usb_device_get_name(dev)); return EOK; }
/** Initialize a new ddf driver instance of UHCI root hub. * * @param[in] device DDF instance of the device to initialize. * @return Error code. */ static int uhci_rh_dev_add(ddf_dev_t *device) { if (!device) return EINVAL; usb_log_debug2("uhci_rh_dev_add(handle=%" PRIun ")\n", device->handle); uintptr_t io_regs = 0; size_t io_size = 0; uhci_root_hub_t *rh = NULL; int ret = EOK; #define CHECK_RET_FREE_RH_RETURN(ret, message...) \ if (ret != EOK) { \ usb_log_error(message); \ if (rh) \ free(rh); \ return ret; \ } else (void)0 ret = hc_get_my_registers(device, &io_regs, &io_size); CHECK_RET_FREE_RH_RETURN(ret, "Failed to get registers from HC: %s.\n", str_error(ret)); usb_log_debug("I/O regs at %p (size %zuB).\n", (void *) io_regs, io_size); rh = malloc(sizeof(uhci_root_hub_t)); ret = (rh == NULL) ? ENOMEM : EOK; CHECK_RET_FREE_RH_RETURN(ret, "Failed to allocate rh driver instance.\n"); ret = uhci_root_hub_init(rh, (void*)io_regs, io_size, device); CHECK_RET_FREE_RH_RETURN(ret, "Failed(%d) to initialize rh driver instance: %s.\n", ret, str_error(ret)); device->driver_data = rh; usb_log_info("Controlling root hub '%s' (%" PRIun ").\n", device->name, device->handle); return EOK; }
static int usb_generic_get_report_descriptor(ddf_fun_t *fun, uint8_t *desc, size_t size, size_t *actual_size) { usb_log_debug2("Generic HID: Get report descriptor.\n"); if (fun == NULL || fun->driver_data == NULL) { usb_log_debug("No function"); return EINVAL; } const usb_hid_dev_t *hid_dev = fun->driver_data; if (hid_dev->report_desc_size > size) { return EINVAL; } memcpy(desc, hid_dev->report_desc, hid_dev->report_desc_size); *actual_size = hid_dev->report_desc_size; return EOK; }
/** Remove endpoint from the list and queue. * * @param[in] instance List to use. * @param[in] endpoint Endpoint to remove. */ void endpoint_list_remove_ep(endpoint_list_t *instance, ohci_endpoint_t *ep) { assert(instance); assert(instance->list_head); assert(ep); assert(ep->ed); fibril_mutex_lock(&instance->guard); usb_log_debug2("Queue %s: removing endpoint(%p).\n", instance->name, ep); const char *qpos = NULL; ed_t *prev_ed; /* Remove from the hardware queue */ if (list_first(&instance->endpoint_list) == &ep->link) { /* I'm the first one here */ prev_ed = instance->list_head; qpos = "FIRST"; } else { ohci_endpoint_t *prev = list_get_instance(ep->link.prev, ohci_endpoint_t, link); prev_ed = prev->ed; qpos = "NOT FIRST"; } assert(ed_next(prev_ed) == addr_to_phys(ep->ed)); prev_ed->next = ep->ed->next; /* Make sure ED is updated */ write_barrier(); usb_log_debug("HCD EP(%p) removed (%s) from %s, next %x.\n", ep, qpos, instance->name, ep->ed->next); /* Remove from the endpoint list */ list_remove(&ep->link); fibril_mutex_unlock(&instance->guard); }
/** Root Hub driver structure initialization. * * Reads info registers and prepares descriptors. Sets power mode. */ void rh_init(rh_t *instance, ohci_regs_t *regs) { assert(instance); assert(regs); instance->registers = regs; instance->port_count = OHCI_RD(regs->rh_desc_a) & RHDA_NDS_MASK; usb_log_debug2("rh_desc_a: %x.\n", OHCI_RD(regs->rh_desc_a)); if (instance->port_count > 15) { usb_log_warning("OHCI specification does not allow more than 15" " ports. Max 15 ports will be used"); instance->port_count = 15; } /* Don't forget the hub status bit and round up */ instance->interrupt_mask_size = 1 + (instance->port_count / 8); instance->unfinished_interrupt_transfer = NULL; #if defined OHCI_POWER_SWITCH_no usb_log_debug("OHCI rh: Set power mode to no power switching.\n"); /* Set port power mode to no power-switching. (always on) */ OHCI_SET(regs->rh_desc_a, RHDA_NPS_FLAG); /* Set to no over-current reporting */ OHCI_SET(regs->rh_desc_a, RHDA_NOCP_FLAG); #elif defined OHCI_POWER_SWITCH_ganged usb_log_debug("OHCI rh: Set power mode to ganged power switching.\n"); /* Set port power mode to ganged power-switching. */ OHCI_CLR(regs->rh_desc_a, RHDA_NPS_FLAG); OHCI_CLR(regs->rh_desc_a, RHDA_PSM_FLAG); /* Turn off power (hub driver will turn this back on)*/ OHCI_WR(regs->rh_status, RHS_CLEAR_GLOBAL_POWER); /* Set to global over-current */ OHCI_CLR(regs->rh_desc_a, RHDA_NOCP_FLAG); OHCI_CLR(regs->rh_desc_a, RHDA_OCPM_FLAG); #else usb_log_debug("OHCI rh: Set power mode to per-port power switching.\n"); /* Set port power mode to per port power-switching. */ OHCI_CLR(regs->rh_desc_a, RHDA_NPS_FLAG); OHCI_SET(regs->rh_desc_a, RHDA_PSM_FLAG); /* Control all ports by global switch and turn them off */ OHCI_CLR(regs->rh_desc_b, RHDB_PCC_MASK << RHDB_PCC_SHIFT); OHCI_WR(regs->rh_status, RHS_CLEAR_GLOBAL_POWER); /* Return control to per port state */ OHCI_SET(regs->rh_desc_b, RHDB_PCC_MASK << RHDB_PCC_SHIFT); /* Set per port over-current */ OHCI_CLR(regs->rh_desc_a, RHDA_NOCP_FLAG); OHCI_SET(regs->rh_desc_a, RHDA_OCPM_FLAG); #endif fibril_mutex_initialize(&instance->guard); rh_init_descriptors(instance); usb_log_info("Root hub (%zu ports) initialized.\n", instance->port_count); }
/** Prepare generic usb_transfer_batch and schedule it. * @param hcd Host controller driver. * @param fun DDF fun * @param target address and endpoint number. * @param setup_data Data to use in setup stage (Control communication type) * @param in Callback for device to host communication. * @param out Callback for host to device communication. * @param arg Callback parameter. * @param name Communication identifier (for nicer output). * @return Error code. */ int hcd_send_batch( hcd_t *hcd, usb_target_t target, usb_direction_t direction, void *data, size_t size, uint64_t setup_data, usbhc_iface_transfer_in_callback_t in, usbhc_iface_transfer_out_callback_t out, void *arg, const char* name) { assert(hcd); endpoint_t *ep = usb_bus_find_ep(&hcd->bus, target.address, target.endpoint, direction); if (ep == NULL) { usb_log_error("Endpoint(%d:%d) not registered for %s.\n", target.address, target.endpoint, name); return ENOENT; } usb_log_debug2("%s %d:%d %zu(%zu).\n", name, target.address, target.endpoint, size, ep->max_packet_size); const size_t bw = bandwidth_count_usb11( ep->speed, ep->transfer_type, size, ep->max_packet_size); /* Check if we have enough bandwidth reserved */ if (ep->bandwidth < bw) { usb_log_error("Endpoint(%d:%d) %s needs %zu bw " "but only %zu is reserved.\n", ep->address, ep->endpoint, name, bw, ep->bandwidth); return ENOSPC; } if (!hcd->ops.schedule) { usb_log_error("HCD does not implement scheduler.\n"); return ENOTSUP; } /* Check for commands that reset toggle bit */ if (ep->transfer_type == USB_TRANSFER_CONTROL) { const int reset_toggle = usb_request_needs_toggle_reset( (usb_device_request_setup_packet_t *) &setup_data); if (reset_toggle >= 0) { assert(out); toggle_t *toggle = malloc(sizeof(toggle_t)); if (!toggle) return ENOMEM; toggle->target.address = target.address; toggle->target.endpoint = reset_toggle; toggle->original_callback = out; toggle->original_data = arg; toggle->hcd = hcd; arg = toggle; out = toggle_reset_callback; } } usb_transfer_batch_t *batch = usb_transfer_batch_create( ep, data, size, setup_data, in, out, arg); if (!batch) { usb_log_error("Failed to create transfer batch.\n"); return ENOMEM; } const int ret = hcd->ops.schedule(hcd, batch); if (ret != EOK) usb_transfer_batch_destroy(batch); /* Drop our own reference to ep. */ endpoint_del_ref(ep); return ret; }
/** * Processes key events. * * @note This function was copied from AT keyboard driver and modified to suit * USB keyboard. * * @note Lock keys are not sent to the console, as they are completely handled * in the driver. It may, however, be required later that the driver * sends also these keys to application (otherwise it cannot use those * keys at all). * * @param hid_dev * @param multim_dev * @param type Type of the event (press / release). Recognized values: * KEY_PRESS, KEY_RELEASE * @param key Key code of the key according to HID Usage Tables. */ static void usb_multimedia_push_ev( usb_multimedia_t *multim_dev, int type, unsigned int key) { assert(multim_dev != NULL); const kbd_event_t ev = { .type = type, .key = key, .mods = 0, .c = 0, }; usb_log_debug2(NAME " Sending key %d to the console\n", ev.key); if (multim_dev->console_sess == NULL) { usb_log_warning( "Connection to console not ready, key discarded.\n"); return; } async_exch_t *exch = async_exchange_begin(multim_dev->console_sess); if (exch != NULL) { async_msg_4(exch, KBDEV_EVENT, ev.type, ev.key, ev.mods, ev.c); async_exchange_end(exch); } else { usb_log_warning("Failed to send multimedia key.\n"); } } int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data) { if (hid_dev == NULL || hid_dev->usb_dev == NULL) { return EINVAL; } usb_log_debug(NAME " Initializing HID/multimedia structure...\n"); /* Create the exposed function. */ ddf_fun_t *fun = ddf_fun_create( hid_dev->usb_dev->ddf_dev, fun_exposed, NAME); if (fun == NULL) { usb_log_error("Could not create DDF function node.\n"); return ENOMEM; } ddf_fun_set_ops(fun, &multimedia_ops); usb_multimedia_t *multim_dev = ddf_fun_data_alloc(fun, sizeof(usb_multimedia_t)); if (multim_dev == NULL) { ddf_fun_destroy(fun); return ENOMEM; } multim_dev->console_sess = NULL; //todo Autorepeat? int rc = ddf_fun_bind(fun); if (rc != EOK) { usb_log_error("Could not bind DDF function: %s.\n", str_error(rc)); ddf_fun_destroy(fun); return rc; } usb_log_debug(NAME " function created (handle: %" PRIun ").\n", ddf_fun_get_handle(fun)); rc = ddf_fun_add_to_category(fun, "keyboard"); if (rc != EOK) { usb_log_error( "Could not add DDF function to category 'keyboard': %s.\n", str_error(rc)); if (ddf_fun_unbind(fun) != EOK) { usb_log_error("Failed to unbind %s, won't destroy.\n", ddf_fun_get_name(fun)); } else { ddf_fun_destroy(fun); } return rc; } /* Save the KBD device structure into the HID device structure. */ *data = fun; usb_log_debug(NAME " HID/multimedia structure initialized.\n"); return EOK; } void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data) { ddf_fun_t *fun = data; usb_multimedia_t *multim_dev = ddf_fun_data_get(fun); /* Hangup session to the console */ if (multim_dev->console_sess) async_hangup(multim_dev->console_sess); if (ddf_fun_unbind(fun) != EOK) { usb_log_error("Failed to unbind %s, won't destroy.\n", ddf_fun_get_name(fun)); } else { usb_log_debug2("%s unbound.\n", ddf_fun_get_name(fun)); /* This frees multim_dev too as it was stored in * fun->data */ ddf_fun_destroy(fun); } }
/** Initialize a new ddf driver instance for uhci hc and hub. * * @param[in] device DDF instance of the device to initialize. * @return Error code. */ static int uhci_dev_add(ddf_dev_t *device) { usb_log_debug2("uhci_dev_add() called\n"); assert(device); return hcd_ddf_add_hc(device, &uhci_hc_driver); }
/** Debug function, checks consistency of memory structures. * * @param[in] arg UHCI structure to use. * @return EOK (should never return) */ int hc_debug_checker(void *arg) { hc_t *instance = arg; assert(instance); #define QH(queue) \ instance->transfers_##queue.queue_head while (1) { const uint16_t cmd = pio_read_16(&instance->registers->usbcmd); const uint16_t sts = pio_read_16(&instance->registers->usbsts); const uint16_t intr = pio_read_16(&instance->registers->usbintr); if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) { usb_log_debug2("Command: %X Status: %X Intr: %x\n", cmd, sts, intr); } const uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd) & ~0xfff; if (frame_list != addr_to_phys(instance->frame_list)) { usb_log_debug("Framelist address: %p vs. %p.\n", (void *) frame_list, (void *) addr_to_phys(instance->frame_list)); } int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff; uintptr_t expected_pa = instance->frame_list[frnum] & LINK_POINTER_ADDRESS_MASK; uintptr_t real_pa = addr_to_phys(QH(interrupt)); if (expected_pa != real_pa) { usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.\n", (void *) expected_pa, frnum, (void *) real_pa); } expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK; real_pa = addr_to_phys(QH(control_slow)); if (expected_pa != real_pa) { usb_log_debug("Control Slow QH: %p vs. %p.\n", (void *) expected_pa, (void *) real_pa); } expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK; real_pa = addr_to_phys(QH(control_full)); if (expected_pa != real_pa) { usb_log_debug("Control Full QH: %p vs. %p.\n", (void *) expected_pa, (void *) real_pa); } expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK; real_pa = addr_to_phys(QH(bulk_full)); if (expected_pa != real_pa ) { usb_log_debug("Bulk QH: %p vs. %p.\n", (void *) expected_pa, (void *) real_pa); } async_usleep(UHCI_DEBUGER_TIMEOUT); } return EOK; #undef QH }