source: mainline/uspace/srv/devman/main.c@ 0b5a4131

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

Rename device_handle_t to devman_handle_t and make it explicitly clear that
device_handle_t is a handle understood by 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 ipc_call_t icall;
69 ipc_callid_t iid;
70 driver_t *driver = NULL;
71
72 printf(NAME ": devman_driver_register \n");
73
74 iid = async_get_call(&icall);
75 if (IPC_GET_METHOD(icall) != DEVMAN_DRIVER_REGISTER) {
76 ipc_answer_0(iid, EREFUSED);
77 return NULL;
78 }
79
80 char *drv_name = NULL;
81
82 /* Get driver name. */
83 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
84 if (rc != EOK) {
85 ipc_answer_0(iid, rc);
86 return NULL;
87 }
88
89 printf(NAME ": the %s driver is trying to register by the service.\n",
90 drv_name);
91
92 /* Find driver structure. */
93 driver = find_driver(&drivers_list, drv_name);
94
95 if (driver == NULL) {
96 printf(NAME ": no driver named %s was found.\n", drv_name);
97 free(drv_name);
98 drv_name = NULL;
99 ipc_answer_0(iid, ENOENT);
100 return NULL;
101 }
102
103 free(drv_name);
104 drv_name = NULL;
105
106 /* Create connection to the driver. */
107 printf(NAME ": creating connection to the %s driver.\n", driver->name);
108 ipc_call_t call;
109 ipc_callid_t callid = async_get_call(&call);
110 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
111 ipc_answer_0(callid, ENOTSUP);
112 ipc_answer_0(iid, ENOTSUP);
113 return NULL;
114 }
115
116 /* Remember driver's phone. */
117 set_driver_phone(driver, IPC_GET_ARG5(call));
118
119 printf(NAME ": the %s driver was successfully registered as running.\n",
120 driver->name);
121
122 ipc_answer_0(callid, EOK);
123 ipc_answer_0(iid, EOK);
124
125 return driver;
126}
127
128/** Receive device match ID from the device's parent driver and add it to the
129 * list of devices match ids.
130 *
131 * @param match_ids The list of the device's match ids.
132 * @return Zero 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 "
144 "protocol.\n");
145 ipc_answer_0(callid, EINVAL);
146 delete_match_id(match_id);
147 return EINVAL;
148 }
149
150 if (match_id == NULL) {
151 printf(NAME ": ERROR: devman_receive_match_id - failed to "
152 "allocate match id.\n");
153 ipc_answer_0(callid, ENOMEM);
154 return ENOMEM;
155 }
156
157 ipc_answer_0(callid, EOK);
158
159 match_id->score = IPC_GET_ARG1(call);
160
161 char *match_id_str;
162 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
163 match_id->id = match_id_str;
164 if (rc != EOK) {
165 delete_match_id(match_id);
166 printf(NAME ": devman_receive_match_id - failed to receive "
167 "match id string.\n");
168 return rc;
169 }
170
171 list_append(&match_id->link, &match_ids->ids);
172
173 printf(NAME ": received match id '%s', score = %d \n",
174 match_id->id, match_id->score);
175 return rc;
176}
177
178/** Receive device match IDs from the device's parent driver and add them to the
179 * list of devices match ids.
180 *
181 * @param match_count The number of device's match ids to be received.
182 * @param match_ids The list of the device's match ids.
183 * @return Zero on success, negative error code otherwise.
184 */
185static int devman_receive_match_ids(ipcarg_t match_count,
186 match_id_list_t *match_ids)
187{
188 int ret = EOK;
189 size_t i;
190
191 for (i = 0; i < match_count; i++) {
192 if (EOK != (ret = devman_receive_match_id(match_ids)))
193 return ret;
194 }
195 return ret;
196}
197
198/** Handle child device registration.
199 *
200 * Child devices are registered by their parent's device driver.
201 */
202static void devman_add_child(ipc_callid_t callid, ipc_call_t *call)
203{
204 devman_handle_t parent_handle = IPC_GET_ARG1(*call);
205 ipcarg_t match_count = IPC_GET_ARG2(*call);
206 dev_tree_t *tree = &device_tree;
207
208 fibril_rwlock_write_lock(&tree->rwlock);
209 node_t *parent = find_dev_node_no_lock(&device_tree, parent_handle);
210
211 if (parent == NULL) {
212 fibril_rwlock_write_unlock(&tree->rwlock);
213 ipc_answer_0(callid, ENOENT);
214 return;
215 }
216
217 char *dev_name = NULL;
218 int rc = async_data_write_accept((void **)&dev_name, true, 0, 0, 0, 0);
219 if (rc != EOK) {
220 fibril_rwlock_write_unlock(&tree->rwlock);
221 ipc_answer_0(callid, rc);
222 return;
223 }
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
233 fibril_rwlock_write_unlock(&tree->rwlock);
234
235 printf(NAME ": devman_add_child %s\n", node->pathname);
236
237 devman_receive_match_ids(match_count, &node->match_ids);
238
239 /* Return device handle to parent's driver. */
240 ipc_answer_1(callid, EOK, node->handle);
241
242 /* Try to find suitable driver and assign it to the device. */
243 assign_driver(node, &drivers_list, &device_tree);
244}
245
246static void devmap_register_class_dev(dev_class_info_t *cli)
247{
248 /* Create devmap path and name for the device. */
249 char *devmap_pathname = NULL;
250
251 asprintf(&devmap_pathname, "%s/%s%c%s", DEVMAP_CLASS_NAMESPACE,
252 cli->dev_class->name, DEVMAP_SEPARATOR, cli->dev_name);
253 if (devmap_pathname == NULL)
254 return;
255
256 /*
257 * Register the device by the device mapper and remember its devmap
258 * handle.
259 */
260 devmap_device_register(devmap_pathname, &cli->devmap_handle);
261
262 /*
263 * Add device to the hash map of class devices registered by device
264 * mapper.
265 */
266 class_add_devmap_device(&class_list, cli);
267
268 free(devmap_pathname);
269}
270
271static void devman_add_device_to_class(ipc_callid_t callid, ipc_call_t *call)
272{
273 devman_handle_t handle = IPC_GET_ARG1(*call);
274
275 /* Get class name. */
276 char *class_name;
277 int rc = async_data_write_accept((void **) &class_name, true,
278 0, 0, 0, 0);
279 if (rc != EOK) {
280 ipc_answer_0(callid, rc);
281 return;
282 }
283
284 node_t *dev = find_dev_node(&device_tree, handle);
285 if (dev == NULL) {
286 ipc_answer_0(callid, ENOENT);
287 return;
288 }
289
290 dev_class_t *cl = get_dev_class(&class_list, class_name);
291 dev_class_info_t *class_info = add_device_to_class(dev, cl, NULL);
292
293 /* Register the device's class alias by devmapper. */
294 devmap_register_class_dev(class_info);
295
296 printf(NAME ": device '%s' added to class '%s', class name '%s' was "
297 "asigned to it\n", dev->pathname, class_name, class_info->dev_name);
298
299 ipc_answer_0(callid, EOK);
300}
301
302/** Initialize driver which has registered itself as running and ready.
303 *
304 * The initialization is done in a separate fibril to avoid deadlocks (if the
305 * driver needed to be served by devman during the driver's initialization).
306 */
307static int init_running_drv(void *drv)
308{
309 driver_t *driver = (driver_t *) drv;
310
311 initialize_running_driver(driver, &device_tree);
312 printf(NAME ": the %s driver was successfully initialized. \n",
313 driver->name);
314 return 0;
315}
316
317/** Function for handling connections from a driver to the device manager. */
318static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
319{
320 /* Accept the connection. */
321 ipc_answer_0(iid, EOK);
322
323 driver_t *driver = devman_driver_register();
324 if (driver == NULL)
325 return;
326
327 /*
328 * Initialize the driver as running (e.g. pass assigned devices to it)
329 * in a separate fibril; the separate fibril is used to enable the
330 * driver to use devman service during the driver's initialization.
331 */
332 fid_t fid = fibril_create(init_running_drv, driver);
333 if (fid == 0) {
334 printf(NAME ": Error creating fibril for the initialization of "
335 "the newly registered running driver.\n");
336 return;
337 }
338 fibril_add_ready(fid);
339
340 ipc_callid_t callid;
341 ipc_call_t call;
342 bool cont = true;
343 while (cont) {
344 callid = async_get_call(&call);
345
346 switch (IPC_GET_METHOD(call)) {
347 case IPC_M_PHONE_HUNGUP:
348 cont = false;
349 continue;
350 case DEVMAN_ADD_CHILD_DEVICE:
351 devman_add_child(callid, &call);
352 break;
353 case DEVMAN_ADD_DEVICE_TO_CLASS:
354 devman_add_device_to_class(callid, &call);
355 break;
356 default:
357 ipc_answer_0(callid, EINVAL);
358 break;
359 }
360 }
361}
362
363/** Find handle for the device instance identified by the device's path in the
364 * device tree. */
365static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
366{
367 char *pathname;
368
369 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
370 if (rc != EOK) {
371 ipc_answer_0(iid, rc);
372 return;
373 }
374
375 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
376
377 free(pathname);
378
379 if (dev == NULL) {
380 ipc_answer_0(iid, ENOENT);
381 return;
382 }
383
384 ipc_answer_1(iid, EOK, dev->handle);
385}
386
387
388/** Function for handling connections from a client to the device manager. */
389static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
390{
391 /* Accept connection. */
392 ipc_answer_0(iid, EOK);
393
394 bool cont = true;
395 while (cont) {
396 ipc_call_t call;
397 ipc_callid_t callid = async_get_call(&call);
398
399 switch (IPC_GET_METHOD(call)) {
400 case IPC_M_PHONE_HUNGUP:
401 cont = false;
402 continue;
403 case DEVMAN_DEVICE_GET_HANDLE:
404 devman_device_get_handle(callid, &call);
405 break;
406 default:
407 if (!(callid & IPC_CALLID_NOTIFICATION))
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 %x "
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 "
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 %x).\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.