/* * Copyright (c) 2010 Lenka Trochtova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup devman * @{ */ #include #include #include "devman.h" #include "fun.h" static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *); /* Function nodes */ /** Create a new function node. * * @return A function node structure. */ 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; atomic_set(&fun->refcnt, 0); fibril_mutex_initialize(&fun->busy_lock); link_initialize(&fun->dev_functions); list_initialize(&fun->match_ids.ids); return fun; } /** Delete a function node. * * @param fun The device node structure. */ 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); } /** Increase function node reference count. * * @param fun Function node */ void fun_add_ref(fun_node_t *fun) { atomic_inc(&fun->refcnt); } /** Decrease function node reference count. * * When the count drops to zero the function node is freed. * * @param fun Function node */ void fun_del_ref(fun_node_t *fun) { if (atomic_predec(&fun->refcnt) == 0) delete_fun_node(fun); } /** Make function busy for reconfiguration operations. */ void fun_busy_lock(fun_node_t *fun) { fibril_mutex_lock(&fun->busy_lock); } /** Mark end of reconfiguration operation. */ void fun_busy_unlock(fun_node_t *fun) { fibril_mutex_unlock(&fun->busy_lock); } /** Find the function node with the specified handle. * * @param tree The device tree where we look for the device node. * @param handle The handle of the function. * @return The function node. */ 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; } /** Find the function node with the specified handle. * * @param tree The device tree where we look for the device node. * @param handle The handle of the function. * @return The function node. */ 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; } /** Create and set device's full path in device tree. * * @param tree Device tree * @param node The device's device node. * @param parent The parent device node. * @return True on success, false otherwise (insufficient * resources etc.). */ 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; } /** Find function node with a specified path in the device tree. * * @param path The path of the function node in the device tree. * @param tree The device tree. * @return The function node if it is present in the tree, NULL * otherwise. */ 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); /* * Relative path to the function from its parent (but with '/' at the * beginning) */ 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) { /* Restore the original path. */ next_path_elem[0] = '/'; } rel_path = next_path_elem; } fibril_rwlock_read_unlock(&tree->rwlock); return fun; } /** Find function with a specified name belonging to given device. * * Device tree rwlock should be held at least for reading. * * @param tree Device tree * @param dev Device the function belongs to. * @param name Function name (not path). * @return Function node. * @retval NULL No function with given name. */ 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; } /** Find child function node with a specified name. * * Device tree rwlock should be held at least for reading. * * @param tree Device tree * @param parent The parent function node. * @param name The name of the child function. * @return The child function node. */ 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); } /** @} */