source: mainline/uspace/srv/devman/main.c@ 3ca3430

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3ca3430 was 47a7174f, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

Devmap drivers can customize forwarded connections

It is possible to set an extra parameter for forwarded connections through
devmap. The change shall ensure backward compatibility and allows to connect
to devman-style drivers through their devmap path.

  • 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_IMETHOD(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_IMETHOD(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_IMETHOD(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(sysarg_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
199static int assign_driver_fibril(void *arg)
200{
201 node_t *node = (node_t *) arg;
202 assign_driver(node, &drivers_list, &device_tree);
203 return EOK;
204}
205
206/** Handle child device registration.
207 *
208 * Child devices are registered by their parent's device driver.
209 */
210static void devman_add_child(ipc_callid_t callid, ipc_call_t *call)
211{
212 devman_handle_t parent_handle = IPC_GET_ARG1(*call);
213 sysarg_t match_count = IPC_GET_ARG2(*call);
214 dev_tree_t *tree = &device_tree;
215
216 fibril_rwlock_write_lock(&tree->rwlock);
217 node_t *parent = find_dev_node_no_lock(&device_tree, parent_handle);
218
219 if (parent == NULL) {
220 fibril_rwlock_write_unlock(&tree->rwlock);
221 ipc_answer_0(callid, ENOENT);
222 return;
223 }
224
225 char *dev_name = NULL;
226 int rc = async_data_write_accept((void **)&dev_name, true, 0, 0, 0, 0);
227 if (rc != EOK) {
228 fibril_rwlock_write_unlock(&tree->rwlock);
229 ipc_answer_0(callid, rc);
230 return;
231 }
232
233 node_t *node = create_dev_node();
234 if (!insert_dev_node(&device_tree, node, dev_name, parent)) {
235 fibril_rwlock_write_unlock(&tree->rwlock);
236 delete_dev_node(node);
237 ipc_answer_0(callid, ENOMEM);
238 return;
239 }
240
241 fibril_rwlock_write_unlock(&tree->rwlock);
242
243 printf(NAME ": devman_add_child %s\n", node->pathname);
244
245 devman_receive_match_ids(match_count, &node->match_ids);
246
247 /*
248 * Try to find a suitable driver and assign it to the device. We do
249 * not want to block the current fibril that is used for processing
250 * incoming calls: we will launch a separate fibril to handle the
251 * driver assigning. That is because assign_driver can actually include
252 * task spawning which could take some time.
253 */
254 fid_t assign_fibril = fibril_create(assign_driver_fibril, node);
255 if (assign_fibril == 0) {
256 /*
257 * Fallback in case we are out of memory.
258 * Probably not needed as we will die soon anyway ;-).
259 */
260 (void) assign_driver_fibril(node);
261 } else {
262 fibril_add_ready(assign_fibril);
263 }
264
265 /* Return device handle to parent's driver. */
266 ipc_answer_1(callid, EOK, node->handle);
267}
268
269static void devmap_register_class_dev(dev_class_info_t *cli)
270{
271 /* Create devmap path and name for the device. */
272 char *devmap_pathname = NULL;
273
274 asprintf(&devmap_pathname, "%s/%s%c%s", DEVMAP_CLASS_NAMESPACE,
275 cli->dev_class->name, DEVMAP_SEPARATOR, cli->dev_name);
276 if (devmap_pathname == NULL)
277 return;
278
279 /*
280 * Register the device by the device mapper and remember its devmap
281 * handle.
282 */
283 devmap_device_register(devmap_pathname, &cli->devmap_handle);
284
285 /*
286 * Add device to the hash map of class devices registered by device
287 * mapper.
288 */
289 class_add_devmap_device(&class_list, cli);
290
291 free(devmap_pathname);
292}
293
294static void devman_add_device_to_class(ipc_callid_t callid, ipc_call_t *call)
295{
296 devman_handle_t handle = IPC_GET_ARG1(*call);
297
298 /* Get class name. */
299 char *class_name;
300 int rc = async_data_write_accept((void **) &class_name, true,
301 0, 0, 0, 0);
302 if (rc != EOK) {
303 ipc_answer_0(callid, rc);
304 return;
305 }
306
307 node_t *dev = find_dev_node(&device_tree, handle);
308 if (dev == NULL) {
309 ipc_answer_0(callid, ENOENT);
310 return;
311 }
312
313 dev_class_t *cl = get_dev_class(&class_list, class_name);
314 dev_class_info_t *class_info = add_device_to_class(dev, cl, NULL);
315
316 /* Register the device's class alias by devmapper. */
317 devmap_register_class_dev(class_info);
318
319 printf(NAME ": device '%s' added to class '%s', class name '%s' was "
320 "asigned to it\n", dev->pathname, class_name, class_info->dev_name);
321
322 ipc_answer_0(callid, EOK);
323}
324
325/** Initialize driver which has registered itself as running and ready.
326 *
327 * The initialization is done in a separate fibril to avoid deadlocks (if the
328 * driver needed to be served by devman during the driver's initialization).
329 */
330static int init_running_drv(void *drv)
331{
332 driver_t *driver = (driver_t *) drv;
333
334 initialize_running_driver(driver, &device_tree);
335 printf(NAME ": the %s driver was successfully initialized. \n",
336 driver->name);
337 return 0;
338}
339
340/** Function for handling connections from a driver to the device manager. */
341static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
342{
343 /* Accept the connection. */
344 ipc_answer_0(iid, EOK);
345
346 driver_t *driver = devman_driver_register();
347 if (driver == NULL)
348 return;
349
350 /*
351 * Initialize the driver as running (e.g. pass assigned devices to it)
352 * in a separate fibril; the separate fibril is used to enable the
353 * driver to use devman service during the driver's initialization.
354 */
355 fid_t fid = fibril_create(init_running_drv, driver);
356 if (fid == 0) {
357 printf(NAME ": Error creating fibril for the initialization of "
358 "the newly registered running driver.\n");
359 return;
360 }
361 fibril_add_ready(fid);
362
363 ipc_callid_t callid;
364 ipc_call_t call;
365 bool cont = true;
366 while (cont) {
367 callid = async_get_call(&call);
368
369 switch (IPC_GET_IMETHOD(call)) {
370 case IPC_M_PHONE_HUNGUP:
371 cont = false;
372 continue;
373 case DEVMAN_ADD_CHILD_DEVICE:
374 devman_add_child(callid, &call);
375 break;
376 case DEVMAN_ADD_DEVICE_TO_CLASS:
377 devman_add_device_to_class(callid, &call);
378 break;
379 default:
380 ipc_answer_0(callid, EINVAL);
381 break;
382 }
383 }
384}
385
386/** Find handle for the device instance identified by the device's path in the
387 * device tree. */
388static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
389{
390 char *pathname;
391
392 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
393 if (rc != EOK) {
394 ipc_answer_0(iid, rc);
395 return;
396 }
397
398 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
399
400 free(pathname);
401
402 if (dev == NULL) {
403 ipc_answer_0(iid, ENOENT);
404 return;
405 }
406
407 ipc_answer_1(iid, EOK, dev->handle);
408}
409
410
411/** Function for handling connections from a client to the device manager. */
412static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
413{
414 /* Accept connection. */
415 ipc_answer_0(iid, EOK);
416
417 bool cont = true;
418 while (cont) {
419 ipc_call_t call;
420 ipc_callid_t callid = async_get_call(&call);
421
422 switch (IPC_GET_IMETHOD(call)) {
423 case IPC_M_PHONE_HUNGUP:
424 cont = false;
425 continue;
426 case DEVMAN_DEVICE_GET_HANDLE:
427 devman_device_get_handle(callid, &call);
428 break;
429 default:
430 ipc_answer_0(callid, ENOENT);
431 }
432 }
433}
434
435static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
436 bool drv_to_parent)
437{
438 devman_handle_t handle = IPC_GET_ARG2(*icall);
439
440 node_t *dev = find_dev_node(&device_tree, handle);
441 if (dev == NULL) {
442 printf(NAME ": devman_forward error - no device with handle %" PRIun
443 " was found.\n", handle);
444 ipc_answer_0(iid, ENOENT);
445 return;
446 }
447
448 driver_t *driver = NULL;
449
450 if (drv_to_parent) {
451 if (dev->parent != NULL)
452 driver = dev->parent->drv;
453 } else if (dev->state == DEVICE_USABLE) {
454 driver = dev->drv;
455 assert(driver != NULL);
456 }
457
458 if (driver == NULL) {
459 printf(NAME ": devman_forward error - the device is not in %" PRIun
460 " usable state.\n", handle);
461 ipc_answer_0(iid, ENOENT);
462 return;
463 }
464
465 int method;
466 if (drv_to_parent)
467 method = DRIVER_DRIVER;
468 else
469 method = DRIVER_CLIENT;
470
471 if (driver->phone <= 0) {
472 printf(NAME ": devman_forward: cound not forward to driver %s ",
473 driver->name);
474 printf("the driver's phone is %" PRIun ").\n", driver->phone);
475 ipc_answer_0(iid, EINVAL);
476 return;
477 }
478
479 printf(NAME ": devman_forward: forward connection to device %s to "
480 "driver %s.\n", dev->pathname, driver->name);
481 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
482}
483
484/** Function for handling connections from a client forwarded by the device
485 * mapper to the device manager. */
486static void devman_connection_devmapper(ipc_callid_t iid, ipc_call_t *icall)
487{
488 devmap_handle_t devmap_handle = IPC_GET_ARG2(*icall);
489 node_t *dev;
490
491 dev = find_devmap_tree_device(&device_tree, devmap_handle);
492 if (dev == NULL)
493 dev = find_devmap_class_device(&class_list, devmap_handle);
494
495 if (dev == NULL || dev->drv == NULL) {
496 ipc_answer_0(iid, ENOENT);
497 return;
498 }
499
500 if (dev->state != DEVICE_USABLE || dev->drv->phone <= 0) {
501 ipc_answer_0(iid, EINVAL);
502 return;
503 }
504
505 ipc_forward_fast(iid, dev->drv->phone, DRIVER_CLIENT, dev->handle, 0,
506 IPC_FF_NONE);
507 printf(NAME ": devman_connection_devmapper: forwarded connection to "
508 "device %s to driver %s.\n", dev->pathname, dev->drv->name);
509}
510
511/** Function for handling connections to device manager. */
512static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
513{
514 /* Select interface. */
515 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
516 case DEVMAN_DRIVER:
517 devman_connection_driver(iid, icall);
518 break;
519 case DEVMAN_CLIENT:
520 devman_connection_client(iid, icall);
521 break;
522 case DEVMAN_CONNECT_TO_DEVICE:
523 /* Connect client to selected device. */
524 devman_forward(iid, icall, false);
525 break;
526 case DEVMAN_CONNECT_FROM_DEVMAP:
527 /* Someone connected through devmap node. */
528 devman_connection_devmapper(iid, icall);
529 break;
530 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
531 /* Connect client to selected device. */
532 devman_forward(iid, icall, true);
533 break;
534 default:
535 /* No such interface */
536 ipc_answer_0(iid, ENOENT);
537 }
538}
539
540/** Initialize device manager internal structures. */
541static bool devman_init(void)
542{
543 printf(NAME ": devman_init - looking for available drivers.\n");
544
545 /* Initialize list of available drivers. */
546 init_driver_list(&drivers_list);
547 if (lookup_available_drivers(&drivers_list,
548 DRIVER_DEFAULT_STORE) == 0) {
549 printf(NAME " no drivers found.");
550 return false;
551 }
552
553 printf(NAME ": devman_init - list of drivers has been initialized.\n");
554
555 /* Create root device node. */
556 if (!init_device_tree(&device_tree, &drivers_list)) {
557 printf(NAME " failed to initialize device tree.");
558 return false;
559 }
560
561 init_class_list(&class_list);
562
563 /*
564 * !!! devman_connection ... as the device manager is not a real devmap
565 * driver (it uses a completely different ipc protocol than an ordinary
566 * devmap driver) forwarding a connection from client to the devman by
567 * devmapper would not work.
568 */
569 devmap_driver_register(NAME, devman_connection);
570
571 return true;
572}
573
574int main(int argc, char *argv[])
575{
576 printf(NAME ": HelenOS Device Manager\n");
577
578 if (!devman_init()) {
579 printf(NAME ": Error while initializing service\n");
580 return -1;
581 }
582
583 /* Set a handler of incomming connections. */
584 async_set_client_connection(devman_connection);
585
586 /* Register device manager at naming service. */
587 sysarg_t phonead;
588 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
589 return -1;
590
591 printf(NAME ": Accepting connections\n");
592 async_manager();
593
594 /* Never reached. */
595 return 0;
596}
597
598/** @}
599 */
Note: See TracBrowser for help on using the repository browser.