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
Line 
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>
47#include <str.h>
48#include <dirent.h>
49#include <fcntl.h>
50#include <sys/stat.h>
51#include <ctype.h>
52#include <ipc/devman.h>
53#include <ipc/driver.h>
54#include <thread.h>
55#include <devmap.h>
56
57#include "devman.h"
58
59#define DRIVER_DEFAULT_STORE "/srv/drivers"
60
61static driver_list_t drivers_list;
62static dev_tree_t device_tree;
63static class_list_t class_list;
64
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
81 char *drv_name = NULL;
82
83 // Get driver name
84 int rc = async_data_write_accept((void **)&drv_name, true, 0, 0, 0, 0);
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);
93
94 if (NULL == driver) {
95 printf(NAME ": no driver named %s was found.\n", drv_name);
96 free(drv_name);
97 drv_name = NULL;
98 ipc_answer_0(iid, ENOENT);
99 return NULL;
100 }
101
102 free(drv_name);
103 drv_name = NULL;
104
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
115 // remember driver's phone
116 set_driver_phone(driver, IPC_GET_ARG5(call));
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}
126
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 */
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
155 ipc_answer_0(callid, EOK);
156
157 match_id->score = IPC_GET_ARG1(call);
158
159 char *match_id_str;
160 rc = async_data_write_accept((void **)&match_id_str, true, 0, 0, 0, 0);
161 match_id->id = match_id_str;
162 if (EOK != rc) {
163 delete_match_id(match_id);
164 printf(NAME ": devman_receive_match_id - failed to receive match id string.\n");
165 return rc;
166 }
167
168 list_append(&match_id->link, &match_ids->ids);
169
170 printf(NAME ": received match id '%s', score = %d \n", match_id->id, match_id->score);
171 return rc;
172}
173
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 */
183static int devman_receive_match_ids(ipcarg_t match_count, match_id_list_t *match_ids)
184{
185 int ret = EOK;
186 size_t i;
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
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)
200{
201 //printf(NAME ": devman_add_child\n");
202
203 device_handle_t parent_handle = IPC_GET_ARG1(*call);
204 ipcarg_t match_count = IPC_GET_ARG2(*call);
205 dev_tree_t *tree = &device_tree;
206
207 fibril_rwlock_write_lock(&tree->rwlock);
208 node_t *parent = find_dev_node_no_lock(&device_tree, parent_handle);
209
210 if (NULL == parent) {
211 fibril_rwlock_write_unlock(&tree->rwlock);
212 ipc_answer_0(callid, ENOENT);
213 return;
214 }
215
216 char *dev_name = NULL;
217 int rc = async_data_write_accept((void **)&dev_name, true, 0, 0, 0, 0);
218 if (EOK != rc) {
219 fibril_rwlock_write_unlock(&tree->rwlock);
220 ipc_answer_0(callid, rc);
221 return;
222 }
223 //printf(NAME ": newly added child device's name is '%s'.\n", dev_name);
224
225 node_t *node = create_dev_node();
226 if (!insert_dev_node(&device_tree, node, dev_name, parent)) {
227 fibril_rwlock_write_unlock(&tree->rwlock);
228 delete_dev_node(node);
229 ipc_answer_0(callid, ENOMEM);
230 return;
231 }
232 fibril_rwlock_write_unlock(&tree->rwlock);
233
234 printf(NAME ": devman_add_child %s\n", node->pathname);
235
236 devman_receive_match_ids(match_count, &node->match_ids);
237
238 // return device handle to parent's driver
239 ipc_answer_1(callid, EOK, node->handle);
240
241 // try to find suitable driver and assign it to the device
242 assign_driver(node, &drivers_list, &device_tree);
243}
244
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;
249 asprintf(&devmap_pathname, "%s/%s%c%s", DEVMAP_CLASS_NAMESPACE, cli->dev_class->name, DEVMAP_SEPARATOR, cli->dev_name);
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
257 // add device to the hash map of class devices registered by device mapper
258 class_add_devmap_device(&class_list, cli);
259
260 free(devmap_pathname);
261}
262
263static void devman_add_device_to_class(ipc_callid_t callid, ipc_call_t *call)
264{
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);
282
283 dev_class_info_t *class_info = add_device_to_class(dev, cl, NULL);
284
285 // register the device's class alias by devmapper
286 devmap_register_class_dev(class_info);
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
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 */
299static int init_running_drv(void *drv)
300{
301 driver_t *driver = (driver_t *)drv;
302 initialize_running_driver(driver, &device_tree);
303 printf(NAME ": the %s driver was successfully initialized. \n", driver->name);
304 return 0;
305}
306
307/** Function for handling connections from a driver to the device manager.
308 */
309static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
310{
311 /* Accept the connection */
312 ipc_answer_0(iid, EOK);
313
314 driver_t *driver = devman_driver_register();
315 if (NULL == driver)
316 return;
317
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.
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");
324 return;
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 }*/
332
333 ipc_callid_t callid;
334 ipc_call_t call;
335 bool cont = true;
336 while (cont) {
337 callid = async_get_call(&call);
338
339 switch (IPC_GET_METHOD(call)) {
340 case IPC_M_PHONE_HUNGUP:
341 cont = false;
342 continue;
343 case DEVMAN_ADD_CHILD_DEVICE:
344 devman_add_child(callid, &call);
345 break;
346 case DEVMAN_ADD_DEVICE_TO_CLASS:
347 devman_add_device_to_class(callid, &call);
348 break;
349 default:
350 ipc_answer_0(callid, EINVAL);
351 break;
352 }
353 }
354}
355
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{
360 char *pathname;
361 int rc = async_data_write_accept((void **)&pathname, true, 0, 0, 0, 0);
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
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);
409 // printf(NAME ": devman_forward - trying to forward connection to device with handle %x.\n", handle);
410
411 node_t *dev = find_dev_node(&device_tree, handle);
412 if (NULL == dev) {
413 printf(NAME ": devman_forward error - no device with handle %x was found.\n", handle);
414 ipc_answer_0(iid, ENOENT);
415 return;
416 }
417
418 driver_t *driver = NULL;
419
420 if (drv_to_parent) {
421 if (NULL != dev->parent) {
422 driver = dev->parent->drv;
423 }
424 } else if (DEVICE_USABLE == dev->state) {
425 driver = dev->drv;
426 assert(NULL != driver);
427 }
428
429 if (NULL == driver) {
430 printf(NAME ": devman_forward error - the device is not in usable state.\n", handle);
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
442 if (driver->phone <= 0) {
443 printf(NAME ": devman_forward: cound not forward to driver %s ", driver->name);
444 printf("the driver's phone is %x).\n", driver->phone);
445 ipc_answer_0(iid, EINVAL);
446 return;
447 }
448 printf(NAME ": devman_forward: forward connection to device %s to driver %s.\n",
449 dev->pathname, driver->name);
450 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
451}
452
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
478/** Function for handling connections to device manager.
479 *
480 */
481static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
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
496 // Select interface
497 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
498 case DEVMAN_DRIVER:
499 devman_connection_driver(iid, icall);
500 break;
501 case DEVMAN_CLIENT:
502 devman_connection_client(iid, icall);
503 break;
504 case DEVMAN_CONNECT_TO_DEVICE:
505 // Connect client to selected device
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;
512 default:
513 /* No such interface */
514 ipc_answer_0(iid, ENOENT);
515 }
516}
517
518/** Initialize device manager internal structures.
519 */
520static bool devman_init()
521{
522 printf(NAME ": devman_init - looking for available drivers. \n");
523
524 // initialize list of available drivers
525 init_driver_list(&drivers_list);
526 if (0 == lookup_available_drivers(&drivers_list, DRIVER_DEFAULT_STORE)) {
527 printf(NAME " no drivers found.");
528 return false;
529 }
530 printf(NAME ": devman_init - list of drivers has been initialized. \n");
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
538 init_class_list(&class_list);
539
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
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}
571
572/** @}
573 */
Note: See TracBrowser for help on using the repository browser.