source: mainline/uspace/srv/devman/main.c@ 041b026

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

Move driver-related devman functionality to a separate module.

  • Property mode set to 100644
File size: 36.3 KB
RevLine 
[e2b9a993]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
[7e752b2]38#include <inttypes.h>
[e2b9a993]39#include <assert.h>
40#include <ipc/services.h>
[79ae36dd]41#include <ns.h>
[e2b9a993]42#include <async.h>
43#include <stdio.h>
44#include <errno.h>
[9b415c9]45#include <str_error.h>
[3e6a98c5]46#include <stdbool.h>
[e2b9a993]47#include <fibril_synch.h>
48#include <stdlib.h>
[c47e1a8]49#include <str.h>
[e2b9a993]50#include <dirent.h>
51#include <fcntl.h>
52#include <sys/stat.h>
53#include <ctype.h>
[9b415c9]54#include <io/log.h>
[e2b9a993]55#include <ipc/devman.h>
[5cd136ab]56#include <ipc/driver.h>
[d347b53]57#include <thread.h>
[15f3c3f]58#include <loc.h>
[e2b9a993]59
60#include "devman.h"
[041b026]61#include "driver.h"
[e2b9a993]62
[a79d88d]63#define DRIVER_DEFAULT_STORE "/drv"
[e2b9a993]64
[0c3666d]65static driver_list_t drivers_list;
[e2b9a993]66static dev_tree_t device_tree;
67
[422722e]68static int init_running_drv(void *drv);
69
[38b3baf]70/** Register running driver. */
[422722e]71static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
[38b3baf]72{
[729fa2d6]73 driver_t *driver = NULL;
[422722e]74 char *drv_name = NULL;
[c6c389ed]75
[a1a101d]76 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
[729fa2d6]77
[38b3baf]78 /* Get driver name. */
79 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
[729fa2d6]80 if (rc != EOK) {
[422722e]81 async_answer_0(callid, rc);
[729fa2d6]82 return NULL;
83 }
[c6c389ed]84
[a1a101d]85 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
[38b3baf]86 drv_name);
[729fa2d6]87
[38b3baf]88 /* Find driver structure. */
[729fa2d6]89 driver = find_driver(&drivers_list, drv_name);
[c6c389ed]90 if (driver == NULL) {
[a1a101d]91 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
[04c7003f]92 free(drv_name);
93 drv_name = NULL;
[422722e]94 async_answer_0(callid, ENOENT);
[729fa2d6]95 return NULL;
96 }
97
[04c7003f]98 free(drv_name);
99 drv_name = NULL;
100
[3ad7b1c]101 fibril_mutex_lock(&driver->driver_mutex);
102
[79ae36dd]103 if (driver->sess) {
[3ad7b1c]104 /* We already have a connection to the driver. */
[a1a101d]105 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
[3ad7b1c]106 driver->name);
107 fibril_mutex_unlock(&driver->driver_mutex);
[422722e]108 async_answer_0(callid, EEXISTS);
[3ad7b1c]109 return NULL;
110 }
111
112 switch (driver->state) {
113 case DRIVER_NOT_STARTED:
114 /* Somebody started the driver manually. */
[a1a101d]115 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
[3ad7b1c]116 driver->name);
117 driver->state = DRIVER_STARTING;
118 break;
119 case DRIVER_STARTING:
120 /* The expected case */
121 break;
122 case DRIVER_RUNNING:
[79ae36dd]123 /* Should not happen since we do not have a connected session */
[3ad7b1c]124 assert(false);
125 }
126
[38b3baf]127 /* Create connection to the driver. */
[a1a101d]128 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
[9b415c9]129 driver->name);
[422722e]130 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
[79ae36dd]131 if (!driver->sess) {
[3ad7b1c]132 fibril_mutex_unlock(&driver->driver_mutex);
[422722e]133 async_answer_0(callid, ENOTSUP);
[729fa2d6]134 return NULL;
135 }
[422722e]136 /* FIXME: Work around problem with callback sessions */
137 async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
[729fa2d6]138
[a1a101d]139 log_msg(LOG_DEFAULT, LVL_NOTE,
[ebcb05a]140 "The `%s' driver was successfully registered as running.",
[38b3baf]141 driver->name);
[729fa2d6]142
[422722e]143 /*
144 * Initialize the driver as running (e.g. pass assigned devices to it)
145 * in a separate fibril; the separate fibril is used to enable the
146 * driver to use devman service during the driver's initialization.
147 */
148 fid_t fid = fibril_create(init_running_drv, driver);
149 if (fid == 0) {
[a1a101d]150 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
[422722e]151 "for driver `%s'.", driver->name);
152 fibril_mutex_unlock(&driver->driver_mutex);
153 async_answer_0(callid, ENOMEM);
154 return NULL;
155 }
[729fa2d6]156
[422722e]157 fibril_add_ready(fid);
158 fibril_mutex_unlock(&driver->driver_mutex);
159
160 async_answer_0(callid, EOK);
[729fa2d6]161 return driver;
162}
[e2b9a993]163
[38b3baf]164/** Receive device match ID from the device's parent driver and add it to the
165 * list of devices match ids.
166 *
167 * @param match_ids The list of the device's match ids.
168 * @return Zero on success, negative error code otherwise.
[3843ecb]169 */
[38b3baf]170static int devman_receive_match_id(match_id_list_t *match_ids)
171{
[66babbd]172 match_id_t *match_id = create_match_id();
173 ipc_callid_t callid;
174 ipc_call_t call;
175 int rc = 0;
176
177 callid = async_get_call(&call);
[228e490]178 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
[a1a101d]179 log_msg(LOG_DEFAULT, LVL_ERROR,
[ebcb05a]180 "Invalid protocol when trying to receive match id.");
[ffa2c8ef]181 async_answer_0(callid, EINVAL);
[66babbd]182 delete_match_id(match_id);
183 return EINVAL;
184 }
185
[c6c389ed]186 if (match_id == NULL) {
[a1a101d]187 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
[ffa2c8ef]188 async_answer_0(callid, ENOMEM);
[66babbd]189 return ENOMEM;
190 }
191
[ffa2c8ef]192 async_answer_0(callid, EOK);
[2480e19]193
[66babbd]194 match_id->score = IPC_GET_ARG1(call);
195
[c47e1a8]196 char *match_id_str;
[38b3baf]197 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
[c47e1a8]198 match_id->id = match_id_str;
[c6c389ed]199 if (rc != EOK) {
[66babbd]200 delete_match_id(match_id);
[a1a101d]201 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
[9b415c9]202 str_error(rc));
[66babbd]203 return rc;
204 }
205
206 list_append(&match_id->link, &match_ids->ids);
[a087f2e]207
[a1a101d]208 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
[38b3baf]209 match_id->id, match_id->score);
[66babbd]210 return rc;
211}
212
[38b3baf]213/** Receive device match IDs from the device's parent driver and add them to the
214 * list of devices match ids.
215 *
216 * @param match_count The number of device's match ids to be received.
217 * @param match_ids The list of the device's match ids.
218 * @return Zero on success, negative error code otherwise.
[3843ecb]219 */
[96b02eb9]220static int devman_receive_match_ids(sysarg_t match_count,
[c6c389ed]221 match_id_list_t *match_ids)
[38b3baf]222{
[66babbd]223 int ret = EOK;
[5cd136ab]224 size_t i;
[38b3baf]225
[66babbd]226 for (i = 0; i < match_count; i++) {
[38b3baf]227 if (EOK != (ret = devman_receive_match_id(match_ids)))
[66babbd]228 return ret;
229 }
230 return ret;
231}
232
[398c4d7]233static int assign_driver_fibril(void *arg)
234{
[ba38f72c]235 dev_node_t *dev_node = (dev_node_t *) arg;
236 assign_driver(dev_node, &drivers_list, &device_tree);
[c1a0488]237
238 /* Delete one reference we got from the caller. */
239 dev_del_ref(dev_node);
[398c4d7]240 return EOK;
241}
242
[1a5b252]243static int online_function(fun_node_t *fun)
244{
245 dev_node_t *dev;
246
247 fibril_rwlock_write_lock(&device_tree.rwlock);
[50ad3f3]248
[c1a0488]249 if (fun->state == FUN_ON_LINE) {
250 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a1a101d]251 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
[c1a0488]252 fun->pathname);
253 return EOK;
254 }
255
[1a5b252]256 if (fun->ftype == fun_inner) {
257 dev = create_dev_node();
258 if (dev == NULL) {
259 fibril_rwlock_write_unlock(&device_tree.rwlock);
260 return ENOMEM;
261 }
[50ad3f3]262
[1a5b252]263 insert_dev_node(&device_tree, dev, fun);
[58cbb0c8]264 dev_add_ref(dev);
[1a5b252]265 }
266
[a1a101d]267 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
[1a5b252]268
269 if (fun->ftype == fun_inner) {
270 dev = fun->child;
271 assert(dev != NULL);
272
[c1a0488]273 /* Give one reference over to assign_driver_fibril(). */
274 dev_add_ref(dev);
[50ad3f3]275
[1a5b252]276 /*
277 * Try to find a suitable driver and assign it to the device. We do
278 * not want to block the current fibril that is used for processing
279 * incoming calls: we will launch a separate fibril to handle the
280 * driver assigning. That is because assign_driver can actually include
281 * task spawning which could take some time.
282 */
283 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
284 if (assign_fibril == 0) {
[a1a101d]285 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
[c1a0488]286 "assigning driver.");
287 /* XXX Cleanup */
288 fibril_rwlock_write_unlock(&device_tree.rwlock);
289 return ENOMEM;
[1a5b252]290 }
[c1a0488]291 fibril_add_ready(assign_fibril);
[50ad3f3]292 } else
[1a5b252]293 loc_register_tree_function(fun, &device_tree);
294
[58cbb0c8]295 fibril_rwlock_write_unlock(&device_tree.rwlock);
296
[1a5b252]297 return EOK;
298}
299
300static int offline_function(fun_node_t *fun)
301{
302 int rc;
303
[58cbb0c8]304 fibril_rwlock_write_lock(&device_tree.rwlock);
305
[c1a0488]306 if (fun->state == FUN_OFF_LINE) {
307 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a1a101d]308 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
[c1a0488]309 fun->pathname);
310 return EOK;
311 }
312
[1a5b252]313 if (fun->ftype == fun_inner) {
[a1a101d]314 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
[224c0e7]315 fun->pathname);
316
[1a5b252]317 if (fun->child != NULL) {
318 dev_node_t *dev = fun->child;
[80a96d2]319 device_state_t dev_state;
[1a5b252]320
[58cbb0c8]321 dev_add_ref(dev);
[80a96d2]322 dev_state = dev->state;
[58cbb0c8]323
[80a96d2]324 fibril_rwlock_write_unlock(&device_tree.rwlock);
325
[f278930]326 /* If device is owned by driver, ask driver to give it up. */
[80a96d2]327 if (dev_state == DEVICE_USABLE) {
[f278930]328 rc = driver_dev_remove(&device_tree, dev);
329 if (rc != EOK) {
330 dev_del_ref(dev);
331 return ENOTSUP;
332 }
[1a5b252]333 }
[58cbb0c8]334
[fb4c877]335 /* Verify that driver removed all functions */
336 fibril_rwlock_read_lock(&device_tree.rwlock);
337 if (!list_empty(&dev->functions)) {
338 fibril_rwlock_read_unlock(&device_tree.rwlock);
[80a96d2]339 dev_del_ref(dev);
[fb4c877]340 return EIO;
341 }
[80a96d2]342
[f278930]343 driver_t *driver = dev->drv;
[fb4c877]344 fibril_rwlock_read_unlock(&device_tree.rwlock);
345
[f278930]346 if (driver)
347 detach_driver(&device_tree, dev);
[58cbb0c8]348
[1a5b252]349 fibril_rwlock_write_lock(&device_tree.rwlock);
350 remove_dev_node(&device_tree, dev);
[58cbb0c8]351
352 /* Delete ref created when node was inserted */
353 dev_del_ref(dev);
354 /* Delete ref created by dev_add_ref(dev) above */
355 dev_del_ref(dev);
[1a5b252]356 }
357 } else {
358 /* Unregister from location service */
359 rc = loc_service_unregister(fun->service_id);
360 if (rc != EOK) {
[58cbb0c8]361 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a1a101d]362 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
[1a5b252]363 return EIO;
364 }
365
366 fun->service_id = 0;
367 }
368
[c1a0488]369 fun->state = FUN_OFF_LINE;
[58cbb0c8]370 fibril_rwlock_write_unlock(&device_tree.rwlock);
371
[1a5b252]372 return EOK;
373}
374
[8b1e15ac]375/** Handle function registration.
[38b3baf]376 *
[3843ecb]377 * Child devices are registered by their parent's device driver.
378 */
[8b1e15ac]379static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
[bda60d9]380{
[8b1e15ac]381 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
382 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
383 sysarg_t match_count = IPC_GET_ARG3(*call);
[957cfa58]384 dev_tree_t *tree = &device_tree;
[66babbd]385
[58cbb0c8]386 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
[ba38f72c]387 if (pdev == NULL) {
[ffa2c8ef]388 async_answer_0(callid, ENOENT);
[d347b53]389 return;
[c6c389ed]390 }
[d347b53]391
[8b1e15ac]392 if (ftype != fun_inner && ftype != fun_exposed) {
393 /* Unknown function type */
[a1a101d]394 log_msg(LOG_DEFAULT, LVL_ERROR,
[ebcb05a]395 "Unknown function type %d provided by driver.",
[9b415c9]396 (int) ftype);
[8b1e15ac]397
[58cbb0c8]398 dev_del_ref(pdev);
[8b1e15ac]399 async_answer_0(callid, EINVAL);
400 return;
401 }
402
[ba38f72c]403 char *fun_name = NULL;
404 int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
[c6c389ed]405 if (rc != EOK) {
[58cbb0c8]406 dev_del_ref(pdev);
[ffa2c8ef]407 async_answer_0(callid, rc);
[d347b53]408 return;
409 }
410
[58cbb0c8]411 fibril_rwlock_write_lock(&tree->rwlock);
412
[e2b9b341]413 /* Check device state */
414 if (pdev->state == DEVICE_REMOVED) {
415 fibril_rwlock_write_unlock(&tree->rwlock);
416 dev_del_ref(pdev);
417 async_answer_0(callid, ENOENT);
418 return;
419 }
420
[0876062]421 /* Check that function with same name is not there already. */
[33fc3ae]422 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
423 if (tfun) {
424 fun_del_ref(tfun); /* drop the new unwanted reference */
[0876062]425 fibril_rwlock_write_unlock(&tree->rwlock);
[58cbb0c8]426 dev_del_ref(pdev);
[0876062]427 async_answer_0(callid, EEXISTS);
428 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
429 fun_name);
430 free(fun_name);
431 return;
432 }
[d0dd7b5]433
[ba38f72c]434 fun_node_t *fun = create_fun_node();
[4820360]435 /* One reference for creation, one for us */
436 fun_add_ref(fun);
[58cbb0c8]437 fun_add_ref(fun);
[d0dd7b5]438 fun->ftype = ftype;
439
[4820360]440 /*
441 * We can lock the function here even when holding the tree because
442 * we know it cannot be held by anyone else yet.
443 */
444 fun_busy_lock(fun);
445
[ba38f72c]446 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
[957cfa58]447 fibril_rwlock_write_unlock(&tree->rwlock);
[58cbb0c8]448 dev_del_ref(pdev);
[4820360]449 fun_busy_unlock(fun);
450 fun_del_ref(fun);
[ba38f72c]451 delete_fun_node(fun);
[ffa2c8ef]452 async_answer_0(callid, ENOMEM);
[d347b53]453 return;
[c6c389ed]454 }
[d347b53]455
[1a5b252]456 fibril_rwlock_write_unlock(&tree->rwlock);
[58cbb0c8]457 dev_del_ref(pdev);
[2480e19]458
[ba38f72c]459 devman_receive_match_ids(match_count, &fun->match_ids);
[1a5b252]460
461 rc = online_function(fun);
462 if (rc != EOK) {
[4820360]463 /* XXX Set some failed state? */
464 fun_busy_unlock(fun);
465 fun_del_ref(fun);
[1a5b252]466 async_answer_0(callid, rc);
467 return;
[398c4d7]468 }
[8b1e15ac]469
[4820360]470 fun_busy_unlock(fun);
471 fun_del_ref(fun);
472
[38b3baf]473 /* Return device handle to parent's driver. */
[ba38f72c]474 async_answer_1(callid, EOK, fun->handle);
[d347b53]475}
476
[1dc4a5e]477static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
[ce89036b]478{
[0b5a4131]479 devman_handle_t handle = IPC_GET_ARG1(*call);
[cc574511]480 category_id_t cat_id;
481 int rc;
[692c40cb]482
[1dc4a5e]483 /* Get category name. */
484 char *cat_name;
485 rc = async_data_write_accept((void **) &cat_name, true,
[38b3baf]486 0, 0, 0, 0);
[692c40cb]487 if (rc != EOK) {
[ffa2c8ef]488 async_answer_0(callid, rc);
[692c40cb]489 return;
[58cbb0c8]490 }
[692c40cb]491
[ba38f72c]492 fun_node_t *fun = find_fun_node(&device_tree, handle);
493 if (fun == NULL) {
[ffa2c8ef]494 async_answer_0(callid, ENOENT);
[692c40cb]495 return;
496 }
497
[58cbb0c8]498 fibril_rwlock_read_lock(&device_tree.rwlock);
499
[e2b9b341]500 /* Check function state */
501 if (fun->state == FUN_REMOVED) {
502 fibril_rwlock_read_unlock(&device_tree.rwlock);
503 async_answer_0(callid, ENOENT);
504 return;
505 }
506
[1dc4a5e]507 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
[cc574511]508 if (rc == EOK) {
509 loc_service_add_to_cat(fun->service_id, cat_id);
[a1a101d]510 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
[a96a982]511 fun->pathname, cat_name);
[cc574511]512 } else {
[a1a101d]513 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
[1dc4a5e]514 "`%s'.", fun->pathname, cat_name);
[cc574511]515 }
516
[58cbb0c8]517 fibril_rwlock_read_unlock(&device_tree.rwlock);
518 fun_del_ref(fun);
[a96a982]519
520 async_answer_0(callid, rc);
[692c40cb]521}
522
[1a5b252]523/** Online function by driver request.
524 *
525 */
526static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
527 driver_t *drv)
528{
529 fun_node_t *fun;
530 int rc;
[a9abe5fc]531
[a1a101d]532 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
[a9abe5fc]533
[58cbb0c8]534 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
535 if (fun == NULL) {
536 async_answer_0(iid, ENOENT);
537 return;
538 }
[1a5b252]539
[4820360]540 fun_busy_lock(fun);
541
[58cbb0c8]542 fibril_rwlock_read_lock(&device_tree.rwlock);
543 if (fun->dev == NULL || fun->dev->drv != drv) {
544 fibril_rwlock_read_unlock(&device_tree.rwlock);
[4820360]545 fun_busy_unlock(fun);
[58cbb0c8]546 fun_del_ref(fun);
[1a5b252]547 async_answer_0(iid, ENOENT);
548 return;
549 }
[58cbb0c8]550 fibril_rwlock_read_unlock(&device_tree.rwlock);
[1a5b252]551
552 rc = online_function(fun);
553 if (rc != EOK) {
[4820360]554 fun_busy_unlock(fun);
[58cbb0c8]555 fun_del_ref(fun);
[1a5b252]556 async_answer_0(iid, (sysarg_t) rc);
557 return;
558 }
[58cbb0c8]559
[4820360]560 fun_busy_unlock(fun);
[58cbb0c8]561 fun_del_ref(fun);
[1a5b252]562
563 async_answer_0(iid, (sysarg_t) EOK);
564}
565
566
567/** Offline function by driver request.
568 *
569 */
570static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
571 driver_t *drv)
572{
573 fun_node_t *fun;
574 int rc;
575
[58cbb0c8]576 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
577 if (fun == NULL) {
578 async_answer_0(iid, ENOENT);
579 return;
580 }
[1a5b252]581
[4820360]582 fun_busy_lock(fun);
583
[58cbb0c8]584 fibril_rwlock_write_lock(&device_tree.rwlock);
585 if (fun->dev == NULL || fun->dev->drv != drv) {
[4820360]586 fun_busy_unlock(fun);
[58cbb0c8]587 fun_del_ref(fun);
[1a5b252]588 async_answer_0(iid, ENOENT);
589 return;
590 }
[58cbb0c8]591 fibril_rwlock_write_unlock(&device_tree.rwlock);
[1a5b252]592
593 rc = offline_function(fun);
594 if (rc != EOK) {
[4820360]595 fun_busy_unlock(fun);
[58cbb0c8]596 fun_del_ref(fun);
[1a5b252]597 async_answer_0(iid, (sysarg_t) rc);
598 return;
599 }
600
[4820360]601 fun_busy_unlock(fun);
[58cbb0c8]602 fun_del_ref(fun);
[1a5b252]603 async_answer_0(iid, (sysarg_t) EOK);
604}
605
[d0dd7b5]606/** Remove function. */
607static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
608{
609 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
610 dev_tree_t *tree = &device_tree;
611 int rc;
612
[58cbb0c8]613 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
[d0dd7b5]614 if (fun == NULL) {
615 async_answer_0(callid, ENOENT);
616 return;
617 }
618
[4820360]619 fun_busy_lock(fun);
620
[58cbb0c8]621 fibril_rwlock_write_lock(&tree->rwlock);
622
[a1a101d]623 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
[d0dd7b5]624
[e2b9b341]625 /* Check function state */
626 if (fun->state == FUN_REMOVED) {
627 fibril_rwlock_write_unlock(&tree->rwlock);
[4820360]628 fun_busy_unlock(fun);
629 fun_del_ref(fun);
[e2b9b341]630 async_answer_0(callid, ENOENT);
631 return;
632 }
633
[d0dd7b5]634 if (fun->ftype == fun_inner) {
[80a96d2]635 /* This is a surprise removal. Handle possible descendants */
[1a5b252]636 if (fun->child != NULL) {
[80a96d2]637 dev_node_t *dev = fun->child;
638 device_state_t dev_state;
639 int gone_rc;
640
641 dev_add_ref(dev);
642 dev_state = dev->state;
643
644 fibril_rwlock_write_unlock(&device_tree.rwlock);
645
646 /* If device is owned by driver, inform driver it is gone. */
647 if (dev_state == DEVICE_USABLE)
648 gone_rc = driver_dev_gone(&device_tree, dev);
649 else
650 gone_rc = EOK;
651
652 fibril_rwlock_read_lock(&device_tree.rwlock);
653
654 /* Verify that driver succeeded and removed all functions */
655 if (gone_rc != EOK || !list_empty(&dev->functions)) {
[a1a101d]656 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
[80a96d2]657 "functions for device that is gone. "
658 "Device node is now defunct.");
659
660 /*
661 * Not much we can do but mark the device
662 * node as having invalid state. This
663 * is a driver bug.
664 */
665 dev->state = DEVICE_INVALID;
666 fibril_rwlock_read_unlock(&device_tree.rwlock);
667 dev_del_ref(dev);
[655cc56]668 if (gone_rc == EOK)
669 gone_rc = ENOTSUP;
[4820360]670 fun_busy_unlock(fun);
671 fun_del_ref(fun);
[655cc56]672 async_answer_0(callid, gone_rc);
[80a96d2]673 return;
674 }
675
676 driver_t *driver = dev->drv;
677 fibril_rwlock_read_unlock(&device_tree.rwlock);
678
679 if (driver)
680 detach_driver(&device_tree, dev);
681
682 fibril_rwlock_write_lock(&device_tree.rwlock);
683 remove_dev_node(&device_tree, dev);
684
685 /* Delete ref created when node was inserted */
686 dev_del_ref(dev);
687 /* Delete ref created by dev_add_ref(dev) above */
688 dev_del_ref(dev);
[1a5b252]689 }
[d0dd7b5]690 } else {
[1a5b252]691 if (fun->service_id != 0) {
692 /* Unregister from location service */
693 rc = loc_service_unregister(fun->service_id);
694 if (rc != EOK) {
[a1a101d]695 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
[1a5b252]696 "service.");
697 fibril_rwlock_write_unlock(&tree->rwlock);
[4820360]698 fun_busy_unlock(fun);
[58cbb0c8]699 fun_del_ref(fun);
[1a5b252]700 async_answer_0(callid, EIO);
701 return;
702 }
[d0dd7b5]703 }
704 }
705
706 remove_fun_node(&device_tree, fun);
707 fibril_rwlock_write_unlock(&tree->rwlock);
[4820360]708 fun_busy_unlock(fun);
[58cbb0c8]709
710 /* Delete ref added when inserting function into tree */
711 fun_del_ref(fun);
712 /* Delete ref added above when looking up function */
713 fun_del_ref(fun);
[d0dd7b5]714
[a1a101d]715 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
[d0dd7b5]716 async_answer_0(callid, EOK);
717}
718
[38b3baf]719/** Initialize driver which has registered itself as running and ready.
720 *
721 * The initialization is done in a separate fibril to avoid deadlocks (if the
722 * driver needed to be served by devman during the driver's initialization).
[3843ecb]723 */
[d347b53]724static int init_running_drv(void *drv)
725{
[38b3baf]726 driver_t *driver = (driver_t *) drv;
727
728 initialize_running_driver(driver, &device_tree);
[a1a101d]729 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
[38b3baf]730 driver->name);
[d347b53]731 return 0;
[bda60d9]732}
733
[38b3baf]734/** Function for handling connections from a driver to the device manager. */
[924c75e1]735static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[38b3baf]736{
[422722e]737 client_t *client;
[c4f7bf6]738 driver_t *driver = NULL;
[422722e]739
[38b3baf]740 /* Accept the connection. */
[ffa2c8ef]741 async_answer_0(iid, EOK);
[e2b9a993]742
[422722e]743 client = async_get_client_data();
744 if (client == NULL) {
[a1a101d]745 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
[3843ecb]746 return;
[d347b53]747 }
748
[79ae36dd]749 while (true) {
750 ipc_call_t call;
751 ipc_callid_t callid = async_get_call(&call);
752
753 if (!IPC_GET_IMETHOD(call))
754 break;
[e2b9a993]755
[422722e]756 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
757 fibril_mutex_lock(&client->mutex);
758 driver = client->driver;
759 fibril_mutex_unlock(&client->mutex);
760 if (driver == NULL) {
761 /* First call must be to DEVMAN_DRIVER_REGISTER */
762 async_answer_0(callid, ENOTSUP);
763 continue;
764 }
765 }
766
[228e490]767 switch (IPC_GET_IMETHOD(call)) {
[422722e]768 case DEVMAN_DRIVER_REGISTER:
769 fibril_mutex_lock(&client->mutex);
770 if (client->driver != NULL) {
771 fibril_mutex_unlock(&client->mutex);
772 async_answer_0(callid, EINVAL);
773 continue;
774 }
775 client->driver = devman_driver_register(callid, &call);
776 fibril_mutex_unlock(&client->mutex);
777 break;
[8b1e15ac]778 case DEVMAN_ADD_FUNCTION:
779 devman_add_function(callid, &call);
[e2b9a993]780 break;
[1dc4a5e]781 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
782 devman_add_function_to_cat(callid, &call);
[692c40cb]783 break;
[1a5b252]784 case DEVMAN_DRV_FUN_ONLINE:
785 devman_drv_fun_online(callid, &call, driver);
786 break;
787 case DEVMAN_DRV_FUN_OFFLINE:
788 devman_drv_fun_offline(callid, &call, driver);
789 break;
[d0dd7b5]790 case DEVMAN_REMOVE_FUNCTION:
791 devman_remove_function(callid, &call);
792 break;
[e2b9a993]793 default:
[1a5b252]794 async_answer_0(callid, EINVAL);
[e2b9a993]795 break;
796 }
797 }
798}
799
[38b3baf]800/** Find handle for the device instance identified by the device's path in the
801 * device tree. */
[ba38f72c]802static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
[f658458]803{
[38b3baf]804 char *pathname;
[58cbb0c8]805 devman_handle_t handle;
[38b3baf]806
807 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
[f658458]808 if (rc != EOK) {
[ffa2c8ef]809 async_answer_0(iid, rc);
[f658458]810 return;
811 }
812
[8b1e15ac]813 fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
[f658458]814
815 free(pathname);
816
[ba38f72c]817 if (fun == NULL) {
[ffa2c8ef]818 async_answer_0(iid, ENOENT);
[f658458]819 return;
820 }
[8b1e15ac]821
[58cbb0c8]822 fibril_rwlock_read_lock(&device_tree.rwlock);
[e2b9b341]823
824 /* Check function state */
825 if (fun->state == FUN_REMOVED) {
826 fibril_rwlock_read_unlock(&device_tree.rwlock);
827 async_answer_0(iid, ENOENT);
828 return;
829 }
[58cbb0c8]830 handle = fun->handle;
[e2b9b341]831
[58cbb0c8]832 fibril_rwlock_read_unlock(&device_tree.rwlock);
833
834 /* Delete reference created above by find_fun_node_by_path() */
835 fun_del_ref(fun);
836
837 async_answer_1(iid, EOK, handle);
[f658458]838}
839
[7beb220]840/** Get device name. */
841static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
842{
843 devman_handle_t handle = IPC_GET_ARG1(*icall);
844
845 fun_node_t *fun = find_fun_node(&device_tree, handle);
846 if (fun == NULL) {
847 async_answer_0(iid, ENOMEM);
848 return;
849 }
850
851 ipc_callid_t data_callid;
852 size_t data_len;
853 if (!async_data_read_receive(&data_callid, &data_len)) {
854 async_answer_0(iid, EINVAL);
[58cbb0c8]855 fun_del_ref(fun);
[7beb220]856 return;
857 }
858
859 void *buffer = malloc(data_len);
860 if (buffer == NULL) {
861 async_answer_0(data_callid, ENOMEM);
862 async_answer_0(iid, ENOMEM);
[58cbb0c8]863 fun_del_ref(fun);
[7beb220]864 return;
865 }
866
[58cbb0c8]867 fibril_rwlock_read_lock(&device_tree.rwlock);
868
[e2b9b341]869 /* Check function state */
870 if (fun->state == FUN_REMOVED) {
871 fibril_rwlock_read_unlock(&device_tree.rwlock);
872 free(buffer);
873
874 async_answer_0(data_callid, ENOENT);
875 async_answer_0(iid, ENOENT);
876 fun_del_ref(fun);
877 return;
878 }
879
[7beb220]880 size_t sent_length = str_size(fun->name);
881 if (sent_length > data_len) {
882 sent_length = data_len;
883 }
884
885 async_data_read_finalize(data_callid, fun->name, sent_length);
886 async_answer_0(iid, EOK);
887
[58cbb0c8]888 fibril_rwlock_read_unlock(&device_tree.rwlock);
889 fun_del_ref(fun);
[7beb220]890 free(buffer);
891}
892
[3f57fb7]893/** Get function driver name. */
894static void devman_fun_get_driver_name(ipc_callid_t iid, ipc_call_t *icall)
895{
896 devman_handle_t handle = IPC_GET_ARG1(*icall);
897
898 fun_node_t *fun = find_fun_node(&device_tree, handle);
899 if (fun == NULL) {
900 async_answer_0(iid, ENOMEM);
901 return;
902 }
903
904 ipc_callid_t data_callid;
905 size_t data_len;
906 if (!async_data_read_receive(&data_callid, &data_len)) {
907 async_answer_0(iid, EINVAL);
908 fun_del_ref(fun);
909 return;
910 }
911
912 void *buffer = malloc(data_len);
913 if (buffer == NULL) {
914 async_answer_0(data_callid, ENOMEM);
915 async_answer_0(iid, ENOMEM);
916 fun_del_ref(fun);
917 return;
918 }
919
920 fibril_rwlock_read_lock(&device_tree.rwlock);
921
922 /* Check function state */
923 if (fun->state == FUN_REMOVED) {
924 fibril_rwlock_read_unlock(&device_tree.rwlock);
925 free(buffer);
926
927 async_answer_0(data_callid, ENOENT);
928 async_answer_0(iid, ENOENT);
929 fun_del_ref(fun);
930 return;
931 }
932
933 /* Check whether function has a driver */
934 if (fun->child == NULL || fun->child->drv == NULL) {
935 fibril_rwlock_read_unlock(&device_tree.rwlock);
936 free(buffer);
937
938 async_answer_0(data_callid, EINVAL);
939 async_answer_0(iid, EINVAL);
940 fun_del_ref(fun);
941 return;
942 }
943
944 size_t sent_length = str_size(fun->child->drv->name);
945 if (sent_length > data_len) {
946 sent_length = data_len;
947 }
948
949 async_data_read_finalize(data_callid, fun->child->drv->name,
950 sent_length);
951 async_answer_0(iid, EOK);
952
953 fibril_rwlock_read_unlock(&device_tree.rwlock);
954 fun_del_ref(fun);
955 free(buffer);
956}
[7beb220]957
958/** Get device path. */
959static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
[431d6d6]960{
961 devman_handle_t handle = IPC_GET_ARG1(*icall);
962
963 fun_node_t *fun = find_fun_node(&device_tree, handle);
964 if (fun == NULL) {
965 async_answer_0(iid, ENOMEM);
966 return;
967 }
968
969 ipc_callid_t data_callid;
970 size_t data_len;
971 if (!async_data_read_receive(&data_callid, &data_len)) {
972 async_answer_0(iid, EINVAL);
[58cbb0c8]973 fun_del_ref(fun);
[431d6d6]974 return;
975 }
976
977 void *buffer = malloc(data_len);
978 if (buffer == NULL) {
979 async_answer_0(data_callid, ENOMEM);
980 async_answer_0(iid, ENOMEM);
[58cbb0c8]981 fun_del_ref(fun);
[431d6d6]982 return;
983 }
[58cbb0c8]984
985 fibril_rwlock_read_lock(&device_tree.rwlock);
986
[e2b9b341]987 /* Check function state */
988 if (fun->state == FUN_REMOVED) {
989 fibril_rwlock_read_unlock(&device_tree.rwlock);
990 free(buffer);
991
992 async_answer_0(data_callid, ENOENT);
993 async_answer_0(iid, ENOENT);
994 fun_del_ref(fun);
995 return;
996 }
997
[431d6d6]998 size_t sent_length = str_size(fun->pathname);
999 if (sent_length > data_len) {
1000 sent_length = data_len;
1001 }
1002
1003 async_data_read_finalize(data_callid, fun->pathname, sent_length);
1004 async_answer_0(iid, EOK);
1005
[58cbb0c8]1006 fibril_rwlock_read_unlock(&device_tree.rwlock);
1007 fun_del_ref(fun);
[431d6d6]1008 free(buffer);
1009}
1010
[7beb220]1011static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
1012{
1013 ipc_callid_t callid;
1014 size_t size;
1015 size_t act_size;
1016 int rc;
1017
1018 if (!async_data_read_receive(&callid, &size)) {
1019 async_answer_0(callid, EREFUSED);
1020 async_answer_0(iid, EREFUSED);
1021 return;
1022 }
1023
1024 fibril_rwlock_read_lock(&device_tree.rwlock);
1025
1026 dev_node_t *dev = find_dev_node_no_lock(&device_tree,
1027 IPC_GET_ARG1(*icall));
[e2b9b341]1028 if (dev == NULL || dev->state == DEVICE_REMOVED) {
[7beb220]1029 fibril_rwlock_read_unlock(&device_tree.rwlock);
1030 async_answer_0(callid, ENOENT);
1031 async_answer_0(iid, ENOENT);
1032 return;
1033 }
1034
1035 devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
1036 if (hdl_buf == NULL) {
1037 fibril_rwlock_read_unlock(&device_tree.rwlock);
1038 async_answer_0(callid, ENOMEM);
1039 async_answer_0(iid, ENOMEM);
1040 return;
1041 }
1042
1043 rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
1044 if (rc != EOK) {
1045 fibril_rwlock_read_unlock(&device_tree.rwlock);
1046 async_answer_0(callid, rc);
1047 async_answer_0(iid, rc);
1048 return;
1049 }
1050
1051 fibril_rwlock_read_unlock(&device_tree.rwlock);
1052
1053 sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
1054 free(hdl_buf);
1055
1056 async_answer_1(iid, retval, act_size);
1057}
1058
1059
1060/** Get handle for child device of a function. */
1061static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
1062{
1063 fun_node_t *fun;
1064
1065 fibril_rwlock_read_lock(&device_tree.rwlock);
1066
[e2b9b341]1067 fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
1068 if (fun == NULL || fun->state == FUN_REMOVED) {
[7beb220]1069 fibril_rwlock_read_unlock(&device_tree.rwlock);
1070 async_answer_0(iid, ENOENT);
1071 return;
1072 }
1073
1074 if (fun->child == NULL) {
1075 fibril_rwlock_read_unlock(&device_tree.rwlock);
1076 async_answer_0(iid, ENOENT);
1077 return;
1078 }
1079
1080 async_answer_1(iid, EOK, fun->child->handle);
1081
1082 fibril_rwlock_read_unlock(&device_tree.rwlock);
1083}
1084
[1a5b252]1085/** Online function.
1086 *
1087 * Send a request to online a function to the responsible driver.
1088 * The driver may offline other functions if necessary (i.e. if the state
1089 * of this function is linked to state of another function somehow).
1090 */
1091static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
1092{
1093 fun_node_t *fun;
1094 int rc;
1095
[58cbb0c8]1096 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
[1a5b252]1097 if (fun == NULL) {
1098 async_answer_0(iid, ENOENT);
1099 return;
1100 }
1101
[58cbb0c8]1102 rc = driver_fun_online(&device_tree, fun);
1103 fun_del_ref(fun);
[1a5b252]1104
1105 async_answer_0(iid, (sysarg_t) rc);
1106}
1107
1108/** Offline function.
1109 *
1110 * Send a request to offline a function to the responsible driver. As
1111 * a result the subtree rooted at that function should be cleanly
1112 * detatched. The driver may offline other functions if necessary
1113 * (i.e. if the state of this function is linked to state of another
1114 * function somehow).
1115 */
1116static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
1117{
1118 fun_node_t *fun;
1119 int rc;
1120
[58cbb0c8]1121 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
[1a5b252]1122 if (fun == NULL) {
1123 async_answer_0(iid, ENOENT);
1124 return;
1125 }
1126
[58cbb0c8]1127 rc = driver_fun_offline(&device_tree, fun);
1128 fun_del_ref(fun);
[1a5b252]1129
1130 async_answer_0(iid, (sysarg_t) rc);
1131}
1132
[e280857]1133/** Find handle for the function instance identified by its service ID. */
1134static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
1135{
1136 fun_node_t *fun;
1137
1138 fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
1139
1140 if (fun == NULL) {
1141 async_answer_0(iid, ENOENT);
1142 return;
1143 }
1144
[58cbb0c8]1145 fibril_rwlock_read_lock(&device_tree.rwlock);
[e2b9b341]1146
1147 /* Check function state */
1148 if (fun->state == FUN_REMOVED) {
1149 fibril_rwlock_read_unlock(&device_tree.rwlock);
1150 async_answer_0(iid, ENOENT);
1151 return;
1152 }
1153
[e280857]1154 async_answer_1(iid, EOK, fun->handle);
[58cbb0c8]1155 fibril_rwlock_read_unlock(&device_tree.rwlock);
1156 fun_del_ref(fun);
[e280857]1157}
[f658458]1158
[38b3baf]1159/** Function for handling connections from a client to the device manager. */
[f658458]1160static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
1161{
[38b3baf]1162 /* Accept connection. */
[ffa2c8ef]1163 async_answer_0(iid, EOK);
[f658458]1164
[79ae36dd]1165 while (true) {
[f658458]1166 ipc_call_t call;
1167 ipc_callid_t callid = async_get_call(&call);
1168
[79ae36dd]1169 if (!IPC_GET_IMETHOD(call))
1170 break;
1171
[228e490]1172 switch (IPC_GET_IMETHOD(call)) {
[f658458]1173 case DEVMAN_DEVICE_GET_HANDLE:
[ba38f72c]1174 devman_function_get_handle(callid, &call);
[f658458]1175 break;
[7beb220]1176 case DEVMAN_DEV_GET_FUNCTIONS:
1177 devman_dev_get_functions(callid, &call);
1178 break;
1179 case DEVMAN_FUN_GET_CHILD:
1180 devman_fun_get_child(callid, &call);
1181 break;
1182 case DEVMAN_FUN_GET_NAME:
1183 devman_fun_get_name(callid, &call);
1184 break;
[3f57fb7]1185 case DEVMAN_FUN_GET_DRIVER_NAME:
1186 devman_fun_get_driver_name(callid, &call);
1187 break;
[7beb220]1188 case DEVMAN_FUN_GET_PATH:
1189 devman_fun_get_path(callid, &call);
[431d6d6]1190 break;
[1a5b252]1191 case DEVMAN_FUN_ONLINE:
1192 devman_fun_online(callid, &call);
1193 break;
1194 case DEVMAN_FUN_OFFLINE:
1195 devman_fun_offline(callid, &call);
1196 break;
[e280857]1197 case DEVMAN_FUN_SID_TO_HANDLE:
1198 devman_fun_sid_to_handle(callid, &call);
1199 break;
[f658458]1200 default:
[ffa2c8ef]1201 async_answer_0(callid, ENOENT);
[f658458]1202 }
[c6c389ed]1203 }
[f658458]1204}
1205
[c6c389ed]1206static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
1207 bool drv_to_parent)
[38b3baf]1208{
[0b5a4131]1209 devman_handle_t handle = IPC_GET_ARG2(*icall);
[8b1e15ac]1210 devman_handle_t fwd_h;
1211 fun_node_t *fun = NULL;
1212 dev_node_t *dev = NULL;
[9a66bc2e]1213
[8b1e15ac]1214 fun = find_fun_node(&device_tree, handle);
1215 if (fun == NULL)
1216 dev = find_dev_node(&device_tree, handle);
[58cbb0c8]1217 else {
1218 fibril_rwlock_read_lock(&device_tree.rwlock);
[8b1e15ac]1219 dev = fun->dev;
[58cbb0c8]1220 if (dev != NULL)
1221 dev_add_ref(dev);
1222 fibril_rwlock_read_unlock(&device_tree.rwlock);
1223 }
[8b1e15ac]1224
[0418050]1225 /*
1226 * For a valid function to connect to we need a device. The root
1227 * function, for example, has no device and cannot be connected to.
1228 * This means @c dev needs to be valid regardless whether we are
1229 * connecting to a device or to a function.
1230 */
1231 if (dev == NULL) {
[a1a101d]1232 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding failed - no device or "
[ebcb05a]1233 "function with handle %" PRIun " was found.", handle);
[ffa2c8ef]1234 async_answer_0(iid, ENOENT);
[58cbb0c8]1235 goto cleanup;
[5cd136ab]1236 }
[8b1e15ac]1237
1238 if (fun == NULL && !drv_to_parent) {
[a1a101d]1239 log_msg(LOG_DEFAULT, LVL_ERROR, NAME ": devman_forward error - cannot "
[ebcb05a]1240 "connect to handle %" PRIun ", refers to a device.",
[9b415c9]1241 handle);
[ffa2c8ef]1242 async_answer_0(iid, ENOENT);
[58cbb0c8]1243 goto cleanup;
[5cd136ab]1244 }
1245
1246 driver_t *driver = NULL;
1247
[e2b9b341]1248 fibril_rwlock_read_lock(&device_tree.rwlock);
1249
[5cd136ab]1250 if (drv_to_parent) {
[8b1e15ac]1251 /* Connect to parent function of a device (or device function). */
1252 if (dev->pfun->dev != NULL)
1253 driver = dev->pfun->dev->drv;
[50ad3f3]1254
[8b1e15ac]1255 fwd_h = dev->pfun->handle;
[c6c389ed]1256 } else if (dev->state == DEVICE_USABLE) {
[8b1e15ac]1257 /* Connect to the specified function */
[38b3baf]1258 driver = dev->drv;
[c6c389ed]1259 assert(driver != NULL);
[50ad3f3]1260
[8b1e15ac]1261 fwd_h = handle;
[5cd136ab]1262 }
1263
[e2b9b341]1264 fibril_rwlock_read_unlock(&device_tree.rwlock);
1265
[c6c389ed]1266 if (driver == NULL) {
[a1a101d]1267 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding refused - " \
[79ae36dd]1268 "the device %" PRIun " is not in usable state.", handle);
[ffa2c8ef]1269 async_answer_0(iid, ENOENT);
[58cbb0c8]1270 goto cleanup;
[5cd136ab]1271 }
1272
[38b3baf]1273 int method;
1274 if (drv_to_parent)
[5cd136ab]1275 method = DRIVER_DRIVER;
[38b3baf]1276 else
[5cd136ab]1277 method = DRIVER_CLIENT;
1278
[79ae36dd]1279 if (!driver->sess) {
[a1a101d]1280 log_msg(LOG_DEFAULT, LVL_ERROR,
[79ae36dd]1281 "Could not forward to driver `%s'.", driver->name);
[ffa2c8ef]1282 async_answer_0(iid, EINVAL);
[58cbb0c8]1283 goto cleanup;
[9a66bc2e]1284 }
[c6c389ed]1285
[8b1e15ac]1286 if (fun != NULL) {
[a1a101d]1287 log_msg(LOG_DEFAULT, LVL_DEBUG,
[ebcb05a]1288 "Forwarding request for `%s' function to driver `%s'.",
[9b415c9]1289 fun->pathname, driver->name);
[8b1e15ac]1290 } else {
[a1a101d]1291 log_msg(LOG_DEFAULT, LVL_DEBUG,
[ebcb05a]1292 "Forwarding request for `%s' device to driver `%s'.",
[9b415c9]1293 dev->pfun->pathname, driver->name);
[8b1e15ac]1294 }
[79ae36dd]1295
1296 async_exch_t *exch = async_exchange_begin(driver->sess);
1297 async_forward_fast(iid, exch, method, fwd_h, 0, IPC_FF_NONE);
1298 async_exchange_end(exch);
[50ad3f3]1299
[58cbb0c8]1300cleanup:
1301 if (dev != NULL)
1302 dev_del_ref(dev);
[50ad3f3]1303
[58cbb0c8]1304 if (fun != NULL)
1305 fun_del_ref(fun);
[5cd136ab]1306}
1307
[15f3c3f]1308/** Function for handling connections from a client forwarded by the location
1309 * service to the device manager. */
1310static void devman_connection_loc(ipc_callid_t iid, ipc_call_t *icall)
[ce89036b]1311{
[15f3c3f]1312 service_id_t service_id = IPC_GET_ARG2(*icall);
[ba38f72c]1313 fun_node_t *fun;
1314 dev_node_t *dev;
[e2b9b341]1315 devman_handle_t handle;
1316 driver_t *driver;
[c6c389ed]1317
[15f3c3f]1318 fun = find_loc_tree_function(&device_tree, service_id);
[ce89036b]1319
[e2b9b341]1320 fibril_rwlock_read_lock(&device_tree.rwlock);
1321
1322 if (fun == NULL || fun->dev == NULL || fun->dev->drv == NULL) {
[a1a101d]1323 log_msg(LOG_DEFAULT, LVL_WARN, "devman_connection_loc(): function "
[12f9f0d0]1324 "not found.\n");
[e2b9b341]1325 fibril_rwlock_read_unlock(&device_tree.rwlock);
[ffa2c8ef]1326 async_answer_0(iid, ENOENT);
[ce89036b]1327 return;
1328 }
1329
[ba38f72c]1330 dev = fun->dev;
[e2b9b341]1331 driver = dev->drv;
1332 handle = fun->handle;
1333
1334 fibril_rwlock_read_unlock(&device_tree.rwlock);
[ba38f72c]1335
[e2b9b341]1336 async_exch_t *exch = async_exchange_begin(driver->sess);
1337 async_forward_fast(iid, exch, DRIVER_CLIENT, handle, 0,
[38b3baf]1338 IPC_FF_NONE);
[79ae36dd]1339 async_exchange_end(exch);
1340
[a1a101d]1341 log_msg(LOG_DEFAULT, LVL_DEBUG,
[15f3c3f]1342 "Forwarding loc service request for `%s' function to driver `%s'.",
[e2b9b341]1343 fun->pathname, driver->name);
1344
1345 fun_del_ref(fun);
[ce89036b]1346}
1347
[38b3baf]1348/** Function for handling connections to device manager. */
[9934f7d]1349static void devman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
[38b3baf]1350{
[422722e]1351 /* Select port. */
[96b02eb9]1352 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
[924c75e1]1353 case DEVMAN_DRIVER:
1354 devman_connection_driver(iid, icall);
1355 break;
[f658458]1356 case DEVMAN_CLIENT:
1357 devman_connection_client(iid, icall);
1358 break;
[924c75e1]1359 case DEVMAN_CONNECT_TO_DEVICE:
[38b3baf]1360 /* Connect client to selected device. */
[5cd136ab]1361 devman_forward(iid, icall, false);
1362 break;
[15f3c3f]1363 case DEVMAN_CONNECT_FROM_LOC:
1364 /* Someone connected through loc node. */
1365 devman_connection_loc(iid, icall);
[47a7174f]1366 break;
[5cd136ab]1367 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
[38b3baf]1368 /* Connect client to selected device. */
[5cd136ab]1369 devman_forward(iid, icall, true);
[38b3baf]1370 break;
[924c75e1]1371 default:
1372 /* No such interface */
[ffa2c8ef]1373 async_answer_0(iid, ENOENT);
[924c75e1]1374 }
1375}
1376
[422722e]1377static void *devman_client_data_create(void)
1378{
1379 client_t *client;
1380
1381 client = calloc(1, sizeof(client_t));
1382 if (client == NULL)
1383 return NULL;
1384
1385 fibril_mutex_initialize(&client->mutex);
1386 return client;
1387}
1388
1389static void devman_client_data_destroy(void *data)
1390{
1391 free(data);
1392}
1393
[38b3baf]1394/** Initialize device manager internal structures. */
1395static bool devman_init(void)
[e2b9a993]1396{
[a1a101d]1397 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_init - looking for available drivers.");
[08d9c4e6]1398
[38b3baf]1399 /* Initialize list of available drivers. */
[0c3666d]1400 init_driver_list(&drivers_list);
[c6c389ed]1401 if (lookup_available_drivers(&drivers_list,
1402 DRIVER_DEFAULT_STORE) == 0) {
[a1a101d]1403 log_msg(LOG_DEFAULT, LVL_FATAL, "No drivers found.");
[e2b9a993]1404 return false;
1405 }
[50ad3f3]1406
[a1a101d]1407 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_init - list of drivers has been initialized.");
[50ad3f3]1408
[38b3baf]1409 /* Create root device node. */
[e2b9a993]1410 if (!init_device_tree(&device_tree, &drivers_list)) {
[a1a101d]1411 log_msg(LOG_DEFAULT, LVL_FATAL, "Failed to initialize device tree.");
[38b3baf]1412 return false;
[e2b9a993]1413 }
[50ad3f3]1414
[38b3baf]1415 /*
[f302586]1416 * Caution: As the device manager is not a real loc
1417 * driver (it uses a completely different IPC protocol
1418 * than an ordinary loc driver), forwarding a connection
1419 * from client to the devman by location service will
1420 * not work.
[38b3baf]1421 */
[f302586]1422 loc_server_register(NAME);
[ce89036b]1423
[e2b9a993]1424 return true;
1425}
1426
1427int main(int argc, char *argv[])
1428{
[50ad3f3]1429 printf("%s: HelenOS Device Manager\n", NAME);
1430
[267f235]1431 int rc = log_init(NAME);
[50ad3f3]1432 if (rc != EOK) {
1433 printf("%s: Error initializing logging subsystem.\n", NAME);
1434 return rc;
[9b415c9]1435 }
[e2b9a993]1436
[422722e]1437 /* Set handlers for incoming connections. */
1438 async_set_client_data_constructor(devman_client_data_create);
1439 async_set_client_data_destructor(devman_client_data_destroy);
[e2b9a993]1440 async_set_client_connection(devman_connection);
[50ad3f3]1441
[f302586]1442 if (!devman_init()) {
[a1a101d]1443 log_msg(LOG_DEFAULT, LVL_ERROR, "Error while initializing service.");
[f302586]1444 return -1;
1445 }
[50ad3f3]1446
[38b3baf]1447 /* Register device manager at naming service. */
[50ad3f3]1448 rc = service_register(SERVICE_DEVMAN);
1449 if (rc != EOK) {
[a1a101d]1450 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering as a service.");
[50ad3f3]1451 return rc;
[9b415c9]1452 }
[50ad3f3]1453
1454 printf("%s: Accepting connections.\n", NAME);
[79ae36dd]1455 task_retval(0);
[e2b9a993]1456 async_manager();
[50ad3f3]1457
[38b3baf]1458 /* Never reached. */
[e2b9a993]1459 return 0;
1460}
[c16cf62]1461
1462/** @}
[38b3baf]1463 */
Note: See TracBrowser for help on using the repository browser.