コード例 #1
0
ファイル: usbhub.c プロジェクト: fhector/helenOS-0.5-Hector
/**
 * 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));
		}
	}
}
コード例 #2
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);
	}
}
コード例 #3
0
ファイル: usbhub.c プロジェクト: fhector/helenOS-0.5-Hector
/**
 * 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;
}
コード例 #4
0
ファイル: hcd.c プロジェクト: jvesely/helenos
/** 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;
}