source: mainline/uspace/srv/devman/main.c@ 38b3baf

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

Cstyle fixes of devman.

  • Property mode set to 100644
File size: 15.6 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
38#include <assert.h>
39#include <ipc/services.h>
40#include <ipc/ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <bool.h>
45#include <fibril_synch.h>
46#include <stdlib.h>
[c47e1a8]47#include <str.h>
[e2b9a993]48#include <dirent.h>
49#include <fcntl.h>
50#include <sys/stat.h>
51#include <ctype.h>
52#include <ipc/devman.h>
[5cd136ab]53#include <ipc/driver.h>
[d347b53]54#include <thread.h>
[ce89036b]55#include <devmap.h>
[e2b9a993]56
57#include "devman.h"
58
[a79d88d]59#define DRIVER_DEFAULT_STORE "/drv"
[e2b9a993]60
[0c3666d]61static driver_list_t drivers_list;
[e2b9a993]62static dev_tree_t device_tree;
[692c40cb]63static class_list_t class_list;
[e2b9a993]64
[38b3baf]65/** Register running driver. */
66static driver_t *devman_driver_register(void)
67{
[729fa2d6]68 printf(NAME ": devman_driver_register \n");
69
70 ipc_call_t icall;
71 ipc_callid_t iid = async_get_call(&icall);
72 driver_t *driver = NULL;
73
74 if (IPC_GET_METHOD(icall) != DEVMAN_DRIVER_REGISTER) {
75 ipc_answer_0(iid, EREFUSED);
76 return NULL;
77 }
78
[92413de]79 char *drv_name = NULL;
[729fa2d6]80
[38b3baf]81 /* Get driver name. */
82 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
[729fa2d6]83 if (rc != EOK) {
84 ipc_answer_0(iid, rc);
85 return NULL;
86 }
[38b3baf]87 printf(NAME ": the %s driver is trying to register by the service.\n",
88 drv_name);
[729fa2d6]89
[38b3baf]90 /* Find driver structure. */
[729fa2d6]91 driver = find_driver(&drivers_list, drv_name);
[92413de]92
[729fa2d6]93 if (NULL == driver) {
94 printf(NAME ": no driver named %s was found.\n", drv_name);
[04c7003f]95 free(drv_name);
96 drv_name = NULL;
[729fa2d6]97 ipc_answer_0(iid, ENOENT);
98 return NULL;
99 }
100
[04c7003f]101 free(drv_name);
102 drv_name = NULL;
103
[38b3baf]104 /* Create connection to the driver. */
105 printf(NAME ": creating connection to the %s driver.\n", driver->name);
[729fa2d6]106 ipc_call_t call;
[38b3baf]107 ipc_callid_t callid = async_get_call(&call);
[729fa2d6]108 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
109 ipc_answer_0(callid, ENOTSUP);
110 ipc_answer_0(iid, ENOTSUP);
111 return NULL;
112 }
113
[38b3baf]114 /* Remember driver's phone. */
[c16cf62]115 set_driver_phone(driver, IPC_GET_ARG5(call));
[729fa2d6]116
[38b3baf]117 printf(NAME ": the %s driver was successfully registered as running.\n",
118 driver->name);
[729fa2d6]119
[38b3baf]120 ipc_answer_0(callid, EOK);
[729fa2d6]121 ipc_answer_0(iid, EOK);
122
123 return driver;
124}
[e2b9a993]125
[38b3baf]126/** Receive device match ID from the device's parent driver and add it to the
127 * list of devices match ids.
128 *
129 * @param match_ids The list of the device's match ids.
130 * @return Zero on success, negative error code otherwise.
[3843ecb]131 */
[38b3baf]132static int devman_receive_match_id(match_id_list_t *match_ids)
133{
[66babbd]134 match_id_t *match_id = create_match_id();
135 ipc_callid_t callid;
136 ipc_call_t call;
137 int rc = 0;
138
139 callid = async_get_call(&call);
140 if (DEVMAN_ADD_MATCH_ID != IPC_GET_METHOD(call)) {
[38b3baf]141 printf(NAME ": ERROR: devman_receive_match_id - invalid "
142 "protocol.\n");
[66babbd]143 ipc_answer_0(callid, EINVAL);
144 delete_match_id(match_id);
145 return EINVAL;
146 }
147
148 if (NULL == match_id) {
[38b3baf]149 printf(NAME ": ERROR: devman_receive_match_id - failed to "
150 "allocate match id.\n");
[66babbd]151 ipc_answer_0(callid, ENOMEM);
152 return ENOMEM;
153 }
154
[2480e19]155 ipc_answer_0(callid, EOK);
156
[66babbd]157 match_id->score = IPC_GET_ARG1(call);
158
[c47e1a8]159 char *match_id_str;
[38b3baf]160 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
[c47e1a8]161 match_id->id = match_id_str;
[66babbd]162 if (EOK != rc) {
163 delete_match_id(match_id);
[38b3baf]164 printf(NAME ": devman_receive_match_id - failed to receive "
165 "match id string.\n");
[66babbd]166 return rc;
167 }
168
169 list_append(&match_id->link, &match_ids->ids);
[a087f2e]170
[38b3baf]171 printf(NAME ": received match id '%s', score = %d \n",
172 match_id->id, match_id->score);
[66babbd]173 return rc;
174}
175
[38b3baf]176/** Receive device match IDs from the device's parent driver and add them to the
177 * list of devices match ids.
178 *
179 * @param match_count The number of device's match ids to be received.
180 * @param match_ids The list of the device's match ids.
181 * @return Zero on success, negative error code otherwise.
[3843ecb]182 */
[38b3baf]183static int
184devman_receive_match_ids(ipcarg_t match_count, match_id_list_t *match_ids)
185{
[66babbd]186 int ret = EOK;
[5cd136ab]187 size_t i;
[38b3baf]188
[66babbd]189 for (i = 0; i < match_count; i++) {
[38b3baf]190 if (EOK != (ret = devman_receive_match_id(match_ids)))
[66babbd]191 return ret;
192 }
193 return ret;
194}
195
[38b3baf]196/** Handle child device registration.
197 *
[3843ecb]198 * Child devices are registered by their parent's device driver.
199 */
200static void devman_add_child(ipc_callid_t callid, ipc_call_t *call)
[bda60d9]201{
[d347b53]202 device_handle_t parent_handle = IPC_GET_ARG1(*call);
[66babbd]203 ipcarg_t match_count = IPC_GET_ARG2(*call);
[957cfa58]204 dev_tree_t *tree = &device_tree;
[66babbd]205
[957cfa58]206 fibril_rwlock_write_lock(&tree->rwlock);
207 node_t *parent = find_dev_node_no_lock(&device_tree, parent_handle);
[d347b53]208
209 if (NULL == parent) {
[957cfa58]210 fibril_rwlock_write_unlock(&tree->rwlock);
[d347b53]211 ipc_answer_0(callid, ENOENT);
212 return;
[66babbd]213 }
[d347b53]214
215 char *dev_name = NULL;
[38b3baf]216 int rc = async_data_write_accept((void **)&dev_name, true, 0, 0, 0, 0);
[66babbd]217 if (EOK != rc) {
[957cfa58]218 fibril_rwlock_write_unlock(&tree->rwlock);
[d347b53]219 ipc_answer_0(callid, rc);
220 return;
221 }
222
223 node_t *node = create_dev_node();
224 if (!insert_dev_node(&device_tree, node, dev_name, parent)) {
[957cfa58]225 fibril_rwlock_write_unlock(&tree->rwlock);
[d347b53]226 delete_dev_node(node);
227 ipc_answer_0(callid, ENOMEM);
228 return;
229 }
[957cfa58]230 fibril_rwlock_write_unlock(&tree->rwlock);
[d347b53]231
[2480e19]232 printf(NAME ": devman_add_child %s\n", node->pathname);
233
[66babbd]234 devman_receive_match_ids(match_count, &node->match_ids);
[bda60d9]235
[38b3baf]236 /* Return device handle to parent's driver. */
[d347b53]237 ipc_answer_1(callid, EOK, node->handle);
[bda60d9]238
[38b3baf]239 /* Try to find suitable driver and assign it to the device. */
240 assign_driver(node, &drivers_list, &device_tree);
[d347b53]241}
242
[ce89036b]243static void devmap_register_class_dev(dev_class_info_t *cli)
244{
[38b3baf]245 /* Create devmap path and name for the device. */
[ce89036b]246 char *devmap_pathname = NULL;
[38b3baf]247 asprintf(&devmap_pathname, "%s/%s%c%s", DEVMAP_CLASS_NAMESPACE,
248 cli->dev_class->name, DEVMAP_SEPARATOR, cli->dev_name);
249 if (NULL == devmap_pathname)
[ce89036b]250 return;
251
[38b3baf]252 /*
253 * Register the device by the device mapper and remember its devmap
254 * handle.
255 */
256 devmap_device_register(devmap_pathname, &cli->devmap_handle);
[ce89036b]257
[38b3baf]258 /*
259 * Add device to the hash map of class devices registered by device
260 * mapper.
261 */
[a32defa]262 class_add_devmap_device(&class_list, cli);
263
[38b3baf]264 free(devmap_pathname);
[ce89036b]265}
266
[692c40cb]267static void devman_add_device_to_class(ipc_callid_t callid, ipc_call_t *call)
[ce89036b]268{
[692c40cb]269 device_handle_t handle = IPC_GET_ARG1(*call);
270
[38b3baf]271 /* Get class name. */
[692c40cb]272 char *class_name;
[38b3baf]273 int rc = async_data_write_accept((void **) &class_name, true,
274 0, 0, 0, 0);
[692c40cb]275 if (rc != EOK) {
276 ipc_answer_0(callid, rc);
277 return;
278 }
279
280 node_t *dev = find_dev_node(&device_tree, handle);
281 if (NULL == dev) {
282 ipc_answer_0(callid, ENOENT);
283 return;
284 }
285
286 dev_class_t *cl = get_dev_class(&class_list, class_name);
287 dev_class_info_t *class_info = add_device_to_class(dev, cl, NULL);
288
[38b3baf]289 /* Register the device's class alias by devmapper. */
[ce89036b]290 devmap_register_class_dev(class_info);
[692c40cb]291
[38b3baf]292 printf(NAME ": device '%s' added to class '%s', class name '%s' was "
293 "asigned to it\n", dev->pathname, class_name, class_info->dev_name);
[692c40cb]294
[38b3baf]295 ipc_answer_0(callid, EOK);
[692c40cb]296}
297
[38b3baf]298/** Initialize driver which has registered itself as running and ready.
299 *
300 * The initialization is done in a separate fibril to avoid deadlocks (if the
301 * driver needed to be served by devman during the driver's initialization).
[3843ecb]302 */
[d347b53]303static int init_running_drv(void *drv)
304{
[38b3baf]305 driver_t *driver = (driver_t *) drv;
306
307 initialize_running_driver(driver, &device_tree);
308 printf(NAME ": the %s driver was successfully initialized. \n",
309 driver->name);
[d347b53]310 return 0;
[bda60d9]311}
312
[38b3baf]313/** Function for handling connections from a driver to the device manager. */
[924c75e1]314static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[38b3baf]315{
316 /* Accept the connection. */
[e2b9a993]317 ipc_answer_0(iid, EOK);
318
[729fa2d6]319 driver_t *driver = devman_driver_register();
[d347b53]320 if (NULL == driver)
[729fa2d6]321 return;
[d347b53]322
[38b3baf]323 /*
324 * Initialize the driver as running (e.g. pass assigned devices to it)
325 * in a separate fibril; the separate fibril is used to enable the
326 * driver to use devman service during the driver's initialization.
327 */
[d347b53]328 fid_t fid = fibril_create(init_running_drv, driver);
329 if (fid == 0) {
[38b3baf]330 printf(NAME ": Error creating fibril for the initialization of "
331 "the newly registered running driver.\n");
[3843ecb]332 return;
[d347b53]333 }
334 fibril_add_ready(fid);
335
[729fa2d6]336 ipc_callid_t callid;
337 ipc_call_t call;
338 bool cont = true;
339 while (cont) {
[e2b9a993]340 callid = async_get_call(&call);
341
342 switch (IPC_GET_METHOD(call)) {
[729fa2d6]343 case IPC_M_PHONE_HUNGUP:
344 cont = false;
345 continue;
[e2b9a993]346 case DEVMAN_ADD_CHILD_DEVICE:
[3843ecb]347 devman_add_child(callid, &call);
[e2b9a993]348 break;
[692c40cb]349 case DEVMAN_ADD_DEVICE_TO_CLASS:
350 devman_add_device_to_class(callid, &call);
351 break;
[e2b9a993]352 default:
353 ipc_answer_0(callid, EINVAL);
354 break;
355 }
356 }
357}
358
[38b3baf]359/** Find handle for the device instance identified by the device's path in the
360 * device tree. */
[f658458]361static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
362{
[38b3baf]363 char *pathname;
364
365 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
[f658458]366 if (rc != EOK) {
367 ipc_answer_0(iid, rc);
368 return;
369 }
370
[38b3baf]371 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
[f658458]372
373 free(pathname);
374
375 if (NULL == dev) {
376 ipc_answer_0(iid, ENOENT);
377 return;
378 }
379
380 ipc_answer_1(iid, EOK, dev->handle);
381}
382
383
[38b3baf]384/** Function for handling connections from a client to the device manager. */
[f658458]385static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
386{
[38b3baf]387 /* Accept connection. */
[f658458]388 ipc_answer_0(iid, EOK);
389
390 bool cont = true;
391 while (cont) {
392 ipc_call_t call;
393 ipc_callid_t callid = async_get_call(&call);
394
395 switch (IPC_GET_METHOD(call)) {
396 case IPC_M_PHONE_HUNGUP:
397 cont = false;
398 continue;
399 case DEVMAN_DEVICE_GET_HANDLE:
400 devman_device_get_handle(callid, &call);
401 break;
402 default:
403 if (!(callid & IPC_CALLID_NOTIFICATION))
404 ipc_answer_0(callid, ENOENT);
405 }
406 }
407}
408
[38b3baf]409static void
410devman_forward(ipc_callid_t iid, ipc_call_t *icall, bool drv_to_parent)
411{
[9a66bc2e]412 device_handle_t handle = IPC_GET_ARG2(*icall);
413
[5cd136ab]414 node_t *dev = find_dev_node(&device_tree, handle);
415 if (NULL == dev) {
[38b3baf]416 printf(NAME ": devman_forward error - no device with handle %x "
417 "was found.\n", handle);
[5cd136ab]418 ipc_answer_0(iid, ENOENT);
419 return;
420 }
421
422 driver_t *driver = NULL;
423
424 if (drv_to_parent) {
[38b3baf]425 if (NULL != dev->parent)
426 driver = dev->parent->drv;
[df747b9c]427 } else if (DEVICE_USABLE == dev->state) {
[38b3baf]428 driver = dev->drv;
[df747b9c]429 assert(NULL != driver);
[5cd136ab]430 }
431
[38b3baf]432 if (NULL == driver) {
433 printf(NAME ": devman_forward error - the device is not in "
434 "usable state.\n", handle);
[5cd136ab]435 ipc_answer_0(iid, ENOENT);
[38b3baf]436 return;
[5cd136ab]437 }
438
[38b3baf]439 int method;
440 if (drv_to_parent)
[5cd136ab]441 method = DRIVER_DRIVER;
[38b3baf]442 else
[5cd136ab]443 method = DRIVER_CLIENT;
444
[9a66bc2e]445 if (driver->phone <= 0) {
[38b3baf]446 printf(NAME ": devman_forward: cound not forward to driver %s ",
447 driver->name);
[3843ecb]448 printf("the driver's phone is %x).\n", driver->phone);
[ce89036b]449 ipc_answer_0(iid, EINVAL);
[9a66bc2e]450 return;
451 }
[38b3baf]452 printf(NAME ": devman_forward: forward connection to device %s to "
453 "driver %s.\n", dev->pathname, driver->name);
454 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
[5cd136ab]455}
456
[38b3baf]457/** Function for handling connections from a client forwarded by the device
458 * mapper to the device manager. */
[ce89036b]459static void devman_connection_devmapper(ipc_callid_t iid, ipc_call_t *icall)
460{
461 dev_handle_t devmap_handle = IPC_GET_METHOD(*icall);
[38b3baf]462
[ce89036b]463 node_t *dev = find_devmap_tree_device(&device_tree, devmap_handle);
[38b3baf]464 if (NULL == dev)
[ce89036b]465 dev = find_devmap_class_device(&class_list, devmap_handle);
466
467 if (NULL == dev || NULL == dev->drv) {
468 ipc_answer_0(iid, ENOENT);
469 return;
470 }
471
472 if (DEVICE_USABLE != dev->state || dev->drv->phone <= 0) {
473 ipc_answer_0(iid, EINVAL);
474 return;
475 }
476
[38b3baf]477 printf(NAME ": devman_connection_devmapper: forward connection to "
478 "device %s to driver %s.\n", dev->pathname, dev->drv->name);
479 ipc_forward_fast(iid, dev->drv->phone, DRIVER_CLIENT, dev->handle, 0,
480 IPC_FF_NONE);
[ce89036b]481}
482
[38b3baf]483/** Function for handling connections to device manager. */
[924c75e1]484static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
[38b3baf]485{
486 /*
487 * Silly hack to enable the device manager to register as a driver by
488 * the device mapper. If the ipc method is not IPC_M_CONNECT_ME_TO, this
489 * is not the forwarded connection from naming service, so it must be a
490 * connection from the devmapper which thinks this is a devmapper-style
491 * driver. So pretend this is a devmapper-style driver. (This does not
492 * work for device with handle == IPC_M_CONNECT_ME_TO, because devmapper
493 * passes device handle to the driver as an ipc method.)
494 */
495 if (IPC_M_CONNECT_ME_TO != IPC_GET_METHOD(*icall))
[ce89036b]496 devman_connection_devmapper(iid, icall);
497
[38b3baf]498 /*
499 * ipc method is IPC_M_CONNECT_ME_TO, so this is forwarded connection
500 * from naming service by which we registered as device manager, so be
501 * device manager.
502 */
[ce89036b]503
[38b3baf]504 /* Select interface. */
[924c75e1]505 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
506 case DEVMAN_DRIVER:
507 devman_connection_driver(iid, icall);
508 break;
[f658458]509 case DEVMAN_CLIENT:
510 devman_connection_client(iid, icall);
511 break;
[924c75e1]512 case DEVMAN_CONNECT_TO_DEVICE:
[38b3baf]513 /* Connect client to selected device. */
[5cd136ab]514 devman_forward(iid, icall, false);
515 break;
516 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
[38b3baf]517 /* Connect client to selected device. */
[5cd136ab]518 devman_forward(iid, icall, true);
[38b3baf]519 break;
[924c75e1]520 default:
521 /* No such interface */
522 ipc_answer_0(iid, ENOENT);
523 }
524}
525
[38b3baf]526/** Initialize device manager internal structures. */
527static bool devman_init(void)
[e2b9a993]528{
[38b3baf]529 printf(NAME ": devman_init - looking for available drivers.\n");
[08d9c4e6]530
[38b3baf]531 /* Initialize list of available drivers. */
[0c3666d]532 init_driver_list(&drivers_list);
[38b3baf]533 if (0 == lookup_available_drivers(&drivers_list,
534 DRIVER_DEFAULT_STORE)) {
[e2b9a993]535 printf(NAME " no drivers found.");
536 return false;
537 }
[38b3baf]538 printf(NAME ": devman_init - list of drivers has been initialized.\n");
[e2b9a993]539
[38b3baf]540 /* Create root device node. */
[e2b9a993]541 if (!init_device_tree(&device_tree, &drivers_list)) {
542 printf(NAME " failed to initialize device tree.");
[38b3baf]543 return false;
[e2b9a993]544 }
545
[692c40cb]546 init_class_list(&class_list);
547
[38b3baf]548 /*
549 * !!! devman_connection ... as the device manager is not a real devmap
550 * driver (it uses a completely different ipc protocol than an ordinary
551 * devmap driver) forwarding a connection from client to the devman by
552 * devmapper would not work.
553 */
554 devmap_driver_register(NAME, devman_connection);
[ce89036b]555
[e2b9a993]556 return true;
557}
558
559int main(int argc, char *argv[])
560{
561 printf(NAME ": HelenOS Device Manager\n");
562
563 if (!devman_init()) {
564 printf(NAME ": Error while initializing service\n");
565 return -1;
566 }
567
[38b3baf]568 /* Set a handler of incomming connections. */
[e2b9a993]569 async_set_client_connection(devman_connection);
570
[38b3baf]571 /* Register device manager at naming service. */
[e2b9a993]572 ipcarg_t phonead;
573 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
574 return -1;
575
576 printf(NAME ": Accepting connections\n");
577 async_manager();
578
[38b3baf]579 /* Never reached. */
[e2b9a993]580 return 0;
581}
[c16cf62]582
583/** @}
[38b3baf]584 */
Note: See TracBrowser for help on using the repository browser.