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
Line 
1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup devman
30 * @{
31 */
32/** @file Device Manager
33 *
34 * Locking order:
35 * (1) driver_t.driver_mutex
36 * (2) dev_tree_t.rwlock
37 *
38 * Synchronization:
39 * - device_tree.rwlock protects:
40 * - tree root, complete tree topology
41 * - complete contents of device and function nodes
42 * - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
43 * being deallocated
44 * - find_xxx() functions increase reference count of returned object
45 * - find_xxx_no_lock() do not increase reference count
46 *
47 * TODO
48 * - Track all steady and transient device/function states
49 * - Check states, wait for steady state on certain operations
50 */
51
52#include <errno.h>
53#include <fcntl.h>
54#include <sys/stat.h>
55#include <io/log.h>
56#include <ipc/driver.h>
57#include <ipc/devman.h>
58#include <loc.h>
59#include <str_error.h>
60#include <stdio.h>
61
62#include "dev.h"
63#include "devman.h"
64#include "driver.h"
65#include "fun.h"
66
67/* hash table operations */
68
69static inline size_t handle_key_hash(void *key)
70{
71 devman_handle_t handle = *(devman_handle_t*)key;
72 return handle;
73}
74
75static size_t devman_devices_hash(const ht_link_t *item)
76{
77 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
78 return handle_key_hash(&dev->handle);
79}
80
81static size_t devman_functions_hash(const ht_link_t *item)
82{
83 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
84 return handle_key_hash(&fun->handle);
85}
86
87static bool devman_devices_key_equal(void *key, const ht_link_t *item)
88{
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;
92}
93
94static bool devman_functions_key_equal(void *key, const ht_link_t *item)
95{
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;
99}
100
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,
125 .equal = NULL,
126 .remove_callback = NULL
127};
128
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,
133 .equal = NULL,
134 .remove_callback = NULL
135};
136
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,
141 .equal = NULL,
142 .remove_callback = NULL
143};
144
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.
150 */
151char *read_match_id(char **buf)
152{
153 char *res = NULL;
154 size_t len = get_nonspace_len(*buf);
155
156 if (len > 0) {
157 res = malloc(len + 1);
158 if (res != NULL) {
159 str_ncpy(res, len + 1, *buf, len);
160 *buf += len;
161 }
162 }
163
164 return res;
165}
166
167/**
168 * Read match ids and associated match scores from a string.
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.
179 */
180bool parse_match_ids(char *buf, match_id_list_t *ids)
181{
182 int score = 0;
183 char *id = NULL;
184 int ids_read = 0;
185
186 while (true) {
187 /* skip spaces */
188 if (!skip_spaces(&buf))
189 break;
190
191 /* read score */
192 score = strtoul(buf, &buf, 10);
193
194 /* skip spaces */
195 if (!skip_spaces(&buf))
196 break;
197
198 /* read id */
199 id = read_match_id(&buf);
200 if (NULL == id)
201 break;
202
203 /* create new match_id structure */
204 match_id_t *mid = create_match_id();
205 mid->id = id;
206 mid->score = score;
207
208 /* add it to the list */
209 add_match_id(ids, mid);
210
211 ids_read++;
212 }
213
214 return ids_read > 0;
215}
216
217/**
218 * Read match ids and associated match scores from a file.
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.
229 */
230bool read_match_ids(const char *conf_path, match_id_list_t *ids)
231{
232 log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
233
234 bool suc = false;
235 char *buf = NULL;
236 bool opened = false;
237 int fd;
238 size_t len = 0;
239
240 fd = open(conf_path, O_RDONLY);
241 if (fd < 0) {
242 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
243 conf_path, str_error(fd));
244 goto cleanup;
245 }
246 opened = true;
247
248 len = lseek(fd, 0, SEEK_END);
249 lseek(fd, 0, SEEK_SET);
250 if (len == 0) {
251 log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
252 conf_path);
253 goto cleanup;
254 }
255
256 buf = malloc(len + 1);
257 if (buf == NULL) {
258 log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
259 "'%s'.", conf_path);
260 goto cleanup;
261 }
262
263 ssize_t read_bytes = read_all(fd, buf, len);
264 if (read_bytes <= 0) {
265 log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
266 read_bytes);
267 goto cleanup;
268 }
269 buf[read_bytes] = 0;
270
271 suc = parse_match_ids(buf, ids);
272
273cleanup:
274 free(buf);
275
276 if (opened)
277 close(fd);
278
279 return suc;
280}
281
282/** Create root device and function node in the device tree.
283 *
284 * @param tree The device tree.
285 * @return True on success, false otherwise.
286 */
287bool create_root_nodes(dev_tree_t *tree)
288{
289 fun_node_t *fun;
290 dev_node_t *dev;
291
292 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
293
294 fibril_rwlock_write_lock(&tree->rwlock);
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;
307 }
308
309 fun_add_ref(fun);
310 insert_fun_node(tree, fun, str_dup(""), NULL);
311
312 match_id_t *id = create_match_id();
313 id->id = str_dup("root");
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
327 dev_add_ref(dev);
328 insert_dev_node(tree, dev, fun);
329
330 fibril_rwlock_write_unlock(&tree->rwlock);
331
332 return dev != NULL;
333}
334
335/** Create loc path and name for the function. */
336void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
337{
338 char *loc_pathname = NULL;
339 char *loc_name = NULL;
340
341 assert(fibril_rwlock_is_locked(&tree->rwlock));
342
343 asprintf(&loc_name, "%s", fun->pathname);
344 if (loc_name == NULL)
345 return;
346
347 replace_char(loc_name, '/', LOC_SEPARATOR);
348
349 asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
350 loc_name);
351 if (loc_pathname == NULL) {
352 free(loc_name);
353 return;
354 }
355
356 loc_service_register_with_iface(loc_pathname,
357 &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
358
359 tree_add_loc_function(tree, fun);
360
361 free(loc_name);
362 free(loc_pathname);
363}
364
365/** Pass a device to running driver.
366 *
367 * @param drv The driver's structure.
368 * @param node The device's node in the device tree.
369 */
370void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
371{
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 */
376 log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
377 drv->name, dev->pfun->name);
378
379 /* Send the device to the driver. */
380 devman_handle_t parent_handle;
381 if (dev->pfun) {
382 parent_handle = dev->pfun->handle;
383 } else {
384 parent_handle = 0;
385 }
386
387 async_exch_t *exch = async_exchange_begin(drv->sess);
388
389 ipc_call_t answer;
390 aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
391 parent_handle, &answer);
392
393 /* Send the device name to the driver. */
394 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
395 str_size(dev->pfun->name) + 1);
396
397 async_exchange_end(exch);
398
399 if (rc != EOK) {
400 /* TODO handle error */
401 }
402
403 /* Wait for answer from the driver. */
404 async_wait_for(req, &rc);
405
406 switch(rc) {
407 case EOK:
408 dev->state = DEVICE_USABLE;
409 break;
410 case ENOENT:
411 dev->state = DEVICE_NOT_PRESENT;
412 break;
413 default:
414 dev->state = DEVICE_INVALID;
415 break;
416 }
417
418 dev->passed_to_driver = true;
419
420 return;
421}
422
423/** Initialize the device tree.
424 *
425 * Create root device node of the tree and assign driver to it.
426 *
427 * @param tree The device tree.
428 * @param drivers_list the list of available drivers.
429 * @return True on success, false otherwise.
430 */
431bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
432{
433 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
434
435 tree->current_handle = 0;
436
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);
440
441 fibril_rwlock_initialize(&tree->rwlock);
442
443 /* Create root function and root device and add them to the device tree. */
444 if (!create_root_nodes(tree))
445 return false;
446
447 /* Find suitable driver and start it. */
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;
454}
455
456/** Insert new device into device tree.
457 *
458 * @param tree The device tree.
459 * @param dev The newly added device node.
460 * @param pfun The parent function node.
461 *
462 * @return True on success, false otherwise (insufficient resources
463 * etc.).
464 */
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
469 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
470 dev, pfun, pfun->pathname);
471
472 /* Add the node to the handle-to-node map. */
473 dev->handle = ++tree->current_handle;
474 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
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
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
492 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
493
494 /* Remove node from the handle-to-node map. */
495 hash_table_remove(&tree->devman_devices, &dev->handle);
496
497 /* Unlink from parent function. */
498 dev->pfun->child = NULL;
499 dev->pfun = NULL;
500
501 dev->state = DEVICE_REMOVED;
502}
503
504
505/** Insert new function into device tree.
506 *
507 * @param tree The device tree.
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.
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)
517{
518 fun_node_t *pfun;
519
520 assert(fun_name != NULL);
521 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
522
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;
530 if (!set_fun_path(tree, fun, pfun)) {
531 return false;
532 }
533
534 /* Add the node to the handle-to-node map. */
535 fun->handle = ++tree->current_handle;
536 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
537
538 /* Add the node to the list of its parent's children. */
539 fun->dev = dev;
540 if (dev != NULL)
541 list_append(&fun->dev_functions, &dev->functions);
542
543 return true;
544}
545
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. */
556 hash_table_remove(&tree->devman_functions, &fun->handle);
557
558 /* Remove the node from the list of its parent's children. */
559 if (fun->dev != NULL)
560 list_remove(&fun->dev_functions);
561
562 fun->dev = NULL;
563 fun->state = FUN_REMOVED;
564}
565
566/* loc devices */
567
568fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
569{
570 fun_node_t *fun = NULL;
571
572 fibril_rwlock_read_lock(&tree->rwlock);
573 ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
574 if (link != NULL) {
575 fun = hash_table_get_inst(link, fun_node_t, loc_fun);
576 fun_add_ref(fun);
577 }
578 fibril_rwlock_read_unlock(&tree->rwlock);
579
580 return fun;
581}
582
583void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
584{
585 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
586
587 hash_table_insert(&tree->loc_functions, &fun->loc_fun);
588}
589
590/** @}
591 */
Note: See TracBrowser for help on using the repository browser.