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

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

Separate module for devman-client connection handling.

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