source: mainline/uspace/srv/devman/drv_conn.c@ 3083c74

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3083c74 was f89204ee, checked in by Jakub Jermar <jakub@…>, 7 years ago

Fix devman function reference counting

After commit 498ced18a4, create_fun_node() returns a fun pointer with an
implicit reference. Adding an extra reference for creation thus adds a
reference that will never be dropped and the function object will be
leaked.

This commit fixes the reference counting issue and also adds the missing
check to the call to insert_fun_node().

  • Property mode set to 100644
File size: 16.5 KB
RevLine 
[181c32f]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/**
[4122410]30 * @addtogroup devman
[181c32f]31 * @{
32 */
33
34/** @file
35 */
36
37#include <assert.h>
38#include <ipc/services.h>
39#include <ns.h>
40#include <async.h>
41#include <stdio.h>
42#include <errno.h>
43#include <str_error.h>
44#include <stdbool.h>
45#include <fibril_synch.h>
46#include <stdlib.h>
47#include <str.h>
48#include <io/log.h>
49#include <ipc/devman.h>
50#include <loc.h>
51
52#include "client_conn.h"
53#include "dev.h"
54#include "devman.h"
55#include "devtree.h"
56#include "driver.h"
57#include "drv_conn.h"
58#include "fun.h"
59#include "loc.h"
60#include "main.h"
61
[b7fd2a0]62static errno_t init_running_drv(void *drv);
[181c32f]63
64/** Register running driver. */
[984a9ba]65static driver_t *devman_driver_register(ipc_call_t *call)
[181c32f]66{
67 driver_t *driver = NULL;
68 char *drv_name = NULL;
69
70 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
[a35b458]71
[181c32f]72 /* Get driver name. */
[b7fd2a0]73 errno_t rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
[181c32f]74 if (rc != EOK) {
[984a9ba]75 async_answer_0(call, rc);
[181c32f]76 return NULL;
77 }
78
79 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
80 drv_name);
[a35b458]81
[181c32f]82 /* Find driver structure. */
[0511549]83 driver = driver_find_by_name(&drivers_list, drv_name);
[181c32f]84 if (driver == NULL) {
85 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
86 free(drv_name);
87 drv_name = NULL;
[984a9ba]88 async_answer_0(call, ENOENT);
[181c32f]89 return NULL;
90 }
[a35b458]91
[181c32f]92 free(drv_name);
93 drv_name = NULL;
[a35b458]94
[181c32f]95 fibril_mutex_lock(&driver->driver_mutex);
[a35b458]96
[181c32f]97 if (driver->sess) {
98 /* We already have a connection to the driver. */
99 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
100 driver->name);
101 fibril_mutex_unlock(&driver->driver_mutex);
[984a9ba]102 async_answer_0(call, EEXIST);
[181c32f]103 return NULL;
104 }
[a35b458]105
[181c32f]106 switch (driver->state) {
107 case DRIVER_NOT_STARTED:
108 /* Somebody started the driver manually. */
109 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
110 driver->name);
111 driver->state = DRIVER_STARTING;
112 break;
113 case DRIVER_STARTING:
114 /* The expected case */
115 break;
116 case DRIVER_RUNNING:
117 /* Should not happen since we do not have a connected session */
118 assert(false);
119 }
[a35b458]120
[181c32f]121 /* Create connection to the driver. */
122 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
123 driver->name);
124 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
125 if (!driver->sess) {
126 fibril_mutex_unlock(&driver->driver_mutex);
[984a9ba]127 async_answer_0(call, ENOTSUP);
[181c32f]128 return NULL;
129 }
130 /* FIXME: Work around problem with callback sessions */
[f9b2cb4c]131 async_sess_args_set(driver->sess, INTERFACE_DDF_DEVMAN, 0, 0);
[a35b458]132
[181c32f]133 log_msg(LOG_DEFAULT, LVL_NOTE,
134 "The `%s' driver was successfully registered as running.",
135 driver->name);
[a35b458]136
[181c32f]137 /*
138 * Initialize the driver as running (e.g. pass assigned devices to it)
139 * in a separate fibril; the separate fibril is used to enable the
140 * driver to use devman service during the driver's initialization.
141 */
142 fid_t fid = fibril_create(init_running_drv, driver);
143 if (fid == 0) {
[5ef16903]144 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril "
[181c32f]145 "for driver `%s'.", driver->name);
146 fibril_mutex_unlock(&driver->driver_mutex);
[984a9ba]147 async_answer_0(call, ENOMEM);
[181c32f]148 return NULL;
149 }
[a35b458]150
[181c32f]151 fibril_add_ready(fid);
152 fibril_mutex_unlock(&driver->driver_mutex);
[a35b458]153
[984a9ba]154 async_answer_0(call, EOK);
[181c32f]155 return driver;
156}
157
158/** Receive device match ID from the device's parent driver and add it to the
159 * list of devices match ids.
160 *
161 * @param match_ids The list of the device's match ids.
[cde999a]162 * @return Zero on success, error code otherwise.
[181c32f]163 */
[b7fd2a0]164static errno_t devman_receive_match_id(match_id_list_t *match_ids)
[181c32f]165{
166 match_id_t *match_id = create_match_id();
167 ipc_call_t call;
[b7fd2a0]168 errno_t rc = 0;
[a35b458]169
[984a9ba]170 async_get_call(&call);
[181c32f]171 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
[acb8766e]172 log_msg(LOG_DEFAULT, LVL_ERROR,
[181c32f]173 "Invalid protocol when trying to receive match id.");
[984a9ba]174 async_answer_0(&call, EINVAL);
[181c32f]175 delete_match_id(match_id);
176 return EINVAL;
177 }
[a35b458]178
[181c32f]179 if (match_id == NULL) {
180 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
[984a9ba]181 async_answer_0(&call, ENOMEM);
[181c32f]182 return ENOMEM;
183 }
[a35b458]184
[984a9ba]185 async_answer_0(&call, EOK);
[a35b458]186
[181c32f]187 match_id->score = IPC_GET_ARG1(call);
[a35b458]188
[181c32f]189 char *match_id_str;
190 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
191 match_id->id = match_id_str;
192 if (rc != EOK) {
193 delete_match_id(match_id);
194 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
195 str_error(rc));
196 return rc;
197 }
[a35b458]198
[181c32f]199 list_append(&match_id->link, &match_ids->ids);
[a35b458]200
[181c32f]201 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
202 match_id->id, match_id->score);
203 return rc;
204}
205
206/** Receive device match IDs from the device's parent driver and add them to the
207 * list of devices match ids.
208 *
209 * @param match_count The number of device's match ids to be received.
210 * @param match_ids The list of the device's match ids.
[cde999a]211 * @return Zero on success, error code otherwise.
[181c32f]212 */
[b7fd2a0]213static errno_t devman_receive_match_ids(sysarg_t match_count,
[181c32f]214 match_id_list_t *match_ids)
215{
[b7fd2a0]216 errno_t ret = EOK;
[181c32f]217 size_t i;
[a35b458]218
[181c32f]219 for (i = 0; i < match_count; i++) {
220 if (EOK != (ret = devman_receive_match_id(match_ids)))
221 return ret;
222 }
223 return ret;
224}
225
226/** Handle function registration.
227 *
228 * Child devices are registered by their parent's device driver.
229 */
[984a9ba]230static void devman_add_function(ipc_call_t *call)
[181c32f]231{
232 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
233 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
234 sysarg_t match_count = IPC_GET_ARG3(*call);
235 dev_tree_t *tree = &device_tree;
[a35b458]236
[181c32f]237 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
238 if (pdev == NULL) {
[984a9ba]239 async_answer_0(call, ENOENT);
[181c32f]240 return;
241 }
[a35b458]242
[181c32f]243 if (ftype != fun_inner && ftype != fun_exposed) {
244 /* Unknown function type */
[acb8766e]245 log_msg(LOG_DEFAULT, LVL_ERROR,
[181c32f]246 "Unknown function type %d provided by driver.",
247 (int) ftype);
248
249 dev_del_ref(pdev);
[984a9ba]250 async_answer_0(call, EINVAL);
[181c32f]251 return;
252 }
[a35b458]253
[181c32f]254 char *fun_name = NULL;
[984a9ba]255 errno_t rc = async_data_write_accept((void **) &fun_name, true, 0, 0, 0, 0);
[181c32f]256 if (rc != EOK) {
257 dev_del_ref(pdev);
[984a9ba]258 async_answer_0(call, rc);
[181c32f]259 return;
260 }
[a35b458]261
[181c32f]262 fibril_rwlock_write_lock(&tree->rwlock);
[a35b458]263
[181c32f]264 /* Check device state */
265 if (pdev->state == DEVICE_REMOVED) {
266 fibril_rwlock_write_unlock(&tree->rwlock);
267 dev_del_ref(pdev);
[984a9ba]268 async_answer_0(call, ENOENT);
[181c32f]269 return;
270 }
[a35b458]271
[181c32f]272 /* Check that function with same name is not there already. */
273 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
274 if (tfun) {
275 fun_del_ref(tfun); /* drop the new unwanted reference */
276 fibril_rwlock_write_unlock(&tree->rwlock);
277 dev_del_ref(pdev);
[984a9ba]278 async_answer_0(call, EEXIST);
[181c32f]279 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
280 fun_name);
281 free(fun_name);
282 return;
283 }
[a35b458]284
[181c32f]285 fun_node_t *fun = create_fun_node();
[f89204ee]286 /*
287 * Hold a temporary reference while we work with fun. The reference from
288 * create_fun_node() moves to the device tree.
289 */
[181c32f]290 fun_add_ref(fun);
291 fun->ftype = ftype;
[a35b458]292
[181c32f]293 /*
294 * We can lock the function here even when holding the tree because
295 * we know it cannot be held by anyone else yet.
296 */
297 fun_busy_lock(fun);
[a35b458]298
[181c32f]299 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
300 fibril_rwlock_write_unlock(&tree->rwlock);
301 dev_del_ref(pdev);
302 fun_busy_unlock(fun);
303 fun_del_ref(fun);
[f89204ee]304 fun_del_ref(fun); /* fun is destroyed */
[984a9ba]305 async_answer_0(call, ENOMEM);
[181c32f]306 return;
307 }
[a35b458]308
[181c32f]309 fibril_rwlock_write_unlock(&tree->rwlock);
310 dev_del_ref(pdev);
[a35b458]311
[181c32f]312 devman_receive_match_ids(match_count, &fun->match_ids);
[a35b458]313
[02e5e34]314 rc = fun_online(fun);
[181c32f]315 if (rc != EOK) {
316 /* XXX Set some failed state? */
317 fun_busy_unlock(fun);
318 fun_del_ref(fun);
[984a9ba]319 async_answer_0(call, rc);
[181c32f]320 return;
321 }
[a35b458]322
[181c32f]323 fun_busy_unlock(fun);
324 fun_del_ref(fun);
[a35b458]325
[181c32f]326 /* Return device handle to parent's driver. */
[984a9ba]327 async_answer_1(call, EOK, fun->handle);
[181c32f]328}
329
[984a9ba]330static void devman_add_function_to_cat(ipc_call_t *call)
[181c32f]331{
332 devman_handle_t handle = IPC_GET_ARG1(*call);
333 category_id_t cat_id;
[b7fd2a0]334 errno_t rc;
[a35b458]335
[181c32f]336 /* Get category name. */
337 char *cat_name;
338 rc = async_data_write_accept((void **) &cat_name, true,
339 0, 0, 0, 0);
340 if (rc != EOK) {
[984a9ba]341 async_answer_0(call, rc);
[181c32f]342 return;
343 }
[a35b458]344
[181c32f]345 fun_node_t *fun = find_fun_node(&device_tree, handle);
346 if (fun == NULL) {
[984a9ba]347 async_answer_0(call, ENOENT);
[181c32f]348 return;
349 }
[a35b458]350
[181c32f]351 fibril_rwlock_read_lock(&device_tree.rwlock);
[a35b458]352
[181c32f]353 /* Check function state */
354 if (fun->state == FUN_REMOVED) {
355 fibril_rwlock_read_unlock(&device_tree.rwlock);
[984a9ba]356 async_answer_0(call, ENOENT);
[181c32f]357 return;
358 }
[a35b458]359
[181c32f]360 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
[30785f1]361 if (rc == EOK)
362 rc = loc_service_add_to_cat(fun->service_id, cat_id);
[181c32f]363 if (rc == EOK) {
364 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
365 fun->pathname, cat_name);
366 } else {
367 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
368 "`%s'.", fun->pathname, cat_name);
369 }
[a35b458]370
[181c32f]371 fibril_rwlock_read_unlock(&device_tree.rwlock);
372 fun_del_ref(fun);
[a35b458]373
[984a9ba]374 async_answer_0(call, rc);
[181c32f]375}
376
377/** Online function by driver request.
378 *
379 */
[984a9ba]380static void devman_drv_fun_online(ipc_call_t *icall, driver_t *drv)
[181c32f]381{
382 fun_node_t *fun;
[b7fd2a0]383 errno_t rc;
[a35b458]384
[181c32f]385 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
[a35b458]386
[181c32f]387 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
388 if (fun == NULL) {
[984a9ba]389 async_answer_0(icall, ENOENT);
[181c32f]390 return;
391 }
[a35b458]392
[181c32f]393 fun_busy_lock(fun);
[a35b458]394
[181c32f]395 fibril_rwlock_read_lock(&device_tree.rwlock);
396 if (fun->dev == NULL || fun->dev->drv != drv) {
397 fibril_rwlock_read_unlock(&device_tree.rwlock);
398 fun_busy_unlock(fun);
399 fun_del_ref(fun);
[984a9ba]400 async_answer_0(icall, ENOENT);
[181c32f]401 return;
402 }
403 fibril_rwlock_read_unlock(&device_tree.rwlock);
[a35b458]404
[acb8766e]405 rc = fun_online(fun);
[181c32f]406 if (rc != EOK) {
407 fun_busy_unlock(fun);
408 fun_del_ref(fun);
[984a9ba]409 async_answer_0(icall, rc);
[181c32f]410 return;
411 }
[a35b458]412
[181c32f]413 fun_busy_unlock(fun);
414 fun_del_ref(fun);
[a35b458]415
[984a9ba]416 async_answer_0(icall, EOK);
[181c32f]417}
418
419/** Offline function by driver request.
420 *
421 */
[984a9ba]422static void devman_drv_fun_offline(ipc_call_t *icall, driver_t *drv)
[181c32f]423{
424 fun_node_t *fun;
[b7fd2a0]425 errno_t rc;
[181c32f]426
427 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
428 if (fun == NULL) {
[984a9ba]429 async_answer_0(icall, ENOENT);
[181c32f]430 return;
431 }
[a35b458]432
[181c32f]433 fun_busy_lock(fun);
[a35b458]434
[181c32f]435 fibril_rwlock_write_lock(&device_tree.rwlock);
436 if (fun->dev == NULL || fun->dev->drv != drv) {
437 fun_busy_unlock(fun);
438 fun_del_ref(fun);
[984a9ba]439 async_answer_0(icall, ENOENT);
[181c32f]440 return;
441 }
442 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]443
[02e5e34]444 rc = fun_offline(fun);
[181c32f]445 if (rc != EOK) {
446 fun_busy_unlock(fun);
447 fun_del_ref(fun);
[984a9ba]448 async_answer_0(icall, rc);
[181c32f]449 return;
450 }
[a35b458]451
[181c32f]452 fun_busy_unlock(fun);
453 fun_del_ref(fun);
[984a9ba]454 async_answer_0(icall, EOK);
[181c32f]455}
456
457/** Remove function. */
[984a9ba]458static void devman_remove_function(ipc_call_t *call)
[181c32f]459{
460 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
461 dev_tree_t *tree = &device_tree;
[b7fd2a0]462 errno_t rc;
[a35b458]463
[181c32f]464 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
465 if (fun == NULL) {
[984a9ba]466 async_answer_0(call, ENOENT);
[181c32f]467 return;
468 }
[a35b458]469
[181c32f]470 fun_busy_lock(fun);
[a35b458]471
[181c32f]472 fibril_rwlock_write_lock(&tree->rwlock);
[a35b458]473
[181c32f]474 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
[a35b458]475
[181c32f]476 /* Check function state */
477 if (fun->state == FUN_REMOVED) {
478 fibril_rwlock_write_unlock(&tree->rwlock);
479 fun_busy_unlock(fun);
480 fun_del_ref(fun);
[984a9ba]481 async_answer_0(call, ENOENT);
[181c32f]482 return;
483 }
[a35b458]484
[181c32f]485 if (fun->ftype == fun_inner) {
486 /* This is a surprise removal. Handle possible descendants */
487 if (fun->child != NULL) {
488 dev_node_t *dev = fun->child;
489 device_state_t dev_state;
[b7fd2a0]490 errno_t gone_rc;
[a35b458]491
[181c32f]492 dev_add_ref(dev);
493 dev_state = dev->state;
[a35b458]494
[181c32f]495 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]496
[181c32f]497 /* If device is owned by driver, inform driver it is gone. */
498 if (dev_state == DEVICE_USABLE)
499 gone_rc = driver_dev_gone(&device_tree, dev);
500 else
501 gone_rc = EOK;
[a35b458]502
[181c32f]503 fibril_rwlock_read_lock(&device_tree.rwlock);
[a35b458]504
[181c32f]505 /* Verify that driver succeeded and removed all functions */
506 if (gone_rc != EOK || !list_empty(&dev->functions)) {
507 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
508 "functions for device that is gone. "
509 "Device node is now defunct.");
[a35b458]510
[181c32f]511 /*
512 * Not much we can do but mark the device
513 * node as having invalid state. This
514 * is a driver bug.
515 */
516 dev->state = DEVICE_INVALID;
517 fibril_rwlock_read_unlock(&device_tree.rwlock);
518 dev_del_ref(dev);
519 if (gone_rc == EOK)
520 gone_rc = ENOTSUP;
521 fun_busy_unlock(fun);
522 fun_del_ref(fun);
[984a9ba]523 async_answer_0(call, gone_rc);
[181c32f]524 return;
525 }
[a35b458]526
[181c32f]527 driver_t *driver = dev->drv;
528 fibril_rwlock_read_unlock(&device_tree.rwlock);
[a35b458]529
[181c32f]530 if (driver)
531 detach_driver(&device_tree, dev);
[a35b458]532
[181c32f]533 fibril_rwlock_write_lock(&device_tree.rwlock);
534 remove_dev_node(&device_tree, dev);
[a35b458]535
[181c32f]536 /* Delete ref created when node was inserted */
537 dev_del_ref(dev);
538 /* Delete ref created by dev_add_ref(dev) above */
539 dev_del_ref(dev);
540 }
541 } else {
542 if (fun->service_id != 0) {
543 /* Unregister from location service */
[96ef672]544 rc = loc_unregister_tree_function(fun, &device_tree);
[181c32f]545 if (rc != EOK) {
546 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
547 "service.");
548 fibril_rwlock_write_unlock(&tree->rwlock);
549 fun_busy_unlock(fun);
550 fun_del_ref(fun);
[984a9ba]551 async_answer_0(call, EIO);
[181c32f]552 return;
553 }
554 }
555 }
[a35b458]556
[181c32f]557 remove_fun_node(&device_tree, fun);
558 fibril_rwlock_write_unlock(&tree->rwlock);
559 fun_busy_unlock(fun);
[a35b458]560
[181c32f]561 /* Delete ref added when inserting function into tree */
562 fun_del_ref(fun);
563 /* Delete ref added above when looking up function */
564 fun_del_ref(fun);
[a35b458]565
[181c32f]566 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
[984a9ba]567 async_answer_0(call, EOK);
[181c32f]568}
569
570/** Initialize driver which has registered itself as running and ready.
571 *
572 * The initialization is done in a separate fibril to avoid deadlocks (if the
573 * driver needed to be served by devman during the driver's initialization).
574 */
[b7fd2a0]575static errno_t init_running_drv(void *drv)
[181c32f]576{
577 driver_t *driver = (driver_t *) drv;
[a35b458]578
[181c32f]579 initialize_running_driver(driver, &device_tree);
580 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
581 driver->name);
582 return 0;
583}
584
585/** Function for handling connections from a driver to the device manager. */
[984a9ba]586void devman_connection_driver(ipc_call_t *icall, void *arg)
[181c32f]587{
588 client_t *client;
589 driver_t *driver = NULL;
[a35b458]590
[181c32f]591 /* Accept the connection. */
[beb83c1]592 async_accept_0(icall);
[a35b458]593
[181c32f]594 client = async_get_client_data();
595 if (client == NULL) {
596 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
597 return;
598 }
[a35b458]599
[181c32f]600 while (true) {
601 ipc_call_t call;
[984a9ba]602 async_get_call(&call);
[a35b458]603
[889cdb1]604 if (!IPC_GET_IMETHOD(call)) {
605 async_answer_0(&call, EOK);
[181c32f]606 break;
[889cdb1]607 }
[a35b458]608
[181c32f]609 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
610 fibril_mutex_lock(&client->mutex);
611 driver = client->driver;
612 fibril_mutex_unlock(&client->mutex);
613 if (driver == NULL) {
614 /* First call must be to DEVMAN_DRIVER_REGISTER */
[984a9ba]615 async_answer_0(&call, ENOTSUP);
[181c32f]616 continue;
617 }
618 }
[a35b458]619
[181c32f]620 switch (IPC_GET_IMETHOD(call)) {
621 case DEVMAN_DRIVER_REGISTER:
622 fibril_mutex_lock(&client->mutex);
623 if (client->driver != NULL) {
624 fibril_mutex_unlock(&client->mutex);
[984a9ba]625 async_answer_0(&call, EINVAL);
[181c32f]626 continue;
627 }
[984a9ba]628 client->driver = devman_driver_register(&call);
[181c32f]629 fibril_mutex_unlock(&client->mutex);
630 break;
631 case DEVMAN_ADD_FUNCTION:
[984a9ba]632 devman_add_function(&call);
[181c32f]633 break;
634 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
[984a9ba]635 devman_add_function_to_cat(&call);
[181c32f]636 break;
637 case DEVMAN_DRV_FUN_ONLINE:
[984a9ba]638 devman_drv_fun_online(&call, driver);
[181c32f]639 break;
640 case DEVMAN_DRV_FUN_OFFLINE:
[984a9ba]641 devman_drv_fun_offline(&call, driver);
[181c32f]642 break;
643 case DEVMAN_REMOVE_FUNCTION:
[984a9ba]644 devman_remove_function(&call);
[181c32f]645 break;
646 default:
[984a9ba]647 async_answer_0(&call, EINVAL);
[181c32f]648 break;
649 }
650 }
651}
652
653/** @}
654 */
Note: See TracBrowser for help on using the repository browser.