source: mainline/uspace/srv/devman/devman.c@ 47a7174f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 47a7174f was 47a7174f, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

Devmap drivers can customize forwarded connections

It is possible to set an extra parameter for forwarded connections through
devmap. The change shall ensure backward compatibility and allows to connect
to devman-style drivers through their devmap path.

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