source: mainline/uspace/srv/devman/main.c@ 04c7003f

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

renamed serial port driver to ns8250, fixed a little bug

  • Property mode set to 100644
File size: 12.1 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 <string.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
56#include "devman.h"
57
58#define DRIVER_DEFAULT_STORE "/srv/drivers"
59
60static driver_list_t drivers_list;
61static dev_tree_t device_tree;
62
63/**
64 * Register running driver.
65 */
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_string_receive(&drv_name, DEVMAN_NAME_MAXLEN, NULL);
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", drv_name);
88
89 // Find driver structure
90 driver = find_driver(&drivers_list, drv_name);
91
92 if (NULL == driver) {
93 printf(NAME ": no driver named %s was found.\n", drv_name);
94 free(drv_name);
95 drv_name = NULL;
96 ipc_answer_0(iid, ENOENT);
97 return NULL;
98 }
99
100 free(drv_name);
101 drv_name = NULL;
102
103 // Create connection to the driver
104 printf(NAME ": creating connection to the %s driver.\n", driver->name);
105 ipc_call_t call;
106 ipc_callid_t callid = async_get_call(&call);
107 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
108 ipc_answer_0(callid, ENOTSUP);
109 ipc_answer_0(iid, ENOTSUP);
110 return NULL;
111 }
112
113 // remember driver's phone
114 set_driver_phone(driver, IPC_GET_ARG5(call));
115
116 printf(NAME ": the %s driver was successfully registered as running.\n", driver->name);
117
118 ipc_answer_0(callid, EOK);
119
120 ipc_answer_0(iid, EOK);
121
122 return driver;
123}
124
125/**
126 * Receive device match ID from the device's parent driver and add it to the list of devices match ids.
127 *
128 * @param match_ids the list of the device's match ids.
129 *
130 * @return 0 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 protocol.\n");
142 ipc_answer_0(callid, EINVAL);
143 delete_match_id(match_id);
144 return EINVAL;
145 }
146
147 if (NULL == match_id) {
148 printf(NAME ": ERROR: devman_receive_match_id - failed to allocate match id.\n");
149 ipc_answer_0(callid, ENOMEM);
150 return ENOMEM;
151 }
152
153 ipc_answer_0(callid, EOK);
154
155 match_id->score = IPC_GET_ARG1(call);
156
157 rc = async_string_receive(&match_id->id, DEVMAN_NAME_MAXLEN, NULL);
158 if (EOK != rc) {
159 delete_match_id(match_id);
160 printf(NAME ": devman_receive_match_id - failed to receive match id string.\n");
161 return rc;
162 }
163
164 list_append(&match_id->link, &match_ids->ids);
165
166 printf(NAME ": received match id '%s', score = %d \n", match_id->id, match_id->score);
167 return rc;
168}
169
170/**
171 * Receive device match IDs from the device's parent driver
172 * and add them to the list of devices match ids.
173 *
174 * @param match_count the number of device's match ids to be received.
175 * @param match_ids the list of the device's match ids.
176 *
177 * @return 0 on success, negative error code otherwise.
178 */
179static int devman_receive_match_ids(ipcarg_t match_count, match_id_list_t *match_ids)
180{
181 int ret = EOK;
182 size_t i;
183 for (i = 0; i < match_count; i++) {
184 if (EOK != (ret = devman_receive_match_id(match_ids))) {
185 return ret;
186 }
187 }
188 return ret;
189}
190
191/** Handle child device registration.
192 *
193 * Child devices are registered by their parent's device driver.
194 */
195static void devman_add_child(ipc_callid_t callid, ipc_call_t *call)
196{
197 // printf(NAME ": devman_add_child\n");
198
199 device_handle_t parent_handle = IPC_GET_ARG1(*call);
200 ipcarg_t match_count = IPC_GET_ARG2(*call);
201
202 node_t *parent = find_dev_node(&device_tree, parent_handle);
203
204 if (NULL == parent) {
205 ipc_answer_0(callid, ENOENT);
206 return;
207 }
208
209 char *dev_name = NULL;
210 int rc = async_string_receive(&dev_name, DEVMAN_NAME_MAXLEN, NULL);
211 if (EOK != rc) {
212 ipc_answer_0(callid, rc);
213 return;
214 }
215 // printf(NAME ": newly added child device's name is '%s'.\n", dev_name);
216
217 node_t *node = create_dev_node();
218 if (!insert_dev_node(&device_tree, node, dev_name, parent)) {
219 delete_dev_node(node);
220 ipc_answer_0(callid, ENOMEM);
221 return;
222 }
223
224 printf(NAME ": devman_add_child %s\n", node->pathname);
225
226 devman_receive_match_ids(match_count, &node->match_ids);
227
228 // return device handle to parent's driver
229 ipc_answer_1(callid, EOK, node->handle);
230
231 // try to find suitable driver and assign it to the device
232 assign_driver(node, &drivers_list);
233}
234
235/**
236 * Initialize driver which has registered itself as running and ready.
237 *
238 * The initialization is done in a separate fibril to avoid deadlocks
239 * (if the driver needed to be served by devman during the driver's initialization).
240 */
241static int init_running_drv(void *drv)
242{
243 driver_t *driver = (driver_t *)drv;
244 initialize_running_driver(driver);
245 printf(NAME ": the %s driver was successfully initialized. \n", driver->name);
246 return 0;
247}
248
249/** Function for handling connections from a driver to the device manager.
250 */
251static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
252{
253 /* Accept the connection */
254 ipc_answer_0(iid, EOK);
255
256 driver_t *driver = devman_driver_register();
257 if (NULL == driver)
258 return;
259
260 // Initialize the driver as running (e.g. pass assigned devices to it) in a separate fibril;
261 // the separate fibril is used to enable the driver
262 // to use devman service during the driver's initialization.
263 fid_t fid = fibril_create(init_running_drv, driver);
264 if (fid == 0) {
265 printf(NAME ": Error creating fibril for the initialization of the newly registered running driver.\n");
266 return;
267 }
268 fibril_add_ready(fid);
269
270 /*thread_id_t tid;
271 if (0 != thread_create(init_running_drv, driver, "init_running_drv", &tid)) {
272 printf(NAME ": failed to start the initialization of the newly registered running driver.\n");
273 }*/
274
275 ipc_callid_t callid;
276 ipc_call_t call;
277 bool cont = true;
278 while (cont) {
279 callid = async_get_call(&call);
280
281 switch (IPC_GET_METHOD(call)) {
282 case IPC_M_PHONE_HUNGUP:
283 cont = false;
284 continue;
285 case DEVMAN_ADD_CHILD_DEVICE:
286 devman_add_child(callid, &call);
287 break;
288 default:
289 ipc_answer_0(callid, EINVAL);
290 break;
291 }
292 }
293}
294
295/** Find handle for the device instance identified by the device's path in the device tree.
296 */
297static void devman_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
298{
299 char *pathname;
300
301 /* Get fqdn */
302 int rc = async_string_receive(&pathname, 0, NULL);
303 if (rc != EOK) {
304 ipc_answer_0(iid, rc);
305 return;
306 }
307
308 node_t * dev = find_dev_node_by_path(&device_tree, pathname);
309
310 free(pathname);
311
312 if (NULL == dev) {
313 ipc_answer_0(iid, ENOENT);
314 return;
315 }
316
317 ipc_answer_1(iid, EOK, dev->handle);
318}
319
320
321/** Function for handling connections from a client to the device manager.
322 */
323static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
324{
325 /* Accept connection */
326 ipc_answer_0(iid, EOK);
327
328 bool cont = true;
329 while (cont) {
330 ipc_call_t call;
331 ipc_callid_t callid = async_get_call(&call);
332
333 switch (IPC_GET_METHOD(call)) {
334 case IPC_M_PHONE_HUNGUP:
335 cont = false;
336 continue;
337 case DEVMAN_DEVICE_GET_HANDLE:
338 devman_device_get_handle(callid, &call);
339 break;
340 default:
341 if (!(callid & IPC_CALLID_NOTIFICATION))
342 ipc_answer_0(callid, ENOENT);
343 }
344 }
345}
346
347static void devman_forward(ipc_callid_t iid, ipc_call_t *icall, bool drv_to_parent) {
348
349 device_handle_t handle = IPC_GET_ARG2(*icall);
350 // printf(NAME ": devman_forward - trying to forward connection to device with handle %x.\n", handle);
351
352 node_t *dev = find_dev_node(&device_tree, handle);
353 if (NULL == dev) {
354 printf(NAME ": devman_forward error - no device with handle %x was found.\n", handle);
355 ipc_answer_0(iid, ENOENT);
356 return;
357 }
358
359 driver_t *driver = NULL;
360
361 if (drv_to_parent) {
362 if (NULL != dev->parent) {
363 driver = dev->parent->drv;
364 }
365 } else if (DEVICE_USABLE == dev->state) {
366 driver = dev->drv;
367 assert(NULL != driver);
368 }
369
370 if (NULL == driver) {
371 printf(NAME ": devman_forward error - the device is not in usable state.\n", handle);
372 ipc_answer_0(iid, ENOENT);
373 return;
374 }
375
376 int method;
377 if (drv_to_parent) {
378 method = DRIVER_DRIVER;
379 } else {
380 method = DRIVER_CLIENT;
381 }
382
383 if (driver->phone <= 0) {
384 printf(NAME ": devman_forward: cound not forward to driver %s ", driver->name);
385 printf("the driver's phone is %x).\n", driver->phone);
386 return;
387 }
388 printf(NAME ": devman_forward: forward connection to device %s to driver %s.\n", dev->pathname, driver->name);
389 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE);
390}
391
392/** Function for handling connections to device manager.
393 *
394 */
395static void devman_connection(ipc_callid_t iid, ipc_call_t *icall)
396{
397 // Select interface
398 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
399 case DEVMAN_DRIVER:
400 devman_connection_driver(iid, icall);
401 break;
402 case DEVMAN_CLIENT:
403 devman_connection_client(iid, icall);
404 break;
405 case DEVMAN_CONNECT_TO_DEVICE:
406 // Connect client to selected device
407 devman_forward(iid, icall, false);
408 break;
409 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:
410 // Connect client to selected device
411 devman_forward(iid, icall, true);
412 break;
413 default:
414 /* No such interface */
415 ipc_answer_0(iid, ENOENT);
416 }
417}
418
419/** Initialize device manager internal structures.
420 */
421static bool devman_init()
422{
423 printf(NAME ": devman_init - looking for available drivers. \n");
424
425 // initialize list of available drivers
426 init_driver_list(&drivers_list);
427 if (0 == lookup_available_drivers(&drivers_list, DRIVER_DEFAULT_STORE)) {
428 printf(NAME " no drivers found.");
429 return false;
430 }
431 printf(NAME ": devman_init - list of drivers has been initialized. \n");
432
433 // create root device node
434 if (!init_device_tree(&device_tree, &drivers_list)) {
435 printf(NAME " failed to initialize device tree.");
436 return false;
437 }
438
439 return true;
440}
441
442int main(int argc, char *argv[])
443{
444 printf(NAME ": HelenOS Device Manager\n");
445
446 if (!devman_init()) {
447 printf(NAME ": Error while initializing service\n");
448 return -1;
449 }
450
451 // Set a handler of incomming connections
452 async_set_client_connection(devman_connection);
453
454 // Register device manager at naming service
455 ipcarg_t phonead;
456 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
457 return -1;
458
459 printf(NAME ": Accepting connections\n");
460 async_manager();
461
462 // Never reached
463 return 0;
464}
465
466/** @}
467 */
Note: See TracBrowser for help on using the repository browser.