source: mainline/uspace/srv/devman/main.c@ 09d5ec8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 09d5ec8 was 38b3baf, checked in by Jakub Jermar <jakub@…>, 15 years ago

Cstyle fixes of devman.

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