Ejemplo n.º 1
0
int usb_generic_hid_init(usb_hid_dev_t *hid_dev, void **data)
{
	if (hid_dev == NULL) {
		return EINVAL;
	}

	/* Create the exposed function. */
	usb_log_debug("Creating DDF function %s...\n", HID_GENERIC_FUN_NAME);
	ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed, 
	    HID_GENERIC_FUN_NAME);
	if (fun == NULL) {
		usb_log_error("Could not create DDF function node.\n");
		return ENOMEM;
	}

	/* This is nasty, both device and this function have the same
	 * driver data, thus destruction causes to double free */
	fun->driver_data = hid_dev;
	fun->ops = &usb_generic_hid_ops;

	int rc = ddf_fun_bind(fun);
	if (rc != EOK) {
		usb_log_error("Could not bind DDF function: %s.\n",
		    str_error(rc));
		fun->driver_data = NULL;
		ddf_fun_destroy(fun);
		return rc;
	}

	usb_log_debug("HID function created. Handle: %" PRIun "\n", fun->handle);
	*data = fun;

	return EOK;
}
Ejemplo n.º 2
0
/**
 * Process root hub request.
 *
 * @param instance Root hub instance
 * @param request Structure containing both request and response information
 * @return Error code
 */
void rh_request(rh_t *instance, usb_transfer_batch_t *request)
{
	assert(instance);
	assert(request);

	switch (request->ep->transfer_type)
	{
	case USB_TRANSFER_CONTROL:
		usb_log_debug("Root hub got CONTROL packet\n");
		control_request(instance, request);
		break;

	case USB_TRANSFER_INTERRUPT:
		usb_log_debug("Root hub got INTERRUPT packet\n");
		fibril_mutex_lock(&instance->guard);
		assert(instance->unfinished_interrupt_transfer == NULL);
		const uint16_t mask = create_interrupt_mask(instance);
		if (mask == 0) {
			usb_log_debug("No changes(%hx)...\n", mask);
			instance->unfinished_interrupt_transfer = request;
		} else {
			usb_log_debug("Processing changes...\n");
			interrupt_request(
			    request, mask, instance->interrupt_mask_size);
		}
		fibril_mutex_unlock(&instance->guard);
		break;

	default:
		usb_log_error("Root hub got unsupported request.\n");
		TRANSFER_END(request, ENOTSUP);
	}
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/** Initialize UHCI hc memory structures.
 *
 * @param[in] instance UHCI structure to use.
 * @return Error code
 * @note Should be called only once on any structure.
 *
 * Structures:
 *  - transfer lists (queue heads need to be accessible by the hw)
 *  - frame list page (needs to be one UHCI hw accessible 4K page)
 */
int hc_init_mem_structures(hc_t *instance)
{
	assert(instance);

	/* Init USB frame list page */
	instance->frame_list = get_page();
	if (!instance->frame_list) {
		return ENOMEM;
	}
	usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);

	/* Init transfer lists */
	int ret = hc_init_transfer_lists(instance);
	if (ret != EOK) {
		usb_log_error("Failed to initialize transfer lists.\n");
		return_page(instance->frame_list);
		return ENOMEM;
	}
	usb_log_debug("Initialized transfer lists.\n");


	/* Set all frames to point to the first queue head */
	const uint32_t queue = LINK_POINTER_QH(
	        addr_to_phys(instance->transfers_interrupt.queue_head));

	for (unsigned i = 0; i < UHCI_FRAME_LIST_COUNT; ++i) {
		instance->frame_list[i] = queue;
	}

	return EOK;
}
Ejemplo n.º 5
0
/**
 * 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;
}
Ejemplo n.º 6
0
/** Callback for polling hub for changes.
 *
 * @param dev Device where the change occured.
 * @param change_bitmap Bitmap of changed ports.
 * @param change_bitmap_size Size of the bitmap in bytes.
 * @param arg Custom argument, points to @c usb_hub_dev_t.
 * @return Whether to continue polling.
 */
bool hub_port_changes_callback(usb_device_t *dev,
    uint8_t *change_bitmap, size_t change_bitmap_size, void *arg)
{
	usb_log_debug("hub_port_changes_callback\n");
	usb_hub_dev_t *hub = arg;
	assert(hub);

	/* It is an error condition if we didn't receive enough data */
	if (change_bitmap_size == 0) {
		return false;
	}

	/* Lowest bit indicates global change */
	const bool change = change_bitmap[0] & 1;
	if (change) {
		usb_hub_global_interrupt(hub);
	}

	/* N + 1 bit indicates change on port N */
	for (size_t port = 0; port < hub->port_count + 1; port++) {
		const size_t bit = port + 1;
		const bool change = (change_bitmap[bit / 8] >> (bit % 8)) & 1;
		if (change) {
			usb_hub_port_process_interrupt(&hub->ports[port], hub);
		}
	}
	return true;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
static inline void interrupt_request(
    usb_transfer_batch_t *request, uint16_t mask, size_t size)
{
	assert(request);
	usb_log_debug("Sending interrupt vector(%zu) %hhx:%hhx.\n",
	    size, ((uint8_t*)&mask)[0], ((uint8_t*)&mask)[1]);
	usb_transfer_batch_finish_error(request, &mask, size, EOK);
	usb_transfer_batch_destroy(request);
}
Ejemplo n.º 9
0
/**
 * Default handler for IPC methods not handled by DDF.
 *
 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
 * assumes the caller is the console and thus it stores IPC session to it for
 * later use by the driver to notify about key events.
 *
 * @param fun Device function handling the call.
 * @param icallid Call id.
 * @param icall Call data.
 */
static void default_connection_handler(ddf_fun_t *fun,
    ipc_callid_t icallid, ipc_call_t *icall)
{
	usb_log_debug(NAME " default_connection_handler()\n");

	usb_multimedia_t *multim_dev = ddf_fun_data_get(fun);

	async_sess_t *sess =
	    async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
	if (sess != NULL) {
		if (multim_dev->console_sess == NULL) {
			multim_dev->console_sess = sess;
			usb_log_debug(NAME " Saved session to console: %p\n",
			    sess);
			async_answer_0(icallid, EOK);
		} else
			async_answer_0(icallid, ELIMIT);
	} else
		async_answer_0(icallid, EINVAL);
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
/**
 * Set configuration of and USB device
 *
 * Check whether there is at least one configuration and sets the first one.
 * This function should be run prior to running any hub-specific action.
 * @param usb_device usb device representation
 * @return error code
 */
static int usb_set_first_configuration(usb_device_t *usb_device)
{
	assert(usb_device);
	/* Get number of possible configurations from device descriptor */
	const size_t configuration_count =
	    usb_device->descriptors.device.configuration_count;
	usb_log_debug("Hub has %zu configurations.\n", configuration_count);

	if (configuration_count < 1) {
		usb_log_error("There are no configurations available\n");
		return EINVAL;
	}

	if (usb_device->descriptors.configuration_size
	    < sizeof(usb_standard_configuration_descriptor_t)) {
	    usb_log_error("Configuration descriptor is not big enough"
	        " to fit standard configuration descriptor.\n");
	    return EOVERFLOW;
	}

	// TODO: Make sure that the cast is correct
	usb_standard_configuration_descriptor_t *config_descriptor
	    = (usb_standard_configuration_descriptor_t *)
	    usb_device->descriptors.configuration;

	/* Set configuration. Use the configuration that was in
	 * usb_device->descriptors.configuration i.e. The first one. */
	const int opResult = usb_request_set_configuration(
	    &usb_device->ctrl_pipe, config_descriptor->configuration_number);
	if (opResult != EOK) {
		usb_log_error("Failed to set hub configuration: %s.\n",
		    str_error(opResult));
	} else {
		usb_log_debug("\tUsed configuration %d\n",
		    config_descriptor->configuration_number);
	}
	return opResult;
}
Ejemplo n.º 12
0
/**
 * Process interrupt on a hub device.
 *
 * If there is no pending interrupt transfer, nothing happens.
 * @param instance
 */
void rh_interrupt(rh_t *instance)
{
	assert(instance);

	fibril_mutex_lock(&instance->guard);
	if (instance->unfinished_interrupt_transfer) {
		usb_log_debug("Finalizing interrupt transfer\n");
		const uint16_t mask = create_interrupt_mask(instance);
		interrupt_request(instance->unfinished_interrupt_transfer,
		    mask, instance->interrupt_mask_size);
		instance->unfinished_interrupt_transfer = NULL;
	}
	fibril_mutex_unlock(&instance->guard);
}
Ejemplo n.º 13
0
/**
 * Send Get Idle request to the HID device.
 *
 * @param[in] hid_dev HID device to send the request to.
 * @param[out] duration Duration value (multiplicate by 4 to get real duration
 *                      in miliseconds).
 *
 * @retval EOK if successful.
 * @retval EINVAL if no HID device is given.
 * @return Other value inherited from one of functions 
 *         usb_pipe_start_session(), usb_pipe_end_session(),
 *         usb_control_request_set().
 */
int usbhid_req_get_idle(usb_pipe_t *ctrl_pipe, int iface_no, uint8_t *duration)
{
	if (ctrl_pipe == NULL) {
		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
		return EINVAL;
	}
	
	if (iface_no < 0) {
		usb_log_warning("usbhid_req_set_report(): no interface given."
		    "\n");
		return EINVAL;
	}
	
	/*
	 * No need for checking other parameters, as they are checked in
	 * the called function (usb_control_request_set()).
	 */
	
	int rc;

	usb_log_debug("Sending Get Idle request to the device ("
	    "iface: %d).\n", iface_no);
	
	uint16_t value = 0;
	uint8_t buffer[1];
	size_t actual_size = 0;
	
	rc = usb_control_request_get(ctrl_pipe, 
	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE, 
	    USB_HIDREQ_GET_IDLE, value, iface_no, buffer, 1, 
	    &actual_size);

	if (rc != EOK) {
		usb_log_warning("Error sending Get Idle request to the device: "
		    "%s.\n", str_error(rc));
		return rc;
	}
	
	if (actual_size != 1) {
		usb_log_warning("Wrong data size: %zu, expected: 1.\n",
			actual_size);
		return ELIMIT;
	}
	
	*duration = buffer[0];
	
	return EOK;
}
Ejemplo n.º 14
0
/** Initialize UHCI hc driver structure
 *
 * @param[in] instance Memory place to initialize.
 * @param[in] regs Address of I/O control registers.
 * @param[in] reg_size Size of I/O control registers.
 * @param[in] interrupts True if hw interrupts should be used.
 * @return Error code.
 * @note Should be called only once on any structure.
 *
 * Initializes memory structures, starts up hw, and launches debugger and
 * interrupt fibrils.
 */
int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interrupts)
{
	assert(reg_size >= sizeof(uhci_regs_t));
	int ret;

#define CHECK_RET_RETURN(ret, message...) \
	if (ret != EOK) { \
		usb_log_error(message); \
		return ret; \
	} else (void) 0

	instance->hw_interrupts = interrupts;
	instance->hw_failures = 0;

	/* allow access to hc control registers */
	uhci_regs_t *io;
	ret = pio_enable(regs, reg_size, (void **)&io);
	CHECK_RET_RETURN(ret, "Failed to gain access to registers at %p: %s.\n",
	    io, str_error(ret));
	instance->registers = io;
	usb_log_debug(
	    "Device registers at %p (%zuB) accessible.\n", io, reg_size);

	ret = hc_init_mem_structures(instance);
	CHECK_RET_RETURN(ret,
	    "Failed to initialize UHCI memory structures: %s.\n",
	    str_error(ret));

#undef CHECK_RET_RETURN

	hcd_init(&instance->generic, USB_SPEED_FULL,
	    BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);

	instance->generic.private_data = instance;
	instance->generic.schedule = hc_schedule;
	instance->generic.ep_add_hook = NULL;

	hc_init_hw(instance);
	if (!interrupts) {
		instance->interrupt_emulator =
		    fibril_create(hc_interrupt_emulator, instance);
		fibril_add_ready(instance->interrupt_emulator);
	}
	(void)hc_debug_checker;

	return EOK;
}
Ejemplo n.º 15
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;
}
Ejemplo n.º 16
0
/** 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);
}
Ejemplo n.º 17
0
/** 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;
}
Ejemplo n.º 18
0
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;
}
Ejemplo n.º 19
0
/**
 * Send Get Report request to the HID device.
 *
 * @param[in] hid_dev HID device to send the request to.
 * @param[in] type Type of the report.
 * @param[in][out] buffer Buffer for the report data.
 * @param[in] buf_size Size of the buffer (in bytes).
 * @param[out] actual_size Actual size of report received from the device 
 *                         (in bytes).
 *
 * @retval EOK if successful.
 * @retval EINVAL if no HID device is given.
 * @return Other value inherited from function usb_control_request_set().
 */
int usbhid_req_get_report(usb_pipe_t *ctrl_pipe, int iface_no, 
    usb_hid_report_type_t type, uint8_t *buffer, size_t buf_size, 
    size_t *actual_size)
{
	if (ctrl_pipe == NULL) {
		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
		return EINVAL;
	}
	
	if (iface_no < 0) {
		usb_log_warning("usbhid_req_set_report(): no interface given."
		    "\n");
		return EINVAL;
	}
	
	/*
	 * No need for checking other parameters, as they are checked in
	 * the called function (usb_control_request_set()).
	 */
	
	int rc;

	uint16_t value = 0;
	value |= (type << 8);
	
	usb_log_debug("Sending Get Report request to the device.\n");
	
	rc = usb_control_request_get(ctrl_pipe, 
	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE, 
	    USB_HIDREQ_GET_REPORT, value, iface_no, buffer, buf_size,
	    actual_size);

	if (rc != EOK) {
		usb_log_warning("Error sending Get Report request to the device: "
		    "%s.\n", str_error(rc));
		return rc;
	}
	
	return EOK;
}
Ejemplo n.º 20
0
/**
 * Send Set Idle request to the HID device.
 *
 * @param hid_dev HID device to send the request to.
 * @param duration Duration value (is multiplicated by 4 by the device to
 *                 get real duration in miliseconds).
 *
 * @retval EOK if successful.
 * @retval EINVAL if no HID device is given.
 * @return Other value inherited from function usb_control_request_set().
 */
int usbhid_req_set_idle(usb_pipe_t *ctrl_pipe, int iface_no, uint8_t duration)
{
	if (ctrl_pipe == NULL) {
		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
		return EINVAL;
	}
	
	if (iface_no < 0) {
		usb_log_warning("usbhid_req_set_report(): no interface given."
		    "\n");
		return EINVAL;
	}
	
	/*
	 * No need for checking other parameters, as they are checked in
	 * the called function (usb_control_request_set()).
	 */
	
	int rc;

	usb_log_debug("Sending Set Idle request to the device ("
	    "duration: %u, iface: %d).\n", duration, iface_no);
	
	uint16_t value = duration << 8;
	
	rc = usb_control_request_set(ctrl_pipe, 
	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE, 
	    USB_HIDREQ_SET_IDLE, value, iface_no, NULL, 0);

	if (rc != EOK) {
		usb_log_warning("Error sending Set Idle request to the device: "
		    "%s.\n", str_error(rc));
		return rc;
	}
	
	return EOK;
}
Ejemplo n.º 21
0
/**
 * Send Set Protocol request to the HID device.
 *
 * @param hid_dev HID device to send the request to.
 * @param protocol Protocol to set.
 *
 * @retval EOK if successful.
 * @retval EINVAL if no HID device is given.
 * @return Other value inherited from function usb_control_request_set().
 */
int usbhid_req_set_protocol(usb_pipe_t *ctrl_pipe, int iface_no,
    usb_hid_protocol_t protocol)
{
	if (ctrl_pipe == NULL) {
		usb_log_warning("usbhid_req_set_report(): no pipe given.\n");
		return EINVAL;
	}
	
	if (iface_no < 0) {
		usb_log_warning("usbhid_req_set_report(): no interface given."
		    "\n");
		return EINVAL;
	}
	
	/*
	 * No need for checking other parameters, as they are checked in
	 * the called function (usb_control_request_set()).
	 */
	
	int rc;

	usb_log_debug("Sending Set Protocol request to the device ("
	    "protocol: %d, iface: %d).\n", protocol, iface_no);
	
	rc = usb_control_request_set(ctrl_pipe, 
	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE, 
	    USB_HIDREQ_SET_PROTOCOL, protocol, iface_no, NULL, 0);

	if (rc != EOK) {
		usb_log_warning("Error sending Set Protocol request to the "
		    "device: %s.\n", str_error(rc));
		return rc;
	}
	
	return EOK;
}
Ejemplo n.º 22
0
/** 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);
}
Ejemplo n.º 23
0
/** Polling fibril.
 *
 * @param arg Pointer to polling_data_t.
 * @return Always EOK.
 */
static int polling_fibril(void *arg)
{
	assert(arg);
	const polling_data_t *data = arg;
	/* Helper to reduce typing. */
	const usb_device_auto_polling_t *params = &data->auto_polling;

	usb_pipe_t *pipe
	    = &data->dev->pipes[data->pipe_index].pipe;

	if (params->debug > 0) {
		const usb_endpoint_mapping_t *mapping
		    = &data->dev->pipes[data->pipe_index];
		usb_log_debug("Poll%p: started polling of `%s' - " \
		    "interface %d (%s,%d,%d), %zuB/%zu.\n",
		    data, data->dev->ddf_dev->name,
		    (int) mapping->interface->interface_number,
		    usb_str_class(mapping->interface->interface_class),
		    (int) mapping->interface->interface_subclass,
		    (int) mapping->interface->interface_protocol,
		    data->request_size, pipe->max_packet_size);
	}

	usb_pipe_start_long_transfer(pipe);
	size_t failed_attempts = 0;
	while (failed_attempts <= params->max_failures) {
		size_t actual_size;
		const int rc = usb_pipe_read(pipe, data->buffer,
		    data->request_size, &actual_size);

		if (params->debug > 1) {
			if (rc == EOK) {
				usb_log_debug(
				    "Poll%p: received: '%s' (%zuB).\n",
				    data,
				    usb_debug_str_buffer(data->buffer,
				        actual_size, 16),
				    actual_size);
			} else {
				usb_log_debug(
				    "Poll%p: polling failed: %s.\n",
				    data, str_error(rc));
			}
		}

		/* If the pipe stalled, we can try to reset the stall. */
		if ((rc == ESTALL) && (params->auto_clear_halt)) {
			/*
			 * We ignore error here as this is usually a futile
			 * attempt anyway.
			 */
			usb_request_clear_endpoint_halt(
			    &data->dev->ctrl_pipe, pipe->endpoint_no);
		}

		if (rc != EOK) {
			++failed_attempts;
			const bool cont = (params->on_error == NULL) ? true :
			    params->on_error(data->dev, rc, params->arg);
			if (!cont) {
				failed_attempts = params->max_failures;
			}
			continue;
		}

		/* We have the data, execute the callback now. */
		assert(params->on_data);
		const bool carry_on = params->on_data(
		    data->dev, data->buffer, actual_size, params->arg);

		if (!carry_on) {
			/* This is user requested abort, erases failures. */
			failed_attempts = 0;
			break;
		}

		/* Reset as something might be only a temporary problem. */
		failed_attempts = 0;

		/* Take a rest before next request. */
		async_usleep(params->delay);
	}

	usb_pipe_end_long_transfer(pipe);

	const bool failed = failed_attempts > 0;

	if (params->on_polling_end != NULL) {
		params->on_polling_end(data->dev, failed, params->arg);
	}

	if (params->debug > 0) {
		if (failed) {
			usb_log_error("Polling of device `%s' terminated: "
			    "recurring failures.\n", data->dev->ddf_dev->name);
		} else {
			usb_log_debug("Polling of device `%s' terminated: "
			    "driver request.\n", data->dev->ddf_dev->name);
		}
	}

	/* Free the allocated memory. */
	free(data->buffer);
	free(data);

	return EOK;
}
Ejemplo n.º 24
0
/** 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
}
Ejemplo n.º 25
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);
}
Ejemplo n.º 26
0
/**
 * 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;
}
Ejemplo n.º 27
0
/**
 * Process hub interrupts.
 *
 * The change can be either in the over-current condition or local-power change.
 * @param hub_dev hub instance
 */
static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev)
{
	assert(hub_dev);
	assert(hub_dev->usb_device);
	usb_log_debug("Global interrupt on a hub\n");
	usb_pipe_t *control_pipe = &hub_dev->usb_device->ctrl_pipe;

	usb_hub_status_t status;
	size_t rcvd_size;
	/* NOTE: We can't use standard USB GET_STATUS request, because
	 * hubs reply is 4byte instead of 2 */
	const int opResult = usb_pipe_control_read(control_pipe,
	    &get_hub_status_request, sizeof(get_hub_status_request),
	    &status, sizeof(usb_hub_status_t), &rcvd_size);
	if (opResult != EOK) {
		usb_log_error("Could not get hub status: %s\n",
		    str_error(opResult));
		return;
	}
	if (rcvd_size != sizeof(usb_hub_status_t)) {
		usb_log_error("Received status has incorrect size\n");
		return;
	}

	/* Handle status changes */
	if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
		usb_hub_over_current(hub_dev, status);
		/* Ack change in hub OC flag */
		const int ret = usb_request_clear_feature(
		    &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS,
		    USB_REQUEST_RECIPIENT_DEVICE,
		    USB_HUB_FEATURE_C_HUB_OVER_CURRENT, 0);
		if (ret != EOK) {
			usb_log_error("Failed to clear hub over-current "
			    "change flag: %s.\n", str_error(opResult));
		}
	}

	if (status & USB_HUB_STATUS_C_LOCAL_POWER) {
		/* NOTE: Handling this is more complicated.
		 * If the transition is from bus power to local power, all
		 * is good and we may signal the parent hub that we don't
		 * need the power.
		 * If the transition is from local power to bus power
		 * the hub should turn off all the ports and devices need
		 * to be reinitialized taking into account the limited power
		 * that is now available.
		 * There is no support for power distribution in HelenOS,
		 * (or other OSes/hub devices that I've seen) so this is not
		 * implemented.
		 * Just ACK the change.
		 */
		const int ret = usb_request_clear_feature(
		    control_pipe, USB_REQUEST_TYPE_CLASS,
		    USB_REQUEST_RECIPIENT_DEVICE,
		    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
		if (opResult != EOK) {
			usb_log_error("Failed to clear hub power change "
			    "flag: %s.\n", str_error(ret));
		}
	}
}
Ejemplo n.º 28
0
/**
 * 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);
	}
}
Ejemplo n.º 29
0
/**
 * 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;
}
Ejemplo n.º 30
0
static int usb_generic_hid_client_connected(ddf_fun_t *fun)
{
	usb_log_debug("Generic HID: Client connected.\n");
	return EOK;
}