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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a32defa was a32defa, checked in by Lenka Trochtova <trochtova.lenka@…>, 15 years ago

Register devices in the device tree by the device mapper so the device hierarchy can be seen from devfs.

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