/** * Callback for passing a new device to the driver. * * @note Currently, only boot-protocol keyboards are supported by this driver. * * @param dev Structure representing the new device. * @return Error code. */ static int usb_hid_device_add(usb_device_t *dev) { usb_log_debug("%s\n", __FUNCTION__); if (dev == NULL) { usb_log_error("Wrong parameter given for add_device().\n"); return EINVAL; } if (usb_device_get_iface_number(dev) < 0) { usb_log_error("Failed to add HID device: endpoints not found." "\n"); return ENOTSUP; } usb_hid_dev_t *hid_dev = usb_device_data_alloc(dev, sizeof(usb_hid_dev_t)); if (hid_dev == NULL) { usb_log_error("Failed to create USB/HID device structure.\n"); return ENOMEM; } int rc = usb_hid_init(hid_dev, dev); if (rc != EOK) { usb_log_error("Failed to initialize USB/HID device.\n"); usb_hid_deinit(hid_dev); return rc; } usb_log_debug("USB/HID device structure initialized.\n"); /* Start automated polling function. * This will create a separate fibril that will query the device * for the data continuously. */ rc = usb_device_auto_poll_desc(dev, /* Index of the polling pipe. */ hid_dev->poll_pipe_mapping->description, /* Callback when data arrives. */ usb_hid_polling_callback, /* How much data to request. */ hid_dev->poll_pipe_mapping->pipe.max_packet_size, /* Delay */ -1, /* Callback when the polling ends. */ usb_hid_polling_ended_callback, /* Custom argument. */ hid_dev); if (rc != EOK) { usb_log_error("Failed to start polling fibril for `%s'.\n", usb_device_get_name(dev)); usb_hid_deinit(hid_dev); return rc; } hid_dev->running = true; usb_log_info("HID device `%s' ready.\n", usb_device_get_name(dev)); return EOK; }
bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data) { // TODO: checks ddf_fun_t *fun = data; if (hid_dev == NULL) { return false; } usb_multimedia_t *multim_dev = ddf_fun_data_get(fun); usb_hid_report_path_t *path = usb_hid_report_path(); if (path == NULL) return true; /* This might be a temporary failure. */ int ret = usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0); if (ret != EOK) { usb_hid_report_path_free(path); return true; /* This might be a temporary failure. */ } usb_hid_report_path_set_report_id(path, hid_dev->report_id); usb_hid_report_field_t *field = usb_hid_report_get_sibling( &hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, USB_HID_REPORT_TYPE_INPUT); //FIXME Is this iterating OK if done multiple times? //FIXME The parsing is not OK. (what's wrong?) while (field != NULL) { if (field->value != 0) { usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n", field->value, field->usage); const unsigned key = usb_multimedia_map_usage(field->usage); const char *key_str = usbhid_multimedia_usage_to_str(field->usage); usb_log_info("Pressed key: %s\n", key_str); usb_multimedia_push_ev(multim_dev, KEY_PRESS, key); } field = usb_hid_report_get_sibling( &hid_dev->report, field, path, USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, USB_HID_REPORT_TYPE_INPUT); } usb_hid_report_path_free(path); return true; }
static int on_data_out(vuhid_interface_t *iface, const void *buffer, size_t buffer_size) { if (buffer_size == 0) { return EEMPTY; } uint8_t leds = ((uint8_t *) buffer)[0]; #define _GET_LED(index, signature) \ (((leds) & (1 << index)) ? (signature) : '-') usb_log_info("%s: LEDs = %c%c%c%c%c\n", iface->name, _GET_LED(0, '0'), _GET_LED(1, 'A'), _GET_LED(2, 's'), _GET_LED(3, 'c'), _GET_LED(4, 'k')); #undef _GET_LED return EOK; }
/** 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; }
/** 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; }
/** * Remove all attached devices * @param usb_dev generic usb device information * @return error code */ int usb_hub_device_gone(usb_device_t *usb_dev) { assert(usb_dev); usb_hub_dev_t *hub = usb_dev->driver_data; assert(hub); unsigned tries = 10; while (hub->running) { async_usleep(100000); if (!tries--) { usb_log_error("Can't remove hub, still running.\n"); return EINPROGRESS; } } assert(!hub->running); for (size_t port = 0; port < hub->port_count; ++port) { if (hub->ports[port].attached_device.fun) { const int ret = usb_hub_port_fini(&hub->ports[port], hub); if (ret != EOK) return ret; } } free(hub->ports); const int ret = ddf_fun_unbind(hub->hub_fun); if (ret != EOK) { usb_log_error("Failed to unbind '%s' function: %s.\n", HUB_FNC_NAME, str_error(ret)); return ret; } ddf_fun_destroy(hub->hub_fun); usb_log_info("USB hub driver, stopped and cleaned.\n"); return EOK; }
/** Register root hub in devman. * * @param arg Host controller device (type <code>device_t *</code>). * @return Error code. */ int hub_register_in_devman_fibril(void *arg) { ddf_fun_t *hc_dev = (ddf_fun_t *) arg; /* * Wait until parent device is properly initialized. */ async_sess_t *sess; do { sess = devman_device_connect(EXCHANGE_SERIALIZE, ddf_fun_get_handle(hc_dev), 0); } while (!sess); async_hangup(sess); int rc; usb_hc_connection_t hc_conn; usb_hc_connection_initialize(&hc_conn, ddf_fun_get_handle(hc_dev)); rc = usb_hc_connection_open(&hc_conn); assert(rc == EOK); ddf_fun_t *hub_dev; rc = usb_hc_new_device_wrapper(ddf_fun_get_dev(hc_dev), &hc_conn, USB_SPEED_FULL, pretend_port_rest, NULL, NULL, &rh_ops, hc_dev, &hub_dev); if (rc != EOK) { usb_log_fatal("Failed to create root hub: %s.\n", str_error(rc)); } usb_hc_connection_close(&hc_conn); usb_log_info("Created root hub function (handle %zu).\n", (size_t) ddf_fun_get_handle(hub_dev)); return 0; }
/** 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); }
/** * Initialize hub device driver structure. * * Creates hub representation and fibril that periodically checks hub's status. * Hub representation is passed to the fibril. * @param usb_dev generic usb device information * @return error code */ int usb_hub_device_add(usb_device_t *usb_dev) { assert(usb_dev); /* Create driver soft-state structure */ usb_hub_dev_t *hub_dev = usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t)); if (hub_dev == NULL) { usb_log_error("Failed to create hub driver structure.\n"); return ENOMEM; } hub_dev->usb_device = usb_dev; hub_dev->pending_ops_count = 0; hub_dev->running = false; fibril_mutex_initialize(&hub_dev->pending_ops_mutex); fibril_condvar_initialize(&hub_dev->pending_ops_cv); int opResult = usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe); if (opResult != EOK) { usb_log_error("Failed to start long ctrl pipe transfer: %s\n", str_error(opResult)); return opResult; } /* Set hub's first configuration. (There should be only one) */ opResult = usb_set_first_configuration(usb_dev); if (opResult != EOK) { usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); usb_log_error("Could not set hub configuration: %s\n", str_error(opResult)); return opResult; } /* Get port count and create attached_devices. */ opResult = usb_hub_process_hub_specific_info(hub_dev); if (opResult != EOK) { usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); usb_log_error("Could process hub specific info, %s\n", str_error(opResult)); return opResult; } /* Create hub control function. */ usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.\n"); hub_dev->hub_fun = ddf_fun_create(hub_dev->usb_device->ddf_dev, fun_exposed, HUB_FNC_NAME); if (hub_dev->hub_fun == NULL) { usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); usb_log_error("Failed to create hub function.\n"); return ENOMEM; } /* Bind hub control function. */ opResult = ddf_fun_bind(hub_dev->hub_fun); if (opResult != EOK) { usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); usb_log_error("Failed to bind hub function: %s.\n", str_error(opResult)); ddf_fun_destroy(hub_dev->hub_fun); return opResult; } /* Start hub operation. */ opResult = usb_device_auto_poll(hub_dev->usb_device, 0, hub_port_changes_callback, ((hub_dev->port_count + 1 + 8) / 8), usb_hub_polling_terminated_callback, hub_dev); if (opResult != EOK) { usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); /* Function is already bound */ ddf_fun_unbind(hub_dev->hub_fun); ddf_fun_destroy(hub_dev->hub_fun); usb_log_error("Failed to create polling fibril: %s.\n", str_error(opResult)); return opResult; } hub_dev->running = true; usb_log_info("Controlling hub '%s' (%zu ports).\n", hub_dev->usb_device->ddf_dev->name, hub_dev->port_count); usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); return EOK; }
/** * Load hub-specific information into hub_dev structure and process if needed * * Read port count and initialize structures holding per port information. * If there are any non-removable devices, start initializing them. * This function is hub-specific and should be run only after the hub is * configured using usb_set_first_configuration function. * @param hub_dev hub representation * @return error code */ static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev) { assert(hub_dev); /* Get hub descriptor. */ usb_log_debug("Retrieving descriptor\n"); usb_pipe_t *control_pipe = &hub_dev->usb_device->ctrl_pipe; usb_hub_descriptor_header_t descriptor; size_t received_size; int opResult = usb_request_get_descriptor(control_pipe, USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE, USB_DESCTYPE_HUB, 0, 0, &descriptor, sizeof(usb_hub_descriptor_header_t), &received_size); if (opResult != EOK) { usb_log_error("Failed to receive hub descriptor: %s.\n", str_error(opResult)); return opResult; } usb_log_debug("Setting port count to %d.\n", descriptor.port_count); hub_dev->port_count = descriptor.port_count; hub_dev->ports = calloc(hub_dev->port_count, sizeof(usb_hub_port_t)); if (!hub_dev->ports) { return ENOMEM; } for (size_t port = 0; port < hub_dev->port_count; ++port) { usb_hub_port_init( &hub_dev->ports[port], port + 1, control_pipe); } hub_dev->power_switched = !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG); hub_dev->per_port_power = descriptor.characteristics & HUB_CHAR_POWER_PER_PORT_FLAG; if (!hub_dev->power_switched) { usb_log_info( "Power switching not supported, ports always powered.\n"); return EOK; } usb_log_info("Hub port power switching enabled.\n"); for (size_t port = 0; port < hub_dev->port_count; ++port) { usb_log_debug("Powering port %zu.\n", port); const int ret = usb_hub_port_set_feature( &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER); if (ret != EOK) { usb_log_error("Cannot power on port %zu: %s.\n", hub_dev->ports[port].port_number, str_error(ret)); } else { if (!hub_dev->per_port_power) { usb_log_debug("Ganged power switching, " "one port is enough.\n"); break; } } } return EOK; }
static int vhc_dev_add(ddf_dev_t *dev) { static int vhc_count = 0; int rc; if (vhc_count > 0) { return ELIMIT; } vhc_data_t *data = ddf_dev_data_alloc(dev, sizeof(vhc_data_t)); if (data == NULL) { usb_log_fatal("Failed to allocate memory.\n"); return ENOMEM; } data->magic = 0xDEADBEEF; rc = usb_endpoint_manager_init(&data->ep_manager, (size_t) -1, bandwidth_count_usb11); if (rc != EOK) { usb_log_fatal("Failed to initialize endpoint manager.\n"); free(data); return rc; } usb_device_manager_init(&data->dev_manager, USB_SPEED_MAX); ddf_fun_t *hc = ddf_fun_create(dev, fun_exposed, "hc"); if (hc == NULL) { usb_log_fatal("Failed to create device function.\n"); free(data); return ENOMEM; } ddf_fun_set_ops(hc, &vhc_ops); list_initialize(&data->devices); fibril_mutex_initialize(&data->guard); data->hub = &virtual_hub_device; data->hc_fun = hc; rc = ddf_fun_bind(hc); if (rc != EOK) { usb_log_fatal("Failed to bind HC function: %s.\n", str_error(rc)); free(data); return rc; } rc = ddf_fun_add_to_category(hc, USB_HC_CATEGORY); if (rc != EOK) { usb_log_fatal("Failed to add function to HC class: %s.\n", str_error(rc)); free(data); return rc; } virtual_hub_device_init(hc); usb_log_info("Virtual USB host controller ready (dev %zu, hc %zu).\n", (size_t) ddf_dev_get_handle(dev), (size_t) ddf_fun_get_handle(hc)); rc = vhc_virtdev_plug_hub(data, data->hub, NULL); if (rc != EOK) { usb_log_fatal("Failed to plug root hub: %s.\n", str_error(rc)); free(data); return rc; } return EOK; }