HelenOS sources
This source file includes following definitions.
- hub_port_state_to_char
- hub_init_port
- hub_init
- hub_connect_device
- hub_disconnect_device
- hub_find_device
- hub_acquire
- hub_release
- hub_set_port_state
- hub_set_port_state_all
- hub_get_port_state
- hub_clear_port_status_change
- hub_get_port_status_change
- hub_get_port_status
- hub_get_status_change_bitmap
- get_hub_port
- set_port_status_change
- clear_port_status_change
- set_port_state_delayed_fibril
- set_port_state_delayed
#include <usb/classes/classes.h>
#include <usbvirt/device.h>
#include <errno.h>
#include <str_error.h>
#include <assert.h>
#include <stdlib.h>
#include <ddf/driver.h>
#include <usb/debug.h>
#include "hub.h"
#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
(( \
((b0) << 0) \
| ((b1) << 1) \
| ((b2) << 2) \
| ((b3) << 3) \
| ((b4) << 4) \
| ((b5) << 5) \
| ((b6) << 6) \
| ((b7) << 7) \
))
static hub_port_t *get_hub_port(hub_t *, size_t);
static void set_port_status_change(hub_port_t *, hub_status_change_t);
static void clear_port_status_change(hub_port_t *, uint16_t);
static errno_t set_port_state_delayed_fibril(void *);
static void set_port_state_delayed(hub_t *, size_t, usec_t, hub_port_state_t,
hub_port_state_t);
char hub_port_state_to_char(hub_port_state_t state)
{
switch (state) {
case HUB_PORT_STATE_NOT_CONFIGURED:
return '-';
case HUB_PORT_STATE_POWERED_OFF:
return 'O';
case HUB_PORT_STATE_DISCONNECTED:
return 'X';
case HUB_PORT_STATE_DISABLED:
return 'D';
case HUB_PORT_STATE_RESETTING:
return 'R';
case HUB_PORT_STATE_ENABLED:
return 'E';
case HUB_PORT_STATE_SUSPENDED:
return 'S';
case HUB_PORT_STATE_RESUMING:
return 'F';
default:
return '?';
}
}
static void hub_init_port(hub_port_t *port, hub_t *hub, size_t index)
{
port->connected_device = NULL;
port->index = index;
port->state = HUB_PORT_STATE_NOT_CONFIGURED;
port->status_change = 0;
port->hub = hub;
}
void hub_init(hub_t *hub)
{
size_t i;
for (i = 0; i < HUB_PORT_COUNT; i++) {
hub_init_port(&hub->ports[i], hub, i + 1);
}
hub->custom_data = NULL;
hub->signal_changes = true;
fibril_mutex_initialize(&hub->guard);
}
size_t hub_connect_device(hub_t *hub, void *device)
{
size_t i;
for (i = 0; i < HUB_PORT_COUNT; i++) {
hub_port_t *port = &hub->ports[i];
if (port->connected_device != NULL) {
continue;
}
port->connected_device = device;
port->state = HUB_PORT_STATE_DISABLED;
set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
return i;
}
return (size_t) -1;
}
errno_t hub_disconnect_device(hub_t *hub, void *device)
{
size_t index = hub_find_device(hub, device);
if (index == (size_t) -1) {
return ENOENT;
}
hub_port_t *port = &hub->ports[index];
port->connected_device = NULL;
port->state = HUB_PORT_STATE_DISCONNECTED;
set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
return EOK;
}
size_t hub_find_device(hub_t *hub, void *device)
{
size_t i;
for (i = 0; i < HUB_PORT_COUNT; i++) {
hub_port_t *port = &hub->ports[i];
if (port->connected_device == device) {
return i;
}
}
return -1;
}
void hub_acquire(hub_t *hub)
{
fibril_mutex_lock(&hub->guard);
}
void hub_release(hub_t *hub)
{
fibril_mutex_unlock(&hub->guard);
}
void hub_set_port_state(hub_t *hub, size_t port_index, hub_port_state_t state)
{
hub_port_t *port = get_hub_port(hub, port_index);
if (port == NULL) {
return;
}
usb_log_debug("Setting port %zu to state %d.", port_index, state);
switch (state) {
case HUB_PORT_STATE_POWERED_OFF:
clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
break;
case HUB_PORT_STATE_RESUMING:
port->state = state;
set_port_state_delayed(hub, port_index,
10, state, HUB_PORT_STATE_ENABLED);
break;
case HUB_PORT_STATE_RESETTING:
port->state = state;
set_port_state_delayed(hub, port_index,
10, state, HUB_PORT_STATE_ENABLED);
break;
case HUB_PORT_STATE_ENABLED:
if (port->state == HUB_PORT_STATE_RESETTING) {
set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
}
break;
default:
break;
}
port->state = state;
}
void hub_set_port_state_all(hub_t *hub, hub_port_state_t state)
{
size_t i;
for (i = 0; i < HUB_PORT_COUNT; i++) {
hub_set_port_state(hub, i, state);
}
}
hub_port_state_t hub_get_port_state(hub_t *hub, size_t port_index)
{
hub_port_t *port = get_hub_port(hub, port_index);
if (port == NULL) {
return HUB_PORT_STATE_UNKNOWN;
}
return port->state;
}
void hub_clear_port_status_change(hub_t *hub, size_t port_index,
hub_status_change_t change)
{
hub_port_t *port = get_hub_port(hub, port_index);
if (port == NULL) {
return;
}
clear_port_status_change(port, change);
}
uint16_t hub_get_port_status_change(hub_t *hub, size_t port_index)
{
hub_port_t *port = get_hub_port(hub, port_index);
if (port == NULL) {
return 0;
}
return port->status_change;
}
uint32_t hub_get_port_status(hub_t *hub, size_t port_index)
{
hub_port_t *port = get_hub_port(hub, port_index);
if (port == NULL) {
return 0;
}
uint32_t status = MAKE_BYTE(
port->connected_device == NULL ? 0 : 1,
port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
(port->state == HUB_PORT_STATE_SUSPENDED) ||
(port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
0,
port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
0, 0, 0);
status |= MAKE_BYTE(
port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
0,
0, 0, 0, 0, 0, 0) << 8;
status |= (port->status_change << 16);
return status;
}
uint8_t hub_get_status_change_bitmap(hub_t *hub)
{
uint8_t change_map = 0;
size_t i;
for (i = 0; i < HUB_PORT_COUNT; i++) {
hub_port_t *port = &hub->ports[i];
if (port->status_change != 0) {
change_map |= (1 << port->index);
}
}
return change_map;
}
static hub_port_t *get_hub_port(hub_t *hub, size_t port)
{
if (port >= HUB_PORT_COUNT) {
return NULL;
}
return &hub->ports[port];
}
static void set_port_status_change(hub_port_t *port,
hub_status_change_t change)
{
assert(port != NULL);
uint16_t old_value = port->status_change;
port->status_change |= change;
usb_log_debug("Changing status change on %zu: %04x => %04x",
port->index,
(unsigned int) old_value, (unsigned int) port->status_change);
port->hub->signal_changes = true;
}
static void clear_port_status_change(hub_port_t *port,
uint16_t change)
{
assert(port != NULL);
port->status_change &= (~change);
port->hub->signal_changes = true;
}
struct delay_port_state_change {
usec_t delay;
hub_port_state_t old_state;
hub_port_state_t new_state;
size_t port;
hub_t *hub;
};
static errno_t set_port_state_delayed_fibril(void *arg)
{
struct delay_port_state_change *change =
(struct delay_port_state_change *) arg;
fibril_usleep(change->delay);
hub_acquire(change->hub);
hub_port_t *port = get_hub_port(change->hub, change->port);
assert(port != NULL);
if (port->state == change->old_state) {
hub_set_port_state(change->hub, change->port,
change->new_state);
}
hub_release(change->hub);
free(change);
return EOK;
}
static void set_port_state_delayed(hub_t *hub, size_t port_index,
usec_t delay_time_ms, hub_port_state_t old_state,
hub_port_state_t new_state)
{
struct delay_port_state_change *change =
malloc(sizeof(struct delay_port_state_change));
change->hub = hub;
change->port = port_index;
change->delay = MSEC2USEC(delay_time_ms);
change->old_state = old_state;
change->new_state = new_state;
fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
if (fibril == 0) {
usb_log_error("Failed to create fibril.");
free(change);
return;
}
fibril_add_ready(fibril);
}
HelenOS homepage, sources at GitHub