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
Line 
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/** @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 */
51
52#include <errno.h>
53#include <fcntl.h>
54#include <sys/stat.h>
55#include <io/log.h>
56#include <ipc/driver.h>
57#include <ipc/devman.h>
58#include <loc.h>
59#include <str_error.h>
60#include <stdio.h>
61
62#include "dev.h"
63#include "devman.h"
64#include "driver.h"
65
66static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
67
68/* hash table operations */
69
70static inline size_t handle_key_hash(void *key)
71{
72 devman_handle_t handle = *(devman_handle_t*)key;
73 return handle;
74}
75
76static size_t devman_devices_hash(const ht_link_t *item)
77{
78 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
79 return handle_key_hash(&dev->handle);
80}
81
82static size_t devman_functions_hash(const ht_link_t *item)
83{
84 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
85 return handle_key_hash(&fun->handle);
86}
87
88static bool devman_devices_key_equal(void *key, const ht_link_t *item)
89{
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;
93}
94
95static bool devman_functions_key_equal(void *key, const ht_link_t *item)
96{
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;
100}
101
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,
126 .equal = NULL,
127 .remove_callback = NULL
128};
129
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,
134 .equal = NULL,
135 .remove_callback = NULL
136};
137
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,
142 .equal = NULL,
143 .remove_callback = NULL
144};
145
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.
151 */
152char *read_match_id(char **buf)
153{
154 char *res = NULL;
155 size_t len = get_nonspace_len(*buf);
156
157 if (len > 0) {
158 res = malloc(len + 1);
159 if (res != NULL) {
160 str_ncpy(res, len + 1, *buf, len);
161 *buf += len;
162 }
163 }
164
165 return res;
166}
167
168/**
169 * Read match ids and associated match scores from a string.
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.
180 */
181bool parse_match_ids(char *buf, match_id_list_t *ids)
182{
183 int score = 0;
184 char *id = NULL;
185 int ids_read = 0;
186
187 while (true) {
188 /* skip spaces */
189 if (!skip_spaces(&buf))
190 break;
191
192 /* read score */
193 score = strtoul(buf, &buf, 10);
194
195 /* skip spaces */
196 if (!skip_spaces(&buf))
197 break;
198
199 /* read id */
200 id = read_match_id(&buf);
201 if (NULL == id)
202 break;
203
204 /* create new match_id structure */
205 match_id_t *mid = create_match_id();
206 mid->id = id;
207 mid->score = score;
208
209 /* add it to the list */
210 add_match_id(ids, mid);
211
212 ids_read++;
213 }
214
215 return ids_read > 0;
216}
217
218/**
219 * Read match ids and associated match scores from a file.
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.
230 */
231bool read_match_ids(const char *conf_path, match_id_list_t *ids)
232{
233 log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
234
235 bool suc = false;
236 char *buf = NULL;
237 bool opened = false;
238 int fd;
239 size_t len = 0;
240
241 fd = open(conf_path, O_RDONLY);
242 if (fd < 0) {
243 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
244 conf_path, str_error(fd));
245 goto cleanup;
246 }
247 opened = true;
248
249 len = lseek(fd, 0, SEEK_END);
250 lseek(fd, 0, SEEK_SET);
251 if (len == 0) {
252 log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
253 conf_path);
254 goto cleanup;
255 }
256
257 buf = malloc(len + 1);
258 if (buf == NULL) {
259 log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
260 "'%s'.", conf_path);
261 goto cleanup;
262 }
263
264 ssize_t read_bytes = read_all(fd, buf, len);
265 if (read_bytes <= 0) {
266 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
267 read_bytes);
268 goto cleanup;
269 }
270 buf[read_bytes] = 0;
271
272 suc = parse_match_ids(buf, ids);
273
274cleanup:
275 free(buf);
276
277 if (opened)
278 close(fd);
279
280 return suc;
281}
282
283/** Create root device and function node in the device tree.
284 *
285 * @param tree The device tree.
286 * @return True on success, false otherwise.
287 */
288bool create_root_nodes(dev_tree_t *tree)
289{
290 fun_node_t *fun;
291 dev_node_t *dev;
292
293 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
294
295 fibril_rwlock_write_lock(&tree->rwlock);
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;
308 }
309
310 fun_add_ref(fun);
311 insert_fun_node(tree, fun, str_dup(""), NULL);
312
313 match_id_t *id = create_match_id();
314 id->id = str_dup("root");
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
328 dev_add_ref(dev);
329 insert_dev_node(tree, dev, fun);
330
331 fibril_rwlock_write_unlock(&tree->rwlock);
332
333 return dev != NULL;
334}
335
336/** Create loc path and name for the function. */
337void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
338{
339 char *loc_pathname = NULL;
340 char *loc_name = NULL;
341
342 assert(fibril_rwlock_is_locked(&tree->rwlock));
343
344 asprintf(&loc_name, "%s", fun->pathname);
345 if (loc_name == NULL)
346 return;
347
348 replace_char(loc_name, '/', LOC_SEPARATOR);
349
350 asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
351 loc_name);
352 if (loc_pathname == NULL) {
353 free(loc_name);
354 return;
355 }
356
357 loc_service_register_with_iface(loc_pathname,
358 &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
359
360 tree_add_loc_function(tree, fun);
361
362 free(loc_name);
363 free(loc_pathname);
364}
365
366/** Pass a device to running driver.
367 *
368 * @param drv The driver's structure.
369 * @param node The device's node in the device tree.
370 */
371void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
372{
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 */
377 log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
378 drv->name, dev->pfun->name);
379
380 /* Send the device to the driver. */
381 devman_handle_t parent_handle;
382 if (dev->pfun) {
383 parent_handle = dev->pfun->handle;
384 } else {
385 parent_handle = 0;
386 }
387
388 async_exch_t *exch = async_exchange_begin(drv->sess);
389
390 ipc_call_t answer;
391 aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
392 parent_handle, &answer);
393
394 /* Send the device name to the driver. */
395 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
396 str_size(dev->pfun->name) + 1);
397
398 async_exchange_end(exch);
399
400 if (rc != EOK) {
401 /* TODO handle error */
402 }
403
404 /* Wait for answer from the driver. */
405 async_wait_for(req, &rc);
406
407 switch(rc) {
408 case EOK:
409 dev->state = DEVICE_USABLE;
410 break;
411 case ENOENT:
412 dev->state = DEVICE_NOT_PRESENT;
413 break;
414 default:
415 dev->state = DEVICE_INVALID;
416 break;
417 }
418
419 dev->passed_to_driver = true;
420
421 return;
422}
423
424/** Initialize the device tree.
425 *
426 * Create root device node of the tree and assign driver to it.
427 *
428 * @param tree The device tree.
429 * @param drivers_list the list of available drivers.
430 * @return True on success, false otherwise.
431 */
432bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
433{
434 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
435
436 tree->current_handle = 0;
437
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);
441
442 fibril_rwlock_initialize(&tree->rwlock);
443
444 /* Create root function and root device and add them to the device tree. */
445 if (!create_root_nodes(tree))
446 return false;
447
448 /* Find suitable driver and start it. */
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;
455}
456
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{
465 fun_node_t *fun;
466
467 fun = calloc(1, sizeof(fun_node_t));
468 if (fun == NULL)
469 return NULL;
470
471 fun->state = FUN_INIT;
472 atomic_set(&fun->refcnt, 0);
473 fibril_mutex_initialize(&fun->busy_lock);
474 link_initialize(&fun->dev_functions);
475 list_initialize(&fun->match_ids.ids);
476
477 return fun;
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);
490 free(fun->name);
491 free(fun->pathname);
492 free(fun);
493}
494
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
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
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{
536 fun_node_t *fun;
537
538 assert(fibril_rwlock_is_locked(&tree->rwlock));
539
540 ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
541 if (link == NULL)
542 return NULL;
543
544 fun = hash_table_get_inst(link, fun_node_t, devman_fun);
545
546 return fun;
547}
548
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);
560
561 fun = find_fun_node_no_lock(tree, handle);
562 if (fun != NULL)
563 fun_add_ref(fun);
564
565 fibril_rwlock_read_unlock(&tree->rwlock);
566
567 return fun;
568}
569
570/** Create and set device's full path in device tree.
571 *
572 * @param tree Device tree
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.).
577 */
578static bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
579{
580 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
581 assert(fun->name != NULL);
582
583 size_t pathsize = (str_size(fun->name) + 1);
584 if (parent != NULL)
585 pathsize += str_size(parent->pathname) + 1;
586
587 fun->pathname = (char *) malloc(pathsize);
588 if (fun->pathname == NULL) {
589 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
590 return false;
591 }
592
593 if (parent != NULL) {
594 str_cpy(fun->pathname, pathsize, parent->pathname);
595 str_append(fun->pathname, pathsize, "/");
596 str_append(fun->pathname, pathsize, fun->name);
597 } else {
598 str_cpy(fun->pathname, pathsize, fun->name);
599 }
600
601 return true;
602}
603
604/** Insert new device into device tree.
605 *
606 * @param tree The device tree.
607 * @param dev The newly added device node.
608 * @param pfun The parent function node.
609 *
610 * @return True on success, false otherwise (insufficient resources
611 * etc.).
612 */
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
617 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
618 dev, pfun, pfun->pathname);
619
620 /* Add the node to the handle-to-node map. */
621 dev->handle = ++tree->current_handle;
622 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
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
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
640 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
641
642 /* Remove node from the handle-to-node map. */
643 hash_table_remove(&tree->devman_devices, &dev->handle);
644
645 /* Unlink from parent function. */
646 dev->pfun->child = NULL;
647 dev->pfun = NULL;
648
649 dev->state = DEVICE_REMOVED;
650}
651
652
653/** Insert new function into device tree.
654 *
655 * @param tree The device tree.
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.
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)
665{
666 fun_node_t *pfun;
667
668 assert(fun_name != NULL);
669 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
670
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;
678 if (!set_fun_path(tree, fun, pfun)) {
679 return false;
680 }
681
682 /* Add the node to the handle-to-node map. */
683 fun->handle = ++tree->current_handle;
684 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
685
686 /* Add the node to the list of its parent's children. */
687 fun->dev = dev;
688 if (dev != NULL)
689 list_append(&fun->dev_functions, &dev->functions);
690
691 return true;
692}
693
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. */
704 hash_table_remove(&tree->devman_functions, &fun->handle);
705
706 /* Remove the node from the list of its parent's children. */
707 if (fun->dev != NULL)
708 list_remove(&fun->dev_functions);
709
710 fun->dev = NULL;
711 fun->state = FUN_REMOVED;
712}
713
714/** Find function node with a specified path in the device tree.
715 *
716 * @param path The path of the function node in the device tree.
717 * @param tree The device tree.
718 * @return The function node if it is present in the tree, NULL
719 * otherwise.
720 */
721fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
722{
723 assert(path != NULL);
724
725 bool is_absolute = path[0] == '/';
726 if (!is_absolute) {
727 return NULL;
728 }
729
730 fibril_rwlock_read_lock(&tree->rwlock);
731
732 fun_node_t *fun = tree->root_node;
733 fun_add_ref(fun);
734 /*
735 * Relative path to the function from its parent (but with '/' at the
736 * beginning)
737 */
738 char *rel_path = path;
739 char *next_path_elem = NULL;
740 bool cont = (rel_path[1] != '\0');
741
742 while (cont && fun != NULL) {
743 next_path_elem = get_path_elem_end(rel_path + 1);
744 if (next_path_elem[0] == '/') {
745 cont = true;
746 next_path_elem[0] = 0;
747 } else {
748 cont = false;
749 }
750
751 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
752 fun_del_ref(fun);
753 fun = cfun;
754
755 if (cont) {
756 /* Restore the original path. */
757 next_path_elem[0] = '/';
758 }
759 rel_path = next_path_elem;
760 }
761
762 fibril_rwlock_read_unlock(&tree->rwlock);
763
764 return fun;
765}
766
767/** Find function with a specified name belonging to given device.
768 *
769 * Device tree rwlock should be held at least for reading.
770 *
771 * @param tree Device tree
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.
776 */
777fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
778 const char *name)
779{
780 assert(name != NULL);
781 assert(fibril_rwlock_is_locked(&tree->rwlock));
782
783 fun_node_t *fun;
784
785 list_foreach(dev->functions, link) {
786 fun = list_get_instance(link, fun_node_t, dev_functions);
787
788 if (str_cmp(name, fun->name) == 0) {
789 fun_add_ref(fun);
790 return fun;
791 }
792 }
793
794 return NULL;
795}
796
797/** Find child function node with a specified name.
798 *
799 * Device tree rwlock should be held at least for reading.
800 *
801 * @param tree Device tree
802 * @param parent The parent function node.
803 * @param name The name of the child function.
804 * @return The child function node.
805 */
806static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
807 const char *name)
808{
809 return find_fun_node_in_device(tree, pfun->child, name);
810}
811
812/* loc devices */
813
814fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
815{
816 fun_node_t *fun = NULL;
817
818 fibril_rwlock_read_lock(&tree->rwlock);
819 ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
820 if (link != NULL) {
821 fun = hash_table_get_inst(link, fun_node_t, loc_fun);
822 fun_add_ref(fun);
823 }
824 fibril_rwlock_read_unlock(&tree->rwlock);
825
826 return fun;
827}
828
829void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
830{
831 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
832
833 hash_table_insert(&tree->loc_functions, &fun->loc_fun);
834}
835
836/** @}
837 */
Note: See TracBrowser for help on using the repository browser.