source: mainline/uspace/srv/devman/main.c@ 16f9782

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

Skeleton of devctl utility. Currently prints device tree.

  • Property mode set to 100644
File size: 22.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/**
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 <bool.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 "devman.h"
61
62#define DRIVER_DEFAULT_STORE "/drv"
63
64static driver_list_t drivers_list;
65static dev_tree_t device_tree;
66
67/** Register running driver. */
68static driver_t *devman_driver_register(void)
69{
70 ipc_call_t icall;
71 ipc_callid_t iid;
72 driver_t *driver = NULL;
73
74 log_msg(LVL_DEBUG, "devman_driver_register");
75
76 iid = async_get_call(&icall);
77 if (IPC_GET_IMETHOD(icall) != DEVMAN_DRIVER_REGISTER) {
78 async_answer_0(iid, EREFUSED);
79 return NULL;
80 }
81
82 char *drv_name = NULL;
83
84 /* Get driver name. */
85 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
86 if (rc != EOK) {
87 async_answer_0(iid, rc);
88 return NULL;
89 }
90
91 log_msg(LVL_DEBUG, "The `%s' driver is trying to register.",
92 drv_name);
93
94 /* Find driver structure. */
95 driver = find_driver(&drivers_list, drv_name);
96 if (driver == NULL) {
97 log_msg(LVL_ERROR, "No driver named `%s' was found.", drv_name);
98 free(drv_name);
99 drv_name = NULL;
100 async_answer_0(iid, ENOENT);
101 return NULL;
102 }
103
104 free(drv_name);
105 drv_name = NULL;
106
107 fibril_mutex_lock(&driver->driver_mutex);
108
109 if (driver->sess) {
110 /* We already have a connection to the driver. */
111 log_msg(LVL_ERROR, "Driver '%s' already started.\n",
112 driver->name);
113 fibril_mutex_unlock(&driver->driver_mutex);
114 async_answer_0(iid, EEXISTS);
115 return NULL;
116 }
117
118 switch (driver->state) {
119 case DRIVER_NOT_STARTED:
120 /* Somebody started the driver manually. */
121 log_msg(LVL_NOTE, "Driver '%s' started manually.\n",
122 driver->name);
123 driver->state = DRIVER_STARTING;
124 break;
125 case DRIVER_STARTING:
126 /* The expected case */
127 break;
128 case DRIVER_RUNNING:
129 /* Should not happen since we do not have a connected session */
130 assert(false);
131 }
132
133 /* Create connection to the driver. */
134 log_msg(LVL_DEBUG, "Creating connection to the `%s' driver.",
135 driver->name);
136 driver->sess = async_callback_receive(EXCHANGE_SERIALIZE);
137 if (!driver->sess) {
138 fibril_mutex_unlock(&driver->driver_mutex);
139 async_answer_0(iid, ENOTSUP);
140 return NULL;
141 }
142
143 fibril_mutex_unlock(&driver->driver_mutex);
144
145 log_msg(LVL_NOTE,
146 "The `%s' driver was successfully registered as running.",
147 driver->name);
148
149 async_answer_0(iid, EOK);
150
151 return driver;
152}
153
154/** Receive device match ID from the device's parent driver and add it to the
155 * list of devices match ids.
156 *
157 * @param match_ids The list of the device's match ids.
158 * @return Zero on success, negative error code otherwise.
159 */
160static int devman_receive_match_id(match_id_list_t *match_ids)
161{
162 match_id_t *match_id = create_match_id();
163 ipc_callid_t callid;
164 ipc_call_t call;
165 int rc = 0;
166
167 callid = async_get_call(&call);
168 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
169 log_msg(LVL_ERROR,
170 "Invalid protocol when trying to receive match id.");
171 async_answer_0(callid, EINVAL);
172 delete_match_id(match_id);
173 return EINVAL;
174 }
175
176 if (match_id == NULL) {
177 log_msg(LVL_ERROR, "Failed to allocate match id.");
178 async_answer_0(callid, ENOMEM);
179 return ENOMEM;
180 }
181
182 async_answer_0(callid, EOK);
183
184 match_id->score = IPC_GET_ARG1(call);
185
186 char *match_id_str;
187 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
188 match_id->id = match_id_str;
189 if (rc != EOK) {
190 delete_match_id(match_id);
191 log_msg(LVL_ERROR, "Failed to receive match id string: %s.",
192 str_error(rc));
193 return rc;
194 }
195
196 list_append(&match_id->link, &match_ids->ids);
197
198 log_msg(LVL_DEBUG, "Received match id `%s', score %d.",
199 match_id->id, match_id->score);
200 return rc;
201}
202
203/** Receive device match IDs from the device's parent driver and add them to the
204 * list of devices match ids.
205 *
206 * @param match_count The number of device's match ids to be received.
207 * @param match_ids The list of the device's match ids.
208 * @return Zero on success, negative error code otherwise.
209 */
210static int devman_receive_match_ids(sysarg_t match_count,
211 match_id_list_t *match_ids)
212{
213 int ret = EOK;
214 size_t i;
215
216 for (i = 0; i < match_count; i++) {
217 if (EOK != (ret = devman_receive_match_id(match_ids)))
218 return ret;
219 }
220 return ret;
221}
222
223static int assign_driver_fibril(void *arg)
224{
225 dev_node_t *dev_node = (dev_node_t *) arg;
226 assign_driver(dev_node, &drivers_list, &device_tree);
227 return EOK;
228}
229
230/** Handle function registration.
231 *
232 * Child devices are registered by their parent's device driver.
233 */
234static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
235{
236 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
237 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
238 sysarg_t match_count = IPC_GET_ARG3(*call);
239 dev_tree_t *tree = &device_tree;
240
241 fibril_rwlock_write_lock(&tree->rwlock);
242
243 dev_node_t *dev = NULL;
244 dev_node_t *pdev = find_dev_node_no_lock(&device_tree, dev_handle);
245
246 if (pdev == NULL) {
247 fibril_rwlock_write_unlock(&tree->rwlock);
248 async_answer_0(callid, ENOENT);
249 return;
250 }
251
252 if (ftype != fun_inner && ftype != fun_exposed) {
253 /* Unknown function type */
254 log_msg(LVL_ERROR,
255 "Unknown function type %d provided by driver.",
256 (int) ftype);
257
258 fibril_rwlock_write_unlock(&tree->rwlock);
259 async_answer_0(callid, EINVAL);
260 return;
261 }
262
263 char *fun_name = NULL;
264 int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
265 if (rc != EOK) {
266 fibril_rwlock_write_unlock(&tree->rwlock);
267 async_answer_0(callid, rc);
268 return;
269 }
270
271 /* Check that function with same name is not there already. */
272 if (find_fun_node_in_device(pdev, fun_name) != NULL) {
273 fibril_rwlock_write_unlock(&tree->rwlock);
274 async_answer_0(callid, EEXISTS);
275 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
276 fun_name);
277 free(fun_name);
278 return;
279 }
280
281 fun_node_t *fun = create_fun_node();
282 fun->ftype = ftype;
283
284 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
285 fibril_rwlock_write_unlock(&tree->rwlock);
286 delete_fun_node(fun);
287 async_answer_0(callid, ENOMEM);
288 return;
289 }
290
291 if (ftype == fun_inner) {
292 dev = create_dev_node();
293 if (dev == NULL) {
294 fibril_rwlock_write_unlock(&tree->rwlock);
295 delete_fun_node(fun);
296 async_answer_0(callid, ENOMEM);
297 return;
298 }
299
300 insert_dev_node(tree, dev, fun);
301 }
302
303 fibril_rwlock_write_unlock(&tree->rwlock);
304
305 log_msg(LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
306
307 devman_receive_match_ids(match_count, &fun->match_ids);
308
309 if (ftype == fun_inner) {
310 assert(dev != NULL);
311 /*
312 * Try to find a suitable driver and assign it to the device. We do
313 * not want to block the current fibril that is used for processing
314 * incoming calls: we will launch a separate fibril to handle the
315 * driver assigning. That is because assign_driver can actually include
316 * task spawning which could take some time.
317 */
318 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
319 if (assign_fibril == 0) {
320 /*
321 * Fallback in case we are out of memory.
322 * Probably not needed as we will die soon anyway ;-).
323 */
324 (void) assign_driver_fibril(fun);
325 } else {
326 fibril_add_ready(assign_fibril);
327 }
328 } else {
329 loc_register_tree_function(fun, tree);
330 }
331
332 /* Return device handle to parent's driver. */
333 async_answer_1(callid, EOK, fun->handle);
334}
335
336static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
337{
338 devman_handle_t handle = IPC_GET_ARG1(*call);
339 category_id_t cat_id;
340 int rc;
341
342 /* Get category name. */
343 char *cat_name;
344 rc = async_data_write_accept((void **) &cat_name, true,
345 0, 0, 0, 0);
346 if (rc != EOK) {
347 async_answer_0(callid, rc);
348 return;
349 }
350
351 fun_node_t *fun = find_fun_node(&device_tree, handle);
352 if (fun == NULL) {
353 async_answer_0(callid, ENOENT);
354 return;
355 }
356
357 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
358 if (rc == EOK) {
359 loc_service_add_to_cat(fun->service_id, cat_id);
360 } else {
361 log_msg(LVL_ERROR, "Failed adding function `%s' to category "
362 "`%s'.", fun->pathname, cat_name);
363 }
364
365 log_msg(LVL_NOTE, "Function `%s' added to category `%s'.",
366 fun->pathname, cat_name);
367
368 async_answer_0(callid, EOK);
369}
370
371/** Remove function. */
372static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
373{
374 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
375 dev_tree_t *tree = &device_tree;
376 int rc;
377
378 fibril_rwlock_write_lock(&tree->rwlock);
379
380 fun_node_t *fun = find_fun_node_no_lock(&device_tree, fun_handle);
381 if (fun == NULL) {
382 fibril_rwlock_write_unlock(&tree->rwlock);
383 async_answer_0(callid, ENOENT);
384 return;
385 }
386
387 log_msg(LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
388
389 if (fun->ftype == fun_inner) {
390 /* Handle possible descendants */
391 /* TODO */
392 log_msg(LVL_WARN, "devman_remove_function(): not handling "
393 "descendants\n");
394 } else {
395 /* Unregister from location service */
396 rc = loc_service_unregister(fun->service_id);
397 if (rc != EOK) {
398 log_msg(LVL_ERROR, "Failed unregistering tree service.");
399 fibril_rwlock_write_unlock(&tree->rwlock);
400 async_answer_0(callid, EIO);
401 return;
402 }
403 }
404
405 remove_fun_node(&device_tree, fun);
406 fibril_rwlock_write_unlock(&tree->rwlock);
407 delete_fun_node(fun);
408
409 log_msg(LVL_DEBUG, "devman_remove_function() succeeded.");
410 async_answer_0(callid, EOK);
411}
412
413/** Initialize driver which has registered itself as running and ready.
414 *
415 * The initialization is done in a separate fibril to avoid deadlocks (if the
416 * driver needed to be served by devman during the driver's initialization).
417 */
418static int init_running_drv(void *drv)
419{
420 driver_t *driver = (driver_t *) drv;
421
422 initialize_running_driver(driver, &device_tree);
423 log_msg(LVL_DEBUG, "The `%s` driver was successfully initialized.",
424 driver->name);
425 return 0;
426}
427
428/** Function for handling connections from a driver to the device manager. */
429static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
430{
431 /* Accept the connection. */
432 async_answer_0(iid, EOK);
433
434 driver_t *driver = devman_driver_register();
435 if (driver == NULL)
436 return;
437
438 /*
439 * Initialize the driver as running (e.g. pass assigned devices to it)
440 * in a separate fibril; the separate fibril is used to enable the
441 * driver to use devman service during the driver's initialization.
442 */
443 fid_t fid = fibril_create(init_running_drv, driver);
444 if (fid == 0) {
445 log_msg(LVL_ERROR, "Failed to create initialization fibril " \
446 "for driver `%s'.", driver->name);
447 return;
448 }
449 fibril_add_ready(fid);
450
451 while (true) {
452 ipc_call_t call;
453 ipc_callid_t callid = async_get_call(&call);
454
455 if (!IPC_GET_IMETHOD(call))
456 break;
457
458 switch (IPC_GET_IMETHOD(call)) {
459 case DEVMAN_ADD_FUNCTION:
460 devman_add_function(callid, &call);
461 break;
462 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
463 devman_add_function_to_cat(callid, &call);
464 break;
465 case DEVMAN_REMOVE_FUNCTION:
466 devman_remove_function(callid, &call);
467 break;
468 default:
469 async_answer_0(callid, EINVAL);
470 break;
471 }
472 }
473}
474
475/** Find handle for the device instance identified by the device's path in the
476 * device tree. */
477static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
478{
479 char *pathname;
480
481 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
482 if (rc != EOK) {
483 async_answer_0(iid, rc);
484 return;
485 }
486
487 fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
488
489 free(pathname);
490
491 if (fun == NULL) {
492 async_answer_0(iid, ENOENT);
493 return;
494 }
495
496 async_answer_1(iid, EOK, fun->handle);
497}
498
499/** Get device name. */
500static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
501{
502 devman_handle_t handle = IPC_GET_ARG1(*icall);
503
504 fun_node_t *fun = find_fun_node(&device_tree, handle);
505 if (fun == NULL) {
506 async_answer_0(iid, ENOMEM);
507 return;
508 }
509
510 ipc_callid_t data_callid;
511 size_t data_len;
512 if (!async_data_read_receive(&data_callid, &data_len)) {
513 async_answer_0(iid, EINVAL);
514 return;
515 }
516
517 void *buffer = malloc(data_len);
518 if (buffer == NULL) {
519 async_answer_0(data_callid, ENOMEM);
520 async_answer_0(iid, ENOMEM);
521 return;
522 }
523
524 size_t sent_length = str_size(fun->name);
525 if (sent_length > data_len) {
526 sent_length = data_len;
527 }
528
529 async_data_read_finalize(data_callid, fun->name, sent_length);
530 async_answer_0(iid, EOK);
531
532 free(buffer);
533}
534
535
536/** Get device path. */
537static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
538{
539 devman_handle_t handle = IPC_GET_ARG1(*icall);
540
541 fun_node_t *fun = find_fun_node(&device_tree, handle);
542 if (fun == NULL) {
543 async_answer_0(iid, ENOMEM);
544 return;
545 }
546
547 ipc_callid_t data_callid;
548 size_t data_len;
549 if (!async_data_read_receive(&data_callid, &data_len)) {
550 async_answer_0(iid, EINVAL);
551 return;
552 }
553
554 void *buffer = malloc(data_len);
555 if (buffer == NULL) {
556 async_answer_0(data_callid, ENOMEM);
557 async_answer_0(iid, ENOMEM);
558 return;
559 }
560
561 size_t sent_length = str_size(fun->pathname);
562 if (sent_length > data_len) {
563 sent_length = data_len;
564 }
565
566 async_data_read_finalize(data_callid, fun->pathname, sent_length);
567 async_answer_0(iid, EOK);
568
569 free(buffer);
570}
571
572static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
573{
574 ipc_callid_t callid;
575 size_t size;
576 size_t act_size;
577 int rc;
578
579 if (!async_data_read_receive(&callid, &size)) {
580 async_answer_0(callid, EREFUSED);
581 async_answer_0(iid, EREFUSED);
582 return;
583 }
584
585 fibril_rwlock_read_lock(&device_tree.rwlock);
586
587 dev_node_t *dev = find_dev_node_no_lock(&device_tree,
588 IPC_GET_ARG1(*icall));
589 if (dev == NULL) {
590 fibril_rwlock_read_unlock(&device_tree.rwlock);
591 async_answer_0(callid, ENOENT);
592 async_answer_0(iid, ENOENT);
593 return;
594 }
595
596 devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
597 if (hdl_buf == NULL) {
598 fibril_rwlock_read_unlock(&device_tree.rwlock);
599 async_answer_0(callid, ENOMEM);
600 async_answer_0(iid, ENOMEM);
601 return;
602 }
603
604 rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
605 if (rc != EOK) {
606 fibril_rwlock_read_unlock(&device_tree.rwlock);
607 async_answer_0(callid, rc);
608 async_answer_0(iid, rc);
609 return;
610 }
611
612 fibril_rwlock_read_unlock(&device_tree.rwlock);
613
614 sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
615 free(hdl_buf);
616
617 async_answer_1(iid, retval, act_size);
618}
619
620
621/** Get handle for child device of a function. */
622static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
623{
624 fun_node_t *fun;
625
626 fibril_rwlock_read_lock(&device_tree.rwlock);
627
628 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
629 if (fun == NULL) {
630 fibril_rwlock_read_unlock(&device_tree.rwlock);
631 async_answer_0(iid, ENOENT);
632 return;
633 }
634
635 if (fun->child == NULL) {
636 fibril_rwlock_read_unlock(&device_tree.rwlock);
637 async_answer_0(iid, ENOENT);
638 return;
639 }
640
641 async_answer_1(iid, EOK, fun->child->handle);
642
643 fibril_rwlock_read_unlock(&device_tree.rwlock);
644}
645
646/** Find handle for the function instance identified by its service ID. */
647static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
648{
649 fun_node_t *fun;
650
651 fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
652
653 if (fun == NULL) {
654 async_answer_0(iid, ENOENT);
655 return;
656 }
657
658 async_answer_1(iid, EOK, fun->handle);
659}
660
661/** Function for handling connections from a client to the device manager. */
662static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
663{
664 /* Accept connection. */
665 async_answer_0(iid, EOK);
666
667 while (true) {
668 ipc_call_t call;
669 ipc_callid_t callid = async_get_call(&call);
670
671 if (!IPC_GET_IMETHOD(call))
672 break;
673
674 switch (IPC_GET_IMETHOD(call)) {
675 case DEVMAN_DEVICE_GET_HANDLE:
676 devman_function_get_handle(callid, &call);
677 break;
678 case DEVMAN_DEV_GET_FUNCTIONS:
679 devman_dev_get_functions(callid, &call);
680 break;
681 case DEVMAN_FUN_GET_CHILD:
682 devman_fun_get_child(callid, &call);
683 break;
684 case DEVMAN_FUN_GET_NAME:
685 devman_fun_get_name(callid, &call);
686 break;
687 case DEVMAN_FUN_GET_PATH:
688 devman_fun_get_path(callid, &call);
689 break;
690 case DEVMAN_FUN_SID_TO_HANDLE:
691 devman_fun_sid_to_handle(callid, &call);
692 break;
693 default:
694 async_answer_0(callid, ENOENT);
695 }
696 }
697}
698
699static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
700 bool drv_to_parent)
701{
702 devman_handle_t handle = IPC_GET_ARG2(*icall);
703 devman_handle_t fwd_h;
704 fun_node_t *fun = NULL;
705 dev_node_t *dev = NULL;
706
707 fun = find_fun_node(&device_tree, handle);
708 if (fun == NULL)
709 dev = find_dev_node(&device_tree, handle);
710 else
711 dev = fun->dev;
712
713 /*
714 * For a valid function to connect to we need a device. The root
715 * function, for example, has no device and cannot be connected to.
716 * This means @c dev needs to be valid regardless whether we are
717 * connecting to a device or to a function.
718 */
719 if (dev == NULL) {
720 log_msg(LVL_ERROR, "IPC forwarding failed - no device or "
721 "function with handle %" PRIun " was found.", handle);
722 async_answer_0(iid, ENOENT);
723 return;
724 }
725
726 if (fun == NULL && !drv_to_parent) {
727 log_msg(LVL_ERROR, NAME ": devman_forward error - cannot "
728 "connect to handle %" PRIun ", refers to a device.",
729 handle);
730 async_answer_0(iid, ENOENT);
731 return;
732 }
733
734 driver_t *driver = NULL;
735
736 if (drv_to_parent) {
737 /* Connect to parent function of a device (or device function). */
738 if (dev->pfun->dev != NULL)
739 driver = dev->pfun->dev->drv;
740 fwd_h = dev->pfun->handle;
741 } else if (dev->state == DEVICE_USABLE) {
742 /* Connect to the specified function */
743 driver = dev->drv;
744 assert(driver != NULL);
745
746 fwd_h = handle;
747 }
748
749 if (driver == NULL) {
750 log_msg(LVL_ERROR, "IPC forwarding refused - " \
751 "the device %" PRIun " is not in usable state.", handle);
752 async_answer_0(iid, ENOENT);
753 return;
754 }
755
756 int method;
757 if (drv_to_parent)
758 method = DRIVER_DRIVER;
759 else
760 method = DRIVER_CLIENT;
761
762 if (!driver->sess) {
763 log_msg(LVL_ERROR,
764 "Could not forward to driver `%s'.", driver->name);
765 async_answer_0(iid, EINVAL);
766 return;
767 }
768
769 if (fun != NULL) {
770 log_msg(LVL_DEBUG,
771 "Forwarding request for `%s' function to driver `%s'.",
772 fun->pathname, driver->name);
773 } else {
774 log_msg(LVL_DEBUG,
775 "Forwarding request for `%s' device to driver `%s'.",
776 dev->pfun->pathname, driver->name);
777 }
778
779 async_exch_t *exch = async_exchange_begin(driver->sess);
780 async_forward_fast(iid, exch, method, fwd_h, 0, IPC_FF_NONE);
781 async_exchange_end(exch);
782}
783
784/** Function for handling connections from a client forwarded by the location
785 * service to the device manager. */
786static void devman_connection_loc(ipc_callid_t iid, ipc_call_t *icall)
787{
788 service_id_t service_id = IPC_GET_ARG2(*icall);
789 fun_node_t *fun;
790 dev_node_t *dev;
791
792 fun = find_loc_tree_function(&device_tree, service_id);
793
794 if (fun == NULL || fun->dev->drv == NULL) {
795 log_msg(LVL_WARN, "devman_connection_loc(): function "
796 "not found.\n");
797 async_answer_0(iid, ENOENT);
798 return;
799 }
800
801 dev = fun->dev;
802
803 async_exch_t *exch = async_exchange_begin(dev->drv->sess);
804 async_forward_fast(iid, exch, DRIVER_CLIENT, fun->handle, 0,
805 IPC_FF_NONE);
806 async_exchange_end(exch);
807
808 log_msg(LVL_DEBUG,
809 "Forwarding loc service request for `%s' function to driver `%s'.",
810 fun->pathname, dev->drv->name);
811}
812
813/** Function for handling connections to device manager. */
814static void devman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
815{
816 /* Select interface. */
817 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
818 case DEVMAN_DRIVER:
819 devman_connection_driver(iid, icall);
820 break;
821 case DEVMAN_CLIENT:
822 devman_connection_client(iid, icall);
823 break;
824 case DEVMAN_CONNECT_TO_DEVICE:
825 /* Connect client to selected device. */
826 devman_forward(iid, icall, false);
827 break;
828 case DEVMAN_CONNECT_FROM_LOC:
829 /* Someone connected through loc node. */
830 devman_connection_loc(iid, icall);
831 break;
832 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
833 /* Connect client to selected device. */
834 devman_forward(iid, icall, true);
835 break;
836 default:
837 /* No such interface */
838 async_answer_0(iid, ENOENT);
839 }
840}
841
842/** Initialize device manager internal structures. */
843static bool devman_init(void)
844{
845 log_msg(LVL_DEBUG, "devman_init - looking for available drivers.");
846
847 /* Initialize list of available drivers. */
848 init_driver_list(&drivers_list);
849 if (lookup_available_drivers(&drivers_list,
850 DRIVER_DEFAULT_STORE) == 0) {
851 log_msg(LVL_FATAL, "No drivers found.");
852 return false;
853 }
854
855 log_msg(LVL_DEBUG, "devman_init - list of drivers has been initialized.");
856
857 /* Create root device node. */
858 if (!init_device_tree(&device_tree, &drivers_list)) {
859 log_msg(LVL_FATAL, "Failed to initialize device tree.");
860 return false;
861 }
862
863 /*
864 * !!! devman_connection ... as the device manager is not a real loc
865 * driver (it uses a completely different ipc protocol than an ordinary
866 * loc driver) forwarding a connection from client to the devman by
867 * location service would not work.
868 */
869 loc_server_register(NAME, devman_connection);
870
871 return true;
872}
873
874int main(int argc, char *argv[])
875{
876 printf(NAME ": HelenOS Device Manager\n");
877
878 if (log_init(NAME, LVL_WARN) != EOK) {
879 printf(NAME ": Error initializing logging subsystem.\n");
880 return -1;
881 }
882
883 if (!devman_init()) {
884 log_msg(LVL_ERROR, "Error while initializing service.");
885 return -1;
886 }
887
888 /* Set a handler of incomming connections. */
889 async_set_client_connection(devman_connection);
890
891 /* Register device manager at naming service. */
892 if (service_register(SERVICE_DEVMAN) != EOK) {
893 log_msg(LVL_ERROR, "Failed registering as a service.");
894 return -1;
895 }
896
897 printf(NAME ": Accepting connections.\n");
898 task_retval(0);
899 async_manager();
900
901 /* Never reached. */
902 return 0;
903}
904
905/** @}
906 */
Note: See TracBrowser for help on using the repository browser.