source: mainline/uspace/srv/devman/devman.c@ d1bafbf

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d1bafbf was d1bafbf, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Move dev-node related devman functionality to a separate module.

  • Property mode set to 100644
File size: 20.4 KB
RevLine 
[0358da0]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
[e2b9a993]29/** @addtogroup devman
[0358da0]30 * @{
31 */
[58cbb0c8]32/** @file Device Manager
33 *
34 * Locking order:
35 * (1) driver_t.driver_mutex
36 * (2) dev_tree_t.rwlock
37 *
38 * Synchronization:
39 * - device_tree.rwlock protects:
40 * - tree root, complete tree topology
41 * - complete contents of device and function nodes
42 * - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
43 * being deallocated
44 * - find_xxx() functions increase reference count of returned object
45 * - find_xxx_no_lock() do not increase reference count
46 *
47 * TODO
48 * - Track all steady and transient device/function states
49 * - Check states, wait for steady state on certain operations
50 */
[0358da0]51
52#include <errno.h>
[e4c4247]53#include <fcntl.h>
[85e48a9]54#include <sys/stat.h>
[9b415c9]55#include <io/log.h>
[084ff99]56#include <ipc/driver.h>
57#include <ipc/devman.h>
[15f3c3f]58#include <loc.h>
[0485135]59#include <str_error.h>
[c7bbf029]60#include <stdio.h>
[0358da0]61
[d1bafbf]62#include "dev.h"
[e2b9a993]63#include "devman.h"
[041b026]64#include "driver.h"
[0358da0]65
[58cbb0c8]66static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
[ba38f72c]67
[38b3baf]68/* hash table operations */
[957cfa58]69
[062d900]70static inline size_t handle_key_hash(void *key)
[957cfa58]71{
[062d900]72 devman_handle_t handle = *(devman_handle_t*)key;
73 return handle;
[957cfa58]74}
75
[062d900]76static size_t devman_devices_hash(const ht_link_t *item)
[957cfa58]77{
[062d900]78 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
79 return handle_key_hash(&dev->handle);
[957cfa58]80}
81
[062d900]82static size_t devman_functions_hash(const ht_link_t *item)
[957cfa58]83{
[062d900]84 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
85 return handle_key_hash(&fun->handle);
[ba38f72c]86}
87
[062d900]88static bool devman_devices_key_equal(void *key, const ht_link_t *item)
[ba38f72c]89{
[062d900]90 devman_handle_t handle = *(devman_handle_t*)key;
91 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
92 return dev->handle == handle;
[957cfa58]93}
94
[062d900]95static bool devman_functions_key_equal(void *key, const ht_link_t *item)
[957cfa58]96{
[062d900]97 devman_handle_t handle = *(devman_handle_t*)key;
98 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
99 return fun->handle == handle;
[957cfa58]100}
101
[062d900]102static inline size_t service_id_key_hash(void *key)
103{
104 service_id_t service_id = *(service_id_t*)key;
105 return service_id;
106}
107
108static size_t loc_functions_hash(const ht_link_t *item)
109{
110 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
111 return service_id_key_hash(&fun->service_id);
112}
113
114static bool loc_functions_key_equal(void *key, const ht_link_t *item)
115{
116 service_id_t service_id = *(service_id_t*)key;
117 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
118 return fun->service_id == service_id;
119}
120
121
122static hash_table_ops_t devman_devices_ops = {
123 .hash = devman_devices_hash,
124 .key_hash = handle_key_hash,
125 .key_equal = devman_devices_key_equal,
[4e00f87]126 .equal = NULL,
127 .remove_callback = NULL
[957cfa58]128};
129
[062d900]130static hash_table_ops_t devman_functions_ops = {
131 .hash = devman_functions_hash,
132 .key_hash = handle_key_hash,
133 .key_equal = devman_functions_key_equal,
[4e00f87]134 .equal = NULL,
135 .remove_callback = NULL
[ba38f72c]136};
137
[062d900]138static hash_table_ops_t loc_devices_ops = {
139 .hash = loc_functions_hash,
140 .key_hash = service_id_key_hash,
141 .key_equal = loc_functions_key_equal,
[4e00f87]142 .equal = NULL,
143 .remove_callback = NULL
[957cfa58]144};
145
[38b3baf]146/** Read match id at the specified position of a string and set the position in
147 * the string to the first character following the id.
148 *
149 * @param buf The position in the input string.
150 * @return The match id.
[0c3666d]151 */
[38b3baf]152char *read_match_id(char **buf)
[e4c4247]153{
154 char *res = NULL;
[e2b9a993]155 size_t len = get_nonspace_len(*buf);
[38b3baf]156
[e4c4247]157 if (len > 0) {
158 res = malloc(len + 1);
159 if (res != NULL) {
[38b3baf]160 str_ncpy(res, len + 1, *buf, len);
[e4c4247]161 *buf += len;
162 }
163 }
[38b3baf]164
[e4c4247]165 return res;
166}
167
[0c3666d]168/**
169 * Read match ids and associated match scores from a string.
[38b3baf]170 *
171 * Each match score in the string is followed by its match id.
172 * The match ids and match scores are separated by whitespaces.
173 * Neither match ids nor match scores can contain whitespaces.
174 *
175 * @param buf The string from which the match ids are read.
176 * @param ids The list of match ids into which the match ids and
177 * scores are added.
178 * @return True if at least one match id and associated match score
179 * was successfully read, false otherwise.
[0c3666d]180 */
[c47e1a8]181bool parse_match_ids(char *buf, match_id_list_t *ids)
[e4c4247]182{
183 int score = 0;
184 char *id = NULL;
185 int ids_read = 0;
186
187 while (true) {
[38b3baf]188 /* skip spaces */
189 if (!skip_spaces(&buf))
[e4c4247]190 break;
[38b3baf]191
192 /* read score */
[e4c4247]193 score = strtoul(buf, &buf, 10);
194
[38b3baf]195 /* skip spaces */
196 if (!skip_spaces(&buf))
[e4c4247]197 break;
198
[38b3baf]199 /* read id */
200 id = read_match_id(&buf);
201 if (NULL == id)
202 break;
[e4c4247]203
[38b3baf]204 /* create new match_id structure */
[e4c4247]205 match_id_t *mid = create_match_id();
206 mid->id = id;
207 mid->score = score;
208
[38b3baf]209 /* add it to the list */
[e4c4247]210 add_match_id(ids, mid);
211
[38b3baf]212 ids_read++;
213 }
[e4c4247]214
215 return ids_read > 0;
216}
217
[0c3666d]218/**
219 * Read match ids and associated match scores from a file.
[38b3baf]220 *
221 * Each match score in the file is followed by its match id.
222 * The match ids and match scores are separated by whitespaces.
223 * Neither match ids nor match scores can contain whitespaces.
224 *
225 * @param buf The path to the file from which the match ids are read.
226 * @param ids The list of match ids into which the match ids and
227 * scores are added.
228 * @return True if at least one match id and associated match score
229 * was successfully read, false otherwise.
[0c3666d]230 */
[38b3baf]231bool read_match_ids(const char *conf_path, match_id_list_t *ids)
232{
[a1a101d]233 log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
[08d9c4e6]234
[38b3baf]235 bool suc = false;
[e4c4247]236 char *buf = NULL;
237 bool opened = false;
[38b3baf]238 int fd;
[c47e1a8]239 size_t len = 0;
[e4c4247]240
241 fd = open(conf_path, O_RDONLY);
242 if (fd < 0) {
[a1a101d]243 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
[9b415c9]244 conf_path, str_error(fd));
[e4c4247]245 goto cleanup;
[38b3baf]246 }
247 opened = true;
[e4c4247]248
249 len = lseek(fd, 0, SEEK_END);
[38b3baf]250 lseek(fd, 0, SEEK_SET);
[e4c4247]251 if (len == 0) {
[a1a101d]252 log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
[9b415c9]253 conf_path);
[38b3baf]254 goto cleanup;
[e4c4247]255 }
256
257 buf = malloc(len + 1);
258 if (buf == NULL) {
[a1a101d]259 log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
[ebcb05a]260 "'%s'.", conf_path);
[e4c4247]261 goto cleanup;
[58b833c]262 }
[e4c4247]263
[8fd04ba9]264 ssize_t read_bytes = read_all(fd, buf, len);
[dc87f3fd]265 if (read_bytes <= 0) {
[a1a101d]266 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
[8fd04ba9]267 read_bytes);
[e4c4247]268 goto cleanup;
269 }
[dc87f3fd]270 buf[read_bytes] = 0;
[e4c4247]271
272 suc = parse_match_ids(buf, ids);
273
274cleanup:
275 free(buf);
276
[58b833c]277 if (opened)
[38b3baf]278 close(fd);
[e4c4247]279
280 return suc;
281}
282
[ba38f72c]283/** Create root device and function node in the device tree.
[38b3baf]284 *
[58b833c]285 * @param tree The device tree.
286 * @return True on success, false otherwise.
[0c3666d]287 */
[ba38f72c]288bool create_root_nodes(dev_tree_t *tree)
[e4c4247]289{
[ba38f72c]290 fun_node_t *fun;
291 dev_node_t *dev;
292
[a1a101d]293 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
[ba38f72c]294
[01b87dc5]295 fibril_rwlock_write_lock(&tree->rwlock);
[ba38f72c]296
297 /*
298 * Create root function. This is a pseudo function to which
299 * the root device node is attached. It allows us to match
300 * the root device driver in a standard manner, i.e. against
301 * the parent function.
302 */
303
304 fun = create_fun_node();
305 if (fun == NULL) {
306 fibril_rwlock_write_unlock(&tree->rwlock);
307 return false;
[85e48a9]308 }
[ba38f72c]309
[58cbb0c8]310 fun_add_ref(fun);
[4022513]311 insert_fun_node(tree, fun, str_dup(""), NULL);
[58cbb0c8]312
[ba38f72c]313 match_id_t *id = create_match_id();
[4022513]314 id->id = str_dup("root");
[ba38f72c]315 id->score = 100;
316 add_match_id(&fun->match_ids, id);
317 tree->root_node = fun;
318
319 /*
320 * Create root device node.
321 */
322 dev = create_dev_node();
323 if (dev == NULL) {
324 fibril_rwlock_write_unlock(&tree->rwlock);
325 return false;
326 }
327
[58cbb0c8]328 dev_add_ref(dev);
[ba38f72c]329 insert_dev_node(tree, dev, fun);
330
[01b87dc5]331 fibril_rwlock_write_unlock(&tree->rwlock);
[ba38f72c]332
333 return dev != NULL;
[85e48a9]334}
335
[15f3c3f]336/** Create loc path and name for the function. */
337void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
[a32defa]338{
[15f3c3f]339 char *loc_pathname = NULL;
340 char *loc_name = NULL;
[a32defa]341
[58cbb0c8]342 assert(fibril_rwlock_is_locked(&tree->rwlock));
343
[15f3c3f]344 asprintf(&loc_name, "%s", fun->pathname);
345 if (loc_name == NULL)
[a32defa]346 return;
347
[15f3c3f]348 replace_char(loc_name, '/', LOC_SEPARATOR);
[a32defa]349
[15f3c3f]350 asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
351 loc_name);
352 if (loc_pathname == NULL) {
353 free(loc_name);
[a32defa]354 return;
[38b3baf]355 }
[a32defa]356
[15f3c3f]357 loc_service_register_with_iface(loc_pathname,
358 &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
[a32defa]359
[15f3c3f]360 tree_add_loc_function(tree, fun);
[a32defa]361
[15f3c3f]362 free(loc_name);
363 free(loc_pathname);
[a32defa]364}
365
[0c3666d]366/** Pass a device to running driver.
[38b3baf]367 *
368 * @param drv The driver's structure.
369 * @param node The device's node in the device tree.
[0c3666d]370 */
[45059d6b]371void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
[85e48a9]372{
[5bee897]373 /*
374 * We do not expect to have driver's mutex locked as we do not
375 * access any structures that would affect driver_t.
376 */
[a1a101d]377 log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
[9b415c9]378 drv->name, dev->pfun->name);
[a78fa2a]379
[38b3baf]380 /* Send the device to the driver. */
[0d6915f]381 devman_handle_t parent_handle;
[ba38f72c]382 if (dev->pfun) {
383 parent_handle = dev->pfun->handle;
[0d6915f]384 } else {
385 parent_handle = 0;
386 }
[79ae36dd]387
[45059d6b]388 async_exch_t *exch = async_exchange_begin(drv->sess);
[79ae36dd]389
390 ipc_call_t answer;
[1a5b252]391 aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
[0d6915f]392 parent_handle, &answer);
[a78fa2a]393
[79ae36dd]394 /* Send the device name to the driver. */
395 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
[ba38f72c]396 str_size(dev->pfun->name) + 1);
[79ae36dd]397
398 async_exchange_end(exch);
399
[38b3baf]400 if (rc != EOK) {
401 /* TODO handle error */
402 }
[398c4d7]403
[38b3baf]404 /* Wait for answer from the driver. */
[a78fa2a]405 async_wait_for(req, &rc);
[5bee897]406
[a78fa2a]407 switch(rc) {
408 case EOK:
[ba38f72c]409 dev->state = DEVICE_USABLE;
[df747b9c]410 break;
[a78fa2a]411 case ENOENT:
[ba38f72c]412 dev->state = DEVICE_NOT_PRESENT;
[a78fa2a]413 break;
[df747b9c]414 default:
[ba38f72c]415 dev->state = DEVICE_INVALID;
[77a69ea]416 break;
[084ff99]417 }
[e85920d]418
[ba38f72c]419 dev->passed_to_driver = true;
[5bee897]420
[5cd136ab]421 return;
[85e48a9]422}
423
[38b3baf]424/** Initialize the device tree.
425 *
[0c3666d]426 * Create root device node of the tree and assign driver to it.
[38b3baf]427 *
428 * @param tree The device tree.
429 * @param drivers_list the list of available drivers.
430 * @return True on success, false otherwise.
[0c3666d]431 */
432bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
[85e48a9]433{
[a1a101d]434 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
[0c3666d]435
[957cfa58]436 tree->current_handle = 0;
437
[062d900]438 hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
439 hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
440 hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
[bda60d9]441
[957cfa58]442 fibril_rwlock_initialize(&tree->rwlock);
[084ff99]443
[ba38f72c]444 /* Create root function and root device and add them to the device tree. */
445 if (!create_root_nodes(tree))
[85e48a9]446 return false;
[58cbb0c8]447
[38b3baf]448 /* Find suitable driver and start it. */
[58cbb0c8]449 dev_node_t *rdev = tree->root_node->child;
450 dev_add_ref(rdev);
451 int rc = assign_driver(rdev, drivers_list, tree);
452 dev_del_ref(rdev);
453
454 return rc;
[e4c4247]455}
456
[ba38f72c]457/* Function nodes */
458
459/** Create a new function node.
460 *
461 * @return A function node structure.
462 */
463fun_node_t *create_fun_node(void)
464{
[58cbb0c8]465 fun_node_t *fun;
466
467 fun = calloc(1, sizeof(fun_node_t));
468 if (fun == NULL)
469 return NULL;
[ba38f72c]470
[c1a0488]471 fun->state = FUN_INIT;
[58cbb0c8]472 atomic_set(&fun->refcnt, 0);
[4820360]473 fibril_mutex_initialize(&fun->busy_lock);
[58cbb0c8]474 link_initialize(&fun->dev_functions);
475 list_initialize(&fun->match_ids.ids);
476
477 return fun;
[ba38f72c]478}
479
480/** Delete a function node.
481 *
482 * @param fun The device node structure.
483 */
484void delete_fun_node(fun_node_t *fun)
485{
486 assert(fun->dev == NULL);
487 assert(fun->child == NULL);
488
489 clean_match_ids(&fun->match_ids);
[5b08d750]490 free(fun->name);
491 free(fun->pathname);
[ba38f72c]492 free(fun);
493}
494
[58cbb0c8]495/** Increase function node reference count.
496 *
497 * @param fun Function node
498 */
499void fun_add_ref(fun_node_t *fun)
500{
501 atomic_inc(&fun->refcnt);
502}
503
504/** Decrease function node reference count.
505 *
506 * When the count drops to zero the function node is freed.
507 *
508 * @param fun Function node
509 */
510void fun_del_ref(fun_node_t *fun)
511{
512 if (atomic_predec(&fun->refcnt) == 0)
513 delete_fun_node(fun);
514}
515
[4820360]516/** Make function busy for reconfiguration operations. */
517void fun_busy_lock(fun_node_t *fun)
518{
519 fibril_mutex_lock(&fun->busy_lock);
520}
521
522/** Mark end of reconfiguration operation. */
523void fun_busy_unlock(fun_node_t *fun)
524{
525 fibril_mutex_unlock(&fun->busy_lock);
526}
527
[ba38f72c]528/** Find the function node with the specified handle.
529 *
530 * @param tree The device tree where we look for the device node.
531 * @param handle The handle of the function.
532 * @return The function node.
533 */
534fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
535{
[58cbb0c8]536 fun_node_t *fun;
[ba38f72c]537
538 assert(fibril_rwlock_is_locked(&tree->rwlock));
539
[062d900]540 ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
[ba38f72c]541 if (link == NULL)
542 return NULL;
543
[062d900]544 fun = hash_table_get_inst(link, fun_node_t, devman_fun);
[58cbb0c8]545
546 return fun;
[791f58c]547}
548
[ba38f72c]549/** Find the function node with the specified handle.
550 *
551 * @param tree The device tree where we look for the device node.
552 * @param handle The handle of the function.
553 * @return The function node.
554 */
555fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
556{
557 fun_node_t *fun = NULL;
558
559 fibril_rwlock_read_lock(&tree->rwlock);
[58cbb0c8]560
[ba38f72c]561 fun = find_fun_node_no_lock(tree, handle);
[58cbb0c8]562 if (fun != NULL)
563 fun_add_ref(fun);
564
[ba38f72c]565 fibril_rwlock_read_unlock(&tree->rwlock);
566
567 return fun;
568}
[791f58c]569
[bda60d9]570/** Create and set device's full path in device tree.
[38b3baf]571 *
[58cbb0c8]572 * @param tree Device tree
[38b3baf]573 * @param node The device's device node.
574 * @param parent The parent device node.
575 * @return True on success, false otherwise (insufficient
576 * resources etc.).
[bda60d9]577 */
[58cbb0c8]578static bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
[38b3baf]579{
[58cbb0c8]580 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[ba38f72c]581 assert(fun->name != NULL);
[bda60d9]582
[ba38f72c]583 size_t pathsize = (str_size(fun->name) + 1);
[58b833c]584 if (parent != NULL)
[38b3baf]585 pathsize += str_size(parent->pathname) + 1;
[bda60d9]586
[ba38f72c]587 fun->pathname = (char *) malloc(pathsize);
588 if (fun->pathname == NULL) {
[a1a101d]589 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
[bda60d9]590 return false;
591 }
592
[58b833c]593 if (parent != NULL) {
[ba38f72c]594 str_cpy(fun->pathname, pathsize, parent->pathname);
595 str_append(fun->pathname, pathsize, "/");
596 str_append(fun->pathname, pathsize, fun->name);
[bda60d9]597 } else {
[ba38f72c]598 str_cpy(fun->pathname, pathsize, fun->name);
[bda60d9]599 }
600
601 return true;
602}
603
604/** Insert new device into device tree.
[38b3baf]605 *
606 * @param tree The device tree.
[58cbb0c8]607 * @param dev The newly added device node.
608 * @param pfun The parent function node.
[58b833c]609 *
[38b3baf]610 * @return True on success, false otherwise (insufficient resources
611 * etc.).
[bda60d9]612 */
[ba38f72c]613bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
614{
615 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
616
[a1a101d]617 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
[9b415c9]618 dev, pfun, pfun->pathname);
619
[ba38f72c]620 /* Add the node to the handle-to-node map. */
621 dev->handle = ++tree->current_handle;
[062d900]622 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
[ba38f72c]623
624 /* Add the node to the list of its parent's children. */
625 dev->pfun = pfun;
626 pfun->child = dev;
627
628 return true;
629}
630
[1a5b252]631/** Remove device from device tree.
632 *
633 * @param tree Device tree
634 * @param dev Device node
635 */
636void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
637{
638 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
639
[a1a101d]640 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
[1a5b252]641
642 /* Remove node from the handle-to-node map. */
[062d900]643 hash_table_remove(&tree->devman_devices, &dev->handle);
[1a5b252]644
645 /* Unlink from parent function. */
646 dev->pfun->child = NULL;
647 dev->pfun = NULL;
[c1a0488]648
649 dev->state = DEVICE_REMOVED;
[1a5b252]650}
651
652
[ba38f72c]653/** Insert new function into device tree.
654 *
655 * @param tree The device tree.
[58cbb0c8]656 * @param fun The newly added function node.
657 * @param fun_name The name of the newly added function.
658 * @param dev Owning device node.
[ba38f72c]659 *
660 * @return True on success, false otherwise (insufficient resources
661 * etc.).
662 */
663bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
664 dev_node_t *dev)
[bda60d9]665{
[ba38f72c]666 fun_node_t *pfun;
667
668 assert(fun_name != NULL);
[01b87dc5]669 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[bda60d9]670
[ba38f72c]671 /*
672 * The root function is a special case, it does not belong to any
673 * device so for the root function dev == NULL.
674 */
675 pfun = (dev != NULL) ? dev->pfun : NULL;
676
677 fun->name = fun_name;
[58cbb0c8]678 if (!set_fun_path(tree, fun, pfun)) {
[38b3baf]679 return false;
[bda60d9]680 }
681
[38b3baf]682 /* Add the node to the handle-to-node map. */
[ba38f72c]683 fun->handle = ++tree->current_handle;
[062d900]684 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
[bda60d9]685
[38b3baf]686 /* Add the node to the list of its parent's children. */
[ba38f72c]687 fun->dev = dev;
688 if (dev != NULL)
689 list_append(&fun->dev_functions, &dev->functions);
[38b3baf]690
[bda60d9]691 return true;
692}
693
[d0dd7b5]694/** Remove function from device tree.
695 *
696 * @param tree Device tree
697 * @param node Function node to remove
698 */
699void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
700{
701 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
702
703 /* Remove the node from the handle-to-node map. */
[062d900]704 hash_table_remove(&tree->devman_functions, &fun->handle);
[d0dd7b5]705
706 /* Remove the node from the list of its parent's children. */
707 if (fun->dev != NULL)
708 list_remove(&fun->dev_functions);
[1a5b252]709
710 fun->dev = NULL;
[c1a0488]711 fun->state = FUN_REMOVED;
[d0dd7b5]712}
713
[ba38f72c]714/** Find function node with a specified path in the device tree.
[5cd136ab]715 *
[ba38f72c]716 * @param path The path of the function node in the device tree.
[38b3baf]717 * @param tree The device tree.
[ba38f72c]718 * @return The function node if it is present in the tree, NULL
[38b3baf]719 * otherwise.
[5cd136ab]720 */
[ba38f72c]721fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
[5cd136ab]722{
[df147c7]723 assert(path != NULL);
724
725 bool is_absolute = path[0] == '/';
726 if (!is_absolute) {
727 return NULL;
728 }
729
[957cfa58]730 fibril_rwlock_read_lock(&tree->rwlock);
731
[ba38f72c]732 fun_node_t *fun = tree->root_node;
[58cbb0c8]733 fun_add_ref(fun);
[38b3baf]734 /*
[ba38f72c]735 * Relative path to the function from its parent (but with '/' at the
[38b3baf]736 * beginning)
737 */
[5cd136ab]738 char *rel_path = path;
739 char *next_path_elem = NULL;
[7beb220]740 bool cont = (rel_path[1] != '\0');
[5cd136ab]741
[ba38f72c]742 while (cont && fun != NULL) {
[38b3baf]743 next_path_elem = get_path_elem_end(rel_path + 1);
[58b833c]744 if (next_path_elem[0] == '/') {
[5cd136ab]745 cont = true;
746 next_path_elem[0] = 0;
747 } else {
748 cont = false;
749 }
750
[58cbb0c8]751 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
752 fun_del_ref(fun);
753 fun = cfun;
[5cd136ab]754
755 if (cont) {
[38b3baf]756 /* Restore the original path. */
[5cd136ab]757 next_path_elem[0] = '/';
758 }
[38b3baf]759 rel_path = next_path_elem;
[5cd136ab]760 }
761
[957cfa58]762 fibril_rwlock_read_unlock(&tree->rwlock);
763
[ba38f72c]764 return fun;
[5cd136ab]765}
766
[0876062]767/** Find function with a specified name belonging to given device.
[38b3baf]768 *
769 * Device tree rwlock should be held at least for reading.
770 *
[58cbb0c8]771 * @param tree Device tree
[0876062]772 * @param dev Device the function belongs to.
773 * @param name Function name (not path).
774 * @return Function node.
775 * @retval NULL No function with given name.
[5cd136ab]776 */
[58cbb0c8]777fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
778 const char *name)
[5cd136ab]779{
[0876062]780 assert(name != NULL);
[58cbb0c8]781 assert(fibril_rwlock_is_locked(&tree->rwlock));
[0876062]782
[ba38f72c]783 fun_node_t *fun;
[0876062]784
[b72efe8]785 list_foreach(dev->functions, link) {
[ba38f72c]786 fun = list_get_instance(link, fun_node_t, dev_functions);
[0876062]787
[58cbb0c8]788 if (str_cmp(name, fun->name) == 0) {
789 fun_add_ref(fun);
[ba38f72c]790 return fun;
[58cbb0c8]791 }
[38b3baf]792 }
[0876062]793
[5cd136ab]794 return NULL;
795}
796
[0876062]797/** Find child function node with a specified name.
798 *
799 * Device tree rwlock should be held at least for reading.
800 *
[58cbb0c8]801 * @param tree Device tree
[0876062]802 * @param parent The parent function node.
803 * @param name The name of the child function.
804 * @return The child function node.
805 */
[58cbb0c8]806static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
807 const char *name)
[0876062]808{
[58cbb0c8]809 return find_fun_node_in_device(tree, pfun->child, name);
[0876062]810}
811
[15f3c3f]812/* loc devices */
[ce89036b]813
[15f3c3f]814fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
[ce89036b]815{
[ba38f72c]816 fun_node_t *fun = NULL;
[ce89036b]817
818 fibril_rwlock_read_lock(&tree->rwlock);
[062d900]819 ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
[58cbb0c8]820 if (link != NULL) {
[062d900]821 fun = hash_table_get_inst(link, fun_node_t, loc_fun);
[58cbb0c8]822 fun_add_ref(fun);
823 }
[ce89036b]824 fibril_rwlock_read_unlock(&tree->rwlock);
825
[ba38f72c]826 return fun;
[ce89036b]827}
828
[15f3c3f]829void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
[791f58c]830{
[58cbb0c8]831 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
832
[062d900]833 hash_table_insert(&tree->loc_functions, &fun->loc_fun);
[791f58c]834}
835
[c16cf62]836/** @}
[58b833c]837 */
Note: See TracBrowser for help on using the repository browser.