| [38e52c92] | 1 | /*
 | 
|---|
 | 2 |  * Copyright (c) 2010 Lenka Trochtova
 | 
|---|
 | 3 |  * All rights reserved.
 | 
|---|
 | 4 |  *
 | 
|---|
 | 5 |  * Redistribution and use in source and binary forms, with or without
 | 
|---|
 | 6 |  * modification, are permitted provided that the following conditions
 | 
|---|
 | 7 |  * are met:
 | 
|---|
 | 8 |  *
 | 
|---|
 | 9 |  * - Redistributions of source code must retain the above copyright
 | 
|---|
 | 10 |  *   notice, this list of conditions and the following disclaimer.
 | 
|---|
 | 11 |  * - Redistributions in binary form must reproduce the above copyright
 | 
|---|
 | 12 |  *   notice, this list of conditions and the following disclaimer in the
 | 
|---|
 | 13 |  *   documentation and/or other materials provided with the distribution.
 | 
|---|
 | 14 |  * - The name of the author may not be used to endorse or promote products
 | 
|---|
 | 15 |  *   derived from this software without specific prior written permission.
 | 
|---|
 | 16 |  *
 | 
|---|
 | 17 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | 
|---|
 | 18 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
|---|
 | 19 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
|---|
 | 20 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
|---|
 | 21 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
|---|
 | 22 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
|---|
 | 23 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
|---|
 | 24 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
|---|
 | 25 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
|---|
 | 26 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
|---|
 | 27 |  */
 | 
|---|
 | 28 | 
 | 
|---|
 | 29 | /** @addtogroup devman
 | 
|---|
 | 30 |  * @{
 | 
|---|
 | 31 |  */
 | 
|---|
 | 32 | 
 | 
|---|
 | 33 | #include <errno.h>
 | 
|---|
 | 34 | #include <io/log.h>
 | 
|---|
| [02e5e34] | 35 | #include <loc.h>
 | 
|---|
| [38e52c92] | 36 | 
 | 
|---|
| [02e5e34] | 37 | #include "dev.h"
 | 
|---|
| [38e52c92] | 38 | #include "devman.h"
 | 
|---|
| [02e5e34] | 39 | #include "devtree.h"
 | 
|---|
 | 40 | #include "driver.h"
 | 
|---|
| [38e52c92] | 41 | #include "fun.h"
 | 
|---|
| [02e5e34] | 42 | #include "main.h"
 | 
|---|
 | 43 | #include "loc.h"
 | 
|---|
| [38e52c92] | 44 | 
 | 
|---|
 | 45 | static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
 | 
|---|
 | 46 | 
 | 
|---|
 | 47 | /* Function nodes */
 | 
|---|
 | 48 | 
 | 
|---|
 | 49 | /** Create a new function node.
 | 
|---|
 | 50 |  *
 | 
|---|
 | 51 |  * @return              A function node structure.
 | 
|---|
 | 52 |  */
 | 
|---|
 | 53 | fun_node_t *create_fun_node(void)
 | 
|---|
 | 54 | {
 | 
|---|
 | 55 |         fun_node_t *fun;
 | 
|---|
 | 56 | 
 | 
|---|
 | 57 |         fun = calloc(1, sizeof(fun_node_t));
 | 
|---|
 | 58 |         if (fun == NULL)
 | 
|---|
 | 59 |                 return NULL;
 | 
|---|
| [a35b458] | 60 | 
 | 
|---|
| [38e52c92] | 61 |         fun->state = FUN_INIT;
 | 
|---|
| [498ced1] | 62 |         refcount_init(&fun->refcnt);
 | 
|---|
| [38e52c92] | 63 |         fibril_mutex_initialize(&fun->busy_lock);
 | 
|---|
 | 64 |         link_initialize(&fun->dev_functions);
 | 
|---|
 | 65 |         list_initialize(&fun->match_ids.ids);
 | 
|---|
| [a35b458] | 66 | 
 | 
|---|
| [38e52c92] | 67 |         return fun;
 | 
|---|
 | 68 | }
 | 
|---|
 | 69 | 
 | 
|---|
 | 70 | /** Delete a function node.
 | 
|---|
 | 71 |  *
 | 
|---|
 | 72 |  * @param fun           The device node structure.
 | 
|---|
 | 73 |  */
 | 
|---|
 | 74 | void delete_fun_node(fun_node_t *fun)
 | 
|---|
 | 75 | {
 | 
|---|
 | 76 |         assert(fun->dev == NULL);
 | 
|---|
 | 77 |         assert(fun->child == NULL);
 | 
|---|
| [a35b458] | 78 | 
 | 
|---|
| [38e52c92] | 79 |         clean_match_ids(&fun->match_ids);
 | 
|---|
 | 80 |         free(fun->name);
 | 
|---|
 | 81 |         free(fun->pathname);
 | 
|---|
 | 82 |         free(fun);
 | 
|---|
 | 83 | }
 | 
|---|
 | 84 | 
 | 
|---|
 | 85 | /** Increase function node reference count.
 | 
|---|
 | 86 |  *
 | 
|---|
 | 87 |  * @param fun   Function node
 | 
|---|
 | 88 |  */
 | 
|---|
 | 89 | void fun_add_ref(fun_node_t *fun)
 | 
|---|
 | 90 | {
 | 
|---|
| [498ced1] | 91 |         refcount_up(&fun->refcnt);
 | 
|---|
| [38e52c92] | 92 | }
 | 
|---|
 | 93 | 
 | 
|---|
 | 94 | /** Decrease function node reference count.
 | 
|---|
 | 95 |  *
 | 
|---|
 | 96 |  * When the count drops to zero the function node is freed.
 | 
|---|
 | 97 |  *
 | 
|---|
 | 98 |  * @param fun   Function node
 | 
|---|
 | 99 |  */
 | 
|---|
 | 100 | void fun_del_ref(fun_node_t *fun)
 | 
|---|
 | 101 | {
 | 
|---|
| [498ced1] | 102 |         if (refcount_down(&fun->refcnt))
 | 
|---|
| [38e52c92] | 103 |                 delete_fun_node(fun);
 | 
|---|
 | 104 | }
 | 
|---|
 | 105 | 
 | 
|---|
 | 106 | /** Make function busy for reconfiguration operations. */
 | 
|---|
 | 107 | void fun_busy_lock(fun_node_t *fun)
 | 
|---|
 | 108 | {
 | 
|---|
 | 109 |         fibril_mutex_lock(&fun->busy_lock);
 | 
|---|
 | 110 | }
 | 
|---|
 | 111 | 
 | 
|---|
 | 112 | /** Mark end of reconfiguration operation. */
 | 
|---|
 | 113 | void fun_busy_unlock(fun_node_t *fun)
 | 
|---|
 | 114 | {
 | 
|---|
 | 115 |         fibril_mutex_unlock(&fun->busy_lock);
 | 
|---|
 | 116 | }
 | 
|---|
 | 117 | 
 | 
|---|
 | 118 | /** Find the function node with the specified handle.
 | 
|---|
 | 119 |  *
 | 
|---|
 | 120 |  * @param tree          The device tree where we look for the device node.
 | 
|---|
 | 121 |  * @param handle        The handle of the function.
 | 
|---|
 | 122 |  * @return              The function node.
 | 
|---|
 | 123 |  */
 | 
|---|
 | 124 | fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
 | 
|---|
 | 125 | {
 | 
|---|
 | 126 |         fun_node_t *fun;
 | 
|---|
| [a35b458] | 127 | 
 | 
|---|
| [38e52c92] | 128 |         assert(fibril_rwlock_is_locked(&tree->rwlock));
 | 
|---|
| [a35b458] | 129 | 
 | 
|---|
| [38e52c92] | 130 |         ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
 | 
|---|
 | 131 |         if (link == NULL)
 | 
|---|
 | 132 |                 return NULL;
 | 
|---|
| [a35b458] | 133 | 
 | 
|---|
| [38e52c92] | 134 |         fun = hash_table_get_inst(link, fun_node_t, devman_fun);
 | 
|---|
| [a35b458] | 135 | 
 | 
|---|
| [38e52c92] | 136 |         return fun;
 | 
|---|
 | 137 | }
 | 
|---|
 | 138 | 
 | 
|---|
 | 139 | /** Find the function node with the specified handle.
 | 
|---|
 | 140 |  *
 | 
|---|
 | 141 |  * @param tree          The device tree where we look for the device node.
 | 
|---|
 | 142 |  * @param handle        The handle of the function.
 | 
|---|
 | 143 |  * @return              The function node.
 | 
|---|
 | 144 |  */
 | 
|---|
 | 145 | fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
 | 
|---|
 | 146 | {
 | 
|---|
 | 147 |         fun_node_t *fun = NULL;
 | 
|---|
| [a35b458] | 148 | 
 | 
|---|
| [38e52c92] | 149 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| [a35b458] | 150 | 
 | 
|---|
| [38e52c92] | 151 |         fun = find_fun_node_no_lock(tree, handle);
 | 
|---|
 | 152 |         if (fun != NULL)
 | 
|---|
 | 153 |                 fun_add_ref(fun);
 | 
|---|
| [a35b458] | 154 | 
 | 
|---|
| [38e52c92] | 155 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| [a35b458] | 156 | 
 | 
|---|
| [38e52c92] | 157 |         return fun;
 | 
|---|
 | 158 | }
 | 
|---|
 | 159 | 
 | 
|---|
 | 160 | /** Create and set device's full path in device tree.
 | 
|---|
 | 161 |  *
 | 
|---|
 | 162 |  * @param tree          Device tree
 | 
|---|
 | 163 |  * @param node          The device's device node.
 | 
|---|
 | 164 |  * @param parent        The parent device node.
 | 
|---|
 | 165 |  * @return              True on success, false otherwise (insufficient
 | 
|---|
 | 166 |  *                      resources etc.).
 | 
|---|
 | 167 |  */
 | 
|---|
 | 168 | bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
 | 
|---|
 | 169 | {
 | 
|---|
 | 170 |         assert(fibril_rwlock_is_write_locked(&tree->rwlock));
 | 
|---|
 | 171 |         assert(fun->name != NULL);
 | 
|---|
| [a35b458] | 172 | 
 | 
|---|
| [38e52c92] | 173 |         size_t pathsize = (str_size(fun->name) + 1);
 | 
|---|
 | 174 |         if (parent != NULL)
 | 
|---|
 | 175 |                 pathsize += str_size(parent->pathname) + 1;
 | 
|---|
| [a35b458] | 176 | 
 | 
|---|
| [38e52c92] | 177 |         fun->pathname = (char *) malloc(pathsize);
 | 
|---|
 | 178 |         if (fun->pathname == NULL) {
 | 
|---|
 | 179 |                 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
 | 
|---|
 | 180 |                 return false;
 | 
|---|
 | 181 |         }
 | 
|---|
| [a35b458] | 182 | 
 | 
|---|
| [38e52c92] | 183 |         if (parent != NULL) {
 | 
|---|
 | 184 |                 str_cpy(fun->pathname, pathsize, parent->pathname);
 | 
|---|
 | 185 |                 str_append(fun->pathname, pathsize, "/");
 | 
|---|
 | 186 |                 str_append(fun->pathname, pathsize, fun->name);
 | 
|---|
 | 187 |         } else {
 | 
|---|
 | 188 |                 str_cpy(fun->pathname, pathsize, fun->name);
 | 
|---|
 | 189 |         }
 | 
|---|
| [a35b458] | 190 | 
 | 
|---|
| [38e52c92] | 191 |         return true;
 | 
|---|
 | 192 | }
 | 
|---|
 | 193 | 
 | 
|---|
 | 194 | /** Find function node with a specified path in the device tree.
 | 
|---|
| [1b20da0] | 195 |  *
 | 
|---|
| [38e52c92] | 196 |  * @param path          The path of the function node in the device tree.
 | 
|---|
 | 197 |  * @param tree          The device tree.
 | 
|---|
 | 198 |  * @return              The function node if it is present in the tree, NULL
 | 
|---|
 | 199 |  *                      otherwise.
 | 
|---|
 | 200 |  */
 | 
|---|
 | 201 | fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
 | 
|---|
 | 202 | {
 | 
|---|
 | 203 |         assert(path != NULL);
 | 
|---|
 | 204 | 
 | 
|---|
 | 205 |         bool is_absolute = path[0] == '/';
 | 
|---|
 | 206 |         if (!is_absolute) {
 | 
|---|
 | 207 |                 return NULL;
 | 
|---|
 | 208 |         }
 | 
|---|
 | 209 | 
 | 
|---|
 | 210 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| [a35b458] | 211 | 
 | 
|---|
| [38e52c92] | 212 |         fun_node_t *fun = tree->root_node;
 | 
|---|
 | 213 |         fun_add_ref(fun);
 | 
|---|
 | 214 |         /*
 | 
|---|
 | 215 |          * Relative path to the function from its parent (but with '/' at the
 | 
|---|
 | 216 |          * beginning)
 | 
|---|
 | 217 |          */
 | 
|---|
 | 218 |         char *rel_path = path;
 | 
|---|
 | 219 |         char *next_path_elem = NULL;
 | 
|---|
 | 220 |         bool cont = (rel_path[1] != '\0');
 | 
|---|
| [a35b458] | 221 | 
 | 
|---|
| [38e52c92] | 222 |         while (cont && fun != NULL) {
 | 
|---|
 | 223 |                 next_path_elem  = get_path_elem_end(rel_path + 1);
 | 
|---|
 | 224 |                 if (next_path_elem[0] == '/') {
 | 
|---|
 | 225 |                         cont = true;
 | 
|---|
 | 226 |                         next_path_elem[0] = 0;
 | 
|---|
 | 227 |                 } else {
 | 
|---|
 | 228 |                         cont = false;
 | 
|---|
 | 229 |                 }
 | 
|---|
| [a35b458] | 230 | 
 | 
|---|
| [38e52c92] | 231 |                 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
 | 
|---|
 | 232 |                 fun_del_ref(fun);
 | 
|---|
 | 233 |                 fun = cfun;
 | 
|---|
| [a35b458] | 234 | 
 | 
|---|
| [38e52c92] | 235 |                 if (cont) {
 | 
|---|
 | 236 |                         /* Restore the original path. */
 | 
|---|
 | 237 |                         next_path_elem[0] = '/';
 | 
|---|
 | 238 |                 }
 | 
|---|
 | 239 |                 rel_path = next_path_elem;
 | 
|---|
 | 240 |         }
 | 
|---|
| [a35b458] | 241 | 
 | 
|---|
| [38e52c92] | 242 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| [a35b458] | 243 | 
 | 
|---|
| [38e52c92] | 244 |         return fun;
 | 
|---|
 | 245 | }
 | 
|---|
 | 246 | 
 | 
|---|
 | 247 | /** Find function with a specified name belonging to given device.
 | 
|---|
 | 248 |  *
 | 
|---|
 | 249 |  * Device tree rwlock should be held at least for reading.
 | 
|---|
 | 250 |  *
 | 
|---|
 | 251 |  * @param tree Device tree
 | 
|---|
 | 252 |  * @param dev Device the function belongs to.
 | 
|---|
 | 253 |  * @param name Function name (not path).
 | 
|---|
 | 254 |  * @return Function node.
 | 
|---|
 | 255 |  * @retval NULL No function with given name.
 | 
|---|
 | 256 |  */
 | 
|---|
 | 257 | fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
 | 
|---|
 | 258 |     const char *name)
 | 
|---|
 | 259 | {
 | 
|---|
 | 260 |         assert(name != NULL);
 | 
|---|
 | 261 |         assert(fibril_rwlock_is_locked(&tree->rwlock));
 | 
|---|
 | 262 | 
 | 
|---|
| [08bc23d] | 263 |         list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
 | 
|---|
| [38e52c92] | 264 |                 if (str_cmp(name, fun->name) == 0) {
 | 
|---|
 | 265 |                         fun_add_ref(fun);
 | 
|---|
 | 266 |                         return fun;
 | 
|---|
 | 267 |                 }
 | 
|---|
 | 268 |         }
 | 
|---|
 | 269 | 
 | 
|---|
 | 270 |         return NULL;
 | 
|---|
 | 271 | }
 | 
|---|
 | 272 | 
 | 
|---|
 | 273 | /** Find child function node with a specified name.
 | 
|---|
 | 274 |  *
 | 
|---|
 | 275 |  * Device tree rwlock should be held at least for reading.
 | 
|---|
 | 276 |  *
 | 
|---|
 | 277 |  * @param tree          Device tree
 | 
|---|
 | 278 |  * @param parent        The parent function node.
 | 
|---|
 | 279 |  * @param name          The name of the child function.
 | 
|---|
 | 280 |  * @return              The child function node.
 | 
|---|
 | 281 |  */
 | 
|---|
 | 282 | static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
 | 
|---|
 | 283 |     const char *name)
 | 
|---|
 | 284 | {
 | 
|---|
 | 285 |         return find_fun_node_in_device(tree, pfun->child, name);
 | 
|---|
 | 286 | }
 | 
|---|
 | 287 | 
 | 
|---|
| [b7fd2a0] | 288 | static errno_t assign_driver_fibril(void *arg)
 | 
|---|
| [02e5e34] | 289 | {
 | 
|---|
 | 290 |         dev_node_t *dev_node = (dev_node_t *) arg;
 | 
|---|
 | 291 |         assign_driver(dev_node, &drivers_list, &device_tree);
 | 
|---|
 | 292 | 
 | 
|---|
 | 293 |         /* Delete one reference we got from the caller. */
 | 
|---|
 | 294 |         dev_del_ref(dev_node);
 | 
|---|
 | 295 |         return EOK;
 | 
|---|
 | 296 | }
 | 
|---|
 | 297 | 
 | 
|---|
| [b7fd2a0] | 298 | errno_t fun_online(fun_node_t *fun)
 | 
|---|
| [02e5e34] | 299 | {
 | 
|---|
 | 300 |         dev_node_t *dev;
 | 
|---|
| [a35b458] | 301 | 
 | 
|---|
| [02e5e34] | 302 |         fibril_rwlock_write_lock(&device_tree.rwlock);
 | 
|---|
| [a35b458] | 303 | 
 | 
|---|
| [02e5e34] | 304 |         if (fun->state == FUN_ON_LINE) {
 | 
|---|
 | 305 |                 fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 306 |                 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
 | 
|---|
 | 307 |                     fun->pathname);
 | 
|---|
 | 308 |                 return EOK;
 | 
|---|
 | 309 |         }
 | 
|---|
| [a35b458] | 310 | 
 | 
|---|
| [02e5e34] | 311 |         if (fun->ftype == fun_inner) {
 | 
|---|
 | 312 |                 dev = create_dev_node();
 | 
|---|
 | 313 |                 if (dev == NULL) {
 | 
|---|
 | 314 |                         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 315 |                         return ENOMEM;
 | 
|---|
 | 316 |                 }
 | 
|---|
| [a35b458] | 317 | 
 | 
|---|
| [02e5e34] | 318 |                 insert_dev_node(&device_tree, dev, fun);
 | 
|---|
 | 319 |         }
 | 
|---|
| [a35b458] | 320 | 
 | 
|---|
| [02e5e34] | 321 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
 | 
|---|
| [a35b458] | 322 | 
 | 
|---|
| [02e5e34] | 323 |         if (fun->ftype == fun_inner) {
 | 
|---|
 | 324 |                 dev = fun->child;
 | 
|---|
 | 325 |                 assert(dev != NULL);
 | 
|---|
| [a35b458] | 326 | 
 | 
|---|
| [02e5e34] | 327 |                 /* Give one reference over to assign_driver_fibril(). */
 | 
|---|
 | 328 |                 dev_add_ref(dev);
 | 
|---|
| [a35b458] | 329 | 
 | 
|---|
| [02e5e34] | 330 |                 /*
 | 
|---|
 | 331 |                  * Try to find a suitable driver and assign it to the device.  We do
 | 
|---|
 | 332 |                  * not want to block the current fibril that is used for processing
 | 
|---|
 | 333 |                  * incoming calls: we will launch a separate fibril to handle the
 | 
|---|
 | 334 |                  * driver assigning. That is because assign_driver can actually include
 | 
|---|
 | 335 |                  * task spawning which could take some time.
 | 
|---|
 | 336 |                  */
 | 
|---|
 | 337 |                 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
 | 
|---|
 | 338 |                 if (assign_fibril == 0) {
 | 
|---|
 | 339 |                         log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
 | 
|---|
 | 340 |                             "assigning driver.");
 | 
|---|
 | 341 |                         /* XXX Cleanup */
 | 
|---|
 | 342 |                         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 343 |                         return ENOMEM;
 | 
|---|
 | 344 |                 }
 | 
|---|
 | 345 |                 fibril_add_ready(assign_fibril);
 | 
|---|
 | 346 |         } else
 | 
|---|
 | 347 |                 loc_register_tree_function(fun, &device_tree);
 | 
|---|
| [a35b458] | 348 | 
 | 
|---|
| [ba0eac5] | 349 |         fun->state = FUN_ON_LINE;
 | 
|---|
| [02e5e34] | 350 |         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
| [a35b458] | 351 | 
 | 
|---|
| [02e5e34] | 352 |         return EOK;
 | 
|---|
 | 353 | }
 | 
|---|
 | 354 | 
 | 
|---|
| [b7fd2a0] | 355 | errno_t fun_offline(fun_node_t *fun)
 | 
|---|
| [02e5e34] | 356 | {
 | 
|---|
| [b7fd2a0] | 357 |         errno_t rc;
 | 
|---|
| [a35b458] | 358 | 
 | 
|---|
| [02e5e34] | 359 |         fibril_rwlock_write_lock(&device_tree.rwlock);
 | 
|---|
| [a35b458] | 360 | 
 | 
|---|
| [02e5e34] | 361 |         if (fun->state == FUN_OFF_LINE) {
 | 
|---|
 | 362 |                 fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 363 |                 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
 | 
|---|
 | 364 |                     fun->pathname);
 | 
|---|
 | 365 |                 return EOK;
 | 
|---|
 | 366 |         }
 | 
|---|
| [a35b458] | 367 | 
 | 
|---|
| [02e5e34] | 368 |         if (fun->ftype == fun_inner) {
 | 
|---|
 | 369 |                 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
 | 
|---|
 | 370 |                     fun->pathname);
 | 
|---|
| [a35b458] | 371 | 
 | 
|---|
| [02e5e34] | 372 |                 if (fun->child != NULL) {
 | 
|---|
 | 373 |                         dev_node_t *dev = fun->child;
 | 
|---|
 | 374 |                         device_state_t dev_state;
 | 
|---|
| [a35b458] | 375 | 
 | 
|---|
| [02e5e34] | 376 |                         dev_add_ref(dev);
 | 
|---|
 | 377 |                         dev_state = dev->state;
 | 
|---|
| [a35b458] | 378 | 
 | 
|---|
| [02e5e34] | 379 |                         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 380 | 
 | 
|---|
 | 381 |                         /* If device is owned by driver, ask driver to give it up. */
 | 
|---|
 | 382 |                         if (dev_state == DEVICE_USABLE) {
 | 
|---|
 | 383 |                                 rc = driver_dev_remove(&device_tree, dev);
 | 
|---|
 | 384 |                                 if (rc != EOK) {
 | 
|---|
 | 385 |                                         dev_del_ref(dev);
 | 
|---|
 | 386 |                                         return ENOTSUP;
 | 
|---|
 | 387 |                                 }
 | 
|---|
 | 388 |                         }
 | 
|---|
| [a35b458] | 389 | 
 | 
|---|
| [02e5e34] | 390 |                         /* Verify that driver removed all functions */
 | 
|---|
 | 391 |                         fibril_rwlock_read_lock(&device_tree.rwlock);
 | 
|---|
 | 392 |                         if (!list_empty(&dev->functions)) {
 | 
|---|
 | 393 |                                 fibril_rwlock_read_unlock(&device_tree.rwlock);
 | 
|---|
 | 394 |                                 dev_del_ref(dev);
 | 
|---|
 | 395 |                                 return EIO;
 | 
|---|
 | 396 |                         }
 | 
|---|
| [a35b458] | 397 | 
 | 
|---|
| [02e5e34] | 398 |                         driver_t *driver = dev->drv;
 | 
|---|
 | 399 |                         fibril_rwlock_read_unlock(&device_tree.rwlock);
 | 
|---|
| [a35b458] | 400 | 
 | 
|---|
| [02e5e34] | 401 |                         if (driver)
 | 
|---|
 | 402 |                                 detach_driver(&device_tree, dev);
 | 
|---|
| [a35b458] | 403 | 
 | 
|---|
| [02e5e34] | 404 |                         fibril_rwlock_write_lock(&device_tree.rwlock);
 | 
|---|
 | 405 |                         remove_dev_node(&device_tree, dev);
 | 
|---|
| [a35b458] | 406 | 
 | 
|---|
| [02e5e34] | 407 |                         /* Delete ref created when node was inserted */
 | 
|---|
 | 408 |                         dev_del_ref(dev);
 | 
|---|
 | 409 |                         /* Delete ref created by dev_add_ref(dev) above */
 | 
|---|
 | 410 |                         dev_del_ref(dev);
 | 
|---|
 | 411 |                 }
 | 
|---|
 | 412 |         } else {
 | 
|---|
 | 413 |                 /* Unregister from location service */
 | 
|---|
| [96ef672] | 414 |                 rc = loc_unregister_tree_function(fun, &device_tree);
 | 
|---|
| [02e5e34] | 415 |                 if (rc != EOK) {
 | 
|---|
 | 416 |                         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
 | 417 |                         log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
 | 
|---|
 | 418 |                         return EIO;
 | 
|---|
 | 419 |                 }
 | 
|---|
| [a35b458] | 420 | 
 | 
|---|
| [02e5e34] | 421 |                 fun->service_id = 0;
 | 
|---|
 | 422 |         }
 | 
|---|
| [a35b458] | 423 | 
 | 
|---|
| [02e5e34] | 424 |         fun->state = FUN_OFF_LINE;
 | 
|---|
 | 425 |         fibril_rwlock_write_unlock(&device_tree.rwlock);
 | 
|---|
| [a35b458] | 426 | 
 | 
|---|
| [02e5e34] | 427 |         return EOK;
 | 
|---|
 | 428 | }
 | 
|---|
 | 429 | 
 | 
|---|
| [38e52c92] | 430 | /** @}
 | 
|---|
 | 431 |  */
 | 
|---|