source: mainline/uspace/srv/devman/devman.c@ 38e52c92

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

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

  • Property mode set to 100644
File size: 14.8 KB
RevLine 
[0358da0]1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[e2b9a993]29/** @addtogroup devman
[0358da0]30 * @{
31 */
[58cbb0c8]32/** @file Device Manager
33 *
34 * Locking order:
35 * (1) driver_t.driver_mutex
36 * (2) dev_tree_t.rwlock
37 *
38 * Synchronization:
39 * - device_tree.rwlock protects:
40 * - tree root, complete tree topology
41 * - complete contents of device and function nodes
42 * - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
43 * being deallocated
44 * - find_xxx() functions increase reference count of returned object
45 * - find_xxx_no_lock() do not increase reference count
46 *
47 * TODO
48 * - Track all steady and transient device/function states
49 * - Check states, wait for steady state on certain operations
50 */
[0358da0]51
52#include <errno.h>
[e4c4247]53#include <fcntl.h>
[85e48a9]54#include <sys/stat.h>
[9b415c9]55#include <io/log.h>
[084ff99]56#include <ipc/driver.h>
57#include <ipc/devman.h>
[15f3c3f]58#include <loc.h>
[0485135]59#include <str_error.h>
[c7bbf029]60#include <stdio.h>
[0358da0]61
[d1bafbf]62#include "dev.h"
[e2b9a993]63#include "devman.h"
[041b026]64#include "driver.h"
[38e52c92]65#include "fun.h"
[ba38f72c]66
[38b3baf]67/* hash table operations */
[957cfa58]68
[062d900]69static inline size_t handle_key_hash(void *key)
[957cfa58]70{
[062d900]71 devman_handle_t handle = *(devman_handle_t*)key;
72 return handle;
[957cfa58]73}
74
[062d900]75static size_t devman_devices_hash(const ht_link_t *item)
[957cfa58]76{
[062d900]77 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
78 return handle_key_hash(&dev->handle);
[957cfa58]79}
80
[062d900]81static size_t devman_functions_hash(const ht_link_t *item)
[957cfa58]82{
[062d900]83 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
84 return handle_key_hash(&fun->handle);
[ba38f72c]85}
86
[062d900]87static bool devman_devices_key_equal(void *key, const ht_link_t *item)
[ba38f72c]88{
[062d900]89 devman_handle_t handle = *(devman_handle_t*)key;
90 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
91 return dev->handle == handle;
[957cfa58]92}
93
[062d900]94static bool devman_functions_key_equal(void *key, const ht_link_t *item)
[957cfa58]95{
[062d900]96 devman_handle_t handle = *(devman_handle_t*)key;
97 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
98 return fun->handle == handle;
[957cfa58]99}
100
[062d900]101static inline size_t service_id_key_hash(void *key)
102{
103 service_id_t service_id = *(service_id_t*)key;
104 return service_id;
105}
106
107static size_t loc_functions_hash(const ht_link_t *item)
108{
109 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
110 return service_id_key_hash(&fun->service_id);
111}
112
113static bool loc_functions_key_equal(void *key, const ht_link_t *item)
114{
115 service_id_t service_id = *(service_id_t*)key;
116 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
117 return fun->service_id == service_id;
118}
119
120
121static hash_table_ops_t devman_devices_ops = {
122 .hash = devman_devices_hash,
123 .key_hash = handle_key_hash,
124 .key_equal = devman_devices_key_equal,
[4e00f87]125 .equal = NULL,
126 .remove_callback = NULL
[957cfa58]127};
128
[062d900]129static hash_table_ops_t devman_functions_ops = {
130 .hash = devman_functions_hash,
131 .key_hash = handle_key_hash,
132 .key_equal = devman_functions_key_equal,
[4e00f87]133 .equal = NULL,
134 .remove_callback = NULL
[ba38f72c]135};
136
[062d900]137static hash_table_ops_t loc_devices_ops = {
138 .hash = loc_functions_hash,
139 .key_hash = service_id_key_hash,
140 .key_equal = loc_functions_key_equal,
[4e00f87]141 .equal = NULL,
142 .remove_callback = NULL
[957cfa58]143};
144
[38b3baf]145/** Read match id at the specified position of a string and set the position in
146 * the string to the first character following the id.
147 *
148 * @param buf The position in the input string.
149 * @return The match id.
[0c3666d]150 */
[38b3baf]151char *read_match_id(char **buf)
[e4c4247]152{
153 char *res = NULL;
[e2b9a993]154 size_t len = get_nonspace_len(*buf);
[38b3baf]155
[e4c4247]156 if (len > 0) {
157 res = malloc(len + 1);
158 if (res != NULL) {
[38b3baf]159 str_ncpy(res, len + 1, *buf, len);
[e4c4247]160 *buf += len;
161 }
162 }
[38b3baf]163
[e4c4247]164 return res;
165}
166
[0c3666d]167/**
168 * Read match ids and associated match scores from a string.
[38b3baf]169 *
170 * Each match score in the string is followed by its match id.
171 * The match ids and match scores are separated by whitespaces.
172 * Neither match ids nor match scores can contain whitespaces.
173 *
174 * @param buf The string from which the match ids are read.
175 * @param ids The list of match ids into which the match ids and
176 * scores are added.
177 * @return True if at least one match id and associated match score
178 * was successfully read, false otherwise.
[0c3666d]179 */
[c47e1a8]180bool parse_match_ids(char *buf, match_id_list_t *ids)
[e4c4247]181{
182 int score = 0;
183 char *id = NULL;
184 int ids_read = 0;
185
186 while (true) {
[38b3baf]187 /* skip spaces */
188 if (!skip_spaces(&buf))
[e4c4247]189 break;
[38b3baf]190
191 /* read score */
[e4c4247]192 score = strtoul(buf, &buf, 10);
193
[38b3baf]194 /* skip spaces */
195 if (!skip_spaces(&buf))
[e4c4247]196 break;
197
[38b3baf]198 /* read id */
199 id = read_match_id(&buf);
200 if (NULL == id)
201 break;
[e4c4247]202
[38b3baf]203 /* create new match_id structure */
[e4c4247]204 match_id_t *mid = create_match_id();
205 mid->id = id;
206 mid->score = score;
207
[38b3baf]208 /* add it to the list */
[e4c4247]209 add_match_id(ids, mid);
210
[38b3baf]211 ids_read++;
212 }
[e4c4247]213
214 return ids_read > 0;
215}
216
[0c3666d]217/**
218 * Read match ids and associated match scores from a file.
[38b3baf]219 *
220 * Each match score in the file is followed by its match id.
221 * The match ids and match scores are separated by whitespaces.
222 * Neither match ids nor match scores can contain whitespaces.
223 *
224 * @param buf The path to the file from which the match ids are read.
225 * @param ids The list of match ids into which the match ids and
226 * scores are added.
227 * @return True if at least one match id and associated match score
228 * was successfully read, false otherwise.
[0c3666d]229 */
[38b3baf]230bool read_match_ids(const char *conf_path, match_id_list_t *ids)
231{
[a1a101d]232 log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
[08d9c4e6]233
[38b3baf]234 bool suc = false;
[e4c4247]235 char *buf = NULL;
236 bool opened = false;
[38b3baf]237 int fd;
[c47e1a8]238 size_t len = 0;
[e4c4247]239
240 fd = open(conf_path, O_RDONLY);
241 if (fd < 0) {
[a1a101d]242 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
[9b415c9]243 conf_path, str_error(fd));
[e4c4247]244 goto cleanup;
[38b3baf]245 }
246 opened = true;
[e4c4247]247
248 len = lseek(fd, 0, SEEK_END);
[38b3baf]249 lseek(fd, 0, SEEK_SET);
[e4c4247]250 if (len == 0) {
[a1a101d]251 log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
[9b415c9]252 conf_path);
[38b3baf]253 goto cleanup;
[e4c4247]254 }
255
256 buf = malloc(len + 1);
257 if (buf == NULL) {
[a1a101d]258 log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
[ebcb05a]259 "'%s'.", conf_path);
[e4c4247]260 goto cleanup;
[58b833c]261 }
[e4c4247]262
[8fd04ba9]263 ssize_t read_bytes = read_all(fd, buf, len);
[dc87f3fd]264 if (read_bytes <= 0) {
[a1a101d]265 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
[8fd04ba9]266 read_bytes);
[e4c4247]267 goto cleanup;
268 }
[dc87f3fd]269 buf[read_bytes] = 0;
[e4c4247]270
271 suc = parse_match_ids(buf, ids);
272
273cleanup:
274 free(buf);
275
[58b833c]276 if (opened)
[38b3baf]277 close(fd);
[e4c4247]278
279 return suc;
280}
281
[ba38f72c]282/** Create root device and function node in the device tree.
[38b3baf]283 *
[58b833c]284 * @param tree The device tree.
285 * @return True on success, false otherwise.
[0c3666d]286 */
[ba38f72c]287bool create_root_nodes(dev_tree_t *tree)
[e4c4247]288{
[ba38f72c]289 fun_node_t *fun;
290 dev_node_t *dev;
291
[a1a101d]292 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
[ba38f72c]293
[01b87dc5]294 fibril_rwlock_write_lock(&tree->rwlock);
[ba38f72c]295
296 /*
297 * Create root function. This is a pseudo function to which
298 * the root device node is attached. It allows us to match
299 * the root device driver in a standard manner, i.e. against
300 * the parent function.
301 */
302
303 fun = create_fun_node();
304 if (fun == NULL) {
305 fibril_rwlock_write_unlock(&tree->rwlock);
306 return false;
[85e48a9]307 }
[ba38f72c]308
[58cbb0c8]309 fun_add_ref(fun);
[4022513]310 insert_fun_node(tree, fun, str_dup(""), NULL);
[58cbb0c8]311
[ba38f72c]312 match_id_t *id = create_match_id();
[4022513]313 id->id = str_dup("root");
[ba38f72c]314 id->score = 100;
315 add_match_id(&fun->match_ids, id);
316 tree->root_node = fun;
317
318 /*
319 * Create root device node.
320 */
321 dev = create_dev_node();
322 if (dev == NULL) {
323 fibril_rwlock_write_unlock(&tree->rwlock);
324 return false;
325 }
326
[58cbb0c8]327 dev_add_ref(dev);
[ba38f72c]328 insert_dev_node(tree, dev, fun);
329
[01b87dc5]330 fibril_rwlock_write_unlock(&tree->rwlock);
[ba38f72c]331
332 return dev != NULL;
[85e48a9]333}
334
[15f3c3f]335/** Create loc path and name for the function. */
336void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
[a32defa]337{
[15f3c3f]338 char *loc_pathname = NULL;
339 char *loc_name = NULL;
[a32defa]340
[58cbb0c8]341 assert(fibril_rwlock_is_locked(&tree->rwlock));
342
[15f3c3f]343 asprintf(&loc_name, "%s", fun->pathname);
344 if (loc_name == NULL)
[a32defa]345 return;
346
[15f3c3f]347 replace_char(loc_name, '/', LOC_SEPARATOR);
[a32defa]348
[15f3c3f]349 asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
350 loc_name);
351 if (loc_pathname == NULL) {
352 free(loc_name);
[a32defa]353 return;
[38b3baf]354 }
[a32defa]355
[15f3c3f]356 loc_service_register_with_iface(loc_pathname,
357 &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
[a32defa]358
[15f3c3f]359 tree_add_loc_function(tree, fun);
[a32defa]360
[15f3c3f]361 free(loc_name);
362 free(loc_pathname);
[a32defa]363}
364
[0c3666d]365/** Pass a device to running driver.
[38b3baf]366 *
367 * @param drv The driver's structure.
368 * @param node The device's node in the device tree.
[0c3666d]369 */
[45059d6b]370void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
[85e48a9]371{
[5bee897]372 /*
373 * We do not expect to have driver's mutex locked as we do not
374 * access any structures that would affect driver_t.
375 */
[a1a101d]376 log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
[9b415c9]377 drv->name, dev->pfun->name);
[a78fa2a]378
[38b3baf]379 /* Send the device to the driver. */
[0d6915f]380 devman_handle_t parent_handle;
[ba38f72c]381 if (dev->pfun) {
382 parent_handle = dev->pfun->handle;
[0d6915f]383 } else {
384 parent_handle = 0;
385 }
[79ae36dd]386
[45059d6b]387 async_exch_t *exch = async_exchange_begin(drv->sess);
[79ae36dd]388
389 ipc_call_t answer;
[1a5b252]390 aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
[0d6915f]391 parent_handle, &answer);
[a78fa2a]392
[79ae36dd]393 /* Send the device name to the driver. */
394 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
[ba38f72c]395 str_size(dev->pfun->name) + 1);
[79ae36dd]396
397 async_exchange_end(exch);
398
[38b3baf]399 if (rc != EOK) {
400 /* TODO handle error */
401 }
[398c4d7]402
[38b3baf]403 /* Wait for answer from the driver. */
[a78fa2a]404 async_wait_for(req, &rc);
[5bee897]405
[a78fa2a]406 switch(rc) {
407 case EOK:
[ba38f72c]408 dev->state = DEVICE_USABLE;
[df747b9c]409 break;
[a78fa2a]410 case ENOENT:
[ba38f72c]411 dev->state = DEVICE_NOT_PRESENT;
[a78fa2a]412 break;
[df747b9c]413 default:
[ba38f72c]414 dev->state = DEVICE_INVALID;
[77a69ea]415 break;
[084ff99]416 }
[e85920d]417
[ba38f72c]418 dev->passed_to_driver = true;
[5bee897]419
[5cd136ab]420 return;
[85e48a9]421}
422
[38b3baf]423/** Initialize the device tree.
424 *
[0c3666d]425 * Create root device node of the tree and assign driver to it.
[38b3baf]426 *
427 * @param tree The device tree.
428 * @param drivers_list the list of available drivers.
429 * @return True on success, false otherwise.
[0c3666d]430 */
431bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
[85e48a9]432{
[a1a101d]433 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
[0c3666d]434
[957cfa58]435 tree->current_handle = 0;
436
[062d900]437 hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
438 hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
439 hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
[bda60d9]440
[957cfa58]441 fibril_rwlock_initialize(&tree->rwlock);
[084ff99]442
[ba38f72c]443 /* Create root function and root device and add them to the device tree. */
444 if (!create_root_nodes(tree))
[85e48a9]445 return false;
[58cbb0c8]446
[38b3baf]447 /* Find suitable driver and start it. */
[58cbb0c8]448 dev_node_t *rdev = tree->root_node->child;
449 dev_add_ref(rdev);
450 int rc = assign_driver(rdev, drivers_list, tree);
451 dev_del_ref(rdev);
452
453 return rc;
[e4c4247]454}
455
[bda60d9]456/** Insert new device into device tree.
[38b3baf]457 *
458 * @param tree The device tree.
[58cbb0c8]459 * @param dev The newly added device node.
460 * @param pfun The parent function node.
[58b833c]461 *
[38b3baf]462 * @return True on success, false otherwise (insufficient resources
463 * etc.).
[bda60d9]464 */
[ba38f72c]465bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
466{
467 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
468
[a1a101d]469 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
[9b415c9]470 dev, pfun, pfun->pathname);
471
[ba38f72c]472 /* Add the node to the handle-to-node map. */
473 dev->handle = ++tree->current_handle;
[062d900]474 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
[ba38f72c]475
476 /* Add the node to the list of its parent's children. */
477 dev->pfun = pfun;
478 pfun->child = dev;
479
480 return true;
481}
482
[1a5b252]483/** Remove device from device tree.
484 *
485 * @param tree Device tree
486 * @param dev Device node
487 */
488void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
489{
490 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
491
[a1a101d]492 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
[1a5b252]493
494 /* Remove node from the handle-to-node map. */
[062d900]495 hash_table_remove(&tree->devman_devices, &dev->handle);
[1a5b252]496
497 /* Unlink from parent function. */
498 dev->pfun->child = NULL;
499 dev->pfun = NULL;
[c1a0488]500
501 dev->state = DEVICE_REMOVED;
[1a5b252]502}
503
504
[ba38f72c]505/** Insert new function into device tree.
506 *
507 * @param tree The device tree.
[58cbb0c8]508 * @param fun The newly added function node.
509 * @param fun_name The name of the newly added function.
510 * @param dev Owning device node.
[ba38f72c]511 *
512 * @return True on success, false otherwise (insufficient resources
513 * etc.).
514 */
515bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
516 dev_node_t *dev)
[bda60d9]517{
[ba38f72c]518 fun_node_t *pfun;
519
520 assert(fun_name != NULL);
[01b87dc5]521 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[bda60d9]522
[ba38f72c]523 /*
524 * The root function is a special case, it does not belong to any
525 * device so for the root function dev == NULL.
526 */
527 pfun = (dev != NULL) ? dev->pfun : NULL;
528
529 fun->name = fun_name;
[58cbb0c8]530 if (!set_fun_path(tree, fun, pfun)) {
[38b3baf]531 return false;
[bda60d9]532 }
533
[38b3baf]534 /* Add the node to the handle-to-node map. */
[ba38f72c]535 fun->handle = ++tree->current_handle;
[062d900]536 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
[bda60d9]537
[38b3baf]538 /* Add the node to the list of its parent's children. */
[ba38f72c]539 fun->dev = dev;
540 if (dev != NULL)
541 list_append(&fun->dev_functions, &dev->functions);
[38b3baf]542
[bda60d9]543 return true;
544}
545
[d0dd7b5]546/** Remove function from device tree.
547 *
548 * @param tree Device tree
549 * @param node Function node to remove
550 */
551void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
552{
553 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
554
555 /* Remove the node from the handle-to-node map. */
[062d900]556 hash_table_remove(&tree->devman_functions, &fun->handle);
[d0dd7b5]557
558 /* Remove the node from the list of its parent's children. */
559 if (fun->dev != NULL)
560 list_remove(&fun->dev_functions);
[1a5b252]561
562 fun->dev = NULL;
[c1a0488]563 fun->state = FUN_REMOVED;
[d0dd7b5]564}
565
[15f3c3f]566/* loc devices */
[ce89036b]567
[15f3c3f]568fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
[ce89036b]569{
[ba38f72c]570 fun_node_t *fun = NULL;
[ce89036b]571
572 fibril_rwlock_read_lock(&tree->rwlock);
[062d900]573 ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
[58cbb0c8]574 if (link != NULL) {
[062d900]575 fun = hash_table_get_inst(link, fun_node_t, loc_fun);
[58cbb0c8]576 fun_add_ref(fun);
577 }
[ce89036b]578 fibril_rwlock_read_unlock(&tree->rwlock);
579
[ba38f72c]580 return fun;
[ce89036b]581}
582
[15f3c3f]583void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
[791f58c]584{
[58cbb0c8]585 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
586
[062d900]587 hash_table_insert(&tree->loc_functions, &fun->loc_fun);
[791f58c]588}
589
[c16cf62]590/** @}
[58b833c]591 */
Note: See TracBrowser for help on using the repository browser.