source: mainline/uspace/srv/devman/main.c@ d80d7a8

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

Separate module for devman-client connection handling.

  • Property mode set to 100644
File size: 27.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/**
30 * @defgroup devman Device manager.
31 * @brief HelenOS device manager.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <inttypes.h>
39#include <assert.h>
40#include <ipc/services.h>
41#include <ns.h>
42#include <async.h>
43#include <stdio.h>
44#include <errno.h>
45#include <str_error.h>
46#include <stdbool.h>
47#include <fibril_synch.h>
48#include <stdlib.h>
49#include <str.h>
50#include <dirent.h>
51#include <fcntl.h>
52#include <sys/stat.h>
53#include <ctype.h>
54#include <io/log.h>
55#include <ipc/devman.h>
56#include <ipc/driver.h>
57#include <thread.h>
58#include <loc.h>
59
60#include "client_conn.h"
61#include "dev.h"
62#include "devman.h"
63#include "devtree.h"
64#include "driver.h"
65#include "fun.h"
66#include "loc.h"
67
68#define DRIVER_DEFAULT_STORE "/drv"
69
70static driver_list_t drivers_list;
71dev_tree_t device_tree;
72
73static int init_running_drv(void *drv);
74
75/** Register running driver. */
76static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
77{
78 driver_t *driver = NULL;
79 char *drv_name = NULL;
80
81 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
82
83 /* Get driver name. */
84 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
85 if (rc != EOK) {
86 async_answer_0(callid, rc);
87 return NULL;
88 }
89
90 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
91 drv_name);
92
93 /* Find driver structure. */
94 driver = find_driver(&drivers_list, drv_name);
95 if (driver == NULL) {
96 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
97 free(drv_name);
98 drv_name = NULL;
99 async_answer_0(callid, ENOENT);
100 return NULL;
101 }
102
103 free(drv_name);
104 drv_name = NULL;
105
106 fibril_mutex_lock(&driver->driver_mutex);
107
108 if (driver->sess) {
109 /* We already have a connection to the driver. */
110 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
111 driver->name);
112 fibril_mutex_unlock(&driver->driver_mutex);
113 async_answer_0(callid, EEXISTS);
114 return NULL;
115 }
116
117 switch (driver->state) {
118 case DRIVER_NOT_STARTED:
119 /* Somebody started the driver manually. */
120 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
121 driver->name);
122 driver->state = DRIVER_STARTING;
123 break;
124 case DRIVER_STARTING:
125 /* The expected case */
126 break;
127 case DRIVER_RUNNING:
128 /* Should not happen since we do not have a connected session */
129 assert(false);
130 }
131
132 /* Create connection to the driver. */
133 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
134 driver->name);
135 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
136 if (!driver->sess) {
137 fibril_mutex_unlock(&driver->driver_mutex);
138 async_answer_0(callid, ENOTSUP);
139 return NULL;
140 }
141 /* FIXME: Work around problem with callback sessions */
142 async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
143
144 log_msg(LOG_DEFAULT, LVL_NOTE,
145 "The `%s' driver was successfully registered as running.",
146 driver->name);
147
148 /*
149 * Initialize the driver as running (e.g. pass assigned devices to it)
150 * in a separate fibril; the separate fibril is used to enable the
151 * driver to use devman service during the driver's initialization.
152 */
153 fid_t fid = fibril_create(init_running_drv, driver);
154 if (fid == 0) {
155 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
156 "for driver `%s'.", driver->name);
157 fibril_mutex_unlock(&driver->driver_mutex);
158 async_answer_0(callid, ENOMEM);
159 return NULL;
160 }
161
162 fibril_add_ready(fid);
163 fibril_mutex_unlock(&driver->driver_mutex);
164
165 async_answer_0(callid, EOK);
166 return driver;
167}
168
169/** Receive device match ID from the device's parent driver and add it to the
170 * list of devices match ids.
171 *
172 * @param match_ids The list of the device's match ids.
173 * @return Zero on success, negative error code otherwise.
174 */
175static int devman_receive_match_id(match_id_list_t *match_ids)
176{
177 match_id_t *match_id = create_match_id();
178 ipc_callid_t callid;
179 ipc_call_t call;
180 int rc = 0;
181
182 callid = async_get_call(&call);
183 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
184 log_msg(LOG_DEFAULT, LVL_ERROR,
185 "Invalid protocol when trying to receive match id.");
186 async_answer_0(callid, EINVAL);
187 delete_match_id(match_id);
188 return EINVAL;
189 }
190
191 if (match_id == NULL) {
192 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
193 async_answer_0(callid, ENOMEM);
194 return ENOMEM;
195 }
196
197 async_answer_0(callid, EOK);
198
199 match_id->score = IPC_GET_ARG1(call);
200
201 char *match_id_str;
202 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
203 match_id->id = match_id_str;
204 if (rc != EOK) {
205 delete_match_id(match_id);
206 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
207 str_error(rc));
208 return rc;
209 }
210
211 list_append(&match_id->link, &match_ids->ids);
212
213 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
214 match_id->id, match_id->score);
215 return rc;
216}
217
218/** Receive device match IDs from the device's parent driver and add them to the
219 * list of devices match ids.
220 *
221 * @param match_count The number of device's match ids to be received.
222 * @param match_ids The list of the device's match ids.
223 * @return Zero on success, negative error code otherwise.
224 */
225static int devman_receive_match_ids(sysarg_t match_count,
226 match_id_list_t *match_ids)
227{
228 int ret = EOK;
229 size_t i;
230
231 for (i = 0; i < match_count; i++) {
232 if (EOK != (ret = devman_receive_match_id(match_ids)))
233 return ret;
234 }
235 return ret;
236}
237
238static int assign_driver_fibril(void *arg)
239{
240 dev_node_t *dev_node = (dev_node_t *) arg;
241 assign_driver(dev_node, &drivers_list, &device_tree);
242
243 /* Delete one reference we got from the caller. */
244 dev_del_ref(dev_node);
245 return EOK;
246}
247
248static int online_function(fun_node_t *fun)
249{
250 dev_node_t *dev;
251
252 fibril_rwlock_write_lock(&device_tree.rwlock);
253
254 if (fun->state == FUN_ON_LINE) {
255 fibril_rwlock_write_unlock(&device_tree.rwlock);
256 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
257 fun->pathname);
258 return EOK;
259 }
260
261 if (fun->ftype == fun_inner) {
262 dev = create_dev_node();
263 if (dev == NULL) {
264 fibril_rwlock_write_unlock(&device_tree.rwlock);
265 return ENOMEM;
266 }
267
268 insert_dev_node(&device_tree, dev, fun);
269 dev_add_ref(dev);
270 }
271
272 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
273
274 if (fun->ftype == fun_inner) {
275 dev = fun->child;
276 assert(dev != NULL);
277
278 /* Give one reference over to assign_driver_fibril(). */
279 dev_add_ref(dev);
280
281 /*
282 * Try to find a suitable driver and assign it to the device. We do
283 * not want to block the current fibril that is used for processing
284 * incoming calls: we will launch a separate fibril to handle the
285 * driver assigning. That is because assign_driver can actually include
286 * task spawning which could take some time.
287 */
288 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
289 if (assign_fibril == 0) {
290 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
291 "assigning driver.");
292 /* XXX Cleanup */
293 fibril_rwlock_write_unlock(&device_tree.rwlock);
294 return ENOMEM;
295 }
296 fibril_add_ready(assign_fibril);
297 } else
298 loc_register_tree_function(fun, &device_tree);
299
300 fibril_rwlock_write_unlock(&device_tree.rwlock);
301
302 return EOK;
303}
304
305static int offline_function(fun_node_t *fun)
306{
307 int rc;
308
309 fibril_rwlock_write_lock(&device_tree.rwlock);
310
311 if (fun->state == FUN_OFF_LINE) {
312 fibril_rwlock_write_unlock(&device_tree.rwlock);
313 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
314 fun->pathname);
315 return EOK;
316 }
317
318 if (fun->ftype == fun_inner) {
319 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
320 fun->pathname);
321
322 if (fun->child != NULL) {
323 dev_node_t *dev = fun->child;
324 device_state_t dev_state;
325
326 dev_add_ref(dev);
327 dev_state = dev->state;
328
329 fibril_rwlock_write_unlock(&device_tree.rwlock);
330
331 /* If device is owned by driver, ask driver to give it up. */
332 if (dev_state == DEVICE_USABLE) {
333 rc = driver_dev_remove(&device_tree, dev);
334 if (rc != EOK) {
335 dev_del_ref(dev);
336 return ENOTSUP;
337 }
338 }
339
340 /* Verify that driver removed all functions */
341 fibril_rwlock_read_lock(&device_tree.rwlock);
342 if (!list_empty(&dev->functions)) {
343 fibril_rwlock_read_unlock(&device_tree.rwlock);
344 dev_del_ref(dev);
345 return EIO;
346 }
347
348 driver_t *driver = dev->drv;
349 fibril_rwlock_read_unlock(&device_tree.rwlock);
350
351 if (driver)
352 detach_driver(&device_tree, dev);
353
354 fibril_rwlock_write_lock(&device_tree.rwlock);
355 remove_dev_node(&device_tree, dev);
356
357 /* Delete ref created when node was inserted */
358 dev_del_ref(dev);
359 /* Delete ref created by dev_add_ref(dev) above */
360 dev_del_ref(dev);
361 }
362 } else {
363 /* Unregister from location service */
364 rc = loc_service_unregister(fun->service_id);
365 if (rc != EOK) {
366 fibril_rwlock_write_unlock(&device_tree.rwlock);
367 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
368 return EIO;
369 }
370
371 fun->service_id = 0;
372 }
373
374 fun->state = FUN_OFF_LINE;
375 fibril_rwlock_write_unlock(&device_tree.rwlock);
376
377 return EOK;
378}
379
380/** Handle function registration.
381 *
382 * Child devices are registered by their parent's device driver.
383 */
384static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
385{
386 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
387 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
388 sysarg_t match_count = IPC_GET_ARG3(*call);
389 dev_tree_t *tree = &device_tree;
390
391 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
392 if (pdev == NULL) {
393 async_answer_0(callid, ENOENT);
394 return;
395 }
396
397 if (ftype != fun_inner && ftype != fun_exposed) {
398 /* Unknown function type */
399 log_msg(LOG_DEFAULT, LVL_ERROR,
400 "Unknown function type %d provided by driver.",
401 (int) ftype);
402
403 dev_del_ref(pdev);
404 async_answer_0(callid, EINVAL);
405 return;
406 }
407
408 char *fun_name = NULL;
409 int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
410 if (rc != EOK) {
411 dev_del_ref(pdev);
412 async_answer_0(callid, rc);
413 return;
414 }
415
416 fibril_rwlock_write_lock(&tree->rwlock);
417
418 /* Check device state */
419 if (pdev->state == DEVICE_REMOVED) {
420 fibril_rwlock_write_unlock(&tree->rwlock);
421 dev_del_ref(pdev);
422 async_answer_0(callid, ENOENT);
423 return;
424 }
425
426 /* Check that function with same name is not there already. */
427 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
428 if (tfun) {
429 fun_del_ref(tfun); /* drop the new unwanted reference */
430 fibril_rwlock_write_unlock(&tree->rwlock);
431 dev_del_ref(pdev);
432 async_answer_0(callid, EEXISTS);
433 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
434 fun_name);
435 free(fun_name);
436 return;
437 }
438
439 fun_node_t *fun = create_fun_node();
440 /* One reference for creation, one for us */
441 fun_add_ref(fun);
442 fun_add_ref(fun);
443 fun->ftype = ftype;
444
445 /*
446 * We can lock the function here even when holding the tree because
447 * we know it cannot be held by anyone else yet.
448 */
449 fun_busy_lock(fun);
450
451 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
452 fibril_rwlock_write_unlock(&tree->rwlock);
453 dev_del_ref(pdev);
454 fun_busy_unlock(fun);
455 fun_del_ref(fun);
456 delete_fun_node(fun);
457 async_answer_0(callid, ENOMEM);
458 return;
459 }
460
461 fibril_rwlock_write_unlock(&tree->rwlock);
462 dev_del_ref(pdev);
463
464 devman_receive_match_ids(match_count, &fun->match_ids);
465
466 rc = online_function(fun);
467 if (rc != EOK) {
468 /* XXX Set some failed state? */
469 fun_busy_unlock(fun);
470 fun_del_ref(fun);
471 async_answer_0(callid, rc);
472 return;
473 }
474
475 fun_busy_unlock(fun);
476 fun_del_ref(fun);
477
478 /* Return device handle to parent's driver. */
479 async_answer_1(callid, EOK, fun->handle);
480}
481
482static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
483{
484 devman_handle_t handle = IPC_GET_ARG1(*call);
485 category_id_t cat_id;
486 int rc;
487
488 /* Get category name. */
489 char *cat_name;
490 rc = async_data_write_accept((void **) &cat_name, true,
491 0, 0, 0, 0);
492 if (rc != EOK) {
493 async_answer_0(callid, rc);
494 return;
495 }
496
497 fun_node_t *fun = find_fun_node(&device_tree, handle);
498 if (fun == NULL) {
499 async_answer_0(callid, ENOENT);
500 return;
501 }
502
503 fibril_rwlock_read_lock(&device_tree.rwlock);
504
505 /* Check function state */
506 if (fun->state == FUN_REMOVED) {
507 fibril_rwlock_read_unlock(&device_tree.rwlock);
508 async_answer_0(callid, ENOENT);
509 return;
510 }
511
512 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
513 if (rc == EOK) {
514 loc_service_add_to_cat(fun->service_id, cat_id);
515 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
516 fun->pathname, cat_name);
517 } else {
518 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
519 "`%s'.", fun->pathname, cat_name);
520 }
521
522 fibril_rwlock_read_unlock(&device_tree.rwlock);
523 fun_del_ref(fun);
524
525 async_answer_0(callid, rc);
526}
527
528/** Online function by driver request.
529 *
530 */
531static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
532 driver_t *drv)
533{
534 fun_node_t *fun;
535 int rc;
536
537 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
538
539 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
540 if (fun == NULL) {
541 async_answer_0(iid, ENOENT);
542 return;
543 }
544
545 fun_busy_lock(fun);
546
547 fibril_rwlock_read_lock(&device_tree.rwlock);
548 if (fun->dev == NULL || fun->dev->drv != drv) {
549 fibril_rwlock_read_unlock(&device_tree.rwlock);
550 fun_busy_unlock(fun);
551 fun_del_ref(fun);
552 async_answer_0(iid, ENOENT);
553 return;
554 }
555 fibril_rwlock_read_unlock(&device_tree.rwlock);
556
557 rc = online_function(fun);
558 if (rc != EOK) {
559 fun_busy_unlock(fun);
560 fun_del_ref(fun);
561 async_answer_0(iid, (sysarg_t) rc);
562 return;
563 }
564
565 fun_busy_unlock(fun);
566 fun_del_ref(fun);
567
568 async_answer_0(iid, (sysarg_t) EOK);
569}
570
571
572/** Offline function by driver request.
573 *
574 */
575static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
576 driver_t *drv)
577{
578 fun_node_t *fun;
579 int rc;
580
581 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
582 if (fun == NULL) {
583 async_answer_0(iid, ENOENT);
584 return;
585 }
586
587 fun_busy_lock(fun);
588
589 fibril_rwlock_write_lock(&device_tree.rwlock);
590 if (fun->dev == NULL || fun->dev->drv != drv) {
591 fun_busy_unlock(fun);
592 fun_del_ref(fun);
593 async_answer_0(iid, ENOENT);
594 return;
595 }
596 fibril_rwlock_write_unlock(&device_tree.rwlock);
597
598 rc = offline_function(fun);
599 if (rc != EOK) {
600 fun_busy_unlock(fun);
601 fun_del_ref(fun);
602 async_answer_0(iid, (sysarg_t) rc);
603 return;
604 }
605
606 fun_busy_unlock(fun);
607 fun_del_ref(fun);
608 async_answer_0(iid, (sysarg_t) EOK);
609}
610
611/** Remove function. */
612static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
613{
614 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
615 dev_tree_t *tree = &device_tree;
616 int rc;
617
618 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
619 if (fun == NULL) {
620 async_answer_0(callid, ENOENT);
621 return;
622 }
623
624 fun_busy_lock(fun);
625
626 fibril_rwlock_write_lock(&tree->rwlock);
627
628 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
629
630 /* Check function state */
631 if (fun->state == FUN_REMOVED) {
632 fibril_rwlock_write_unlock(&tree->rwlock);
633 fun_busy_unlock(fun);
634 fun_del_ref(fun);
635 async_answer_0(callid, ENOENT);
636 return;
637 }
638
639 if (fun->ftype == fun_inner) {
640 /* This is a surprise removal. Handle possible descendants */
641 if (fun->child != NULL) {
642 dev_node_t *dev = fun->child;
643 device_state_t dev_state;
644 int gone_rc;
645
646 dev_add_ref(dev);
647 dev_state = dev->state;
648
649 fibril_rwlock_write_unlock(&device_tree.rwlock);
650
651 /* If device is owned by driver, inform driver it is gone. */
652 if (dev_state == DEVICE_USABLE)
653 gone_rc = driver_dev_gone(&device_tree, dev);
654 else
655 gone_rc = EOK;
656
657 fibril_rwlock_read_lock(&device_tree.rwlock);
658
659 /* Verify that driver succeeded and removed all functions */
660 if (gone_rc != EOK || !list_empty(&dev->functions)) {
661 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
662 "functions for device that is gone. "
663 "Device node is now defunct.");
664
665 /*
666 * Not much we can do but mark the device
667 * node as having invalid state. This
668 * is a driver bug.
669 */
670 dev->state = DEVICE_INVALID;
671 fibril_rwlock_read_unlock(&device_tree.rwlock);
672 dev_del_ref(dev);
673 if (gone_rc == EOK)
674 gone_rc = ENOTSUP;
675 fun_busy_unlock(fun);
676 fun_del_ref(fun);
677 async_answer_0(callid, gone_rc);
678 return;
679 }
680
681 driver_t *driver = dev->drv;
682 fibril_rwlock_read_unlock(&device_tree.rwlock);
683
684 if (driver)
685 detach_driver(&device_tree, dev);
686
687 fibril_rwlock_write_lock(&device_tree.rwlock);
688 remove_dev_node(&device_tree, dev);
689
690 /* Delete ref created when node was inserted */
691 dev_del_ref(dev);
692 /* Delete ref created by dev_add_ref(dev) above */
693 dev_del_ref(dev);
694 }
695 } else {
696 if (fun->service_id != 0) {
697 /* Unregister from location service */
698 rc = loc_service_unregister(fun->service_id);
699 if (rc != EOK) {
700 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
701 "service.");
702 fibril_rwlock_write_unlock(&tree->rwlock);
703 fun_busy_unlock(fun);
704 fun_del_ref(fun);
705 async_answer_0(callid, EIO);
706 return;
707 }
708 }
709 }
710
711 remove_fun_node(&device_tree, fun);
712 fibril_rwlock_write_unlock(&tree->rwlock);
713 fun_busy_unlock(fun);
714
715 /* Delete ref added when inserting function into tree */
716 fun_del_ref(fun);
717 /* Delete ref added above when looking up function */
718 fun_del_ref(fun);
719
720 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
721 async_answer_0(callid, EOK);
722}
723
724/** Initialize driver which has registered itself as running and ready.
725 *
726 * The initialization is done in a separate fibril to avoid deadlocks (if the
727 * driver needed to be served by devman during the driver's initialization).
728 */
729static int init_running_drv(void *drv)
730{
731 driver_t *driver = (driver_t *) drv;
732
733 initialize_running_driver(driver, &device_tree);
734 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
735 driver->name);
736 return 0;
737}
738
739/** Function for handling connections from a driver to the device manager. */
740static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
741{
742 client_t *client;
743 driver_t *driver = NULL;
744
745 /* Accept the connection. */
746 async_answer_0(iid, EOK);
747
748 client = async_get_client_data();
749 if (client == NULL) {
750 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
751 return;
752 }
753
754 while (true) {
755 ipc_call_t call;
756 ipc_callid_t callid = async_get_call(&call);
757
758 if (!IPC_GET_IMETHOD(call))
759 break;
760
761 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
762 fibril_mutex_lock(&client->mutex);
763 driver = client->driver;
764 fibril_mutex_unlock(&client->mutex);
765 if (driver == NULL) {
766 /* First call must be to DEVMAN_DRIVER_REGISTER */
767 async_answer_0(callid, ENOTSUP);
768 continue;
769 }
770 }
771
772 switch (IPC_GET_IMETHOD(call)) {
773 case DEVMAN_DRIVER_REGISTER:
774 fibril_mutex_lock(&client->mutex);
775 if (client->driver != NULL) {
776 fibril_mutex_unlock(&client->mutex);
777 async_answer_0(callid, EINVAL);
778 continue;
779 }
780 client->driver = devman_driver_register(callid, &call);
781 fibril_mutex_unlock(&client->mutex);
782 break;
783 case DEVMAN_ADD_FUNCTION:
784 devman_add_function(callid, &call);
785 break;
786 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
787 devman_add_function_to_cat(callid, &call);
788 break;
789 case DEVMAN_DRV_FUN_ONLINE:
790 devman_drv_fun_online(callid, &call, driver);
791 break;
792 case DEVMAN_DRV_FUN_OFFLINE:
793 devman_drv_fun_offline(callid, &call, driver);
794 break;
795 case DEVMAN_REMOVE_FUNCTION:
796 devman_remove_function(callid, &call);
797 break;
798 default:
799 async_answer_0(callid, EINVAL);
800 break;
801 }
802 }
803}
804
805static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
806 bool drv_to_parent)
807{
808 devman_handle_t handle = IPC_GET_ARG2(*icall);
809 devman_handle_t fwd_h;
810 fun_node_t *fun = NULL;
811 dev_node_t *dev = NULL;
812
813 fun = find_fun_node(&device_tree, handle);
814 if (fun == NULL)
815 dev = find_dev_node(&device_tree, handle);
816 else {
817 fibril_rwlock_read_lock(&device_tree.rwlock);
818 dev = fun->dev;
819 if (dev != NULL)
820 dev_add_ref(dev);
821 fibril_rwlock_read_unlock(&device_tree.rwlock);
822 }
823
824 /*
825 * For a valid function to connect to we need a device. The root
826 * function, for example, has no device and cannot be connected to.
827 * This means @c dev needs to be valid regardless whether we are
828 * connecting to a device or to a function.
829 */
830 if (dev == NULL) {
831 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding failed - no device or "
832 "function with handle %" PRIun " was found.", handle);
833 async_answer_0(iid, ENOENT);
834 goto cleanup;
835 }
836
837 if (fun == NULL && !drv_to_parent) {
838 log_msg(LOG_DEFAULT, LVL_ERROR, NAME ": devman_forward error - cannot "
839 "connect to handle %" PRIun ", refers to a device.",
840 handle);
841 async_answer_0(iid, ENOENT);
842 goto cleanup;
843 }
844
845 driver_t *driver = NULL;
846
847 fibril_rwlock_read_lock(&device_tree.rwlock);
848
849 if (drv_to_parent) {
850 /* Connect to parent function of a device (or device function). */
851 if (dev->pfun->dev != NULL)
852 driver = dev->pfun->dev->drv;
853
854 fwd_h = dev->pfun->handle;
855 } else if (dev->state == DEVICE_USABLE) {
856 /* Connect to the specified function */
857 driver = dev->drv;
858 assert(driver != NULL);
859
860 fwd_h = handle;
861 }
862
863 fibril_rwlock_read_unlock(&device_tree.rwlock);
864
865 if (driver == NULL) {
866 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding refused - " \
867 "the device %" PRIun " is not in usable state.", handle);
868 async_answer_0(iid, ENOENT);
869 goto cleanup;
870 }
871
872 int method;
873 if (drv_to_parent)
874 method = DRIVER_DRIVER;
875 else
876 method = DRIVER_CLIENT;
877
878 if (!driver->sess) {
879 log_msg(LOG_DEFAULT, LVL_ERROR,
880 "Could not forward to driver `%s'.", driver->name);
881 async_answer_0(iid, EINVAL);
882 goto cleanup;
883 }
884
885 if (fun != NULL) {
886 log_msg(LOG_DEFAULT, LVL_DEBUG,
887 "Forwarding request for `%s' function to driver `%s'.",
888 fun->pathname, driver->name);
889 } else {
890 log_msg(LOG_DEFAULT, LVL_DEBUG,
891 "Forwarding request for `%s' device to driver `%s'.",
892 dev->pfun->pathname, driver->name);
893 }
894
895 async_exch_t *exch = async_exchange_begin(driver->sess);
896 async_forward_fast(iid, exch, method, fwd_h, 0, IPC_FF_NONE);
897 async_exchange_end(exch);
898
899cleanup:
900 if (dev != NULL)
901 dev_del_ref(dev);
902
903 if (fun != NULL)
904 fun_del_ref(fun);
905}
906
907/** Function for handling connections from a client forwarded by the location
908 * service to the device manager. */
909static void devman_connection_loc(ipc_callid_t iid, ipc_call_t *icall)
910{
911 service_id_t service_id = IPC_GET_ARG2(*icall);
912 fun_node_t *fun;
913 dev_node_t *dev;
914 devman_handle_t handle;
915 driver_t *driver;
916
917 fun = find_loc_tree_function(&device_tree, service_id);
918
919 fibril_rwlock_read_lock(&device_tree.rwlock);
920
921 if (fun == NULL || fun->dev == NULL || fun->dev->drv == NULL) {
922 log_msg(LOG_DEFAULT, LVL_WARN, "devman_connection_loc(): function "
923 "not found.\n");
924 fibril_rwlock_read_unlock(&device_tree.rwlock);
925 async_answer_0(iid, ENOENT);
926 return;
927 }
928
929 dev = fun->dev;
930 driver = dev->drv;
931 handle = fun->handle;
932
933 fibril_rwlock_read_unlock(&device_tree.rwlock);
934
935 async_exch_t *exch = async_exchange_begin(driver->sess);
936 async_forward_fast(iid, exch, DRIVER_CLIENT, handle, 0,
937 IPC_FF_NONE);
938 async_exchange_end(exch);
939
940 log_msg(LOG_DEFAULT, LVL_DEBUG,
941 "Forwarding loc service request for `%s' function to driver `%s'.",
942 fun->pathname, driver->name);
943
944 fun_del_ref(fun);
945}
946
947/** Function for handling connections to device manager. */
948static void devman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
949{
950 /* Select port. */
951 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
952 case DEVMAN_DRIVER:
953 devman_connection_driver(iid, icall);
954 break;
955 case DEVMAN_CLIENT:
956 devman_connection_client(iid, icall);
957 break;
958 case DEVMAN_CONNECT_TO_DEVICE:
959 /* Connect client to selected device. */
960 devman_forward(iid, icall, false);
961 break;
962 case DEVMAN_CONNECT_FROM_LOC:
963 /* Someone connected through loc node. */
964 devman_connection_loc(iid, icall);
965 break;
966 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
967 /* Connect client to selected device. */
968 devman_forward(iid, icall, true);
969 break;
970 default:
971 /* No such interface */
972 async_answer_0(iid, ENOENT);
973 }
974}
975
976static void *devman_client_data_create(void)
977{
978 client_t *client;
979
980 client = calloc(1, sizeof(client_t));
981 if (client == NULL)
982 return NULL;
983
984 fibril_mutex_initialize(&client->mutex);
985 return client;
986}
987
988static void devman_client_data_destroy(void *data)
989{
990 free(data);
991}
992
993/** Initialize device manager internal structures. */
994static bool devman_init(void)
995{
996 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_init - looking for available drivers.");
997
998 /* Initialize list of available drivers. */
999 init_driver_list(&drivers_list);
1000 if (lookup_available_drivers(&drivers_list,
1001 DRIVER_DEFAULT_STORE) == 0) {
1002 log_msg(LOG_DEFAULT, LVL_FATAL, "No drivers found.");
1003 return false;
1004 }
1005
1006 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_init - list of drivers has been initialized.");
1007
1008 /* Create root device node. */
1009 if (!init_device_tree(&device_tree, &drivers_list)) {
1010 log_msg(LOG_DEFAULT, LVL_FATAL, "Failed to initialize device tree.");
1011 return false;
1012 }
1013
1014 /*
1015 * Caution: As the device manager is not a real loc
1016 * driver (it uses a completely different IPC protocol
1017 * than an ordinary loc driver), forwarding a connection
1018 * from client to the devman by location service will
1019 * not work.
1020 */
1021 loc_server_register(NAME);
1022
1023 return true;
1024}
1025
1026int main(int argc, char *argv[])
1027{
1028 printf("%s: HelenOS Device Manager\n", NAME);
1029
1030 int rc = log_init(NAME);
1031 if (rc != EOK) {
1032 printf("%s: Error initializing logging subsystem.\n", NAME);
1033 return rc;
1034 }
1035
1036 /* Set handlers for incoming connections. */
1037 async_set_client_data_constructor(devman_client_data_create);
1038 async_set_client_data_destructor(devman_client_data_destroy);
1039 async_set_client_connection(devman_connection);
1040
1041 if (!devman_init()) {
1042 log_msg(LOG_DEFAULT, LVL_ERROR, "Error while initializing service.");
1043 return -1;
1044 }
1045
1046 /* Register device manager at naming service. */
1047 rc = service_register(SERVICE_DEVMAN);
1048 if (rc != EOK) {
1049 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering as a service.");
1050 return rc;
1051 }
1052
1053 printf("%s: Accepting connections.\n", NAME);
1054 task_retval(0);
1055 async_manager();
1056
1057 /* Never reached. */
1058 return 0;
1059}
1060
1061/** @}
1062 */
Note: See TracBrowser for help on using the repository browser.