HelenOS sources
This source file includes following definitions.
- create_fun_node
- delete_fun_node
- fun_add_ref
- fun_del_ref
- fun_busy_lock
- fun_busy_unlock
- find_fun_node_no_lock
- find_fun_node
- set_fun_path
- find_fun_node_by_path
- find_fun_node_in_device
- find_node_child
- assign_driver_fibril
- fun_online
- fun_offline
#include <errno.h>
#include <io/log.h>
#include <loc.h>
#include "dev.h"
#include "devman.h"
#include "devtree.h"
#include "driver.h"
#include "fun.h"
#include "main.h"
#include "loc.h"
static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
fun_node_t *create_fun_node(void)
{
fun_node_t *fun;
fun = calloc(1, sizeof(fun_node_t));
if (fun == NULL)
return NULL;
fun->state = FUN_INIT;
refcount_init(&fun->refcnt);
fibril_mutex_initialize(&fun->busy_lock);
link_initialize(&fun->dev_functions);
list_initialize(&fun->match_ids.ids);
return fun;
}
void delete_fun_node(fun_node_t *fun)
{
assert(fun->dev == NULL);
assert(fun->child == NULL);
clean_match_ids(&fun->match_ids);
free(fun->name);
free(fun->pathname);
free(fun);
}
void fun_add_ref(fun_node_t *fun)
{
refcount_up(&fun->refcnt);
}
void fun_del_ref(fun_node_t *fun)
{
if (refcount_down(&fun->refcnt))
delete_fun_node(fun);
}
void fun_busy_lock(fun_node_t *fun)
{
fibril_mutex_lock(&fun->busy_lock);
}
void fun_busy_unlock(fun_node_t *fun)
{
fibril_mutex_unlock(&fun->busy_lock);
}
fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
{
fun_node_t *fun;
assert(fibril_rwlock_is_locked(&tree->rwlock));
ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
if (link == NULL)
return NULL;
fun = hash_table_get_inst(link, fun_node_t, devman_fun);
return fun;
}
fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
{
fun_node_t *fun = NULL;
fibril_rwlock_read_lock(&tree->rwlock);
fun = find_fun_node_no_lock(tree, handle);
if (fun != NULL)
fun_add_ref(fun);
fibril_rwlock_read_unlock(&tree->rwlock);
return fun;
}
bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
{
assert(fibril_rwlock_is_write_locked(&tree->rwlock));
assert(fun->name != NULL);
size_t pathsize = (str_size(fun->name) + 1);
if (parent != NULL)
pathsize += str_size(parent->pathname) + 1;
fun->pathname = (char *) malloc(pathsize);
if (fun->pathname == NULL) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
return false;
}
if (parent != NULL) {
str_cpy(fun->pathname, pathsize, parent->pathname);
str_append(fun->pathname, pathsize, "/");
str_append(fun->pathname, pathsize, fun->name);
} else {
str_cpy(fun->pathname, pathsize, fun->name);
}
return true;
}
fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
{
assert(path != NULL);
bool is_absolute = path[0] == '/';
if (!is_absolute) {
return NULL;
}
fibril_rwlock_read_lock(&tree->rwlock);
fun_node_t *fun = tree->root_node;
fun_add_ref(fun);
char *rel_path = path;
char *next_path_elem = NULL;
bool cont = (rel_path[1] != '\0');
while (cont && fun != NULL) {
next_path_elem = get_path_elem_end(rel_path + 1);
if (next_path_elem[0] == '/') {
cont = true;
next_path_elem[0] = 0;
} else {
cont = false;
}
fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
fun_del_ref(fun);
fun = cfun;
if (cont) {
next_path_elem[0] = '/';
}
rel_path = next_path_elem;
}
fibril_rwlock_read_unlock(&tree->rwlock);
return fun;
}
fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
const char *name)
{
assert(name != NULL);
assert(fibril_rwlock_is_locked(&tree->rwlock));
list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
if (str_cmp(name, fun->name) == 0) {
fun_add_ref(fun);
return fun;
}
}
return NULL;
}
static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
const char *name)
{
return find_fun_node_in_device(tree, pfun->child, name);
}
static errno_t assign_driver_fibril(void *arg)
{
dev_node_t *dev_node = (dev_node_t *) arg;
assign_driver(dev_node, &drivers_list, &device_tree);
dev_del_ref(dev_node);
return EOK;
}
errno_t fun_online(fun_node_t *fun)
{
dev_node_t *dev;
fibril_rwlock_write_lock(&device_tree.rwlock);
if (fun->state == FUN_ON_LINE) {
fibril_rwlock_write_unlock(&device_tree.rwlock);
log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
fun->pathname);
return EOK;
}
if (fun->ftype == fun_inner) {
dev = create_dev_node();
if (dev == NULL) {
fibril_rwlock_write_unlock(&device_tree.rwlock);
return ENOMEM;
}
insert_dev_node(&device_tree, dev, fun);
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
if (fun->ftype == fun_inner) {
dev = fun->child;
assert(dev != NULL);
dev_add_ref(dev);
fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
if (assign_fibril == 0) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
"assigning driver.");
fibril_rwlock_write_unlock(&device_tree.rwlock);
return ENOMEM;
}
fibril_add_ready(assign_fibril);
} else
loc_register_tree_function(fun, &device_tree);
fun->state = FUN_ON_LINE;
fibril_rwlock_write_unlock(&device_tree.rwlock);
return EOK;
}
errno_t fun_offline(fun_node_t *fun)
{
errno_t rc;
fibril_rwlock_write_lock(&device_tree.rwlock);
if (fun->state == FUN_OFF_LINE) {
fibril_rwlock_write_unlock(&device_tree.rwlock);
log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
fun->pathname);
return EOK;
}
if (fun->ftype == fun_inner) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
fun->pathname);
if (fun->child != NULL) {
dev_node_t *dev = fun->child;
device_state_t dev_state;
dev_add_ref(dev);
dev_state = dev->state;
fibril_rwlock_write_unlock(&device_tree.rwlock);
if (dev_state == DEVICE_USABLE) {
rc = driver_dev_remove(&device_tree, dev);
if (rc != EOK) {
dev_del_ref(dev);
return ENOTSUP;
}
}
fibril_rwlock_read_lock(&device_tree.rwlock);
if (!list_empty(&dev->functions)) {
fibril_rwlock_read_unlock(&device_tree.rwlock);
dev_del_ref(dev);
return EIO;
}
driver_t *driver = dev->drv;
fibril_rwlock_read_unlock(&device_tree.rwlock);
if (driver)
detach_driver(&device_tree, dev);
fibril_rwlock_write_lock(&device_tree.rwlock);
remove_dev_node(&device_tree, dev);
dev_del_ref(dev);
dev_del_ref(dev);
}
} else {
rc = loc_unregister_tree_function(fun, &device_tree);
if (rc != EOK) {
fibril_rwlock_write_unlock(&device_tree.rwlock);
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
return EIO;
}
fun->service_id = 0;
}
fun->state = FUN_OFF_LINE;
fibril_rwlock_write_unlock(&device_tree.rwlock);
return EOK;
}
HelenOS homepage, sources at GitHub