source: mainline/uspace/srv/devman/client_conn.c@ 02e5e34

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

Separate module for devman-client connection handling.

  • Property mode set to 100644
File size: 11.2 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
320/** Get handle for child device of a function. */
321static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
322{
323 fun_node_t *fun;
324
325 fibril_rwlock_read_lock(&device_tree.rwlock);
326
327 fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
328 if (fun == NULL || fun->state == FUN_REMOVED) {
329 fibril_rwlock_read_unlock(&device_tree.rwlock);
330 async_answer_0(iid, ENOENT);
331 return;
332 }
333
334 if (fun->child == NULL) {
335 fibril_rwlock_read_unlock(&device_tree.rwlock);
336 async_answer_0(iid, ENOENT);
337 return;
338 }
339
340 async_answer_1(iid, EOK, fun->child->handle);
341
342 fibril_rwlock_read_unlock(&device_tree.rwlock);
343}
344
345/** Online function.
346 *
347 * Send a request to online a function to the responsible driver.
348 * The driver may offline other functions if necessary (i.e. if the state
349 * of this function is linked to state of another function somehow).
350 */
351static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
352{
353 fun_node_t *fun;
354 int rc;
355
356 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
357 if (fun == NULL) {
358 async_answer_0(iid, ENOENT);
359 return;
360 }
361
362 rc = driver_fun_online(&device_tree, fun);
363 fun_del_ref(fun);
364
365 async_answer_0(iid, (sysarg_t) rc);
366}
367
368/** Offline function.
369 *
370 * Send a request to offline a function to the responsible driver. As
371 * a result the subtree rooted at that function should be cleanly
372 * detatched. The driver may offline other functions if necessary
373 * (i.e. if the state of this function is linked to state of another
374 * function somehow).
375 */
376static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
377{
378 fun_node_t *fun;
379 int rc;
380
381 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
382 if (fun == NULL) {
383 async_answer_0(iid, ENOENT);
384 return;
385 }
386
387 rc = driver_fun_offline(&device_tree, fun);
388 fun_del_ref(fun);
389
390 async_answer_0(iid, (sysarg_t) rc);
391}
392
393/** Find handle for the function instance identified by its service ID. */
394static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
395{
396 fun_node_t *fun;
397
398 fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
399
400 if (fun == NULL) {
401 async_answer_0(iid, ENOENT);
402 return;
403 }
404
405 fibril_rwlock_read_lock(&device_tree.rwlock);
406
407 /* Check function state */
408 if (fun->state == FUN_REMOVED) {
409 fibril_rwlock_read_unlock(&device_tree.rwlock);
410 async_answer_0(iid, ENOENT);
411 return;
412 }
413
414 async_answer_1(iid, EOK, fun->handle);
415 fibril_rwlock_read_unlock(&device_tree.rwlock);
416 fun_del_ref(fun);
417}
418
419/** Function for handling connections from a client to the device manager. */
420void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
421{
422 /* Accept connection. */
423 async_answer_0(iid, EOK);
424
425 while (true) {
426 ipc_call_t call;
427 ipc_callid_t callid = async_get_call(&call);
428
429 if (!IPC_GET_IMETHOD(call))
430 break;
431
432 switch (IPC_GET_IMETHOD(call)) {
433 case DEVMAN_DEVICE_GET_HANDLE:
434 devman_function_get_handle(callid, &call);
435 break;
436 case DEVMAN_DEV_GET_FUNCTIONS:
437 devman_dev_get_functions(callid, &call);
438 break;
439 case DEVMAN_FUN_GET_CHILD:
440 devman_fun_get_child(callid, &call);
441 break;
442 case DEVMAN_FUN_GET_NAME:
443 devman_fun_get_name(callid, &call);
444 break;
445 case DEVMAN_FUN_GET_DRIVER_NAME:
446 devman_fun_get_driver_name(callid, &call);
447 break;
448 case DEVMAN_FUN_GET_PATH:
449 devman_fun_get_path(callid, &call);
450 break;
451 case DEVMAN_FUN_ONLINE:
452 devman_fun_online(callid, &call);
453 break;
454 case DEVMAN_FUN_OFFLINE:
455 devman_fun_offline(callid, &call);
456 break;
457 case DEVMAN_FUN_SID_TO_HANDLE:
458 devman_fun_sid_to_handle(callid, &call);
459 break;
460 default:
461 async_answer_0(callid, ENOENT);
462 }
463 }
464}
465
466
467/** @}
468 */
Note: See TracBrowser for help on using the repository browser.