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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b17518e was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

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