HelenOS sources
This source file includes following definitions.
- usb_port_init
- enumerate_worker
- usb_port_connected
- usb_port_enabled
- remove_worker
- fork_remove_worker
- usb_port_disabled
- usb_port_fini
- usb_port_condvar_wait_timeout
#include <stdlib.h>
#include <fibril.h>
#include <assert.h>
#include <usb/debug.h>
#include <usb/port.h>
void usb_port_init(usb_port_t *port)
{
fibril_mutex_initialize(&port->guard);
fibril_condvar_initialize(&port->finished_cv);
fibril_condvar_initialize(&port->enabled_cv);
}
struct enumerate_worker_args {
usb_port_t *port;
usb_port_enumerate_t handler;
};
static int enumerate_worker(void *arg)
{
struct enumerate_worker_args *const args = arg;
usb_port_t *port = args->port;
usb_port_enumerate_t handler = args->handler;
free(args);
fibril_mutex_lock(&port->guard);
if (port->state == PORT_ERROR) {
port->state = PORT_DISABLED;
goto out;
}
assert(port->state == PORT_CONNECTING);
port->state = handler(port) ? PORT_DISABLED : PORT_ENUMERATED;
out:
fibril_condvar_broadcast(&port->finished_cv);
fibril_mutex_unlock(&port->guard);
return EOK;
}
int usb_port_connected(usb_port_t *port, usb_port_enumerate_t handler)
{
assert(port);
int ret = ENOMEM;
fibril_mutex_lock(&port->guard);
if (port->state != PORT_DISABLED) {
usb_log_warning("a connected event come for port that is not disabled.");
ret = EINVAL;
goto out;
}
struct enumerate_worker_args *args = malloc(sizeof(*args));
if (!args)
goto out;
fid_t fibril = fibril_create(&enumerate_worker, args);
if (!fibril) {
free(args);
goto out;
}
args->port = port;
args->handler = handler;
port->state = PORT_CONNECTING;
fibril_add_ready(fibril);
out:
fibril_mutex_unlock(&port->guard);
return ret;
}
void usb_port_enabled(usb_port_t *port)
{
assert(port);
fibril_mutex_lock(&port->guard);
fibril_condvar_broadcast(&port->enabled_cv);
fibril_mutex_unlock(&port->guard);
}
struct remove_worker_args {
usb_port_t *port;
usb_port_remove_t handler;
};
static int remove_worker(void *arg)
{
struct remove_worker_args *const args = arg;
usb_port_t *port = args->port;
usb_port_remove_t handler = args->handler;
free(args);
fibril_mutex_lock(&port->guard);
assert(port->state == PORT_DISCONNECTING);
handler(port);
port->state = PORT_DISABLED;
fibril_condvar_broadcast(&port->finished_cv);
fibril_mutex_unlock(&port->guard);
return EOK;
}
static void fork_remove_worker(usb_port_t *port, usb_port_remove_t handler)
{
struct remove_worker_args *args = malloc(sizeof(*args));
if (!args)
return;
fid_t fibril = fibril_create(&remove_worker, args);
if (!fibril) {
free(args);
return;
}
args->port = port;
args->handler = handler;
port->state = PORT_DISCONNECTING;
fibril_add_ready(fibril);
}
void usb_port_disabled(usb_port_t *port, usb_port_remove_t handler)
{
assert(port);
fibril_mutex_lock(&port->guard);
switch (port->state) {
case PORT_ENUMERATED:
fork_remove_worker(port, handler);
break;
case PORT_CONNECTING:
port->state = PORT_ERROR;
fibril_condvar_broadcast(&port->enabled_cv);
case PORT_ERROR:
fibril_condvar_wait(&port->finished_cv, &port->guard);
case PORT_DISCONNECTING:
case PORT_DISABLED:
break;
}
fibril_mutex_unlock(&port->guard);
}
void usb_port_fini(usb_port_t *port)
{
assert(port);
fibril_mutex_lock(&port->guard);
switch (port->state) {
case PORT_ENUMERATED:
port->state = PORT_DISABLED;
case PORT_DISABLED:
break;
case PORT_CONNECTING:
port->state = PORT_ERROR;
fibril_condvar_broadcast(&port->enabled_cv);
case PORT_ERROR:
case PORT_DISCONNECTING:
fibril_condvar_wait(&port->finished_cv, &port->guard);
break;
}
fibril_mutex_unlock(&port->guard);
}
int usb_port_condvar_wait_timeout(usb_port_t *port, fibril_condvar_t *cv,
usec_t timeout)
{
assert(port);
assert(port->state == PORT_CONNECTING);
assert(fibril_mutex_is_locked(&port->guard));
if (fibril_condvar_wait_timeout(cv, &port->guard, timeout))
return ETIMEOUT;
return port->state == PORT_CONNECTING ? EOK : EINTR;
}
HelenOS homepage, sources at GitHub