source: mainline/uspace/srv/devman/client_conn.c@ 7969087

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7969087 was 7969087, checked in by Jiri Svoboda <jiri@…>, 12 years ago

devctl load-drv to manually load a driver.

  • Property mode set to 100644
File size: 14.3 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 <ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <str_error.h>
45#include <stdbool.h>
46#include <fibril_synch.h>
47#include <stdlib.h>
48#include <str.h>
49#include <ctype.h>
50#include <ipc/devman.h>
51
52#include "client_conn.h"
53#include "dev.h"
54#include "devman.h"
55#include "driver.h"
56#include "fun.h"
57#include "loc.h"
58#include "main.h"
59
60/** Find handle for the device instance identified by the device's path in the
61 * device tree. */
62static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
63{
64 char *pathname;
65 devman_handle_t handle;
66
67 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
68 if (rc != EOK) {
69 async_answer_0(iid, rc);
70 return;
71 }
72
73 fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
74
75 free(pathname);
76
77 if (fun == NULL) {
78 async_answer_0(iid, ENOENT);
79 return;
80 }
81
82 fibril_rwlock_read_lock(&device_tree.rwlock);
83
84 /* Check function state */
85 if (fun->state == FUN_REMOVED) {
86 fibril_rwlock_read_unlock(&device_tree.rwlock);
87 async_answer_0(iid, ENOENT);
88 return;
89 }
90 handle = fun->handle;
91
92 fibril_rwlock_read_unlock(&device_tree.rwlock);
93
94 /* Delete reference created above by find_fun_node_by_path() */
95 fun_del_ref(fun);
96
97 async_answer_1(iid, EOK, handle);
98}
99
100/** Get device name. */
101static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
102{
103 devman_handle_t handle = IPC_GET_ARG1(*icall);
104
105 fun_node_t *fun = find_fun_node(&device_tree, handle);
106 if (fun == NULL) {
107 async_answer_0(iid, ENOMEM);
108 return;
109 }
110
111 ipc_callid_t data_callid;
112 size_t data_len;
113 if (!async_data_read_receive(&data_callid, &data_len)) {
114 async_answer_0(iid, EINVAL);
115 fun_del_ref(fun);
116 return;
117 }
118
119 void *buffer = malloc(data_len);
120 if (buffer == NULL) {
121 async_answer_0(data_callid, ENOMEM);
122 async_answer_0(iid, ENOMEM);
123 fun_del_ref(fun);
124 return;
125 }
126
127 fibril_rwlock_read_lock(&device_tree.rwlock);
128
129 /* Check function state */
130 if (fun->state == FUN_REMOVED) {
131 fibril_rwlock_read_unlock(&device_tree.rwlock);
132 free(buffer);
133
134 async_answer_0(data_callid, ENOENT);
135 async_answer_0(iid, ENOENT);
136 fun_del_ref(fun);
137 return;
138 }
139
140 size_t sent_length = str_size(fun->name);
141 if (sent_length > data_len) {
142 sent_length = data_len;
143 }
144
145 async_data_read_finalize(data_callid, fun->name, sent_length);
146 async_answer_0(iid, EOK);
147
148 fibril_rwlock_read_unlock(&device_tree.rwlock);
149 fun_del_ref(fun);
150 free(buffer);
151}
152
153/** Get function driver name. */
154static void devman_fun_get_driver_name(ipc_callid_t iid, ipc_call_t *icall)
155{
156 devman_handle_t handle = IPC_GET_ARG1(*icall);
157
158 fun_node_t *fun = find_fun_node(&device_tree, handle);
159 if (fun == NULL) {
160 async_answer_0(iid, ENOMEM);
161 return;
162 }
163
164 ipc_callid_t data_callid;
165 size_t data_len;
166 if (!async_data_read_receive(&data_callid, &data_len)) {
167 async_answer_0(iid, EINVAL);
168 fun_del_ref(fun);
169 return;
170 }
171
172 void *buffer = malloc(data_len);
173 if (buffer == NULL) {
174 async_answer_0(data_callid, ENOMEM);
175 async_answer_0(iid, ENOMEM);
176 fun_del_ref(fun);
177 return;
178 }
179
180 fibril_rwlock_read_lock(&device_tree.rwlock);
181
182 /* Check function state */
183 if (fun->state == FUN_REMOVED) {
184 fibril_rwlock_read_unlock(&device_tree.rwlock);
185 free(buffer);
186
187 async_answer_0(data_callid, ENOENT);
188 async_answer_0(iid, ENOENT);
189 fun_del_ref(fun);
190 return;
191 }
192
193 /* Check whether function has a driver */
194 if (fun->child == NULL || fun->child->drv == NULL) {
195 fibril_rwlock_read_unlock(&device_tree.rwlock);
196 free(buffer);
197
198 async_answer_0(data_callid, EINVAL);
199 async_answer_0(iid, EINVAL);
200 fun_del_ref(fun);
201 return;
202 }
203
204 size_t sent_length = str_size(fun->child->drv->name);
205 if (sent_length > data_len) {
206 sent_length = data_len;
207 }
208
209 async_data_read_finalize(data_callid, fun->child->drv->name,
210 sent_length);
211 async_answer_0(iid, EOK);
212
213 fibril_rwlock_read_unlock(&device_tree.rwlock);
214 fun_del_ref(fun);
215 free(buffer);
216}
217
218/** Get device path. */
219static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
220{
221 devman_handle_t handle = IPC_GET_ARG1(*icall);
222
223 fun_node_t *fun = find_fun_node(&device_tree, handle);
224 if (fun == NULL) {
225 async_answer_0(iid, ENOMEM);
226 return;
227 }
228
229 ipc_callid_t data_callid;
230 size_t data_len;
231 if (!async_data_read_receive(&data_callid, &data_len)) {
232 async_answer_0(iid, EINVAL);
233 fun_del_ref(fun);
234 return;
235 }
236
237 void *buffer = malloc(data_len);
238 if (buffer == NULL) {
239 async_answer_0(data_callid, ENOMEM);
240 async_answer_0(iid, ENOMEM);
241 fun_del_ref(fun);
242 return;
243 }
244
245 fibril_rwlock_read_lock(&device_tree.rwlock);
246
247 /* Check function state */
248 if (fun->state == FUN_REMOVED) {
249 fibril_rwlock_read_unlock(&device_tree.rwlock);
250 free(buffer);
251
252 async_answer_0(data_callid, ENOENT);
253 async_answer_0(iid, ENOENT);
254 fun_del_ref(fun);
255 return;
256 }
257
258 size_t sent_length = str_size(fun->pathname);
259 if (sent_length > data_len) {
260 sent_length = data_len;
261 }
262
263 async_data_read_finalize(data_callid, fun->pathname, sent_length);
264 async_answer_0(iid, EOK);
265
266 fibril_rwlock_read_unlock(&device_tree.rwlock);
267 fun_del_ref(fun);
268 free(buffer);
269}
270
271static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
272{
273 ipc_callid_t callid;
274 size_t size;
275 size_t act_size;
276 int rc;
277
278 if (!async_data_read_receive(&callid, &size)) {
279 async_answer_0(callid, EREFUSED);
280 async_answer_0(iid, EREFUSED);
281 return;
282 }
283
284 fibril_rwlock_read_lock(&device_tree.rwlock);
285
286 dev_node_t *dev = find_dev_node_no_lock(&device_tree,
287 IPC_GET_ARG1(*icall));
288 if (dev == NULL || dev->state == DEVICE_REMOVED) {
289 fibril_rwlock_read_unlock(&device_tree.rwlock);
290 async_answer_0(callid, ENOENT);
291 async_answer_0(iid, ENOENT);
292 return;
293 }
294
295 devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
296 if (hdl_buf == NULL) {
297 fibril_rwlock_read_unlock(&device_tree.rwlock);
298 async_answer_0(callid, ENOMEM);
299 async_answer_0(iid, ENOMEM);
300 return;
301 }
302
303 rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
304 if (rc != EOK) {
305 fibril_rwlock_read_unlock(&device_tree.rwlock);
306 async_answer_0(callid, rc);
307 async_answer_0(iid, rc);
308 return;
309 }
310
311 fibril_rwlock_read_unlock(&device_tree.rwlock);
312
313 sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
314 free(hdl_buf);
315
316 async_answer_1(iid, retval, act_size);
317}
318
319/** Get handle for child device of a function. */
320static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
321{
322 fun_node_t *fun;
323
324 fibril_rwlock_read_lock(&device_tree.rwlock);
325
326 fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
327 if (fun == NULL || fun->state == FUN_REMOVED) {
328 fibril_rwlock_read_unlock(&device_tree.rwlock);
329 async_answer_0(iid, ENOENT);
330 return;
331 }
332
333 if (fun->child == NULL) {
334 fibril_rwlock_read_unlock(&device_tree.rwlock);
335 async_answer_0(iid, ENOENT);
336 return;
337 }
338
339 async_answer_1(iid, EOK, fun->child->handle);
340
341 fibril_rwlock_read_unlock(&device_tree.rwlock);
342}
343
344/** Online function.
345 *
346 * Send a request to online a function to the responsible driver.
347 * The driver may offline other functions if necessary (i.e. if the state
348 * of this function is linked to state of another function somehow).
349 */
350static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
351{
352 fun_node_t *fun;
353 int rc;
354
355 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
356 if (fun == NULL) {
357 async_answer_0(iid, ENOENT);
358 return;
359 }
360
361 rc = driver_fun_online(&device_tree, fun);
362 fun_del_ref(fun);
363
364 async_answer_0(iid, (sysarg_t) rc);
365}
366
367/** Offline function.
368 *
369 * Send a request to offline a function to the responsible driver. As
370 * a result the subtree rooted at that function should be cleanly
371 * detatched. The driver may offline other functions if necessary
372 * (i.e. if the state of this function is linked to state of another
373 * function somehow).
374 */
375static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
376{
377 fun_node_t *fun;
378 int rc;
379
380 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
381 if (fun == NULL) {
382 async_answer_0(iid, ENOENT);
383 return;
384 }
385
386 rc = driver_fun_offline(&device_tree, fun);
387 fun_del_ref(fun);
388
389 async_answer_0(iid, (sysarg_t) rc);
390}
391
392/** Find handle for the function instance identified by its service ID. */
393static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
394{
395 fun_node_t *fun;
396
397 fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
398
399 if (fun == NULL) {
400 async_answer_0(iid, ENOENT);
401 return;
402 }
403
404 fibril_rwlock_read_lock(&device_tree.rwlock);
405
406 /* Check function state */
407 if (fun->state == FUN_REMOVED) {
408 fibril_rwlock_read_unlock(&device_tree.rwlock);
409 async_answer_0(iid, ENOENT);
410 return;
411 }
412
413 async_answer_1(iid, EOK, fun->handle);
414 fibril_rwlock_read_unlock(&device_tree.rwlock);
415 fun_del_ref(fun);
416}
417
418/** Get list of all registered drivers. */
419static void devman_get_drivers(ipc_callid_t iid, ipc_call_t *icall)
420{
421 ipc_callid_t callid;
422 size_t size;
423 size_t act_size;
424 int rc;
425
426 if (!async_data_read_receive(&callid, &size)) {
427 async_answer_0(callid, EREFUSED);
428 async_answer_0(iid, EREFUSED);
429 return;
430 }
431
432 devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
433 if (hdl_buf == NULL) {
434 async_answer_0(callid, ENOMEM);
435 async_answer_0(iid, ENOMEM);
436 return;
437 }
438
439 rc = driver_get_list(&drivers_list, hdl_buf, size, &act_size);
440 if (rc != EOK) {
441 async_answer_0(callid, rc);
442 async_answer_0(iid, rc);
443 return;
444 }
445
446 sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
447 free(hdl_buf);
448
449 async_answer_1(iid, retval, act_size);
450}
451
452/** Find driver by name. */
453static void devman_driver_get_handle(ipc_callid_t iid, ipc_call_t *icall)
454{
455 char *drvname;
456
457 int rc = async_data_write_accept((void **) &drvname, true, 0, 0, 0, 0);
458 if (rc != EOK) {
459 async_answer_0(iid, rc);
460 return;
461 }
462
463 driver_t *driver = driver_find_by_name(&drivers_list, drvname);
464
465 free(drvname);
466
467 if (driver == NULL) {
468 async_answer_0(iid, ENOENT);
469 return;
470 }
471
472 async_answer_1(iid, EOK, driver->handle);
473}
474
475/** Get driver name. */
476static void devman_driver_get_name(ipc_callid_t iid, ipc_call_t *icall)
477{
478 devman_handle_t handle = IPC_GET_ARG1(*icall);
479
480 driver_t *drv = driver_find(&drivers_list, handle);
481 if (drv == NULL) {
482 async_answer_0(iid, ENOMEM);
483 return;
484 }
485
486 ipc_callid_t data_callid;
487 size_t data_len;
488 if (!async_data_read_receive(&data_callid, &data_len)) {
489 async_answer_0(iid, EINVAL);
490 return;
491 }
492
493 void *buffer = malloc(data_len);
494 if (buffer == NULL) {
495 async_answer_0(data_callid, ENOMEM);
496 async_answer_0(iid, ENOMEM);
497 return;
498 }
499
500 fibril_mutex_lock(&drv->driver_mutex);
501
502 size_t sent_length = str_size(drv->name);
503 if (sent_length > data_len) {
504 sent_length = data_len;
505 }
506
507 async_data_read_finalize(data_callid, drv->name, sent_length);
508 async_answer_0(iid, EOK);
509
510 fibril_mutex_unlock(&drv->driver_mutex);
511
512 free(buffer);
513}
514
515/** Get driver state. */
516static void devman_driver_get_state(ipc_callid_t iid, ipc_call_t *icall)
517{
518 driver_t *drv;
519
520 drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
521 if (drv == NULL) {
522 async_answer_0(iid, ENOENT);
523 return;
524 }
525
526 async_answer_1(iid, EOK, (sysarg_t) drv->state);
527}
528
529/** Forcibly load a driver. */
530static void devman_driver_load(ipc_callid_t iid, ipc_call_t *icall)
531{
532 driver_t *drv;
533 int rc;
534
535 drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
536 if (drv == NULL) {
537 async_answer_0(iid, ENOENT);
538 return;
539 }
540
541 fibril_mutex_lock(&drv->driver_mutex);
542 rc = start_driver(drv) ? EOK : EIO;
543 fibril_mutex_unlock(&drv->driver_mutex);
544
545 async_answer_0(iid, rc);
546}
547
548/** Function for handling connections from a client to the device manager. */
549void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
550{
551 /* Accept connection. */
552 async_answer_0(iid, EOK);
553
554 while (true) {
555 ipc_call_t call;
556 ipc_callid_t callid = async_get_call(&call);
557
558 if (!IPC_GET_IMETHOD(call))
559 break;
560
561 switch (IPC_GET_IMETHOD(call)) {
562 case DEVMAN_DEVICE_GET_HANDLE:
563 devman_function_get_handle(callid, &call);
564 break;
565 case DEVMAN_DEV_GET_FUNCTIONS:
566 devman_dev_get_functions(callid, &call);
567 break;
568 case DEVMAN_FUN_GET_CHILD:
569 devman_fun_get_child(callid, &call);
570 break;
571 case DEVMAN_FUN_GET_NAME:
572 devman_fun_get_name(callid, &call);
573 break;
574 case DEVMAN_FUN_GET_DRIVER_NAME:
575 devman_fun_get_driver_name(callid, &call);
576 break;
577 case DEVMAN_FUN_GET_PATH:
578 devman_fun_get_path(callid, &call);
579 break;
580 case DEVMAN_FUN_ONLINE:
581 devman_fun_online(callid, &call);
582 break;
583 case DEVMAN_FUN_OFFLINE:
584 devman_fun_offline(callid, &call);
585 break;
586 case DEVMAN_FUN_SID_TO_HANDLE:
587 devman_fun_sid_to_handle(callid, &call);
588 break;
589 case DEVMAN_GET_DRIVERS:
590 devman_get_drivers(callid, &call);
591 break;
592 case DEVMAN_DRIVER_GET_HANDLE:
593 devman_driver_get_handle(callid, &call);
594 break;
595 case DEVMAN_DRIVER_GET_NAME:
596 devman_driver_get_name(callid, &call);
597 break;
598 case DEVMAN_DRIVER_GET_STATE:
599 devman_driver_get_state(callid, &call);
600 break;
601 case DEVMAN_DRIVER_LOAD:
602 devman_driver_load(callid, &call);
603 break;
604 default:
605 async_answer_0(callid, ENOENT);
606 }
607 }
608}
609
610
611/** @}
612 */
Note: See TracBrowser for help on using the repository browser.