source: mainline/uspace/srv/devman/main.c@ 7e752b2

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