source: mainline/uspace/srv/devman/drv_conn.c@ 181c32f

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

Separate module for devman-driver communication.

  • Property mode set to 100644
File size: 20.5 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 "drv_conn.h"
66#include "fun.h"
67#include "loc.h"
68#include "main.h"
69
70static int init_running_drv(void *drv);
71
72/** Register running driver. */
73static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
74{
75 driver_t *driver = NULL;
76 char *drv_name = NULL;
77
78 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
79
80 /* Get driver name. */
81 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
82 if (rc != EOK) {
83 async_answer_0(callid, rc);
84 return NULL;
85 }
86
87 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
88 drv_name);
89
90 /* Find driver structure. */
91 driver = find_driver(&drivers_list, drv_name);
92 if (driver == NULL) {
93 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
94 free(drv_name);
95 drv_name = NULL;
96 async_answer_0(callid, ENOENT);
97 return NULL;
98 }
99
100 free(drv_name);
101 drv_name = NULL;
102
103 fibril_mutex_lock(&driver->driver_mutex);
104
105 if (driver->sess) {
106 /* We already have a connection to the driver. */
107 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
108 driver->name);
109 fibril_mutex_unlock(&driver->driver_mutex);
110 async_answer_0(callid, EEXISTS);
111 return NULL;
112 }
113
114 switch (driver->state) {
115 case DRIVER_NOT_STARTED:
116 /* Somebody started the driver manually. */
117 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
118 driver->name);
119 driver->state = DRIVER_STARTING;
120 break;
121 case DRIVER_STARTING:
122 /* The expected case */
123 break;
124 case DRIVER_RUNNING:
125 /* Should not happen since we do not have a connected session */
126 assert(false);
127 }
128
129 /* Create connection to the driver. */
130 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
131 driver->name);
132 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
133 if (!driver->sess) {
134 fibril_mutex_unlock(&driver->driver_mutex);
135 async_answer_0(callid, ENOTSUP);
136 return NULL;
137 }
138 /* FIXME: Work around problem with callback sessions */
139 async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
140
141 log_msg(LOG_DEFAULT, LVL_NOTE,
142 "The `%s' driver was successfully registered as running.",
143 driver->name);
144
145 /*
146 * Initialize the driver as running (e.g. pass assigned devices to it)
147 * in a separate fibril; the separate fibril is used to enable the
148 * driver to use devman service during the driver's initialization.
149 */
150 fid_t fid = fibril_create(init_running_drv, driver);
151 if (fid == 0) {
152 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
153 "for driver `%s'.", driver->name);
154 fibril_mutex_unlock(&driver->driver_mutex);
155 async_answer_0(callid, ENOMEM);
156 return NULL;
157 }
158
159 fibril_add_ready(fid);
160 fibril_mutex_unlock(&driver->driver_mutex);
161
162 async_answer_0(callid, EOK);
163 return driver;
164}
165
166/** Receive device match ID from the device's parent driver and add it to the
167 * list of devices match ids.
168 *
169 * @param match_ids The list of the device's match ids.
170 * @return Zero on success, negative error code otherwise.
171 */
172static int devman_receive_match_id(match_id_list_t *match_ids)
173{
174 match_id_t *match_id = create_match_id();
175 ipc_callid_t callid;
176 ipc_call_t call;
177 int rc = 0;
178
179 callid = async_get_call(&call);
180 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
181 log_msg(LOG_DEFAULT, LVL_ERROR,
182 "Invalid protocol when trying to receive match id.");
183 async_answer_0(callid, EINVAL);
184 delete_match_id(match_id);
185 return EINVAL;
186 }
187
188 if (match_id == NULL) {
189 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
190 async_answer_0(callid, ENOMEM);
191 return ENOMEM;
192 }
193
194 async_answer_0(callid, EOK);
195
196 match_id->score = IPC_GET_ARG1(call);
197
198 char *match_id_str;
199 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
200 match_id->id = match_id_str;
201 if (rc != EOK) {
202 delete_match_id(match_id);
203 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
204 str_error(rc));
205 return rc;
206 }
207
208 list_append(&match_id->link, &match_ids->ids);
209
210 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
211 match_id->id, match_id->score);
212 return rc;
213}
214
215/** Receive device match IDs from the device's parent driver and add them to the
216 * list of devices match ids.
217 *
218 * @param match_count The number of device's match ids to be received.
219 * @param match_ids The list of the device's match ids.
220 * @return Zero on success, negative error code otherwise.
221 */
222static int devman_receive_match_ids(sysarg_t match_count,
223 match_id_list_t *match_ids)
224{
225 int ret = EOK;
226 size_t i;
227
228 for (i = 0; i < match_count; i++) {
229 if (EOK != (ret = devman_receive_match_id(match_ids)))
230 return ret;
231 }
232 return ret;
233}
234
235static int assign_driver_fibril(void *arg)
236{
237 dev_node_t *dev_node = (dev_node_t *) arg;
238 assign_driver(dev_node, &drivers_list, &device_tree);
239
240 /* Delete one reference we got from the caller. */
241 dev_del_ref(dev_node);
242 return EOK;
243}
244
245static int online_function(fun_node_t *fun)
246{
247 dev_node_t *dev;
248
249 fibril_rwlock_write_lock(&device_tree.rwlock);
250
251 if (fun->state == FUN_ON_LINE) {
252 fibril_rwlock_write_unlock(&device_tree.rwlock);
253 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
254 fun->pathname);
255 return EOK;
256 }
257
258 if (fun->ftype == fun_inner) {
259 dev = create_dev_node();
260 if (dev == NULL) {
261 fibril_rwlock_write_unlock(&device_tree.rwlock);
262 return ENOMEM;
263 }
264
265 insert_dev_node(&device_tree, dev, fun);
266 dev_add_ref(dev);
267 }
268
269 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
270
271 if (fun->ftype == fun_inner) {
272 dev = fun->child;
273 assert(dev != NULL);
274
275 /* Give one reference over to assign_driver_fibril(). */
276 dev_add_ref(dev);
277
278 /*
279 * Try to find a suitable driver and assign it to the device. We do
280 * not want to block the current fibril that is used for processing
281 * incoming calls: we will launch a separate fibril to handle the
282 * driver assigning. That is because assign_driver can actually include
283 * task spawning which could take some time.
284 */
285 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
286 if (assign_fibril == 0) {
287 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
288 "assigning driver.");
289 /* XXX Cleanup */
290 fibril_rwlock_write_unlock(&device_tree.rwlock);
291 return ENOMEM;
292 }
293 fibril_add_ready(assign_fibril);
294 } else
295 loc_register_tree_function(fun, &device_tree);
296
297 fibril_rwlock_write_unlock(&device_tree.rwlock);
298
299 return EOK;
300}
301
302static int offline_function(fun_node_t *fun)
303{
304 int rc;
305
306 fibril_rwlock_write_lock(&device_tree.rwlock);
307
308 if (fun->state == FUN_OFF_LINE) {
309 fibril_rwlock_write_unlock(&device_tree.rwlock);
310 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
311 fun->pathname);
312 return EOK;
313 }
314
315 if (fun->ftype == fun_inner) {
316 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
317 fun->pathname);
318
319 if (fun->child != NULL) {
320 dev_node_t *dev = fun->child;
321 device_state_t dev_state;
322
323 dev_add_ref(dev);
324 dev_state = dev->state;
325
326 fibril_rwlock_write_unlock(&device_tree.rwlock);
327
328 /* If device is owned by driver, ask driver to give it up. */
329 if (dev_state == DEVICE_USABLE) {
330 rc = driver_dev_remove(&device_tree, dev);
331 if (rc != EOK) {
332 dev_del_ref(dev);
333 return ENOTSUP;
334 }
335 }
336
337 /* Verify that driver removed all functions */
338 fibril_rwlock_read_lock(&device_tree.rwlock);
339 if (!list_empty(&dev->functions)) {
340 fibril_rwlock_read_unlock(&device_tree.rwlock);
341 dev_del_ref(dev);
342 return EIO;
343 }
344
345 driver_t *driver = dev->drv;
346 fibril_rwlock_read_unlock(&device_tree.rwlock);
347
348 if (driver)
349 detach_driver(&device_tree, dev);
350
351 fibril_rwlock_write_lock(&device_tree.rwlock);
352 remove_dev_node(&device_tree, dev);
353
354 /* Delete ref created when node was inserted */
355 dev_del_ref(dev);
356 /* Delete ref created by dev_add_ref(dev) above */
357 dev_del_ref(dev);
358 }
359 } else {
360 /* Unregister from location service */
361 rc = loc_service_unregister(fun->service_id);
362 if (rc != EOK) {
363 fibril_rwlock_write_unlock(&device_tree.rwlock);
364 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
365 return EIO;
366 }
367
368 fun->service_id = 0;
369 }
370
371 fun->state = FUN_OFF_LINE;
372 fibril_rwlock_write_unlock(&device_tree.rwlock);
373
374 return EOK;
375}
376
377/** Handle function registration.
378 *
379 * Child devices are registered by their parent's device driver.
380 */
381static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
382{
383 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
384 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
385 sysarg_t match_count = IPC_GET_ARG3(*call);
386 dev_tree_t *tree = &device_tree;
387
388 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
389 if (pdev == NULL) {
390 async_answer_0(callid, ENOENT);
391 return;
392 }
393
394 if (ftype != fun_inner && ftype != fun_exposed) {
395 /* Unknown function type */
396 log_msg(LOG_DEFAULT, LVL_ERROR,
397 "Unknown function type %d provided by driver.",
398 (int) ftype);
399
400 dev_del_ref(pdev);
401 async_answer_0(callid, EINVAL);
402 return;
403 }
404
405 char *fun_name = NULL;
406 int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
407 if (rc != EOK) {
408 dev_del_ref(pdev);
409 async_answer_0(callid, rc);
410 return;
411 }
412
413 fibril_rwlock_write_lock(&tree->rwlock);
414
415 /* Check device state */
416 if (pdev->state == DEVICE_REMOVED) {
417 fibril_rwlock_write_unlock(&tree->rwlock);
418 dev_del_ref(pdev);
419 async_answer_0(callid, ENOENT);
420 return;
421 }
422
423 /* Check that function with same name is not there already. */
424 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
425 if (tfun) {
426 fun_del_ref(tfun); /* drop the new unwanted reference */
427 fibril_rwlock_write_unlock(&tree->rwlock);
428 dev_del_ref(pdev);
429 async_answer_0(callid, EEXISTS);
430 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
431 fun_name);
432 free(fun_name);
433 return;
434 }
435
436 fun_node_t *fun = create_fun_node();
437 /* One reference for creation, one for us */
438 fun_add_ref(fun);
439 fun_add_ref(fun);
440 fun->ftype = ftype;
441
442 /*
443 * We can lock the function here even when holding the tree because
444 * we know it cannot be held by anyone else yet.
445 */
446 fun_busy_lock(fun);
447
448 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
449 fibril_rwlock_write_unlock(&tree->rwlock);
450 dev_del_ref(pdev);
451 fun_busy_unlock(fun);
452 fun_del_ref(fun);
453 delete_fun_node(fun);
454 async_answer_0(callid, ENOMEM);
455 return;
456 }
457
458 fibril_rwlock_write_unlock(&tree->rwlock);
459 dev_del_ref(pdev);
460
461 devman_receive_match_ids(match_count, &fun->match_ids);
462
463 rc = online_function(fun);
464 if (rc != EOK) {
465 /* XXX Set some failed state? */
466 fun_busy_unlock(fun);
467 fun_del_ref(fun);
468 async_answer_0(callid, rc);
469 return;
470 }
471
472 fun_busy_unlock(fun);
473 fun_del_ref(fun);
474
475 /* Return device handle to parent's driver. */
476 async_answer_1(callid, EOK, fun->handle);
477}
478
479static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
480{
481 devman_handle_t handle = IPC_GET_ARG1(*call);
482 category_id_t cat_id;
483 int rc;
484
485 /* Get category name. */
486 char *cat_name;
487 rc = async_data_write_accept((void **) &cat_name, true,
488 0, 0, 0, 0);
489 if (rc != EOK) {
490 async_answer_0(callid, rc);
491 return;
492 }
493
494 fun_node_t *fun = find_fun_node(&device_tree, handle);
495 if (fun == NULL) {
496 async_answer_0(callid, ENOENT);
497 return;
498 }
499
500 fibril_rwlock_read_lock(&device_tree.rwlock);
501
502 /* Check function state */
503 if (fun->state == FUN_REMOVED) {
504 fibril_rwlock_read_unlock(&device_tree.rwlock);
505 async_answer_0(callid, ENOENT);
506 return;
507 }
508
509 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
510 if (rc == EOK) {
511 loc_service_add_to_cat(fun->service_id, cat_id);
512 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
513 fun->pathname, cat_name);
514 } else {
515 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
516 "`%s'.", fun->pathname, cat_name);
517 }
518
519 fibril_rwlock_read_unlock(&device_tree.rwlock);
520 fun_del_ref(fun);
521
522 async_answer_0(callid, rc);
523}
524
525/** Online function by driver request.
526 *
527 */
528static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
529 driver_t *drv)
530{
531 fun_node_t *fun;
532 int rc;
533
534 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
535
536 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
537 if (fun == NULL) {
538 async_answer_0(iid, ENOENT);
539 return;
540 }
541
542 fun_busy_lock(fun);
543
544 fibril_rwlock_read_lock(&device_tree.rwlock);
545 if (fun->dev == NULL || fun->dev->drv != drv) {
546 fibril_rwlock_read_unlock(&device_tree.rwlock);
547 fun_busy_unlock(fun);
548 fun_del_ref(fun);
549 async_answer_0(iid, ENOENT);
550 return;
551 }
552 fibril_rwlock_read_unlock(&device_tree.rwlock);
553
554 rc = online_function(fun);
555 if (rc != EOK) {
556 fun_busy_unlock(fun);
557 fun_del_ref(fun);
558 async_answer_0(iid, (sysarg_t) rc);
559 return;
560 }
561
562 fun_busy_unlock(fun);
563 fun_del_ref(fun);
564
565 async_answer_0(iid, (sysarg_t) EOK);
566}
567
568
569/** Offline function by driver request.
570 *
571 */
572static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
573 driver_t *drv)
574{
575 fun_node_t *fun;
576 int rc;
577
578 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
579 if (fun == NULL) {
580 async_answer_0(iid, ENOENT);
581 return;
582 }
583
584 fun_busy_lock(fun);
585
586 fibril_rwlock_write_lock(&device_tree.rwlock);
587 if (fun->dev == NULL || fun->dev->drv != drv) {
588 fun_busy_unlock(fun);
589 fun_del_ref(fun);
590 async_answer_0(iid, ENOENT);
591 return;
592 }
593 fibril_rwlock_write_unlock(&device_tree.rwlock);
594
595 rc = offline_function(fun);
596 if (rc != EOK) {
597 fun_busy_unlock(fun);
598 fun_del_ref(fun);
599 async_answer_0(iid, (sysarg_t) rc);
600 return;
601 }
602
603 fun_busy_unlock(fun);
604 fun_del_ref(fun);
605 async_answer_0(iid, (sysarg_t) EOK);
606}
607
608/** Remove function. */
609static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
610{
611 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
612 dev_tree_t *tree = &device_tree;
613 int rc;
614
615 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
616 if (fun == NULL) {
617 async_answer_0(callid, ENOENT);
618 return;
619 }
620
621 fun_busy_lock(fun);
622
623 fibril_rwlock_write_lock(&tree->rwlock);
624
625 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
626
627 /* Check function state */
628 if (fun->state == FUN_REMOVED) {
629 fibril_rwlock_write_unlock(&tree->rwlock);
630 fun_busy_unlock(fun);
631 fun_del_ref(fun);
632 async_answer_0(callid, ENOENT);
633 return;
634 }
635
636 if (fun->ftype == fun_inner) {
637 /* This is a surprise removal. Handle possible descendants */
638 if (fun->child != NULL) {
639 dev_node_t *dev = fun->child;
640 device_state_t dev_state;
641 int gone_rc;
642
643 dev_add_ref(dev);
644 dev_state = dev->state;
645
646 fibril_rwlock_write_unlock(&device_tree.rwlock);
647
648 /* If device is owned by driver, inform driver it is gone. */
649 if (dev_state == DEVICE_USABLE)
650 gone_rc = driver_dev_gone(&device_tree, dev);
651 else
652 gone_rc = EOK;
653
654 fibril_rwlock_read_lock(&device_tree.rwlock);
655
656 /* Verify that driver succeeded and removed all functions */
657 if (gone_rc != EOK || !list_empty(&dev->functions)) {
658 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
659 "functions for device that is gone. "
660 "Device node is now defunct.");
661
662 /*
663 * Not much we can do but mark the device
664 * node as having invalid state. This
665 * is a driver bug.
666 */
667 dev->state = DEVICE_INVALID;
668 fibril_rwlock_read_unlock(&device_tree.rwlock);
669 dev_del_ref(dev);
670 if (gone_rc == EOK)
671 gone_rc = ENOTSUP;
672 fun_busy_unlock(fun);
673 fun_del_ref(fun);
674 async_answer_0(callid, gone_rc);
675 return;
676 }
677
678 driver_t *driver = dev->drv;
679 fibril_rwlock_read_unlock(&device_tree.rwlock);
680
681 if (driver)
682 detach_driver(&device_tree, dev);
683
684 fibril_rwlock_write_lock(&device_tree.rwlock);
685 remove_dev_node(&device_tree, dev);
686
687 /* Delete ref created when node was inserted */
688 dev_del_ref(dev);
689 /* Delete ref created by dev_add_ref(dev) above */
690 dev_del_ref(dev);
691 }
692 } else {
693 if (fun->service_id != 0) {
694 /* Unregister from location service */
695 rc = loc_service_unregister(fun->service_id);
696 if (rc != EOK) {
697 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
698 "service.");
699 fibril_rwlock_write_unlock(&tree->rwlock);
700 fun_busy_unlock(fun);
701 fun_del_ref(fun);
702 async_answer_0(callid, EIO);
703 return;
704 }
705 }
706 }
707
708 remove_fun_node(&device_tree, fun);
709 fibril_rwlock_write_unlock(&tree->rwlock);
710 fun_busy_unlock(fun);
711
712 /* Delete ref added when inserting function into tree */
713 fun_del_ref(fun);
714 /* Delete ref added above when looking up function */
715 fun_del_ref(fun);
716
717 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
718 async_answer_0(callid, EOK);
719}
720
721/** Initialize driver which has registered itself as running and ready.
722 *
723 * The initialization is done in a separate fibril to avoid deadlocks (if the
724 * driver needed to be served by devman during the driver's initialization).
725 */
726static int init_running_drv(void *drv)
727{
728 driver_t *driver = (driver_t *) drv;
729
730 initialize_running_driver(driver, &device_tree);
731 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
732 driver->name);
733 return 0;
734}
735
736/** Function for handling connections from a driver to the device manager. */
737void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
738{
739 client_t *client;
740 driver_t *driver = NULL;
741
742 /* Accept the connection. */
743 async_answer_0(iid, EOK);
744
745 client = async_get_client_data();
746 if (client == NULL) {
747 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
748 return;
749 }
750
751 while (true) {
752 ipc_call_t call;
753 ipc_callid_t callid = async_get_call(&call);
754
755 if (!IPC_GET_IMETHOD(call))
756 break;
757
758 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
759 fibril_mutex_lock(&client->mutex);
760 driver = client->driver;
761 fibril_mutex_unlock(&client->mutex);
762 if (driver == NULL) {
763 /* First call must be to DEVMAN_DRIVER_REGISTER */
764 async_answer_0(callid, ENOTSUP);
765 continue;
766 }
767 }
768
769 switch (IPC_GET_IMETHOD(call)) {
770 case DEVMAN_DRIVER_REGISTER:
771 fibril_mutex_lock(&client->mutex);
772 if (client->driver != NULL) {
773 fibril_mutex_unlock(&client->mutex);
774 async_answer_0(callid, EINVAL);
775 continue;
776 }
777 client->driver = devman_driver_register(callid, &call);
778 fibril_mutex_unlock(&client->mutex);
779 break;
780 case DEVMAN_ADD_FUNCTION:
781 devman_add_function(callid, &call);
782 break;
783 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
784 devman_add_function_to_cat(callid, &call);
785 break;
786 case DEVMAN_DRV_FUN_ONLINE:
787 devman_drv_fun_online(callid, &call, driver);
788 break;
789 case DEVMAN_DRV_FUN_OFFLINE:
790 devman_drv_fun_offline(callid, &call, driver);
791 break;
792 case DEVMAN_REMOVE_FUNCTION:
793 devman_remove_function(callid, &call);
794 break;
795 default:
796 async_answer_0(callid, EINVAL);
797 break;
798 }
799 }
800}
801
802/** @}
803 */
Note: See TracBrowser for help on using the repository browser.