source: mainline/uspace/srv/devman/main.c@ 692c40cb

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

Introduce device classes.Device class specifies functional type of the device. Device classes are identified by their string names (actually device class is just a string value). Device classes can be dynamically added. A device can be added to any number of classes.

  • Property mode set to 100644
File size: 13.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
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>
[e2b9a993]55
56#include "devman.h"
57
58#define DRIVER_DEFAULT_STORE "/srv/drivers"
59
[0c3666d]60static driver_list_t drivers_list;
[e2b9a993]61static dev_tree_t device_tree;
[692c40cb]62static class_list_t class_list;
[e2b9a993]63
[c47e1a8]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
[d347b53]241 // try to find suitable driver and assign it to the device
[957cfa58]242 assign_driver(node, &drivers_list);
[d347b53]243}
244
[692c40cb]245static void devman_add_device_to_class(ipc_callid_t callid, ipc_call_t *call)
246{
247 device_handle_t handle = IPC_GET_ARG1(*call);
248
249 // Get class name
250 char *class_name;
251 int rc = async_data_write_accept((void **)&class_name, true, 0, 0, 0, 0);
252 if (rc != EOK) {
253 ipc_answer_0(callid, rc);
254 return;
255 }
256
257 node_t *dev = find_dev_node(&device_tree, handle);
258 if (NULL == dev) {
259 ipc_answer_0(callid, ENOENT);
260 return;
261 }
262
263 dev_class_t *cl = get_dev_class(&class_list, class_name);
264
265 dev_class_info_t *class_info = add_device_to_class(dev, cl, NULL);
266
267 // TODO register the device's class alias by devmapper
268
269 printf(NAME ": device '%s' added to class '%s', class name '%s' was asigned to it\n", dev->pathname, class_name, class_info->dev_name);
270
271 ipc_answer_0(callid, EOK);
272}
273
[3843ecb]274/**
275 * Initialize driver which has registered itself as running and ready.
276 *
277 * The initialization is done in a separate fibril to avoid deadlocks
278 * (if the driver needed to be served by devman during the driver's initialization).
279 */
[d347b53]280static int init_running_drv(void *drv)
281{
282 driver_t *driver = (driver_t *)drv;
283 initialize_running_driver(driver);
284 printf(NAME ": the %s driver was successfully initialized. \n", driver->name);
285 return 0;
[bda60d9]286}
287
[3843ecb]288/** Function for handling connections from a driver to the device manager.
[e2b9a993]289 */
[924c75e1]290static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[729fa2d6]291{
[e2b9a993]292 /* Accept the connection */
293 ipc_answer_0(iid, EOK);
294
[729fa2d6]295 driver_t *driver = devman_driver_register();
[d347b53]296 if (NULL == driver)
[729fa2d6]297 return;
[d347b53]298
[3843ecb]299 // Initialize the driver as running (e.g. pass assigned devices to it) in a separate fibril;
300 // the separate fibril is used to enable the driver
301 // to use devman service during the driver's initialization.
[d347b53]302 fid_t fid = fibril_create(init_running_drv, driver);
303 if (fid == 0) {
304 printf(NAME ": Error creating fibril for the initialization of the newly registered running driver.\n");
[3843ecb]305 return;
[d347b53]306 }
307 fibril_add_ready(fid);
308
309 /*thread_id_t tid;
310 if (0 != thread_create(init_running_drv, driver, "init_running_drv", &tid)) {
311 printf(NAME ": failed to start the initialization of the newly registered running driver.\n");
312 }*/
[729fa2d6]313
314 ipc_callid_t callid;
315 ipc_call_t call;
316 bool cont = true;
317 while (cont) {
[e2b9a993]318 callid = async_get_call(&call);
319
320 switch (IPC_GET_METHOD(call)) {
[729fa2d6]321 case IPC_M_PHONE_HUNGUP:
322 cont = false;
323 continue;
[e2b9a993]324 case DEVMAN_ADD_CHILD_DEVICE:
[3843ecb]325 devman_add_child(callid, &call);
[e2b9a993]326 break;
[692c40cb]327 case DEVMAN_ADD_DEVICE_TO_CLASS:
328 devman_add_device_to_class(callid, &call);
329 break;
[e2b9a993]330 default:
331 ipc_answer_0(callid, EINVAL);
332 break;
333 }
334 }
335}
336
[f658458]337/** Find handle for the device instance identified by the device's path in the device tree.
338 */
339static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
340{
[c9f3b45c]341 char *pathname;
342 int rc = async_data_write_accept((void **)&pathname, true, 0, 0, 0, 0);
[f658458]343 if (rc != EOK) {
344 ipc_answer_0(iid, rc);
345 return;
346 }
347
348 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
349
350 free(pathname);
351
352 if (NULL == dev) {
353 ipc_answer_0(iid, ENOENT);
354 return;
355 }
356
357 ipc_answer_1(iid, EOK, dev->handle);
358}
359
360
361/** Function for handling connections from a client to the device manager.
362 */
363static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
364{
365 /* Accept connection */
366 ipc_answer_0(iid, EOK);
367
368 bool cont = true;
369 while (cont) {
370 ipc_call_t call;
371 ipc_callid_t callid = async_get_call(&call);
372
373 switch (IPC_GET_METHOD(call)) {
374 case IPC_M_PHONE_HUNGUP:
375 cont = false;
376 continue;
377 case DEVMAN_DEVICE_GET_HANDLE:
378 devman_device_get_handle(callid, &call);
379 break;
380 default:
381 if (!(callid & IPC_CALLID_NOTIFICATION))
382 ipc_answer_0(callid, ENOENT);
383 }
384 }
385}
386
[9a66bc2e]387static void devman_forward(ipc_callid_t iid, ipc_call_t *icall, bool drv_to_parent) {
388
389 device_handle_t handle = IPC_GET_ARG2(*icall);
[2480e19]390 // printf(NAME ": devman_forward - trying to forward connection to device with handle %x.\n", handle);
[9a66bc2e]391
[5cd136ab]392 node_t *dev = find_dev_node(&device_tree, handle);
393 if (NULL == dev) {
[9a66bc2e]394 printf(NAME ": devman_forward error - no device with handle %x was found.\n", handle);
[5cd136ab]395 ipc_answer_0(iid, ENOENT);
396 return;
397 }
398
399 driver_t *driver = NULL;
400
401 if (drv_to_parent) {
[9a66bc2e]402 if (NULL != dev->parent) {
403 driver = dev->parent->drv;
404 }
[df747b9c]405 } else if (DEVICE_USABLE == dev->state) {
[5cd136ab]406 driver = dev->drv;
[df747b9c]407 assert(NULL != driver);
[5cd136ab]408 }
409
[9a66bc2e]410 if (NULL == driver) {
[df747b9c]411 printf(NAME ": devman_forward error - the device is not in usable state.\n", handle);
[5cd136ab]412 ipc_answer_0(iid, ENOENT);
413 return;
414 }
415
416 int method;
417 if (drv_to_parent) {
418 method = DRIVER_DRIVER;
419 } else {
420 method = DRIVER_CLIENT;
421 }
422
[9a66bc2e]423 if (driver->phone <= 0) {
[3843ecb]424 printf(NAME ": devman_forward: cound not forward to driver %s ", driver->name);
425 printf("the driver's phone is %x).\n", driver->phone);
[9a66bc2e]426 return;
427 }
[df747b9c]428 printf(NAME ": devman_forward: forward connection to device %s to driver %s.\n", dev->pathname, driver->name);
[5cd136ab]429 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
430}
431
[924c75e1]432/** Function for handling connections to device manager.
433 *
434 */
435static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
436{
437 // Select interface
438 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
439 case DEVMAN_DRIVER:
440 devman_connection_driver(iid, icall);
441 break;
[f658458]442 case DEVMAN_CLIENT:
443 devman_connection_client(iid, icall);
444 break;
[924c75e1]445 case DEVMAN_CONNECT_TO_DEVICE:
446 // Connect client to selected device
[5cd136ab]447 devman_forward(iid, icall, false);
448 break;
449 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
450 // Connect client to selected device
451 devman_forward(iid, icall, true);
452 break;
[924c75e1]453 default:
454 /* No such interface */
455 ipc_answer_0(iid, ENOENT);
456 }
457}
458
[e2b9a993]459/** Initialize device manager internal structures.
460 */
461static bool devman_init()
462{
[0c3666d]463 printf(NAME ": devman_init - looking for available drivers. \n");
[08d9c4e6]464
[e2b9a993]465 // initialize list of available drivers
[0c3666d]466 init_driver_list(&drivers_list);
[e2b9a993]467 if (0 == lookup_available_drivers(&drivers_list, DRIVER_DEFAULT_STORE)) {
468 printf(NAME " no drivers found.");
469 return false;
470 }
[08d9c4e6]471 printf(NAME ": devman_init - list of drivers has been initialized. \n");
[e2b9a993]472
473 // create root device node
474 if (!init_device_tree(&device_tree, &drivers_list)) {
475 printf(NAME " failed to initialize device tree.");
476 return false;
477 }
478
[692c40cb]479 init_class_list(&class_list);
480
[e2b9a993]481 return true;
482}
483
484int main(int argc, char *argv[])
485{
486 printf(NAME ": HelenOS Device Manager\n");
487
488 if (!devman_init()) {
489 printf(NAME ": Error while initializing service\n");
490 return -1;
491 }
492
493 // Set a handler of incomming connections
494 async_set_client_connection(devman_connection);
495
496 // Register device manager at naming service
497 ipcarg_t phonead;
498 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
499 return -1;
500
501 printf(NAME ": Accepting connections\n");
502 async_manager();
503
504 // Never reached
505 return 0;
506}
[c16cf62]507
508/** @}
509 */
Note: See TracBrowser for help on using the repository browser.