Changes in uspace/srv/devman/main.c [c4f7bf6:f9b2cb4c] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/devman/main.c
rc4f7bf6 rf9b2cb4c 36 36 */ 37 37 38 #include <inttypes.h>39 38 #include <assert.h> 40 39 #include <ipc/services.h> … … 43 42 #include <stdio.h> 44 43 #include <errno.h> 45 #include <str_error.h>46 44 #include <stdbool.h> 47 45 #include <fibril_synch.h> 48 46 #include <stdlib.h> 49 47 #include <str.h> 50 #include <dirent.h>51 #include <fcntl.h>52 #include <sys/stat.h>53 48 #include <ctype.h> 54 49 #include <io/log.h> 55 50 #include <ipc/devman.h> 56 #include <ipc/driver.h>57 #include <thread.h>58 51 #include <loc.h> 59 52 53 #include "client_conn.h" 54 #include "dev.h" 60 55 #include "devman.h" 56 #include "devtree.h" 57 #include "drv_conn.h" 58 #include "driver.h" 59 #include "fun.h" 60 #include "loc.h" 61 61 62 62 #define DRIVER_DEFAULT_STORE "/drv" 63 63 64 static driver_list_t drivers_list; 65 static dev_tree_t device_tree; 66 67 static int init_running_drv(void *drv); 68 69 /** Register running driver. */ 70 static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call) 71 { 72 driver_t *driver = NULL; 73 char *drv_name = NULL; 74 75 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register"); 76 77 /* Get driver name. */ 78 int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0); 79 if (rc != EOK) { 80 async_answer_0(callid, rc); 81 return NULL; 82 } 83 84 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.", 85 drv_name); 86 87 /* Find driver structure. */ 88 driver = find_driver(&drivers_list, drv_name); 89 if (driver == NULL) { 90 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name); 91 free(drv_name); 92 drv_name = NULL; 93 async_answer_0(callid, ENOENT); 94 return NULL; 95 } 96 97 free(drv_name); 98 drv_name = NULL; 99 100 fibril_mutex_lock(&driver->driver_mutex); 101 102 if (driver->sess) { 103 /* We already have a connection to the driver. */ 104 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n", 105 driver->name); 106 fibril_mutex_unlock(&driver->driver_mutex); 107 async_answer_0(callid, EEXISTS); 108 return NULL; 109 } 110 111 switch (driver->state) { 112 case DRIVER_NOT_STARTED: 113 /* Somebody started the driver manually. */ 114 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n", 115 driver->name); 116 driver->state = DRIVER_STARTING; 117 break; 118 case DRIVER_STARTING: 119 /* The expected case */ 120 break; 121 case DRIVER_RUNNING: 122 /* Should not happen since we do not have a connected session */ 123 assert(false); 124 } 125 126 /* Create connection to the driver. */ 127 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.", 128 driver->name); 129 driver->sess = async_callback_receive(EXCHANGE_PARALLEL); 130 if (!driver->sess) { 131 fibril_mutex_unlock(&driver->driver_mutex); 132 async_answer_0(callid, ENOTSUP); 133 return NULL; 134 } 135 /* FIXME: Work around problem with callback sessions */ 136 async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0); 137 138 log_msg(LOG_DEFAULT, LVL_NOTE, 139 "The `%s' driver was successfully registered as running.", 140 driver->name); 141 142 /* 143 * Initialize the driver as running (e.g. pass assigned devices to it) 144 * in a separate fibril; the separate fibril is used to enable the 145 * driver to use devman service during the driver's initialization. 146 */ 147 fid_t fid = fibril_create(init_running_drv, driver); 148 if (fid == 0) { 149 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \ 150 "for driver `%s'.", driver->name); 151 fibril_mutex_unlock(&driver->driver_mutex); 152 async_answer_0(callid, ENOMEM); 153 return NULL; 154 } 155 156 fibril_add_ready(fid); 157 fibril_mutex_unlock(&driver->driver_mutex); 158 159 async_answer_0(callid, EOK); 160 return driver; 161 } 162 163 /** Receive device match ID from the device's parent driver and add it to the 164 * list of devices match ids. 165 * 166 * @param match_ids The list of the device's match ids. 167 * @return Zero on success, negative error code otherwise. 168 */ 169 static int devman_receive_match_id(match_id_list_t *match_ids) 170 { 171 match_id_t *match_id = create_match_id(); 172 ipc_callid_t callid; 173 ipc_call_t call; 174 int rc = 0; 175 176 callid = async_get_call(&call); 177 if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) { 178 log_msg(LOG_DEFAULT, LVL_ERROR, 179 "Invalid protocol when trying to receive match id."); 180 async_answer_0(callid, EINVAL); 181 delete_match_id(match_id); 182 return EINVAL; 183 } 184 185 if (match_id == NULL) { 186 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id."); 187 async_answer_0(callid, ENOMEM); 188 return ENOMEM; 189 } 190 191 async_answer_0(callid, EOK); 192 193 match_id->score = IPC_GET_ARG1(call); 194 195 char *match_id_str; 196 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0); 197 match_id->id = match_id_str; 198 if (rc != EOK) { 199 delete_match_id(match_id); 200 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.", 201 str_error(rc)); 202 return rc; 203 } 204 205 list_append(&match_id->link, &match_ids->ids); 206 207 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.", 208 match_id->id, match_id->score); 209 return rc; 210 } 211 212 /** Receive device match IDs from the device's parent driver and add them to the 213 * list of devices match ids. 214 * 215 * @param match_count The number of device's match ids to be received. 216 * @param match_ids The list of the device's match ids. 217 * @return Zero on success, negative error code otherwise. 218 */ 219 static int devman_receive_match_ids(sysarg_t match_count, 220 match_id_list_t *match_ids) 221 { 222 int ret = EOK; 223 size_t i; 224 225 for (i = 0; i < match_count; i++) { 226 if (EOK != (ret = devman_receive_match_id(match_ids))) 227 return ret; 228 } 229 return ret; 230 } 231 232 static int assign_driver_fibril(void *arg) 233 { 234 dev_node_t *dev_node = (dev_node_t *) arg; 235 assign_driver(dev_node, &drivers_list, &device_tree); 236 237 /* Delete one reference we got from the caller. */ 238 dev_del_ref(dev_node); 239 return EOK; 240 } 241 242 static int online_function(fun_node_t *fun) 243 { 244 dev_node_t *dev; 245 246 fibril_rwlock_write_lock(&device_tree.rwlock); 247 248 if (fun->state == FUN_ON_LINE) { 249 fibril_rwlock_write_unlock(&device_tree.rwlock); 250 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.", 251 fun->pathname); 252 return EOK; 253 } 254 255 if (fun->ftype == fun_inner) { 256 dev = create_dev_node(); 257 if (dev == NULL) { 258 fibril_rwlock_write_unlock(&device_tree.rwlock); 259 return ENOMEM; 260 } 261 262 insert_dev_node(&device_tree, dev, fun); 263 dev_add_ref(dev); 264 } 265 266 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname); 267 268 if (fun->ftype == fun_inner) { 269 dev = fun->child; 270 assert(dev != NULL); 271 272 /* Give one reference over to assign_driver_fibril(). */ 273 dev_add_ref(dev); 274 275 /* 276 * Try to find a suitable driver and assign it to the device. We do 277 * not want to block the current fibril that is used for processing 278 * incoming calls: we will launch a separate fibril to handle the 279 * driver assigning. That is because assign_driver can actually include 280 * task spawning which could take some time. 281 */ 282 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev); 283 if (assign_fibril == 0) { 284 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for " 285 "assigning driver."); 286 /* XXX Cleanup */ 287 fibril_rwlock_write_unlock(&device_tree.rwlock); 288 return ENOMEM; 289 } 290 fibril_add_ready(assign_fibril); 291 } else 292 loc_register_tree_function(fun, &device_tree); 293 294 fibril_rwlock_write_unlock(&device_tree.rwlock); 295 296 return EOK; 297 } 298 299 static int offline_function(fun_node_t *fun) 300 { 301 int rc; 302 303 fibril_rwlock_write_lock(&device_tree.rwlock); 304 305 if (fun->state == FUN_OFF_LINE) { 306 fibril_rwlock_write_unlock(&device_tree.rwlock); 307 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.", 308 fun->pathname); 309 return EOK; 310 } 311 312 if (fun->ftype == fun_inner) { 313 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.", 314 fun->pathname); 315 316 if (fun->child != NULL) { 317 dev_node_t *dev = fun->child; 318 device_state_t dev_state; 319 320 dev_add_ref(dev); 321 dev_state = dev->state; 322 323 fibril_rwlock_write_unlock(&device_tree.rwlock); 324 325 /* If device is owned by driver, ask driver to give it up. */ 326 if (dev_state == DEVICE_USABLE) { 327 rc = driver_dev_remove(&device_tree, dev); 328 if (rc != EOK) { 329 dev_del_ref(dev); 330 return ENOTSUP; 331 } 332 } 333 334 /* Verify that driver removed all functions */ 335 fibril_rwlock_read_lock(&device_tree.rwlock); 336 if (!list_empty(&dev->functions)) { 337 fibril_rwlock_read_unlock(&device_tree.rwlock); 338 dev_del_ref(dev); 339 return EIO; 340 } 341 342 driver_t *driver = dev->drv; 343 fibril_rwlock_read_unlock(&device_tree.rwlock); 344 345 if (driver) 346 detach_driver(&device_tree, dev); 347 348 fibril_rwlock_write_lock(&device_tree.rwlock); 349 remove_dev_node(&device_tree, dev); 350 351 /* Delete ref created when node was inserted */ 352 dev_del_ref(dev); 353 /* Delete ref created by dev_add_ref(dev) above */ 354 dev_del_ref(dev); 355 } 356 } else { 357 /* Unregister from location service */ 358 rc = loc_service_unregister(fun->service_id); 359 if (rc != EOK) { 360 fibril_rwlock_write_unlock(&device_tree.rwlock); 361 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service."); 362 return EIO; 363 } 364 365 fun->service_id = 0; 366 } 367 368 fun->state = FUN_OFF_LINE; 369 fibril_rwlock_write_unlock(&device_tree.rwlock); 370 371 return EOK; 372 } 373 374 /** Handle function registration. 375 * 376 * Child devices are registered by their parent's device driver. 377 */ 378 static void devman_add_function(ipc_callid_t callid, ipc_call_t *call) 379 { 380 fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call); 381 devman_handle_t dev_handle = IPC_GET_ARG2(*call); 382 sysarg_t match_count = IPC_GET_ARG3(*call); 383 dev_tree_t *tree = &device_tree; 384 385 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle); 386 if (pdev == NULL) { 387 async_answer_0(callid, ENOENT); 388 return; 389 } 390 391 if (ftype != fun_inner && ftype != fun_exposed) { 392 /* Unknown function type */ 393 log_msg(LOG_DEFAULT, LVL_ERROR, 394 "Unknown function type %d provided by driver.", 395 (int) ftype); 396 397 dev_del_ref(pdev); 398 async_answer_0(callid, EINVAL); 399 return; 400 } 401 402 char *fun_name = NULL; 403 int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0); 404 if (rc != EOK) { 405 dev_del_ref(pdev); 406 async_answer_0(callid, rc); 407 return; 408 } 409 410 fibril_rwlock_write_lock(&tree->rwlock); 411 412 /* Check device state */ 413 if (pdev->state == DEVICE_REMOVED) { 414 fibril_rwlock_write_unlock(&tree->rwlock); 415 dev_del_ref(pdev); 416 async_answer_0(callid, ENOENT); 417 return; 418 } 419 420 /* Check that function with same name is not there already. */ 421 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name); 422 if (tfun) { 423 fun_del_ref(tfun); /* drop the new unwanted reference */ 424 fibril_rwlock_write_unlock(&tree->rwlock); 425 dev_del_ref(pdev); 426 async_answer_0(callid, EEXISTS); 427 printf(NAME ": Warning, driver tried to register `%s' twice.\n", 428 fun_name); 429 free(fun_name); 430 return; 431 } 432 433 fun_node_t *fun = create_fun_node(); 434 /* One reference for creation, one for us */ 435 fun_add_ref(fun); 436 fun_add_ref(fun); 437 fun->ftype = ftype; 438 439 /* 440 * We can lock the function here even when holding the tree because 441 * we know it cannot be held by anyone else yet. 442 */ 443 fun_busy_lock(fun); 444 445 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) { 446 fibril_rwlock_write_unlock(&tree->rwlock); 447 dev_del_ref(pdev); 448 fun_busy_unlock(fun); 449 fun_del_ref(fun); 450 delete_fun_node(fun); 451 async_answer_0(callid, ENOMEM); 452 return; 453 } 454 455 fibril_rwlock_write_unlock(&tree->rwlock); 456 dev_del_ref(pdev); 457 458 devman_receive_match_ids(match_count, &fun->match_ids); 459 460 rc = online_function(fun); 461 if (rc != EOK) { 462 /* XXX Set some failed state? */ 463 fun_busy_unlock(fun); 464 fun_del_ref(fun); 465 async_answer_0(callid, rc); 466 return; 467 } 468 469 fun_busy_unlock(fun); 470 fun_del_ref(fun); 471 472 /* Return device handle to parent's driver. */ 473 async_answer_1(callid, EOK, fun->handle); 474 } 475 476 static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call) 477 { 478 devman_handle_t handle = IPC_GET_ARG1(*call); 479 category_id_t cat_id; 480 int rc; 481 482 /* Get category name. */ 483 char *cat_name; 484 rc = async_data_write_accept((void **) &cat_name, true, 485 0, 0, 0, 0); 486 if (rc != EOK) { 487 async_answer_0(callid, rc); 488 return; 489 } 64 driver_list_t drivers_list; 65 dev_tree_t device_tree; 66 67 static void devman_connection_device(ipc_callid_t iid, ipc_call_t *icall, 68 void *arg) 69 { 70 devman_handle_t handle = IPC_GET_ARG2(*icall); 71 dev_node_t *dev = NULL; 490 72 491 73 fun_node_t *fun = find_fun_node(&device_tree, handle); 492 74 if (fun == NULL) { 493 async_answer_0(callid, ENOENT); 494 return; 495 } 496 497 fibril_rwlock_read_lock(&device_tree.rwlock); 498 499 /* Check function state */ 500 if (fun->state == FUN_REMOVED) { 501 fibril_rwlock_read_unlock(&device_tree.rwlock); 502 async_answer_0(callid, ENOENT); 503 return; 504 } 505 506 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING); 507 if (rc == EOK) { 508 loc_service_add_to_cat(fun->service_id, cat_id); 509 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.", 510 fun->pathname, cat_name); 75 dev = find_dev_node(&device_tree, handle); 511 76 } else { 512 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category " 513 "`%s'.", fun->pathname, cat_name); 514 } 515 516 fibril_rwlock_read_unlock(&device_tree.rwlock); 517 fun_del_ref(fun); 518 519 async_answer_0(callid, rc); 520 } 521 522 /** Online function by driver request. 523 * 524 */ 525 static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall, 526 driver_t *drv) 527 { 528 fun_node_t *fun; 529 int rc; 530 531 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()"); 532 533 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall)); 534 if (fun == NULL) { 535 async_answer_0(iid, ENOENT); 536 return; 537 } 538 539 fun_busy_lock(fun); 540 541 fibril_rwlock_read_lock(&device_tree.rwlock); 542 if (fun->dev == NULL || fun->dev->drv != drv) { 543 fibril_rwlock_read_unlock(&device_tree.rwlock); 544 fun_busy_unlock(fun); 545 fun_del_ref(fun); 546 async_answer_0(iid, ENOENT); 547 return; 548 } 549 fibril_rwlock_read_unlock(&device_tree.rwlock); 550 551 rc = online_function(fun); 552 if (rc != EOK) { 553 fun_busy_unlock(fun); 554 fun_del_ref(fun); 555 async_answer_0(iid, (sysarg_t) rc); 556 return; 557 } 558 559 fun_busy_unlock(fun); 560 fun_del_ref(fun); 561 562 async_answer_0(iid, (sysarg_t) EOK); 563 } 564 565 566 /** Offline function by driver request. 567 * 568 */ 569 static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall, 570 driver_t *drv) 571 { 572 fun_node_t *fun; 573 int rc; 574 575 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall)); 576 if (fun == NULL) { 577 async_answer_0(iid, ENOENT); 578 return; 579 } 580 581 fun_busy_lock(fun); 582 583 fibril_rwlock_write_lock(&device_tree.rwlock); 584 if (fun->dev == NULL || fun->dev->drv != drv) { 585 fun_busy_unlock(fun); 586 fun_del_ref(fun); 587 async_answer_0(iid, ENOENT); 588 return; 589 } 590 fibril_rwlock_write_unlock(&device_tree.rwlock); 591 592 rc = offline_function(fun); 593 if (rc != EOK) { 594 fun_busy_unlock(fun); 595 fun_del_ref(fun); 596 async_answer_0(iid, (sysarg_t) rc); 597 return; 598 } 599 600 fun_busy_unlock(fun); 601 fun_del_ref(fun); 602 async_answer_0(iid, (sysarg_t) EOK); 603 } 604 605 /** Remove function. */ 606 static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call) 607 { 608 devman_handle_t fun_handle = IPC_GET_ARG1(*call); 609 dev_tree_t *tree = &device_tree; 610 int rc; 611 612 fun_node_t *fun = find_fun_node(&device_tree, fun_handle); 613 if (fun == NULL) { 614 async_answer_0(callid, ENOENT); 615 return; 616 } 617 618 fun_busy_lock(fun); 619 620 fibril_rwlock_write_lock(&tree->rwlock); 621 622 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname); 623 624 /* Check function state */ 625 if (fun->state == FUN_REMOVED) { 626 fibril_rwlock_write_unlock(&tree->rwlock); 627 fun_busy_unlock(fun); 628 fun_del_ref(fun); 629 async_answer_0(callid, ENOENT); 630 return; 631 } 632 633 if (fun->ftype == fun_inner) { 634 /* This is a surprise removal. Handle possible descendants */ 635 if (fun->child != NULL) { 636 dev_node_t *dev = fun->child; 637 device_state_t dev_state; 638 int gone_rc; 639 640 dev_add_ref(dev); 641 dev_state = dev->state; 642 643 fibril_rwlock_write_unlock(&device_tree.rwlock); 644 645 /* If device is owned by driver, inform driver it is gone. */ 646 if (dev_state == DEVICE_USABLE) 647 gone_rc = driver_dev_gone(&device_tree, dev); 648 else 649 gone_rc = EOK; 650 651 fibril_rwlock_read_lock(&device_tree.rwlock); 652 653 /* Verify that driver succeeded and removed all functions */ 654 if (gone_rc != EOK || !list_empty(&dev->functions)) { 655 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove " 656 "functions for device that is gone. " 657 "Device node is now defunct."); 658 659 /* 660 * Not much we can do but mark the device 661 * node as having invalid state. This 662 * is a driver bug. 663 */ 664 dev->state = DEVICE_INVALID; 665 fibril_rwlock_read_unlock(&device_tree.rwlock); 666 dev_del_ref(dev); 667 if (gone_rc == EOK) 668 gone_rc = ENOTSUP; 669 fun_busy_unlock(fun); 670 fun_del_ref(fun); 671 async_answer_0(callid, gone_rc); 672 return; 673 } 674 675 driver_t *driver = dev->drv; 676 fibril_rwlock_read_unlock(&device_tree.rwlock); 677 678 if (driver) 679 detach_driver(&device_tree, dev); 680 681 fibril_rwlock_write_lock(&device_tree.rwlock); 682 remove_dev_node(&device_tree, dev); 683 684 /* Delete ref created when node was inserted */ 685 dev_del_ref(dev); 686 /* Delete ref created by dev_add_ref(dev) above */ 687 dev_del_ref(dev); 688 } 689 } else { 690 if (fun->service_id != 0) { 691 /* Unregister from location service */ 692 rc = loc_service_unregister(fun->service_id); 693 if (rc != EOK) { 694 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree " 695 "service."); 696 fibril_rwlock_write_unlock(&tree->rwlock); 697 fun_busy_unlock(fun); 698 fun_del_ref(fun); 699 async_answer_0(callid, EIO); 700 return; 701 } 702 } 703 } 704 705 remove_fun_node(&device_tree, fun); 706 fibril_rwlock_write_unlock(&tree->rwlock); 707 fun_busy_unlock(fun); 708 709 /* Delete ref added when inserting function into tree */ 710 fun_del_ref(fun); 711 /* Delete ref added above when looking up function */ 712 fun_del_ref(fun); 713 714 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded."); 715 async_answer_0(callid, EOK); 716 } 717 718 /** Initialize driver which has registered itself as running and ready. 719 * 720 * The initialization is done in a separate fibril to avoid deadlocks (if the 721 * driver needed to be served by devman during the driver's initialization). 722 */ 723 static int init_running_drv(void *drv) 724 { 725 driver_t *driver = (driver_t *) drv; 726 727 initialize_running_driver(driver, &device_tree); 728 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.", 729 driver->name); 730 return 0; 731 } 732 733 /** Function for handling connections from a driver to the device manager. */ 734 static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall) 735 { 736 client_t *client; 737 driver_t *driver = NULL; 738 739 /* Accept the connection. */ 740 async_answer_0(iid, EOK); 741 742 client = async_get_client_data(); 743 if (client == NULL) { 744 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data."); 745 return; 746 } 747 748 while (true) { 749 ipc_call_t call; 750 ipc_callid_t callid = async_get_call(&call); 77 fibril_rwlock_read_lock(&device_tree.rwlock); 751 78 752 if (!IPC_GET_IMETHOD(call))753 break;754 755 if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {756 fibril_mutex_lock(&client->mutex);757 driver = client->driver;758 fibril_mutex_unlock(&client->mutex);759 if (driver == NULL) {760 /* First call must be to DEVMAN_DRIVER_REGISTER */761 async_answer_0(callid, ENOTSUP);762 continue;763 }764 }765 766 switch (IPC_GET_IMETHOD(call)) {767 case DEVMAN_DRIVER_REGISTER:768 fibril_mutex_lock(&client->mutex);769 if (client->driver != NULL) {770 fibril_mutex_unlock(&client->mutex);771 async_answer_0(callid, EINVAL);772 continue;773 }774 client->driver = devman_driver_register(callid, &call);775 fibril_mutex_unlock(&client->mutex);776 break;777 case DEVMAN_ADD_FUNCTION:778 devman_add_function(callid, &call);779 break;780 case DEVMAN_ADD_DEVICE_TO_CATEGORY:781 devman_add_function_to_cat(callid, &call);782 break;783 case DEVMAN_DRV_FUN_ONLINE:784 devman_drv_fun_online(callid, &call, driver);785 break;786 case DEVMAN_DRV_FUN_OFFLINE:787 devman_drv_fun_offline(callid, &call, driver);788 break;789 case DEVMAN_REMOVE_FUNCTION:790 devman_remove_function(callid, &call);791 break;792 default:793 async_answer_0(callid, EINVAL);794 break;795 }796 }797 }798 799 /** Find handle for the device instance identified by the device's path in the800 * device tree. */801 static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)802 {803 char *pathname;804 devman_handle_t handle;805 806 int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);807 if (rc != EOK) {808 async_answer_0(iid, rc);809 return;810 }811 812 fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);813 814 free(pathname);815 816 if (fun == NULL) {817 async_answer_0(iid, ENOENT);818 return;819 }820 821 fibril_rwlock_read_lock(&device_tree.rwlock);822 823 /* Check function state */824 if (fun->state == FUN_REMOVED) {825 fibril_rwlock_read_unlock(&device_tree.rwlock);826 async_answer_0(iid, ENOENT);827 return;828 }829 handle = fun->handle;830 831 fibril_rwlock_read_unlock(&device_tree.rwlock);832 833 /* Delete reference created above by find_fun_node_by_path() */834 fun_del_ref(fun);835 836 async_answer_1(iid, EOK, handle);837 }838 839 /** Get device name. */840 static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)841 {842 devman_handle_t handle = IPC_GET_ARG1(*icall);843 844 fun_node_t *fun = find_fun_node(&device_tree, handle);845 if (fun == NULL) {846 async_answer_0(iid, ENOMEM);847 return;848 }849 850 ipc_callid_t data_callid;851 size_t data_len;852 if (!async_data_read_receive(&data_callid, &data_len)) {853 async_answer_0(iid, EINVAL);854 fun_del_ref(fun);855 return;856 }857 858 void *buffer = malloc(data_len);859 if (buffer == NULL) {860 async_answer_0(data_callid, ENOMEM);861 async_answer_0(iid, ENOMEM);862 fun_del_ref(fun);863 return;864 }865 866 fibril_rwlock_read_lock(&device_tree.rwlock);867 868 /* Check function state */869 if (fun->state == FUN_REMOVED) {870 fibril_rwlock_read_unlock(&device_tree.rwlock);871 free(buffer);872 873 async_answer_0(data_callid, ENOENT);874 async_answer_0(iid, ENOENT);875 fun_del_ref(fun);876 return;877 }878 879 size_t sent_length = str_size(fun->name);880 if (sent_length > data_len) {881 sent_length = data_len;882 }883 884 async_data_read_finalize(data_callid, fun->name, sent_length);885 async_answer_0(iid, EOK);886 887 fibril_rwlock_read_unlock(&device_tree.rwlock);888 fun_del_ref(fun);889 free(buffer);890 }891 892 /** Get function driver name. */893 static void devman_fun_get_driver_name(ipc_callid_t iid, ipc_call_t *icall)894 {895 devman_handle_t handle = IPC_GET_ARG1(*icall);896 897 fun_node_t *fun = find_fun_node(&device_tree, handle);898 if (fun == NULL) {899 async_answer_0(iid, ENOMEM);900 return;901 }902 903 ipc_callid_t data_callid;904 size_t data_len;905 if (!async_data_read_receive(&data_callid, &data_len)) {906 async_answer_0(iid, EINVAL);907 fun_del_ref(fun);908 return;909 }910 911 void *buffer = malloc(data_len);912 if (buffer == NULL) {913 async_answer_0(data_callid, ENOMEM);914 async_answer_0(iid, ENOMEM);915 fun_del_ref(fun);916 return;917 }918 919 fibril_rwlock_read_lock(&device_tree.rwlock);920 921 /* Check function state */922 if (fun->state == FUN_REMOVED) {923 fibril_rwlock_read_unlock(&device_tree.rwlock);924 free(buffer);925 926 async_answer_0(data_callid, ENOENT);927 async_answer_0(iid, ENOENT);928 fun_del_ref(fun);929 return;930 }931 932 /* Check whether function has a driver */933 if (fun->child == NULL || fun->child->drv == NULL) {934 fibril_rwlock_read_unlock(&device_tree.rwlock);935 free(buffer);936 937 async_answer_0(data_callid, EINVAL);938 async_answer_0(iid, EINVAL);939 fun_del_ref(fun);940 return;941 }942 943 size_t sent_length = str_size(fun->child->drv->name);944 if (sent_length > data_len) {945 sent_length = data_len;946 }947 948 async_data_read_finalize(data_callid, fun->child->drv->name,949 sent_length);950 async_answer_0(iid, EOK);951 952 fibril_rwlock_read_unlock(&device_tree.rwlock);953 fun_del_ref(fun);954 free(buffer);955 }956 957 /** Get device path. */958 static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)959 {960 devman_handle_t handle = IPC_GET_ARG1(*icall);961 962 fun_node_t *fun = find_fun_node(&device_tree, handle);963 if (fun == NULL) {964 async_answer_0(iid, ENOMEM);965 return;966 }967 968 ipc_callid_t data_callid;969 size_t data_len;970 if (!async_data_read_receive(&data_callid, &data_len)) {971 async_answer_0(iid, EINVAL);972 fun_del_ref(fun);973 return;974 }975 976 void *buffer = malloc(data_len);977 if (buffer == NULL) {978 async_answer_0(data_callid, ENOMEM);979 async_answer_0(iid, ENOMEM);980 fun_del_ref(fun);981 return;982 }983 984 fibril_rwlock_read_lock(&device_tree.rwlock);985 986 /* Check function state */987 if (fun->state == FUN_REMOVED) {988 fibril_rwlock_read_unlock(&device_tree.rwlock);989 free(buffer);990 991 async_answer_0(data_callid, ENOENT);992 async_answer_0(iid, ENOENT);993 fun_del_ref(fun);994 return;995 }996 997 size_t sent_length = str_size(fun->pathname);998 if (sent_length > data_len) {999 sent_length = data_len;1000 }1001 1002 async_data_read_finalize(data_callid, fun->pathname, sent_length);1003 async_answer_0(iid, EOK);1004 1005 fibril_rwlock_read_unlock(&device_tree.rwlock);1006 fun_del_ref(fun);1007 free(buffer);1008 }1009 1010 static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)1011 {1012 ipc_callid_t callid;1013 size_t size;1014 size_t act_size;1015 int rc;1016 1017 if (!async_data_read_receive(&callid, &size)) {1018 async_answer_0(callid, EREFUSED);1019 async_answer_0(iid, EREFUSED);1020 return;1021 }1022 1023 fibril_rwlock_read_lock(&device_tree.rwlock);1024 1025 dev_node_t *dev = find_dev_node_no_lock(&device_tree,1026 IPC_GET_ARG1(*icall));1027 if (dev == NULL || dev->state == DEVICE_REMOVED) {1028 fibril_rwlock_read_unlock(&device_tree.rwlock);1029 async_answer_0(callid, ENOENT);1030 async_answer_0(iid, ENOENT);1031 return;1032 }1033 1034 devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);1035 if (hdl_buf == NULL) {1036 fibril_rwlock_read_unlock(&device_tree.rwlock);1037 async_answer_0(callid, ENOMEM);1038 async_answer_0(iid, ENOMEM);1039 return;1040 }1041 1042 rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);1043 if (rc != EOK) {1044 fibril_rwlock_read_unlock(&device_tree.rwlock);1045 async_answer_0(callid, rc);1046 async_answer_0(iid, rc);1047 return;1048 }1049 1050 fibril_rwlock_read_unlock(&device_tree.rwlock);1051 1052 sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);1053 free(hdl_buf);1054 1055 async_answer_1(iid, retval, act_size);1056 }1057 1058 1059 /** Get handle for child device of a function. */1060 static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)1061 {1062 fun_node_t *fun;1063 1064 fibril_rwlock_read_lock(&device_tree.rwlock);1065 1066 fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));1067 if (fun == NULL || fun->state == FUN_REMOVED) {1068 fibril_rwlock_read_unlock(&device_tree.rwlock);1069 async_answer_0(iid, ENOENT);1070 return;1071 }1072 1073 if (fun->child == NULL) {1074 fibril_rwlock_read_unlock(&device_tree.rwlock);1075 async_answer_0(iid, ENOENT);1076 return;1077 }1078 1079 async_answer_1(iid, EOK, fun->child->handle);1080 1081 fibril_rwlock_read_unlock(&device_tree.rwlock);1082 }1083 1084 /** Online function.1085 *1086 * Send a request to online a function to the responsible driver.1087 * The driver may offline other functions if necessary (i.e. if the state1088 * of this function is linked to state of another function somehow).1089 */1090 static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)1091 {1092 fun_node_t *fun;1093 int rc;1094 1095 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));1096 if (fun == NULL) {1097 async_answer_0(iid, ENOENT);1098 return;1099 }1100 1101 rc = driver_fun_online(&device_tree, fun);1102 fun_del_ref(fun);1103 1104 async_answer_0(iid, (sysarg_t) rc);1105 }1106 1107 /** Offline function.1108 *1109 * Send a request to offline a function to the responsible driver. As1110 * a result the subtree rooted at that function should be cleanly1111 * detatched. The driver may offline other functions if necessary1112 * (i.e. if the state of this function is linked to state of another1113 * function somehow).1114 */1115 static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)1116 {1117 fun_node_t *fun;1118 int rc;1119 1120 fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));1121 if (fun == NULL) {1122 async_answer_0(iid, ENOENT);1123 return;1124 }1125 1126 rc = driver_fun_offline(&device_tree, fun);1127 fun_del_ref(fun);1128 1129 async_answer_0(iid, (sysarg_t) rc);1130 }1131 1132 /** Find handle for the function instance identified by its service ID. */1133 static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)1134 {1135 fun_node_t *fun;1136 1137 fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));1138 1139 if (fun == NULL) {1140 async_answer_0(iid, ENOENT);1141 return;1142 }1143 1144 fibril_rwlock_read_lock(&device_tree.rwlock);1145 1146 /* Check function state */1147 if (fun->state == FUN_REMOVED) {1148 fibril_rwlock_read_unlock(&device_tree.rwlock);1149 async_answer_0(iid, ENOENT);1150 return;1151 }1152 1153 async_answer_1(iid, EOK, fun->handle);1154 fibril_rwlock_read_unlock(&device_tree.rwlock);1155 fun_del_ref(fun);1156 }1157 1158 /** Function for handling connections from a client to the device manager. */1159 static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)1160 {1161 /* Accept connection. */1162 async_answer_0(iid, EOK);1163 1164 while (true) {1165 ipc_call_t call;1166 ipc_callid_t callid = async_get_call(&call);1167 1168 if (!IPC_GET_IMETHOD(call))1169 break;1170 1171 switch (IPC_GET_IMETHOD(call)) {1172 case DEVMAN_DEVICE_GET_HANDLE:1173 devman_function_get_handle(callid, &call);1174 break;1175 case DEVMAN_DEV_GET_FUNCTIONS:1176 devman_dev_get_functions(callid, &call);1177 break;1178 case DEVMAN_FUN_GET_CHILD:1179 devman_fun_get_child(callid, &call);1180 break;1181 case DEVMAN_FUN_GET_NAME:1182 devman_fun_get_name(callid, &call);1183 break;1184 case DEVMAN_FUN_GET_DRIVER_NAME:1185 devman_fun_get_driver_name(callid, &call);1186 break;1187 case DEVMAN_FUN_GET_PATH:1188 devman_fun_get_path(callid, &call);1189 break;1190 case DEVMAN_FUN_ONLINE:1191 devman_fun_online(callid, &call);1192 break;1193 case DEVMAN_FUN_OFFLINE:1194 devman_fun_offline(callid, &call);1195 break;1196 case DEVMAN_FUN_SID_TO_HANDLE:1197 devman_fun_sid_to_handle(callid, &call);1198 break;1199 default:1200 async_answer_0(callid, ENOENT);1201 }1202 }1203 }1204 1205 static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,1206 bool drv_to_parent)1207 {1208 devman_handle_t handle = IPC_GET_ARG2(*icall);1209 devman_handle_t fwd_h;1210 fun_node_t *fun = NULL;1211 dev_node_t *dev = NULL;1212 1213 fun = find_fun_node(&device_tree, handle);1214 if (fun == NULL)1215 dev = find_dev_node(&device_tree, handle);1216 else {1217 fibril_rwlock_read_lock(&device_tree.rwlock);1218 79 dev = fun->dev; 1219 80 if (dev != NULL) 1220 81 dev_add_ref(dev); 82 1221 83 fibril_rwlock_read_unlock(&device_tree.rwlock); 1222 84 } 1223 85 1224 86 /* 1225 87 * For a valid function to connect to we need a device. The root … … 1234 96 goto cleanup; 1235 97 } 1236 1237 if (fun == NULL && !drv_to_parent) {98 99 if (fun == NULL) { 1238 100 log_msg(LOG_DEFAULT, LVL_ERROR, NAME ": devman_forward error - cannot " 1239 101 "connect to handle %" PRIun ", refers to a device.", … … 1243 105 } 1244 106 1245 driver_t *driver = NULL;1246 1247 107 fibril_rwlock_read_lock(&device_tree.rwlock); 1248 108 1249 if (drv_to_parent) { 1250 /* Connect to parent function of a device (or device function). */ 1251 if (dev->pfun->dev != NULL) 1252 driver = dev->pfun->dev->drv; 1253 1254 fwd_h = dev->pfun->handle; 1255 } else if (dev->state == DEVICE_USABLE) { 1256 /* Connect to the specified function */ 1257 driver = dev->drv; 1258 assert(driver != NULL); 1259 1260 fwd_h = handle; 1261 } 109 /* Connect to the specified function */ 110 driver_t *driver = dev->drv; 1262 111 1263 112 fibril_rwlock_read_unlock(&device_tree.rwlock); … … 1269 118 goto cleanup; 1270 119 } 1271 1272 int method;1273 if (drv_to_parent)1274 method = DRIVER_DRIVER;1275 else1276 method = DRIVER_CLIENT;1277 120 1278 121 if (!driver->sess) { … … 1282 125 goto cleanup; 1283 126 } 1284 127 1285 128 if (fun != NULL) { 1286 129 log_msg(LOG_DEFAULT, LVL_DEBUG, … … 1294 137 1295 138 async_exch_t *exch = async_exchange_begin(driver->sess); 1296 async_forward_fast(iid, exch, method, fwd_h, 0, IPC_FF_NONE);139 async_forward_fast(iid, exch, INTERFACE_DDF_CLIENT, handle, 0, IPC_FF_NONE); 1297 140 async_exchange_end(exch); 1298 141 … … 1305 148 } 1306 149 1307 /** Function for handling connections from a client forwarded by the location 1308 * service to the device manager. */ 1309 static void devman_connection_loc(ipc_callid_t iid, ipc_call_t *icall) 1310 { 150 static void devman_connection_parent(ipc_callid_t iid, ipc_call_t *icall, 151 void *arg) 152 { 153 devman_handle_t handle = IPC_GET_ARG2(*icall); 154 dev_node_t *dev = NULL; 155 156 fun_node_t *fun = find_fun_node(&device_tree, handle); 157 if (fun == NULL) { 158 dev = find_dev_node(&device_tree, handle); 159 } else { 160 fibril_rwlock_read_lock(&device_tree.rwlock); 161 162 dev = fun->dev; 163 if (dev != NULL) 164 dev_add_ref(dev); 165 166 fibril_rwlock_read_unlock(&device_tree.rwlock); 167 } 168 169 /* 170 * For a valid function to connect to we need a device. The root 171 * function, for example, has no device and cannot be connected to. 172 * This means @c dev needs to be valid regardless whether we are 173 * connecting to a device or to a function. 174 */ 175 if (dev == NULL) { 176 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding failed - no device or " 177 "function with handle %" PRIun " was found.", handle); 178 async_answer_0(iid, ENOENT); 179 goto cleanup; 180 } 181 182 driver_t *driver = NULL; 183 184 fibril_rwlock_read_lock(&device_tree.rwlock); 185 186 /* Connect to parent function of a device (or device function). */ 187 if (dev->pfun->dev != NULL) 188 driver = dev->pfun->dev->drv; 189 190 devman_handle_t fun_handle = dev->pfun->handle; 191 192 fibril_rwlock_read_unlock(&device_tree.rwlock); 193 194 if (driver == NULL) { 195 log_msg(LOG_DEFAULT, LVL_ERROR, "IPC forwarding refused - " \ 196 "the device %" PRIun " is not in usable state.", handle); 197 async_answer_0(iid, ENOENT); 198 goto cleanup; 199 } 200 201 if (!driver->sess) { 202 log_msg(LOG_DEFAULT, LVL_ERROR, 203 "Could not forward to driver `%s'.", driver->name); 204 async_answer_0(iid, EINVAL); 205 goto cleanup; 206 } 207 208 if (fun != NULL) { 209 log_msg(LOG_DEFAULT, LVL_DEBUG, 210 "Forwarding request for `%s' function to driver `%s'.", 211 fun->pathname, driver->name); 212 } else { 213 log_msg(LOG_DEFAULT, LVL_DEBUG, 214 "Forwarding request for `%s' device to driver `%s'.", 215 dev->pfun->pathname, driver->name); 216 } 217 218 async_exch_t *exch = async_exchange_begin(driver->sess); 219 async_forward_fast(iid, exch, INTERFACE_DDF_DRIVER, fun_handle, 0, IPC_FF_NONE); 220 async_exchange_end(exch); 221 222 cleanup: 223 if (dev != NULL) 224 dev_del_ref(dev); 225 226 if (fun != NULL) 227 fun_del_ref(fun); 228 } 229 230 static void devman_forward(ipc_callid_t iid, ipc_call_t *icall, void *arg) 231 { 232 iface_t iface = IPC_GET_ARG1(*icall); 1311 233 service_id_t service_id = IPC_GET_ARG2(*icall); 1312 fun_node_t *fun; 1313 dev_node_t *dev; 1314 devman_handle_t handle; 1315 driver_t *driver; 1316 1317 fun = find_loc_tree_function(&device_tree, service_id); 234 235 fun_node_t *fun = find_loc_tree_function(&device_tree, service_id); 1318 236 1319 237 fibril_rwlock_read_lock(&device_tree.rwlock); 1320 238 1321 if ( fun == NULL || fun->dev == NULL || fun->dev->drv == NULL) {1322 log_msg(LOG_DEFAULT, LVL_WARN, "devman_ connection_loc(): function "239 if ((fun == NULL) || (fun->dev == NULL) || (fun->dev->drv == NULL)) { 240 log_msg(LOG_DEFAULT, LVL_WARN, "devman_forward(): function " 1323 241 "not found.\n"); 1324 242 fibril_rwlock_read_unlock(&device_tree.rwlock); … … 1327 245 } 1328 246 1329 dev = fun->dev;1330 driver = dev->drv;1331 handle = fun->handle;247 dev_node_t *dev = fun->dev; 248 driver_t *driver = dev->drv; 249 devman_handle_t handle = fun->handle; 1332 250 1333 251 fibril_rwlock_read_unlock(&device_tree.rwlock); 1334 252 1335 253 async_exch_t *exch = async_exchange_begin(driver->sess); 1336 async_forward_fast(iid, exch, DRIVER_CLIENT, handle, 0, 1337 IPC_FF_NONE); 254 async_forward_fast(iid, exch, iface, handle, 0, IPC_FF_NONE); 1338 255 async_exchange_end(exch); 1339 256 1340 257 log_msg(LOG_DEFAULT, LVL_DEBUG, 1341 "Forwarding locservice request for `%s' function to driver `%s'.",258 "Forwarding service request for `%s' function to driver `%s'.", 1342 259 fun->pathname, driver->name); 1343 260 1344 261 fun_del_ref(fun); 1345 }1346 1347 /** Function for handling connections to device manager. */1348 static void devman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)1349 {1350 /* Select port. */1351 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {1352 case DEVMAN_DRIVER:1353 devman_connection_driver(iid, icall);1354 break;1355 case DEVMAN_CLIENT:1356 devman_connection_client(iid, icall);1357 break;1358 case DEVMAN_CONNECT_TO_DEVICE:1359 /* Connect client to selected device. */1360 devman_forward(iid, icall, false);1361 break;1362 case DEVMAN_CONNECT_FROM_LOC:1363 /* Someone connected through loc node. */1364 devman_connection_loc(iid, icall);1365 break;1366 case DEVMAN_CONNECT_TO_PARENTS_DEVICE:1367 /* Connect client to selected device. */1368 devman_forward(iid, icall, true);1369 break;1370 default:1371 /* No such interface */1372 async_answer_0(iid, ENOENT);1373 }1374 262 } 1375 263 … … 1437 325 async_set_client_data_constructor(devman_client_data_create); 1438 326 async_set_client_data_destructor(devman_client_data_destroy); 1439 async_set_client_connection(devman_connection); 327 328 port_id_t port; 329 rc = async_create_port(INTERFACE_DDF_DRIVER, 330 devman_connection_driver, NULL, &port); 331 if (rc != EOK) 332 return rc; 333 334 rc = async_create_port(INTERFACE_DDF_CLIENT, 335 devman_connection_client, NULL, &port); 336 if (rc != EOK) 337 return rc; 338 339 rc = async_create_port(INTERFACE_DEVMAN_DEVICE, 340 devman_connection_device, NULL, &port); 341 if (rc != EOK) 342 return rc; 343 344 rc = async_create_port(INTERFACE_DEVMAN_PARENT, 345 devman_connection_parent, NULL, &port); 346 if (rc != EOK) 347 return rc; 348 349 async_set_fallback_port_handler(devman_forward, NULL); 1440 350 1441 351 if (!devman_init()) {
Note:
See TracChangeset
for help on using the changeset viewer.