source: mainline/uspace/srv/devman/main.c@ c47e1a8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c47e1a8 was c47e1a8, checked in by Lenka Trochtova <trochtova.lenka@…>, 15 years ago

merge mainline changes (rev. 451)

  • Property mode set to 100644
File size: 13.6 KB
RevLine 
[e2b9a993]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>
[c47e1a8]47#include <str.h>
[e2b9a993]48#include <dirent.h>
49#include <fcntl.h>
50#include <sys/stat.h>
51#include <ctype.h>
52#include <ipc/devman.h>
[5cd136ab]53#include <ipc/driver.h>
[d347b53]54#include <thread.h>
[e2b9a993]55
56#include "devman.h"
57
58#define DRIVER_DEFAULT_STORE "/srv/drivers"
59
[0c3666d]60static driver_list_t drivers_list;
[e2b9a993]61static dev_tree_t device_tree;
62
[c47e1a8]63/** Wrapper for receiving strings
64 *
65 * This wrapper only makes it more comfortable to use async_data_write_*
66 * functions to receive strings.
67 *
68 * @param str Pointer to string pointer (which should be later disposed
69 * by free()). If the operation fails, the pointer is not
70 * touched.
71 * @param max_size Maximum size (in bytes) of the string to receive. 0 means
72 * no limit.
73 * @param received If not NULL, the size of the received data is stored here.
74 *
75 * @return Zero on success or a value from @ref errno.h on failure.
76 *
77 */
78static int async_string_receive(char **str, const size_t max_size, size_t *received)
79{
80 ipc_callid_t callid;
81 size_t size;
82 if (!async_data_write_receive(&callid, &size)) {
83 ipc_answer_0(callid, EINVAL);
84 return EINVAL;
85 }
86
87 if ((max_size > 0) && (size > max_size)) {
88 ipc_answer_0(callid, EINVAL);
89 return EINVAL;
90 }
91
92 char *data = (char *) malloc(size + 1);
93 if (data == NULL) {
94 ipc_answer_0(callid, ENOMEM);
95 return ENOMEM;
96 }
97
98 int rc = async_data_write_finalize(callid, data, size);
99 if (rc != EOK) {
100 free(data);
101 return rc;
102 }
103
104 data[size] = 0;
105 *str = data;
106 if (received != NULL)
107 *received = size;
108
109 return EOK;
110}
111
[729fa2d6]112/**
113 * Register running driver.
114 */
115static driver_t * devman_driver_register(void)
116{
117 printf(NAME ": devman_driver_register \n");
118
119 ipc_call_t icall;
120 ipc_callid_t iid = async_get_call(&icall);
121 driver_t *driver = NULL;
122
123 if (IPC_GET_METHOD(icall) != DEVMAN_DRIVER_REGISTER) {
124 ipc_answer_0(iid, EREFUSED);
125 return NULL;
126 }
127
[92413de]128 char *drv_name = NULL;
[729fa2d6]129
130 // Get driver name
[92413de]131 int rc = async_string_receive(&drv_name, DEVMAN_NAME_MAXLEN, NULL);
[729fa2d6]132 if (rc != EOK) {
133 ipc_answer_0(iid, rc);
134 return NULL;
135 }
136 printf(NAME ": the %s driver is trying to register by the service.\n", drv_name);
137
138 // Find driver structure
139 driver = find_driver(&drivers_list, drv_name);
[92413de]140
[729fa2d6]141 if (NULL == driver) {
142 printf(NAME ": no driver named %s was found.\n", drv_name);
[04c7003f]143 free(drv_name);
144 drv_name = NULL;
[729fa2d6]145 ipc_answer_0(iid, ENOENT);
146 return NULL;
147 }
148
[04c7003f]149 free(drv_name);
150 drv_name = NULL;
151
[729fa2d6]152 // Create connection to the driver
153 printf(NAME ": creating connection to the %s driver.\n", driver->name);
154 ipc_call_t call;
155 ipc_callid_t callid = async_get_call(&call);
156 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
157 ipc_answer_0(callid, ENOTSUP);
158 ipc_answer_0(iid, ENOTSUP);
159 return NULL;
160 }
161
[c16cf62]162 // remember driver's phone
163 set_driver_phone(driver, IPC_GET_ARG5(call));
[729fa2d6]164
165 printf(NAME ": the %s driver was successfully registered as running.\n", driver->name);
166
167 ipc_answer_0(callid, EOK);
168
169 ipc_answer_0(iid, EOK);
170
171 return driver;
172}
[e2b9a993]173
[3843ecb]174/**
175 * Receive device match ID from the device's parent driver and add it to the list of devices match ids.
176 *
177 * @param match_ids the list of the device's match ids.
178 *
179 * @return 0 on success, negative error code otherwise.
180 */
[66babbd]181static int devman_receive_match_id(match_id_list_t *match_ids) {
182
183 match_id_t *match_id = create_match_id();
184 ipc_callid_t callid;
185 ipc_call_t call;
186 int rc = 0;
187
188 callid = async_get_call(&call);
189 if (DEVMAN_ADD_MATCH_ID != IPC_GET_METHOD(call)) {
190 printf(NAME ": ERROR: devman_receive_match_id - invalid protocol.\n");
191 ipc_answer_0(callid, EINVAL);
192 delete_match_id(match_id);
193 return EINVAL;
194 }
195
196 if (NULL == match_id) {
197 printf(NAME ": ERROR: devman_receive_match_id - failed to allocate match id.\n");
198 ipc_answer_0(callid, ENOMEM);
199 return ENOMEM;
200 }
201
[2480e19]202 ipc_answer_0(callid, EOK);
203
[66babbd]204 match_id->score = IPC_GET_ARG1(call);
205
[c47e1a8]206 char *match_id_str;
207 rc = async_string_receive(&match_id_str, DEVMAN_NAME_MAXLEN, NULL);
208 match_id->id = match_id_str;
[66babbd]209 if (EOK != rc) {
210 delete_match_id(match_id);
[2480e19]211 printf(NAME ": devman_receive_match_id - failed to receive match id string.\n");
[66babbd]212 return rc;
213 }
214
215 list_append(&match_id->link, &match_ids->ids);
[a087f2e]216
217 printf(NAME ": received match id '%s', score = %d \n", match_id->id, match_id->score);
[66babbd]218 return rc;
219}
220
[3843ecb]221/**
222 * Receive device match IDs from the device's parent driver
223 * and add them to the list of devices match ids.
224 *
225 * @param match_count the number of device's match ids to be received.
226 * @param match_ids the list of the device's match ids.
227 *
228 * @return 0 on success, negative error code otherwise.
229 */
[66babbd]230static int devman_receive_match_ids(ipcarg_t match_count, match_id_list_t *match_ids)
[2480e19]231{
[66babbd]232 int ret = EOK;
[5cd136ab]233 size_t i;
[66babbd]234 for (i = 0; i < match_count; i++) {
235 if (EOK != (ret = devman_receive_match_id(match_ids))) {
236 return ret;
237 }
238 }
239 return ret;
240}
241
[3843ecb]242/** Handle child device registration.
243 *
244 * Child devices are registered by their parent's device driver.
245 */
246static void devman_add_child(ipc_callid_t callid, ipc_call_t *call)
[bda60d9]247{
[2480e19]248 // printf(NAME ": devman_add_child\n");
[66babbd]249
[d347b53]250 device_handle_t parent_handle = IPC_GET_ARG1(*call);
[66babbd]251 ipcarg_t match_count = IPC_GET_ARG2(*call);
252
[d347b53]253 node_t *parent = find_dev_node(&device_tree, parent_handle);
254
255 if (NULL == parent) {
256 ipc_answer_0(callid, ENOENT);
257 return;
[66babbd]258 }
[d347b53]259
260 char *dev_name = NULL;
261 int rc = async_string_receive(&dev_name, DEVMAN_NAME_MAXLEN, NULL);
[66babbd]262 if (EOK != rc) {
[d347b53]263 ipc_answer_0(callid, rc);
264 return;
265 }
[2480e19]266 // printf(NAME ": newly added child device's name is '%s'.\n", dev_name);
[d347b53]267
268 node_t *node = create_dev_node();
269 if (!insert_dev_node(&device_tree, node, dev_name, parent)) {
270 delete_dev_node(node);
271 ipc_answer_0(callid, ENOMEM);
272 return;
273 }
274
[2480e19]275 printf(NAME ": devman_add_child %s\n", node->pathname);
276
[66babbd]277 devman_receive_match_ids(match_count, &node->match_ids);
[bda60d9]278
[d347b53]279 // return device handle to parent's driver
280 ipc_answer_1(callid, EOK, node->handle);
[bda60d9]281
[d347b53]282 // try to find suitable driver and assign it to the device
283 assign_driver(node, &drivers_list);
284}
285
[3843ecb]286/**
287 * Initialize driver which has registered itself as running and ready.
288 *
289 * The initialization is done in a separate fibril to avoid deadlocks
290 * (if the driver needed to be served by devman during the driver's initialization).
291 */
[d347b53]292static int init_running_drv(void *drv)
293{
294 driver_t *driver = (driver_t *)drv;
295 initialize_running_driver(driver);
296 printf(NAME ": the %s driver was successfully initialized. \n", driver->name);
297 return 0;
[bda60d9]298}
299
[3843ecb]300/** Function for handling connections from a driver to the device manager.
[e2b9a993]301 */
[924c75e1]302static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[729fa2d6]303{
[e2b9a993]304 /* Accept the connection */
305 ipc_answer_0(iid, EOK);
306
[729fa2d6]307 driver_t *driver = devman_driver_register();
[d347b53]308 if (NULL == driver)
[729fa2d6]309 return;
[d347b53]310
[3843ecb]311 // Initialize the driver as running (e.g. pass assigned devices to it) in a separate fibril;
312 // the separate fibril is used to enable the driver
313 // to use devman service during the driver's initialization.
[d347b53]314 fid_t fid = fibril_create(init_running_drv, driver);
315 if (fid == 0) {
316 printf(NAME ": Error creating fibril for the initialization of the newly registered running driver.\n");
[3843ecb]317 return;
[d347b53]318 }
319 fibril_add_ready(fid);
320
321 /*thread_id_t tid;
322 if (0 != thread_create(init_running_drv, driver, "init_running_drv", &tid)) {
323 printf(NAME ": failed to start the initialization of the newly registered running driver.\n");
324 }*/
[729fa2d6]325
326 ipc_callid_t callid;
327 ipc_call_t call;
328 bool cont = true;
329 while (cont) {
[e2b9a993]330 callid = async_get_call(&call);
331
332 switch (IPC_GET_METHOD(call)) {
[729fa2d6]333 case IPC_M_PHONE_HUNGUP:
334 cont = false;
335 continue;
[e2b9a993]336 case DEVMAN_ADD_CHILD_DEVICE:
[3843ecb]337 devman_add_child(callid, &call);
[e2b9a993]338 break;
339 default:
340 ipc_answer_0(callid, EINVAL);
341 break;
342 }
343 }
344}
345
[f658458]346/** Find handle for the device instance identified by the device's path in the device tree.
347 */
348static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
349{
350 char *pathname;
351
352 /* Get fqdn */
353 int rc = async_string_receive(&pathname, 0, NULL);
354 if (rc != EOK) {
355 ipc_answer_0(iid, rc);
356 return;
357 }
358
359 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
360
361 free(pathname);
362
363 if (NULL == dev) {
364 ipc_answer_0(iid, ENOENT);
365 return;
366 }
367
368 ipc_answer_1(iid, EOK, dev->handle);
369}
370
371
372/** Function for handling connections from a client to the device manager.
373 */
374static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
375{
376 /* Accept connection */
377 ipc_answer_0(iid, EOK);
378
379 bool cont = true;
380 while (cont) {
381 ipc_call_t call;
382 ipc_callid_t callid = async_get_call(&call);
383
384 switch (IPC_GET_METHOD(call)) {
385 case IPC_M_PHONE_HUNGUP:
386 cont = false;
387 continue;
388 case DEVMAN_DEVICE_GET_HANDLE:
389 devman_device_get_handle(callid, &call);
390 break;
391 default:
392 if (!(callid & IPC_CALLID_NOTIFICATION))
393 ipc_answer_0(callid, ENOENT);
394 }
395 }
396}
397
[9a66bc2e]398static void devman_forward(ipc_callid_t iid, ipc_call_t *icall, bool drv_to_parent) {
399
400 device_handle_t handle = IPC_GET_ARG2(*icall);
[2480e19]401 // printf(NAME ": devman_forward - trying to forward connection to device with handle %x.\n", handle);
[9a66bc2e]402
[5cd136ab]403 node_t *dev = find_dev_node(&device_tree, handle);
404 if (NULL == dev) {
[9a66bc2e]405 printf(NAME ": devman_forward error - no device with handle %x was found.\n", handle);
[5cd136ab]406 ipc_answer_0(iid, ENOENT);
407 return;
408 }
409
410 driver_t *driver = NULL;
411
412 if (drv_to_parent) {
[9a66bc2e]413 if (NULL != dev->parent) {
414 driver = dev->parent->drv;
415 }
[df747b9c]416 } else if (DEVICE_USABLE == dev->state) {
[5cd136ab]417 driver = dev->drv;
[df747b9c]418 assert(NULL != driver);
[5cd136ab]419 }
420
[9a66bc2e]421 if (NULL == driver) {
[df747b9c]422 printf(NAME ": devman_forward error - the device is not in usable state.\n", handle);
[5cd136ab]423 ipc_answer_0(iid, ENOENT);
424 return;
425 }
426
427 int method;
428 if (drv_to_parent) {
429 method = DRIVER_DRIVER;
430 } else {
431 method = DRIVER_CLIENT;
432 }
433
[9a66bc2e]434 if (driver->phone <= 0) {
[3843ecb]435 printf(NAME ": devman_forward: cound not forward to driver %s ", driver->name);
436 printf("the driver's phone is %x).\n", driver->phone);
[9a66bc2e]437 return;
438 }
[df747b9c]439 printf(NAME ": devman_forward: forward connection to device %s to driver %s.\n", dev->pathname, driver->name);
[5cd136ab]440 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
441}
442
[924c75e1]443/** Function for handling connections to device manager.
444 *
445 */
446static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
447{
448 // Select interface
449 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
450 case DEVMAN_DRIVER:
451 devman_connection_driver(iid, icall);
452 break;
[f658458]453 case DEVMAN_CLIENT:
454 devman_connection_client(iid, icall);
455 break;
[924c75e1]456 case DEVMAN_CONNECT_TO_DEVICE:
457 // Connect client to selected device
[5cd136ab]458 devman_forward(iid, icall, false);
459 break;
460 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
461 // Connect client to selected device
462 devman_forward(iid, icall, true);
463 break;
[924c75e1]464 default:
465 /* No such interface */
466 ipc_answer_0(iid, ENOENT);
467 }
468}
469
[e2b9a993]470/** Initialize device manager internal structures.
471 */
472static bool devman_init()
473{
[0c3666d]474 printf(NAME ": devman_init - looking for available drivers. \n");
[08d9c4e6]475
[e2b9a993]476 // initialize list of available drivers
[0c3666d]477 init_driver_list(&drivers_list);
[e2b9a993]478 if (0 == lookup_available_drivers(&drivers_list, DRIVER_DEFAULT_STORE)) {
479 printf(NAME " no drivers found.");
480 return false;
481 }
[08d9c4e6]482 printf(NAME ": devman_init - list of drivers has been initialized. \n");
[e2b9a993]483
484 // create root device node
485 if (!init_device_tree(&device_tree, &drivers_list)) {
486 printf(NAME " failed to initialize device tree.");
487 return false;
488 }
489
490 return true;
491}
492
493int main(int argc, char *argv[])
494{
495 printf(NAME ": HelenOS Device Manager\n");
496
497 if (!devman_init()) {
498 printf(NAME ": Error while initializing service\n");
499 return -1;
500 }
501
502 // Set a handler of incomming connections
503 async_set_client_connection(devman_connection);
504
505 // Register device manager at naming service
506 ipcarg_t phonead;
507 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
508 return -1;
509
510 printf(NAME ": Accepting connections\n");
511 async_manager();
512
513 // Never reached
514 return 0;
515}
[c16cf62]516
517/** @}
518 */
Note: See TracBrowser for help on using the repository browser.