source: mainline/uspace/srv/devman/drv_conn.c@ 46f7b64

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 46f7b64 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 16.4 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 <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 <io/log.h>
50#include <ipc/devman.h>
51#include <loc.h>
52
53#include "client_conn.h"
54#include "dev.h"
55#include "devman.h"
56#include "devtree.h"
57#include "driver.h"
58#include "drv_conn.h"
59#include "fun.h"
60#include "loc.h"
61#include "main.h"
62
63static errno_t init_running_drv(void *drv);
64
65/** Register running driver. */
66static driver_t *devman_driver_register(ipc_call_t *call)
67{
68 driver_t *driver = NULL;
69 char *drv_name = NULL;
70
71 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
72
73 /* Get driver name. */
74 errno_t rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
75 if (rc != EOK) {
76 async_answer_0(call, rc);
77 return NULL;
78 }
79
80 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
81 drv_name);
82
83 /* Find driver structure. */
84 driver = driver_find_by_name(&drivers_list, drv_name);
85 if (driver == NULL) {
86 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
87 free(drv_name);
88 drv_name = NULL;
89 async_answer_0(call, ENOENT);
90 return NULL;
91 }
92
93 free(drv_name);
94 drv_name = NULL;
95
96 fibril_mutex_lock(&driver->driver_mutex);
97
98 if (driver->sess) {
99 /* We already have a connection to the driver. */
100 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
101 driver->name);
102 fibril_mutex_unlock(&driver->driver_mutex);
103 async_answer_0(call, EEXIST);
104 return NULL;
105 }
106
107 switch (driver->state) {
108 case DRIVER_NOT_STARTED:
109 /* Somebody started the driver manually. */
110 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
111 driver->name);
112 driver->state = DRIVER_STARTING;
113 break;
114 case DRIVER_STARTING:
115 /* The expected case */
116 break;
117 case DRIVER_RUNNING:
118 /* Should not happen since we do not have a connected session */
119 assert(false);
120 }
121
122 /* Create connection to the driver. */
123 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
124 driver->name);
125 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
126 if (!driver->sess) {
127 fibril_mutex_unlock(&driver->driver_mutex);
128 async_answer_0(call, ENOTSUP);
129 return NULL;
130 }
131 /* FIXME: Work around problem with callback sessions */
132 async_sess_args_set(driver->sess, INTERFACE_DDF_DEVMAN, 0, 0);
133
134 log_msg(LOG_DEFAULT, LVL_NOTE,
135 "The `%s' driver was successfully registered as running.",
136 driver->name);
137
138 /*
139 * Initialize the driver as running (e.g. pass assigned devices to it)
140 * in a separate fibril; the separate fibril is used to enable the
141 * driver to use devman service during the driver's initialization.
142 */
143 fid_t fid = fibril_create(init_running_drv, driver);
144 if (fid == 0) {
145 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril "
146 "for driver `%s'.", driver->name);
147 fibril_mutex_unlock(&driver->driver_mutex);
148 async_answer_0(call, ENOMEM);
149 return NULL;
150 }
151
152 fibril_add_ready(fid);
153 fibril_mutex_unlock(&driver->driver_mutex);
154
155 async_answer_0(call, EOK);
156 return driver;
157}
158
159/** Receive device match ID from the device's parent driver and add it to the
160 * list of devices match ids.
161 *
162 * @param match_ids The list of the device's match ids.
163 * @return Zero on success, error code otherwise.
164 */
165static errno_t devman_receive_match_id(match_id_list_t *match_ids)
166{
167 match_id_t *match_id = create_match_id();
168 ipc_call_t call;
169 errno_t rc = 0;
170
171 async_get_call(&call);
172 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
173 log_msg(LOG_DEFAULT, LVL_ERROR,
174 "Invalid protocol when trying to receive match id.");
175 async_answer_0(&call, EINVAL);
176 delete_match_id(match_id);
177 return EINVAL;
178 }
179
180 if (match_id == NULL) {
181 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
182 async_answer_0(&call, ENOMEM);
183 return ENOMEM;
184 }
185
186 async_answer_0(&call, EOK);
187
188 match_id->score = IPC_GET_ARG1(call);
189
190 char *match_id_str;
191 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
192 match_id->id = match_id_str;
193 if (rc != EOK) {
194 delete_match_id(match_id);
195 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
196 str_error(rc));
197 return rc;
198 }
199
200 list_append(&match_id->link, &match_ids->ids);
201
202 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
203 match_id->id, match_id->score);
204 return rc;
205}
206
207/** Receive device match IDs from the device's parent driver and add them to the
208 * list of devices match ids.
209 *
210 * @param match_count The number of device's match ids to be received.
211 * @param match_ids The list of the device's match ids.
212 * @return Zero on success, error code otherwise.
213 */
214static errno_t devman_receive_match_ids(sysarg_t match_count,
215 match_id_list_t *match_ids)
216{
217 errno_t ret = EOK;
218 size_t i;
219
220 for (i = 0; i < match_count; i++) {
221 if (EOK != (ret = devman_receive_match_id(match_ids)))
222 return ret;
223 }
224 return ret;
225}
226
227/** Handle function registration.
228 *
229 * Child devices are registered by their parent's device driver.
230 */
231static void devman_add_function(ipc_call_t *call)
232{
233 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
234 devman_handle_t dev_handle = IPC_GET_ARG2(*call);
235 sysarg_t match_count = IPC_GET_ARG3(*call);
236 dev_tree_t *tree = &device_tree;
237
238 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
239 if (pdev == NULL) {
240 async_answer_0(call, ENOENT);
241 return;
242 }
243
244 if (ftype != fun_inner && ftype != fun_exposed) {
245 /* Unknown function type */
246 log_msg(LOG_DEFAULT, LVL_ERROR,
247 "Unknown function type %d provided by driver.",
248 (int) ftype);
249
250 dev_del_ref(pdev);
251 async_answer_0(call, EINVAL);
252 return;
253 }
254
255 char *fun_name = NULL;
256 errno_t rc = async_data_write_accept((void **) &fun_name, true, 0, 0, 0, 0);
257 if (rc != EOK) {
258 dev_del_ref(pdev);
259 async_answer_0(call, rc);
260 return;
261 }
262
263 fibril_rwlock_write_lock(&tree->rwlock);
264
265 /* Check device state */
266 if (pdev->state == DEVICE_REMOVED) {
267 fibril_rwlock_write_unlock(&tree->rwlock);
268 dev_del_ref(pdev);
269 async_answer_0(call, ENOENT);
270 return;
271 }
272
273 /* Check that function with same name is not there already. */
274 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
275 if (tfun) {
276 fun_del_ref(tfun); /* drop the new unwanted reference */
277 fibril_rwlock_write_unlock(&tree->rwlock);
278 dev_del_ref(pdev);
279 async_answer_0(call, EEXIST);
280 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
281 fun_name);
282 free(fun_name);
283 return;
284 }
285
286 fun_node_t *fun = create_fun_node();
287 /* One reference for creation, one for us */
288 fun_add_ref(fun);
289 fun_add_ref(fun);
290 fun->ftype = ftype;
291
292 /*
293 * We can lock the function here even when holding the tree because
294 * we know it cannot be held by anyone else yet.
295 */
296 fun_busy_lock(fun);
297
298 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
299 fibril_rwlock_write_unlock(&tree->rwlock);
300 dev_del_ref(pdev);
301 fun_busy_unlock(fun);
302 fun_del_ref(fun);
303 delete_fun_node(fun);
304 async_answer_0(call, ENOMEM);
305 return;
306 }
307
308 fibril_rwlock_write_unlock(&tree->rwlock);
309 dev_del_ref(pdev);
310
311 devman_receive_match_ids(match_count, &fun->match_ids);
312
313 rc = fun_online(fun);
314 if (rc != EOK) {
315 /* XXX Set some failed state? */
316 fun_busy_unlock(fun);
317 fun_del_ref(fun);
318 async_answer_0(call, rc);
319 return;
320 }
321
322 fun_busy_unlock(fun);
323 fun_del_ref(fun);
324
325 /* Return device handle to parent's driver. */
326 async_answer_1(call, EOK, fun->handle);
327}
328
329static void devman_add_function_to_cat(ipc_call_t *call)
330{
331 devman_handle_t handle = IPC_GET_ARG1(*call);
332 category_id_t cat_id;
333 errno_t rc;
334
335 /* Get category name. */
336 char *cat_name;
337 rc = async_data_write_accept((void **) &cat_name, true,
338 0, 0, 0, 0);
339 if (rc != EOK) {
340 async_answer_0(call, rc);
341 return;
342 }
343
344 fun_node_t *fun = find_fun_node(&device_tree, handle);
345 if (fun == NULL) {
346 async_answer_0(call, ENOENT);
347 return;
348 }
349
350 fibril_rwlock_read_lock(&device_tree.rwlock);
351
352 /* Check function state */
353 if (fun->state == FUN_REMOVED) {
354 fibril_rwlock_read_unlock(&device_tree.rwlock);
355 async_answer_0(call, ENOENT);
356 return;
357 }
358
359 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
360 if (rc == EOK) {
361 loc_service_add_to_cat(fun->service_id, cat_id);
362 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
363 fun->pathname, cat_name);
364 } else {
365 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
366 "`%s'.", fun->pathname, cat_name);
367 }
368
369 fibril_rwlock_read_unlock(&device_tree.rwlock);
370 fun_del_ref(fun);
371
372 async_answer_0(call, rc);
373}
374
375/** Online function by driver request.
376 *
377 */
378static void devman_drv_fun_online(ipc_call_t *icall, driver_t *drv)
379{
380 fun_node_t *fun;
381 errno_t rc;
382
383 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
384
385 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
386 if (fun == NULL) {
387 async_answer_0(icall, ENOENT);
388 return;
389 }
390
391 fun_busy_lock(fun);
392
393 fibril_rwlock_read_lock(&device_tree.rwlock);
394 if (fun->dev == NULL || fun->dev->drv != drv) {
395 fibril_rwlock_read_unlock(&device_tree.rwlock);
396 fun_busy_unlock(fun);
397 fun_del_ref(fun);
398 async_answer_0(icall, ENOENT);
399 return;
400 }
401 fibril_rwlock_read_unlock(&device_tree.rwlock);
402
403 rc = fun_online(fun);
404 if (rc != EOK) {
405 fun_busy_unlock(fun);
406 fun_del_ref(fun);
407 async_answer_0(icall, rc);
408 return;
409 }
410
411 fun_busy_unlock(fun);
412 fun_del_ref(fun);
413
414 async_answer_0(icall, EOK);
415}
416
417
418/** Offline function by driver request.
419 *
420 */
421static void devman_drv_fun_offline(ipc_call_t *icall, driver_t *drv)
422{
423 fun_node_t *fun;
424 errno_t rc;
425
426 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
427 if (fun == NULL) {
428 async_answer_0(icall, ENOENT);
429 return;
430 }
431
432 fun_busy_lock(fun);
433
434 fibril_rwlock_write_lock(&device_tree.rwlock);
435 if (fun->dev == NULL || fun->dev->drv != drv) {
436 fun_busy_unlock(fun);
437 fun_del_ref(fun);
438 async_answer_0(icall, ENOENT);
439 return;
440 }
441 fibril_rwlock_write_unlock(&device_tree.rwlock);
442
443 rc = fun_offline(fun);
444 if (rc != EOK) {
445 fun_busy_unlock(fun);
446 fun_del_ref(fun);
447 async_answer_0(icall, rc);
448 return;
449 }
450
451 fun_busy_unlock(fun);
452 fun_del_ref(fun);
453 async_answer_0(icall, EOK);
454}
455
456/** Remove function. */
457static void devman_remove_function(ipc_call_t *call)
458{
459 devman_handle_t fun_handle = IPC_GET_ARG1(*call);
460 dev_tree_t *tree = &device_tree;
461 errno_t rc;
462
463 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
464 if (fun == NULL) {
465 async_answer_0(call, ENOENT);
466 return;
467 }
468
469 fun_busy_lock(fun);
470
471 fibril_rwlock_write_lock(&tree->rwlock);
472
473 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
474
475 /* Check function state */
476 if (fun->state == FUN_REMOVED) {
477 fibril_rwlock_write_unlock(&tree->rwlock);
478 fun_busy_unlock(fun);
479 fun_del_ref(fun);
480 async_answer_0(call, ENOENT);
481 return;
482 }
483
484 if (fun->ftype == fun_inner) {
485 /* This is a surprise removal. Handle possible descendants */
486 if (fun->child != NULL) {
487 dev_node_t *dev = fun->child;
488 device_state_t dev_state;
489 errno_t gone_rc;
490
491 dev_add_ref(dev);
492 dev_state = dev->state;
493
494 fibril_rwlock_write_unlock(&device_tree.rwlock);
495
496 /* If device is owned by driver, inform driver it is gone. */
497 if (dev_state == DEVICE_USABLE)
498 gone_rc = driver_dev_gone(&device_tree, dev);
499 else
500 gone_rc = EOK;
501
502 fibril_rwlock_read_lock(&device_tree.rwlock);
503
504 /* Verify that driver succeeded and removed all functions */
505 if (gone_rc != EOK || !list_empty(&dev->functions)) {
506 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
507 "functions for device that is gone. "
508 "Device node is now defunct.");
509
510 /*
511 * Not much we can do but mark the device
512 * node as having invalid state. This
513 * is a driver bug.
514 */
515 dev->state = DEVICE_INVALID;
516 fibril_rwlock_read_unlock(&device_tree.rwlock);
517 dev_del_ref(dev);
518 if (gone_rc == EOK)
519 gone_rc = ENOTSUP;
520 fun_busy_unlock(fun);
521 fun_del_ref(fun);
522 async_answer_0(call, gone_rc);
523 return;
524 }
525
526 driver_t *driver = dev->drv;
527 fibril_rwlock_read_unlock(&device_tree.rwlock);
528
529 if (driver)
530 detach_driver(&device_tree, dev);
531
532 fibril_rwlock_write_lock(&device_tree.rwlock);
533 remove_dev_node(&device_tree, dev);
534
535 /* Delete ref created when node was inserted */
536 dev_del_ref(dev);
537 /* Delete ref created by dev_add_ref(dev) above */
538 dev_del_ref(dev);
539 }
540 } else {
541 if (fun->service_id != 0) {
542 /* Unregister from location service */
543 rc = loc_unregister_tree_function(fun, &device_tree);
544 if (rc != EOK) {
545 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
546 "service.");
547 fibril_rwlock_write_unlock(&tree->rwlock);
548 fun_busy_unlock(fun);
549 fun_del_ref(fun);
550 async_answer_0(call, EIO);
551 return;
552 }
553 }
554 }
555
556 remove_fun_node(&device_tree, fun);
557 fibril_rwlock_write_unlock(&tree->rwlock);
558 fun_busy_unlock(fun);
559
560 /* Delete ref added when inserting function into tree */
561 fun_del_ref(fun);
562 /* Delete ref added above when looking up function */
563 fun_del_ref(fun);
564
565 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
566 async_answer_0(call, EOK);
567}
568
569/** Initialize driver which has registered itself as running and ready.
570 *
571 * The initialization is done in a separate fibril to avoid deadlocks (if the
572 * driver needed to be served by devman during the driver's initialization).
573 */
574static errno_t init_running_drv(void *drv)
575{
576 driver_t *driver = (driver_t *) drv;
577
578 initialize_running_driver(driver, &device_tree);
579 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
580 driver->name);
581 return 0;
582}
583
584/** Function for handling connections from a driver to the device manager. */
585void devman_connection_driver(ipc_call_t *icall, void *arg)
586{
587 client_t *client;
588 driver_t *driver = NULL;
589
590 /* Accept the connection. */
591 async_answer_0(icall, EOK);
592
593 client = async_get_client_data();
594 if (client == NULL) {
595 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
596 return;
597 }
598
599 while (true) {
600 ipc_call_t call;
601 async_get_call(&call);
602
603 if (!IPC_GET_IMETHOD(call))
604 break;
605
606 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
607 fibril_mutex_lock(&client->mutex);
608 driver = client->driver;
609 fibril_mutex_unlock(&client->mutex);
610 if (driver == NULL) {
611 /* First call must be to DEVMAN_DRIVER_REGISTER */
612 async_answer_0(&call, ENOTSUP);
613 continue;
614 }
615 }
616
617 switch (IPC_GET_IMETHOD(call)) {
618 case DEVMAN_DRIVER_REGISTER:
619 fibril_mutex_lock(&client->mutex);
620 if (client->driver != NULL) {
621 fibril_mutex_unlock(&client->mutex);
622 async_answer_0(&call, EINVAL);
623 continue;
624 }
625 client->driver = devman_driver_register(&call);
626 fibril_mutex_unlock(&client->mutex);
627 break;
628 case DEVMAN_ADD_FUNCTION:
629 devman_add_function(&call);
630 break;
631 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
632 devman_add_function_to_cat(&call);
633 break;
634 case DEVMAN_DRV_FUN_ONLINE:
635 devman_drv_fun_online(&call, driver);
636 break;
637 case DEVMAN_DRV_FUN_OFFLINE:
638 devman_drv_fun_offline(&call, driver);
639 break;
640 case DEVMAN_REMOVE_FUNCTION:
641 devman_remove_function(&call);
642 break;
643 default:
644 async_answer_0(&call, EINVAL);
645 break;
646 }
647 }
648}
649
650/** @}
651 */
Note: See TracBrowser for help on using the repository browser.