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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 25a179e was 25a179e, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

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