source: mainline/uspace/srv/devman/devman.c@ 1c99eae

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

Set connection handler for callback session from devman to driver
(driver→sess). Now devman does not need to create a new connection
every time it actually has some request.

  • Property mode set to 100644
File size: 30.8 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
33#include <errno.h>
34#include <fcntl.h>
35#include <sys/stat.h>
36#include <io/log.h>
37#include <ipc/driver.h>
38#include <ipc/devman.h>
39#include <loc.h>
40#include <str_error.h>
41#include <stdio.h>
42
43#include "devman.h"
44
45fun_node_t *find_node_child(fun_node_t *parent, const char *name);
46
47/* hash table operations */
48
49static hash_index_t devices_hash(unsigned long key[])
50{
51 return key[0] % DEVICE_BUCKETS;
52}
53
54static int devman_devices_compare(unsigned long key[], hash_count_t keys,
55 link_t *item)
56{
57 dev_node_t *dev = hash_table_get_instance(item, dev_node_t, devman_dev);
58 return (dev->handle == (devman_handle_t) key[0]);
59}
60
61static int devman_functions_compare(unsigned long key[], hash_count_t keys,
62 link_t *item)
63{
64 fun_node_t *fun = hash_table_get_instance(item, fun_node_t, devman_fun);
65 return (fun->handle == (devman_handle_t) key[0]);
66}
67
68static int loc_functions_compare(unsigned long key[], hash_count_t keys,
69 link_t *item)
70{
71 fun_node_t *fun = hash_table_get_instance(item, fun_node_t, loc_fun);
72 return (fun->service_id == (service_id_t) key[0]);
73}
74
75static void devices_remove_callback(link_t *item)
76{
77}
78
79static hash_table_operations_t devman_devices_ops = {
80 .hash = devices_hash,
81 .compare = devman_devices_compare,
82 .remove_callback = devices_remove_callback
83};
84
85static hash_table_operations_t devman_functions_ops = {
86 .hash = devices_hash,
87 .compare = devman_functions_compare,
88 .remove_callback = devices_remove_callback
89};
90
91static hash_table_operations_t loc_devices_ops = {
92 .hash = devices_hash,
93 .compare = loc_functions_compare,
94 .remove_callback = devices_remove_callback
95};
96
97/**
98 * Initialize the list of device driver's.
99 *
100 * @param drv_list the list of device driver's.
101 *
102 */
103void init_driver_list(driver_list_t *drv_list)
104{
105 assert(drv_list != NULL);
106
107 list_initialize(&drv_list->drivers);
108 fibril_mutex_initialize(&drv_list->drivers_mutex);
109}
110
111/** Allocate and initialize a new driver structure.
112 *
113 * @return Driver structure.
114 */
115driver_t *create_driver(void)
116{
117 driver_t *res = malloc(sizeof(driver_t));
118 if (res != NULL)
119 init_driver(res);
120 return res;
121}
122
123/** Add a driver to the list of drivers.
124 *
125 * @param drivers_list List of drivers.
126 * @param drv Driver structure.
127 */
128void add_driver(driver_list_t *drivers_list, driver_t *drv)
129{
130 fibril_mutex_lock(&drivers_list->drivers_mutex);
131 list_prepend(&drv->drivers, &drivers_list->drivers);
132 fibril_mutex_unlock(&drivers_list->drivers_mutex);
133
134 log_msg(LVL_NOTE, "Driver `%s' was added to the list of available "
135 "drivers.", drv->name);
136}
137
138/** Read match id at the specified position of a string and set the position in
139 * the string to the first character following the id.
140 *
141 * @param buf The position in the input string.
142 * @return The match id.
143 */
144char *read_match_id(char **buf)
145{
146 char *res = NULL;
147 size_t len = get_nonspace_len(*buf);
148
149 if (len > 0) {
150 res = malloc(len + 1);
151 if (res != NULL) {
152 str_ncpy(res, len + 1, *buf, len);
153 *buf += len;
154 }
155 }
156
157 return res;
158}
159
160/**
161 * Read match ids and associated match scores from a string.
162 *
163 * Each match score in the string is followed by its match id.
164 * The match ids and match scores are separated by whitespaces.
165 * Neither match ids nor match scores can contain whitespaces.
166 *
167 * @param buf The string from which the match ids are read.
168 * @param ids The list of match ids into which the match ids and
169 * scores are added.
170 * @return True if at least one match id and associated match score
171 * was successfully read, false otherwise.
172 */
173bool parse_match_ids(char *buf, match_id_list_t *ids)
174{
175 int score = 0;
176 char *id = NULL;
177 int ids_read = 0;
178
179 while (true) {
180 /* skip spaces */
181 if (!skip_spaces(&buf))
182 break;
183
184 /* read score */
185 score = strtoul(buf, &buf, 10);
186
187 /* skip spaces */
188 if (!skip_spaces(&buf))
189 break;
190
191 /* read id */
192 id = read_match_id(&buf);
193 if (NULL == id)
194 break;
195
196 /* create new match_id structure */
197 match_id_t *mid = create_match_id();
198 mid->id = id;
199 mid->score = score;
200
201 /* add it to the list */
202 add_match_id(ids, mid);
203
204 ids_read++;
205 }
206
207 return ids_read > 0;
208}
209
210/**
211 * Read match ids and associated match scores from a file.
212 *
213 * Each match score in the file is followed by its match id.
214 * The match ids and match scores are separated by whitespaces.
215 * Neither match ids nor match scores can contain whitespaces.
216 *
217 * @param buf The path to the file from which the match ids are read.
218 * @param ids The list of match ids into which the match ids and
219 * scores are added.
220 * @return True if at least one match id and associated match score
221 * was successfully read, false otherwise.
222 */
223bool read_match_ids(const char *conf_path, match_id_list_t *ids)
224{
225 log_msg(LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
226
227 bool suc = false;
228 char *buf = NULL;
229 bool opened = false;
230 int fd;
231 size_t len = 0;
232
233 fd = open(conf_path, O_RDONLY);
234 if (fd < 0) {
235 log_msg(LVL_ERROR, "Unable to open `%s' for reading: %s.",
236 conf_path, str_error(fd));
237 goto cleanup;
238 }
239 opened = true;
240
241 len = lseek(fd, 0, SEEK_END);
242 lseek(fd, 0, SEEK_SET);
243 if (len == 0) {
244 log_msg(LVL_ERROR, "Configuration file '%s' is empty.",
245 conf_path);
246 goto cleanup;
247 }
248
249 buf = malloc(len + 1);
250 if (buf == NULL) {
251 log_msg(LVL_ERROR, "Memory allocation failed when parsing file "
252 "'%s'.", conf_path);
253 goto cleanup;
254 }
255
256 ssize_t read_bytes = read_all(fd, buf, len);
257 if (read_bytes <= 0) {
258 log_msg(LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
259 read_bytes);
260 goto cleanup;
261 }
262 buf[read_bytes] = 0;
263
264 suc = parse_match_ids(buf, ids);
265
266cleanup:
267 free(buf);
268
269 if (opened)
270 close(fd);
271
272 return suc;
273}
274
275/**
276 * Get information about a driver.
277 *
278 * Each driver has its own directory in the base directory.
279 * The name of the driver's directory is the same as the name of the driver.
280 * The driver's directory contains driver's binary (named as the driver without
281 * extension) and the configuration file with match ids for device-to-driver
282 * matching (named as the driver with a special extension).
283 *
284 * This function searches for the driver's directory and containing
285 * configuration files. If all the files needed are found, they are parsed and
286 * the information about the driver is stored in the driver's structure.
287 *
288 * @param base_path The base directory, in which we look for driver's
289 * subdirectory.
290 * @param name The name of the driver.
291 * @param drv The driver structure to fill information in.
292 *
293 * @return True on success, false otherwise.
294 */
295bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
296{
297 log_msg(LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
298 base_path, name);
299
300 assert(base_path != NULL && name != NULL && drv != NULL);
301
302 bool suc = false;
303 char *match_path = NULL;
304 size_t name_size = 0;
305
306 /* Read the list of match ids from the driver's configuration file. */
307 match_path = get_abs_path(base_path, name, MATCH_EXT);
308 if (match_path == NULL)
309 goto cleanup;
310
311 if (!read_match_ids(match_path, &drv->match_ids))
312 goto cleanup;
313
314 /* Allocate and fill driver's name. */
315 name_size = str_size(name) + 1;
316 drv->name = malloc(name_size);
317 if (drv->name == NULL)
318 goto cleanup;
319 str_cpy(drv->name, name_size, name);
320
321 /* Initialize path with driver's binary. */
322 drv->binary_path = get_abs_path(base_path, name, "");
323 if (drv->binary_path == NULL)
324 goto cleanup;
325
326 /* Check whether the driver's binary exists. */
327 struct stat s;
328 if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
329 log_msg(LVL_ERROR, "Driver not found at path `%s'.",
330 drv->binary_path);
331 goto cleanup;
332 }
333
334 suc = true;
335
336cleanup:
337 if (!suc) {
338 free(drv->binary_path);
339 free(drv->name);
340 /* Set the driver structure to the default state. */
341 init_driver(drv);
342 }
343
344 free(match_path);
345
346 return suc;
347}
348
349/** Lookup drivers in the directory.
350 *
351 * @param drivers_list The list of available drivers.
352 * @param dir_path The path to the directory where we search for drivers.
353 * @return Number of drivers which were found.
354 */
355int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
356{
357 log_msg(LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
358
359 int drv_cnt = 0;
360 DIR *dir = NULL;
361 struct dirent *diren;
362
363 dir = opendir(dir_path);
364
365 if (dir != NULL) {
366 driver_t *drv = create_driver();
367 while ((diren = readdir(dir))) {
368 if (get_driver_info(dir_path, diren->d_name, drv)) {
369 add_driver(drivers_list, drv);
370 drv_cnt++;
371 drv = create_driver();
372 }
373 }
374 delete_driver(drv);
375 closedir(dir);
376 }
377
378 return drv_cnt;
379}
380
381/** Create root device and function node in the device tree.
382 *
383 * @param tree The device tree.
384 * @return True on success, false otherwise.
385 */
386bool create_root_nodes(dev_tree_t *tree)
387{
388 fun_node_t *fun;
389 dev_node_t *dev;
390
391 log_msg(LVL_DEBUG, "create_root_nodes()");
392
393 fibril_rwlock_write_lock(&tree->rwlock);
394
395 /*
396 * Create root function. This is a pseudo function to which
397 * the root device node is attached. It allows us to match
398 * the root device driver in a standard manner, i.e. against
399 * the parent function.
400 */
401
402 fun = create_fun_node();
403 if (fun == NULL) {
404 fibril_rwlock_write_unlock(&tree->rwlock);
405 return false;
406 }
407
408 insert_fun_node(tree, fun, str_dup(""), NULL);
409 match_id_t *id = create_match_id();
410 id->id = str_dup("root");
411 id->score = 100;
412 add_match_id(&fun->match_ids, id);
413 tree->root_node = fun;
414
415 /*
416 * Create root device node.
417 */
418 dev = create_dev_node();
419 if (dev == NULL) {
420 fibril_rwlock_write_unlock(&tree->rwlock);
421 return false;
422 }
423
424 insert_dev_node(tree, dev, fun);
425
426 fibril_rwlock_write_unlock(&tree->rwlock);
427
428 return dev != NULL;
429}
430
431/** Lookup the best matching driver for the specified device in the list of
432 * drivers.
433 *
434 * A match between a device and a driver is found if one of the driver's match
435 * ids match one of the device's match ids. The score of the match is the
436 * product of the driver's and device's score associated with the matching id.
437 * The best matching driver for a device is the driver with the highest score
438 * of the match between the device and the driver.
439 *
440 * @param drivers_list The list of drivers, where we look for the driver
441 * suitable for handling the device.
442 * @param node The device node structure of the device.
443 * @return The best matching driver or NULL if no matching driver
444 * is found.
445 */
446driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
447{
448 driver_t *best_drv = NULL, *drv = NULL;
449 int best_score = 0, score = 0;
450
451 fibril_mutex_lock(&drivers_list->drivers_mutex);
452
453 list_foreach(drivers_list->drivers, link) {
454 drv = list_get_instance(link, driver_t, drivers);
455 score = get_match_score(drv, node);
456 if (score > best_score) {
457 best_score = score;
458 best_drv = drv;
459 }
460 }
461
462 fibril_mutex_unlock(&drivers_list->drivers_mutex);
463
464 return best_drv;
465}
466
467/** Assign a driver to a device.
468 *
469 * @param node The device's node in the device tree.
470 * @param drv The driver.
471 */
472void attach_driver(dev_node_t *dev, driver_t *drv)
473{
474 log_msg(LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
475 dev->pfun->pathname, drv->name);
476
477 fibril_mutex_lock(&drv->driver_mutex);
478
479 dev->drv = drv;
480 list_append(&dev->driver_devices, &drv->devices);
481
482 fibril_mutex_unlock(&drv->driver_mutex);
483}
484
485/** Start a driver
486 *
487 * @param drv The driver's structure.
488 * @return True if the driver's task is successfully spawned, false
489 * otherwise.
490 */
491bool start_driver(driver_t *drv)
492{
493 int rc;
494
495 assert(fibril_mutex_is_locked(&drv->driver_mutex));
496
497 log_msg(LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
498
499 rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
500 if (rc != EOK) {
501 log_msg(LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
502 drv->name, drv->binary_path, str_error(rc));
503 return false;
504 }
505
506 drv->state = DRIVER_STARTING;
507 return true;
508}
509
510/** Find device driver in the list of device drivers.
511 *
512 * @param drv_list The list of device drivers.
513 * @param drv_name The name of the device driver which is searched.
514 * @return The device driver of the specified name, if it is in the
515 * list, NULL otherwise.
516 */
517driver_t *find_driver(driver_list_t *drv_list, const char *drv_name)
518{
519 driver_t *res = NULL;
520 driver_t *drv = NULL;
521
522 fibril_mutex_lock(&drv_list->drivers_mutex);
523
524 list_foreach(drv_list->drivers, link) {
525 drv = list_get_instance(link, driver_t, drivers);
526 if (str_cmp(drv->name, drv_name) == 0) {
527 res = drv;
528 break;
529 }
530 }
531
532 fibril_mutex_unlock(&drv_list->drivers_mutex);
533
534 return res;
535}
536
537/** Notify driver about the devices to which it was assigned.
538 *
539 * @param driver The driver to which the devices are passed.
540 */
541static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
542{
543 dev_node_t *dev;
544 link_t *link;
545
546 log_msg(LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
547 driver->name);
548
549 fibril_mutex_lock(&driver->driver_mutex);
550
551 /*
552 * Go through devices list as long as there is some device
553 * that has not been passed to the driver.
554 */
555 link = driver->devices.head.next;
556 while (link != &driver->devices.head) {
557 dev = list_get_instance(link, dev_node_t, driver_devices);
558 if (dev->passed_to_driver) {
559 link = link->next;
560 continue;
561 }
562
563 /*
564 * We remove the device from the list to allow safe adding
565 * of new devices (no one will touch our item this way).
566 */
567 list_remove(link);
568
569 /*
570 * Unlock to avoid deadlock when adding device
571 * handled by itself.
572 */
573 fibril_mutex_unlock(&driver->driver_mutex);
574
575 add_device(driver, dev, tree);
576
577 /*
578 * Lock again as we will work with driver's
579 * structure.
580 */
581 fibril_mutex_lock(&driver->driver_mutex);
582
583 /*
584 * Insert the device back.
585 * The order is not relevant here so no harm is done
586 * (actually, the order would be preserved in most cases).
587 */
588 list_append(link, &driver->devices);
589
590 /*
591 * Restart the cycle to go through all devices again.
592 */
593 link = driver->devices.head.next;
594 }
595
596 /*
597 * Once we passed all devices to the driver, we need to mark the
598 * driver as running.
599 * It is vital to do it here and inside critical section.
600 *
601 * If we would change the state earlier, other devices added to
602 * the driver would be added to the device list and started
603 * immediately and possibly started here as well.
604 */
605 log_msg(LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
606 driver->state = DRIVER_RUNNING;
607
608 fibril_mutex_unlock(&driver->driver_mutex);
609}
610
611/** Finish the initialization of a driver after it has succesfully started
612 * and after it has registered itself by the device manager.
613 *
614 * Pass devices formerly matched to the driver to the driver and remember the
615 * driver is running and fully functional now.
616 *
617 * @param driver The driver which registered itself as running by the
618 * device manager.
619 */
620void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
621{
622 log_msg(LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
623 driver->name);
624
625 /*
626 * Pass devices which have been already assigned to the driver to the
627 * driver.
628 */
629 pass_devices_to_driver(driver, tree);
630}
631
632/** Initialize device driver structure.
633 *
634 * @param drv The device driver structure.
635 */
636void init_driver(driver_t *drv)
637{
638 assert(drv != NULL);
639
640 memset(drv, 0, sizeof(driver_t));
641 list_initialize(&drv->match_ids.ids);
642 list_initialize(&drv->devices);
643 fibril_mutex_initialize(&drv->driver_mutex);
644 drv->sess = NULL;
645}
646
647/** Device driver structure clean-up.
648 *
649 * @param drv The device driver structure.
650 */
651void clean_driver(driver_t *drv)
652{
653 assert(drv != NULL);
654
655 free_not_null(drv->name);
656 free_not_null(drv->binary_path);
657
658 clean_match_ids(&drv->match_ids);
659
660 init_driver(drv);
661}
662
663/** Delete device driver structure.
664 *
665 * @param drv The device driver structure.
666 */
667void delete_driver(driver_t *drv)
668{
669 assert(drv != NULL);
670
671 clean_driver(drv);
672 free(drv);
673}
674
675/** Create loc path and name for the function. */
676void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
677{
678 char *loc_pathname = NULL;
679 char *loc_name = NULL;
680
681 asprintf(&loc_name, "%s", fun->pathname);
682 if (loc_name == NULL)
683 return;
684
685 replace_char(loc_name, '/', LOC_SEPARATOR);
686
687 asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
688 loc_name);
689 if (loc_pathname == NULL) {
690 free(loc_name);
691 return;
692 }
693
694 loc_service_register_with_iface(loc_pathname,
695 &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
696
697 tree_add_loc_function(tree, fun);
698
699 free(loc_name);
700 free(loc_pathname);
701}
702
703/** Pass a device to running driver.
704 *
705 * @param drv The driver's structure.
706 * @param node The device's node in the device tree.
707 */
708void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
709{
710 /*
711 * We do not expect to have driver's mutex locked as we do not
712 * access any structures that would affect driver_t.
713 */
714 log_msg(LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
715 drv->name, dev->pfun->name);
716
717 /* Send the device to the driver. */
718 devman_handle_t parent_handle;
719 if (dev->pfun) {
720 parent_handle = dev->pfun->handle;
721 } else {
722 parent_handle = 0;
723 }
724
725 async_exch_t *exch = async_exchange_begin(drv->sess);
726
727 ipc_call_t answer;
728 aid_t req = async_send_2(exch, DRIVER_ADD_DEVICE, dev->handle,
729 parent_handle, &answer);
730
731 /* Send the device name to the driver. */
732 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
733 str_size(dev->pfun->name) + 1);
734
735 async_exchange_end(exch);
736
737 if (rc != EOK) {
738 /* TODO handle error */
739 }
740
741 /* Wait for answer from the driver. */
742 async_wait_for(req, &rc);
743
744 switch(rc) {
745 case EOK:
746 dev->state = DEVICE_USABLE;
747 break;
748 case ENOENT:
749 dev->state = DEVICE_NOT_PRESENT;
750 break;
751 default:
752 dev->state = DEVICE_INVALID;
753 }
754
755 dev->passed_to_driver = true;
756
757 return;
758}
759
760/** Find suitable driver for a device and assign the driver to it.
761 *
762 * @param node The device node of the device in the device tree.
763 * @param drivers_list The list of available drivers.
764 * @return True if the suitable driver is found and
765 * successfully assigned to the device, false otherwise.
766 */
767bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
768 dev_tree_t *tree)
769{
770 assert(dev != NULL);
771 assert(drivers_list != NULL);
772 assert(tree != NULL);
773
774 /*
775 * Find the driver which is the most suitable for handling this device.
776 */
777 driver_t *drv = find_best_match_driver(drivers_list, dev);
778 if (drv == NULL) {
779 log_msg(LVL_ERROR, "No driver found for device `%s'.",
780 dev->pfun->pathname);
781 return false;
782 }
783
784 /* Attach the driver to the device. */
785 attach_driver(dev, drv);
786
787 fibril_mutex_lock(&drv->driver_mutex);
788 if (drv->state == DRIVER_NOT_STARTED) {
789 /* Start the driver. */
790 start_driver(drv);
791 }
792 bool is_running = drv->state == DRIVER_RUNNING;
793 fibril_mutex_unlock(&drv->driver_mutex);
794
795 /* Notify the driver about the new device. */
796 if (is_running)
797 add_device(drv, dev, tree);
798
799 return true;
800}
801
802/** Initialize the device tree.
803 *
804 * Create root device node of the tree and assign driver to it.
805 *
806 * @param tree The device tree.
807 * @param drivers_list the list of available drivers.
808 * @return True on success, false otherwise.
809 */
810bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
811{
812 log_msg(LVL_DEBUG, "init_device_tree()");
813
814 tree->current_handle = 0;
815
816 hash_table_create(&tree->devman_devices, DEVICE_BUCKETS, 1,
817 &devman_devices_ops);
818 hash_table_create(&tree->devman_functions, DEVICE_BUCKETS, 1,
819 &devman_functions_ops);
820 hash_table_create(&tree->loc_functions, DEVICE_BUCKETS, 1,
821 &loc_devices_ops);
822
823 fibril_rwlock_initialize(&tree->rwlock);
824
825 /* Create root function and root device and add them to the device tree. */
826 if (!create_root_nodes(tree))
827 return false;
828
829 /* Find suitable driver and start it. */
830 return assign_driver(tree->root_node->child, drivers_list, tree);
831}
832
833/* Device nodes */
834
835/** Create a new device node.
836 *
837 * @return A device node structure.
838 */
839dev_node_t *create_dev_node(void)
840{
841 dev_node_t *res = malloc(sizeof(dev_node_t));
842
843 if (res != NULL) {
844 memset(res, 0, sizeof(dev_node_t));
845 list_initialize(&res->functions);
846 link_initialize(&res->driver_devices);
847 link_initialize(&res->devman_dev);
848 }
849
850 return res;
851}
852
853/** Delete a device node.
854 *
855 * @param node The device node structure.
856 */
857void delete_dev_node(dev_node_t *dev)
858{
859 assert(list_empty(&dev->functions));
860 assert(dev->pfun == NULL);
861 assert(dev->drv == NULL);
862
863 free(dev);
864}
865
866/** Find the device node structure of the device witch has the specified handle.
867 *
868 * @param tree The device tree where we look for the device node.
869 * @param handle The handle of the device.
870 * @return The device node.
871 */
872dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
873{
874 unsigned long key = handle;
875 link_t *link;
876
877 assert(fibril_rwlock_is_locked(&tree->rwlock));
878
879 link = hash_table_find(&tree->devman_devices, &key);
880 return hash_table_get_instance(link, dev_node_t, devman_dev);
881}
882
883/** Find the device node structure of the device witch has the specified handle.
884 *
885 * @param tree The device tree where we look for the device node.
886 * @param handle The handle of the device.
887 * @return The device node.
888 */
889dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
890{
891 dev_node_t *dev = NULL;
892
893 fibril_rwlock_read_lock(&tree->rwlock);
894 dev = find_dev_node_no_lock(tree, handle);
895 fibril_rwlock_read_unlock(&tree->rwlock);
896
897 return dev;
898}
899
900/** Get list of device functions. */
901int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
902 devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
903{
904 size_t act_cnt;
905 size_t buf_cnt;
906
907 assert(fibril_rwlock_is_locked(&tree->rwlock));
908
909 buf_cnt = buf_size / sizeof(devman_handle_t);
910
911 act_cnt = list_count(&dev->functions);
912 *act_size = act_cnt * sizeof(devman_handle_t);
913
914 if (buf_size % sizeof(devman_handle_t) != 0)
915 return EINVAL;
916
917 size_t pos = 0;
918 list_foreach(dev->functions, item) {
919 fun_node_t *fun =
920 list_get_instance(item, fun_node_t, dev_functions);
921
922 if (pos < buf_cnt)
923 hdl_buf[pos] = fun->handle;
924 pos++;
925 }
926
927 return EOK;
928}
929
930
931/* Function nodes */
932
933/** Create a new function node.
934 *
935 * @return A function node structure.
936 */
937fun_node_t *create_fun_node(void)
938{
939 fun_node_t *res = malloc(sizeof(fun_node_t));
940
941 if (res != NULL) {
942 memset(res, 0, sizeof(fun_node_t));
943 link_initialize(&res->dev_functions);
944 list_initialize(&res->match_ids.ids);
945 link_initialize(&res->devman_fun);
946 link_initialize(&res->loc_fun);
947 }
948
949 return res;
950}
951
952/** Delete a function node.
953 *
954 * @param fun The device node structure.
955 */
956void delete_fun_node(fun_node_t *fun)
957{
958 assert(fun->dev == NULL);
959 assert(fun->child == NULL);
960
961 clean_match_ids(&fun->match_ids);
962 free_not_null(fun->name);
963 free_not_null(fun->pathname);
964 free(fun);
965}
966
967/** Find the function node with the specified handle.
968 *
969 * @param tree The device tree where we look for the device node.
970 * @param handle The handle of the function.
971 * @return The function node.
972 */
973fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
974{
975 unsigned long key = handle;
976 link_t *link;
977
978 assert(fibril_rwlock_is_locked(&tree->rwlock));
979
980 link = hash_table_find(&tree->devman_functions, &key);
981 if (link == NULL)
982 return NULL;
983
984 return hash_table_get_instance(link, fun_node_t, devman_fun);
985}
986
987/** Find the function node with the specified handle.
988 *
989 * @param tree The device tree where we look for the device node.
990 * @param handle The handle of the function.
991 * @return The function node.
992 */
993fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
994{
995 fun_node_t *fun = NULL;
996
997 fibril_rwlock_read_lock(&tree->rwlock);
998 fun = find_fun_node_no_lock(tree, handle);
999 fibril_rwlock_read_unlock(&tree->rwlock);
1000
1001 return fun;
1002}
1003
1004/** Create and set device's full path in device tree.
1005 *
1006 * @param node The device's device node.
1007 * @param parent The parent device node.
1008 * @return True on success, false otherwise (insufficient
1009 * resources etc.).
1010 */
1011static bool set_fun_path(fun_node_t *fun, fun_node_t *parent)
1012{
1013 assert(fun->name != NULL);
1014
1015 size_t pathsize = (str_size(fun->name) + 1);
1016 if (parent != NULL)
1017 pathsize += str_size(parent->pathname) + 1;
1018
1019 fun->pathname = (char *) malloc(pathsize);
1020 if (fun->pathname == NULL) {
1021 log_msg(LVL_ERROR, "Failed to allocate device path.");
1022 return false;
1023 }
1024
1025 if (parent != NULL) {
1026 str_cpy(fun->pathname, pathsize, parent->pathname);
1027 str_append(fun->pathname, pathsize, "/");
1028 str_append(fun->pathname, pathsize, fun->name);
1029 } else {
1030 str_cpy(fun->pathname, pathsize, fun->name);
1031 }
1032
1033 return true;
1034}
1035
1036/** Insert new device into device tree.
1037 *
1038 * @param tree The device tree.
1039 * @param node The newly added device node.
1040 * @param dev_name The name of the newly added device.
1041 * @param parent The parent device node.
1042 *
1043 * @return True on success, false otherwise (insufficient resources
1044 * etc.).
1045 */
1046bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
1047{
1048 assert(dev != NULL);
1049 assert(tree != NULL);
1050 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
1051
1052 log_msg(LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
1053 dev, pfun, pfun->pathname);
1054
1055 /* Add the node to the handle-to-node map. */
1056 dev->handle = ++tree->current_handle;
1057 unsigned long key = dev->handle;
1058 hash_table_insert(&tree->devman_devices, &key, &dev->devman_dev);
1059
1060 /* Add the node to the list of its parent's children. */
1061 dev->pfun = pfun;
1062 pfun->child = dev;
1063
1064 return true;
1065}
1066
1067/** Insert new function into device tree.
1068 *
1069 * @param tree The device tree.
1070 * @param node The newly added function node.
1071 * @param dev_name The name of the newly added function.
1072 * @param parent Owning device node.
1073 *
1074 * @return True on success, false otherwise (insufficient resources
1075 * etc.).
1076 */
1077bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
1078 dev_node_t *dev)
1079{
1080 fun_node_t *pfun;
1081
1082 assert(fun != NULL);
1083 assert(tree != NULL);
1084 assert(fun_name != NULL);
1085 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
1086
1087 /*
1088 * The root function is a special case, it does not belong to any
1089 * device so for the root function dev == NULL.
1090 */
1091 pfun = (dev != NULL) ? dev->pfun : NULL;
1092
1093 fun->name = fun_name;
1094 if (!set_fun_path(fun, pfun)) {
1095 return false;
1096 }
1097
1098 /* Add the node to the handle-to-node map. */
1099 fun->handle = ++tree->current_handle;
1100 unsigned long key = fun->handle;
1101 hash_table_insert(&tree->devman_functions, &key, &fun->devman_fun);
1102
1103 /* Add the node to the list of its parent's children. */
1104 fun->dev = dev;
1105 if (dev != NULL)
1106 list_append(&fun->dev_functions, &dev->functions);
1107
1108 return true;
1109}
1110
1111/** Remove function from device tree.
1112 *
1113 * @param tree Device tree
1114 * @param node Function node to remove
1115 */
1116void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
1117{
1118 assert(tree != NULL);
1119 assert(fun != NULL);
1120 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
1121
1122 /* Remove the node from the handle-to-node map. */
1123 unsigned long key = fun->handle;
1124 hash_table_remove(&tree->devman_functions, &key, 1);
1125
1126 /* Remove the node from the list of its parent's children. */
1127 if (fun->dev != NULL)
1128 list_remove(&fun->dev_functions);
1129}
1130
1131/** Find function node with a specified path in the device tree.
1132 *
1133 * @param path The path of the function node in the device tree.
1134 * @param tree The device tree.
1135 * @return The function node if it is present in the tree, NULL
1136 * otherwise.
1137 */
1138fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
1139{
1140 assert(path != NULL);
1141
1142 bool is_absolute = path[0] == '/';
1143 if (!is_absolute) {
1144 return NULL;
1145 }
1146
1147 fibril_rwlock_read_lock(&tree->rwlock);
1148
1149 fun_node_t *fun = tree->root_node;
1150 /*
1151 * Relative path to the function from its parent (but with '/' at the
1152 * beginning)
1153 */
1154 char *rel_path = path;
1155 char *next_path_elem = NULL;
1156 bool cont = (rel_path[1] != '\0');
1157
1158 while (cont && fun != NULL) {
1159 next_path_elem = get_path_elem_end(rel_path + 1);
1160 if (next_path_elem[0] == '/') {
1161 cont = true;
1162 next_path_elem[0] = 0;
1163 } else {
1164 cont = false;
1165 }
1166
1167 fun = find_node_child(fun, rel_path + 1);
1168
1169 if (cont) {
1170 /* Restore the original path. */
1171 next_path_elem[0] = '/';
1172 }
1173 rel_path = next_path_elem;
1174 }
1175
1176 fibril_rwlock_read_unlock(&tree->rwlock);
1177
1178 return fun;
1179}
1180
1181/** Find function with a specified name belonging to given device.
1182 *
1183 * Device tree rwlock should be held at least for reading.
1184 *
1185 * @param dev Device the function belongs to.
1186 * @param name Function name (not path).
1187 * @return Function node.
1188 * @retval NULL No function with given name.
1189 */
1190fun_node_t *find_fun_node_in_device(dev_node_t *dev, const char *name)
1191{
1192 assert(dev != NULL);
1193 assert(name != NULL);
1194
1195 fun_node_t *fun;
1196
1197 list_foreach(dev->functions, link) {
1198 fun = list_get_instance(link, fun_node_t, dev_functions);
1199
1200 if (str_cmp(name, fun->name) == 0)
1201 return fun;
1202 }
1203
1204 return NULL;
1205}
1206
1207/** Find child function node with a specified name.
1208 *
1209 * Device tree rwlock should be held at least for reading.
1210 *
1211 * @param parent The parent function node.
1212 * @param name The name of the child function.
1213 * @return The child function node.
1214 */
1215fun_node_t *find_node_child(fun_node_t *pfun, const char *name)
1216{
1217 return find_fun_node_in_device(pfun->child, name);
1218}
1219
1220/* loc devices */
1221
1222fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
1223{
1224 fun_node_t *fun = NULL;
1225 link_t *link;
1226 unsigned long key = (unsigned long) service_id;
1227
1228 fibril_rwlock_read_lock(&tree->rwlock);
1229 link = hash_table_find(&tree->loc_functions, &key);
1230 if (link != NULL)
1231 fun = hash_table_get_instance(link, fun_node_t, loc_fun);
1232 fibril_rwlock_read_unlock(&tree->rwlock);
1233
1234 return fun;
1235}
1236
1237void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
1238{
1239 unsigned long key = (unsigned long) fun->service_id;
1240 fibril_rwlock_write_lock(&tree->rwlock);
1241 hash_table_insert(&tree->loc_functions, &key, &fun->loc_fun);
1242 fibril_rwlock_write_unlock(&tree->rwlock);
1243}
1244
1245/** @}
1246 */
Note: See TracBrowser for help on using the repository browser.