source: mainline/uspace/srv/devman/drv_conn.c@ 25a9fec

Last change on this file since 25a9fec was 63a3276, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

sysman: Instrumented locsrv for autostart

  • also refactored unit name derivation in other brokers
  • exposee creation is not used in unit's lifecycle (failed assertion)

Conflicts:

uspace/lib/c/generic/loc.c
uspace/srv/devman/driver.c
uspace/srv/devman/drv_conn.c
uspace/srv/hid/compositor/compositor.c
uspace/srv/locsrv/locsrv.c
uspace/srv/vfs/vfs.h
uspace/srv/vfs/vfs_ops.c
uspace/srv/vfs/vfs_register.c

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