1 /* 2 * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "legacy_drivers.h" 8 9 #include <dirent.h> 10 #include <errno.h> 11 #include <new> 12 #include <stdio.h> 13 14 #include <FindDirectory.h> 15 #include <image.h> 16 #include <NodeMonitor.h> 17 18 #include <boot_device.h> 19 #include <elf.h> 20 #include <fs/devfs.h> 21 #include <fs/KPath.h> 22 #include <fs/node_monitor.h> 23 #include <Notifications.h> 24 #include <safemode.h> 25 #include <util/DoublyLinkedList.h> 26 #include <util/OpenHashTable.h> 27 #include <util/Stack.h> 28 #include <vfs.h> 29 30 #include "BaseDevice.h" 31 #include "devfs_private.h" 32 33 34 //#define TRACE_LEGACY_DRIVERS 35 #ifdef TRACE_LEGACY_DRIVERS 36 # define TRACE(x) dprintf x 37 #else 38 # define TRACE(x) 39 #endif 40 41 #define DRIVER_HASH_SIZE 16 42 43 struct legacy_driver; 44 45 class LegacyDevice : public BaseDevice, 46 public DoublyLinkedListLinkImpl<LegacyDevice> { 47 public: 48 LegacyDevice(legacy_driver* driver, 49 const char* path, device_hooks* hooks); 50 virtual ~LegacyDevice(); 51 52 status_t InitCheck() const; 53 54 virtual status_t InitDevice(); 55 virtual void UninitDevice(); 56 57 void SetHooks(device_hooks* hooks); 58 59 legacy_driver* Driver() const { return fDriver; } 60 const char* Path() const { return fPath; } 61 device_hooks* Hooks() const { return fHooks; } 62 63 virtual status_t Open(const char* path, int openMode, 64 void** _cookie); 65 virtual status_t Select(void* cookie, uint8 event, selectsync* sync); 66 67 bool Republished() const { return fRepublished; } 68 void SetRepublished(bool republished) 69 { fRepublished = republished; } 70 private: 71 legacy_driver* fDriver; 72 const char* fPath; 73 device_hooks* fHooks; 74 bool fRepublished; 75 }; 76 77 typedef DoublyLinkedList<LegacyDevice> DeviceList; 78 79 struct legacy_driver { 80 legacy_driver* next; 81 const char* path; 82 const char* name; 83 dev_t device; 84 ino_t node; 85 time_t last_modified; 86 image_id image; 87 uint32 devices_used; 88 bool binary_updated; 89 int32 priority; 90 DeviceList devices; 91 92 // driver image information 93 int32 api_version; 94 device_hooks* (*find_device)(const char *); 95 const char** (*publish_devices)(void); 96 status_t (*uninit_driver)(void); 97 status_t (*uninit_hardware)(void); 98 }; 99 100 struct path_entry : DoublyLinkedListLinkImpl<path_entry> { 101 char path[B_PATH_NAME_LENGTH]; 102 }; 103 104 struct driver_entry : public DoublyLinkedListLinkImpl<driver_entry> { 105 char* path; 106 dev_t device; 107 ino_t node; 108 int32 busses; 109 }; 110 111 typedef DoublyLinkedList<driver_entry> DriverEntryList; 112 113 struct directory_node_entry { 114 HashTableLink<directory_node_entry> link; 115 ino_t node; 116 }; 117 118 struct DirectoryNodeHashDefinition { 119 typedef ino_t* KeyType; 120 typedef directory_node_entry ValueType; 121 122 size_t HashKey(ino_t* key) const 123 { return _Hash(*key); } 124 size_t Hash(directory_node_entry* entry) const 125 { return _Hash(entry->node); } 126 bool Compare(ino_t* key, directory_node_entry* entry) const 127 { return *key == entry->node; } 128 HashTableLink<directory_node_entry>* 129 GetLink(directory_node_entry* entry) const 130 { return &entry->link; } 131 132 uint32 _Hash(ino_t node) const 133 { return (uint32)(node >> 32) + (uint32)node; } 134 }; 135 136 typedef OpenHashTable<DirectoryNodeHashDefinition> DirectoryNodeHash; 137 138 class DirectoryIterator { 139 public: 140 DirectoryIterator(const char *path, 141 const char *subPath = NULL, bool recursive = false); 142 ~DirectoryIterator(); 143 144 void SetTo(const char *path, const char *subPath = NULL, 145 bool recursive = false); 146 147 status_t GetNext(KPath &path, struct stat &stat); 148 const char* CurrentName() const { return fCurrentName; } 149 150 void Unset(); 151 void AddPath(const char *path, const char *subPath = NULL); 152 153 private: 154 Stack<KPath*> fPaths; 155 bool fRecursive; 156 DIR* fDirectory; 157 KPath* fBasePath; 158 const char* fCurrentName; 159 }; 160 161 162 class DirectoryWatcher : public NotificationListener { 163 public: 164 DirectoryWatcher(); 165 virtual ~DirectoryWatcher(); 166 167 virtual void EventOccured(NotificationService& service, 168 const KMessage* event); 169 }; 170 171 class DriverWatcher : public NotificationListener { 172 public: 173 DriverWatcher(); 174 virtual ~DriverWatcher(); 175 176 virtual void EventOccured(NotificationService& service, 177 const KMessage* event); 178 }; 179 180 static status_t unload_driver(legacy_driver *driver); 181 static status_t load_driver(legacy_driver *driver); 182 183 184 static hash_table* sDriverHash; 185 static DriverWatcher sDriverWatcher; 186 static int32 sDriverEvents; 187 static DoublyLinkedList<path_entry> sDriversToAdd; 188 static DirectoryWatcher sDirectoryWatcher; 189 static DirectoryNodeHash sDirectoryNodeHash; 190 static recursive_lock sLock; 191 static bool sWatching; 192 193 194 // #pragma mark - driver private 195 196 197 static uint32 198 driver_entry_hash(void *_driver, const void *_key, uint32 range) 199 { 200 legacy_driver *driver = (legacy_driver *)_driver; 201 const char *key = (const char *)_key; 202 203 if (driver != NULL) 204 return hash_hash_string(driver->name) % range; 205 206 return hash_hash_string(key) % range; 207 } 208 209 210 static int 211 driver_entry_compare(void *_driver, const void *_key) 212 { 213 legacy_driver *driver = (legacy_driver *)_driver; 214 const char *key = (const char *)_key; 215 216 return strcmp(driver->name, key); 217 } 218 219 220 /*! Collects all published devices of a driver, compares them to what the 221 driver would publish now, and then publishes/unpublishes the devices 222 as needed. 223 If the driver does not publish any devices anymore, it is unloaded. 224 */ 225 static status_t 226 republish_driver(legacy_driver* driver) 227 { 228 if (driver->image < 0) { 229 // The driver is not yet loaded - go through the normal load procedure 230 return load_driver(driver); 231 } 232 233 // mark all devices 234 DeviceList::Iterator iterator = driver->devices.GetIterator(); 235 while (LegacyDevice* device = iterator.Next()) { 236 device->SetRepublished(false); 237 } 238 239 // now ask the driver for it's currently published devices 240 const char** devicePaths = driver->publish_devices(); 241 242 int32 exported = 0; 243 for (; devicePaths != NULL && devicePaths[0]; devicePaths++) { 244 LegacyDevice* device; 245 246 iterator = driver->devices.GetIterator(); 247 while ((device = iterator.Next()) != NULL) { 248 if (!strncmp(device->Path(), devicePaths[0], B_PATH_NAME_LENGTH)) { 249 // mark device as republished 250 device->SetRepublished(true); 251 exported++; 252 break; 253 } 254 } 255 256 device_hooks* hooks = driver->find_device(devicePaths[0]); 257 if (hooks == NULL) 258 continue; 259 260 if (device != NULL) { 261 // update hooks 262 device->SetHooks(hooks); 263 continue; 264 } 265 266 // the device was not present before -> publish it now 267 TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0])); 268 device = new(std::nothrow) LegacyDevice(driver, devicePaths[0], hooks); 269 if (device != NULL && device->InitCheck() == B_OK 270 && devfs_publish_device(devicePaths[0], device) == B_OK) { 271 driver->devices.Add(device); 272 exported++; 273 } else 274 delete device; 275 } 276 277 // remove all devices that weren't republished 278 iterator = driver->devices.GetIterator(); 279 while (LegacyDevice* device = iterator.Next()) { 280 if (device->Republished()) 281 continue; 282 283 TRACE(("devfs: unpublishing no more present \"%s\"\n", device->Path())); 284 iterator.Remove(); 285 286 devfs_unpublish_device(device, true); 287 delete device; 288 } 289 290 if (exported == 0) { 291 TRACE(("devfs: driver \"%s\" does not publish any more nodes and is " 292 "unloaded\n", driver->path)); 293 unload_driver(driver); 294 } 295 296 return B_OK; 297 } 298 299 300 static status_t 301 load_driver(legacy_driver *driver) 302 { 303 status_t (*init_hardware)(void); 304 status_t (*init_driver)(void); 305 status_t status; 306 307 driver->binary_updated = false; 308 309 // load the module 310 image_id image = driver->image; 311 if (image < 0) { 312 image = load_kernel_add_on(driver->path); 313 if (image < 0) 314 return image; 315 } 316 317 // For a valid device driver the following exports are required 318 319 int32 *apiVersion; 320 if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA, 321 (void **)&apiVersion) == B_OK) { 322 #if B_CUR_DRIVER_API_VERSION != 2 323 // just in case someone decides to bump up the api version 324 #error Add checks here for new vs old api version! 325 #endif 326 if (*apiVersion > B_CUR_DRIVER_API_VERSION) { 327 dprintf("devfs: \"%s\" api_version %ld not handled\n", driver->name, 328 *apiVersion); 329 status = B_BAD_VALUE; 330 goto error1; 331 } 332 if (*apiVersion < 1) { 333 dprintf("devfs: \"%s\" api_version invalid\n", driver->name); 334 status = B_BAD_VALUE; 335 goto error1; 336 } 337 338 driver->api_version = *apiVersion; 339 } else 340 dprintf("devfs: \"%s\" api_version missing\n", driver->name); 341 342 if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT, 343 (void **)&driver->publish_devices) != B_OK 344 || get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT, 345 (void **)&driver->find_device) != B_OK) { 346 dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n", 347 driver->name); 348 status = B_BAD_VALUE; 349 goto error1; 350 } 351 352 // Init the driver 353 354 if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT, 355 (void **)&init_hardware) == B_OK 356 && (status = init_hardware()) != B_OK) { 357 TRACE(("%s: init_hardware() failed: %s\n", driver->name, 358 strerror(status))); 359 status = ENXIO; 360 goto error1; 361 } 362 363 if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT, 364 (void **)&init_driver) == B_OK 365 && (status = init_driver()) != B_OK) { 366 TRACE(("%s: init_driver() failed: %s\n", driver->name, 367 strerror(status))); 368 status = ENXIO; 369 goto error2; 370 } 371 372 // resolve and cache those for the driver unload code 373 if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT, 374 (void **)&driver->uninit_driver) != B_OK) 375 driver->uninit_driver = NULL; 376 if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT, 377 (void **)&driver->uninit_hardware) != B_OK) 378 driver->uninit_hardware = NULL; 379 380 // The driver has successfully been initialized, now we can 381 // finally publish its device entries 382 383 driver->image = image; 384 return republish_driver(driver); 385 386 error2: 387 if (driver->uninit_hardware) 388 driver->uninit_hardware(); 389 390 error1: 391 if (driver->image < 0) { 392 unload_kernel_add_on(image); 393 driver->image = status; 394 } 395 396 return status; 397 } 398 399 400 static status_t 401 unload_driver(legacy_driver *driver) 402 { 403 if (driver->image < 0) { 404 // driver is not currently loaded 405 return B_NO_INIT; 406 } 407 408 if (driver->uninit_driver) 409 driver->uninit_driver(); 410 411 if (driver->uninit_hardware) 412 driver->uninit_hardware(); 413 414 unload_kernel_add_on(driver->image); 415 driver->image = -1; 416 driver->binary_updated = false; 417 driver->find_device = NULL; 418 driver->publish_devices = NULL; 419 driver->uninit_driver = NULL; 420 driver->uninit_hardware = NULL; 421 422 return B_OK; 423 } 424 425 426 /*! Unpublishes all devices belonging to the \a driver. */ 427 static void 428 unpublish_driver(legacy_driver *driver) 429 { 430 while (LegacyDevice* device = driver->devices.RemoveHead()) { 431 if (devfs_unpublish_device(device, true) == B_OK) 432 delete device; 433 } 434 } 435 436 437 static int32 438 get_priority(const char* path) 439 { 440 // TODO: would it be better to initialize a static structure here 441 // using find_directory()? 442 const directory_which whichPath[] = { 443 B_BEOS_DIRECTORY, 444 B_COMMON_DIRECTORY, 445 B_USER_DIRECTORY 446 }; 447 KPath pathBuffer; 448 449 for (uint32 index = 0; index < sizeof(whichPath) / sizeof(whichPath[0]); 450 index++) { 451 if (find_directory(whichPath[index], gBootDevice, false, 452 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) { 453 pathBuffer.UnlockBuffer(); 454 if (!strncmp(pathBuffer.Path(), path, pathBuffer.BufferSize())) 455 return index; 456 } else 457 pathBuffer.UnlockBuffer(); 458 } 459 460 return -1; 461 } 462 463 464 static const char * 465 get_leaf(const char *path) 466 { 467 const char *name = strrchr(path, '/'); 468 if (name == NULL) 469 return path; 470 471 return name + 1; 472 } 473 474 475 static legacy_driver * 476 find_driver(dev_t device, ino_t node) 477 { 478 hash_iterator iterator; 479 hash_open(sDriverHash, &iterator); 480 legacy_driver *driver; 481 while (true) { 482 driver = (legacy_driver *)hash_next(sDriverHash, &iterator); 483 if (driver == NULL 484 || driver->device == device && driver->node == node) 485 break; 486 } 487 488 hash_close(sDriverHash, &iterator, false); 489 return driver; 490 } 491 492 493 static status_t 494 add_driver(const char *path, image_id image) 495 { 496 // see if we already know this driver 497 498 struct stat stat; 499 if (image >= 0) { 500 // The image ID should be a small number and hopefully the boot FS 501 // doesn't use small negative values -- if it is inode based, we should 502 // be relatively safe. 503 stat.st_dev = -1; 504 stat.st_ino = -1; 505 } else { 506 if (::stat(path, &stat) != 0) 507 return errno; 508 } 509 510 int32 priority = get_priority(path); 511 512 RecursiveLocker _(sLock); 513 514 legacy_driver *driver = (legacy_driver *)hash_lookup(sDriverHash, 515 get_leaf(path)); 516 if (driver != NULL) { 517 // we know this driver 518 if (strcmp(driver->path, path) != 0) { 519 // TODO: do properly, but for now we just update the path if it 520 // isn't the same anymore so rescanning of drivers will work in 521 // case this driver was loaded so early that it has a boot module 522 // path and not a proper driver path 523 free((char*)driver->path); 524 driver->path = strdup(path); 525 driver->name = get_leaf(driver->path); 526 driver->binary_updated = true; 527 } 528 529 // TODO: check if this driver is a different one and has precendence 530 // (ie. common supersedes system). 531 //dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority); 532 if (priority >= driver->priority) { 533 driver->binary_updated = true; 534 return B_OK; 535 } 536 537 // TODO: test for changes here and/or via node monitoring and reload 538 // the driver if necessary 539 if (driver->image < B_OK) 540 return driver->image; 541 542 return B_OK; 543 } 544 545 // we don't know this driver, create a new entry for it 546 547 driver = (legacy_driver *)malloc(sizeof(legacy_driver)); 548 if (driver == NULL) 549 return B_NO_MEMORY; 550 551 driver->path = strdup(path); 552 if (driver->path == NULL) { 553 free(driver); 554 return B_NO_MEMORY; 555 } 556 557 driver->name = get_leaf(driver->path); 558 driver->device = stat.st_dev; 559 driver->node = stat.st_ino; 560 driver->image = image; 561 driver->last_modified = stat.st_mtime; 562 driver->devices_used = 0; 563 driver->binary_updated = false; 564 driver->priority = priority; 565 566 driver->api_version = 1; 567 driver->find_device = NULL; 568 driver->publish_devices = NULL; 569 driver->uninit_driver = NULL; 570 driver->uninit_hardware = NULL; 571 new(&driver->devices) DeviceList; 572 573 hash_insert(sDriverHash, driver); 574 if (stat.st_dev > 0) { 575 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_STAT, 576 sDriverWatcher); 577 } 578 579 // Even if loading the driver fails - its entry will stay with us 580 // so that we don't have to go through it again 581 return load_driver(driver); 582 } 583 584 585 /*! This is no longer part of the public kernel API, so we just export the 586 symbol 587 */ 588 extern "C" status_t load_driver_symbols(const char *driverName); 589 status_t 590 load_driver_symbols(const char *driverName) 591 { 592 // This is done globally for the whole kernel via the settings file. 593 // We don't have to do anything here. 594 595 return B_OK; 596 } 597 598 599 static status_t 600 reload_driver(legacy_driver *driver) 601 { 602 dprintf("devfs: reload driver \"%s\"\n", driver->name); 603 604 unload_driver(driver); 605 606 status_t status = load_driver(driver); 607 if (status < B_OK) 608 unpublish_driver(driver); 609 610 return status; 611 } 612 613 614 static void 615 handle_driver_events(void */*_fs*/, int /*iteration*/) 616 { 617 if (atomic_and(&sDriverEvents, 0) == 0) 618 return; 619 620 // something happened, let's see what it was 621 622 RecursiveLocker locker(sLock); 623 624 while (true) { 625 path_entry* path = sDriversToAdd.RemoveHead(); 626 if (path == NULL) 627 break; 628 629 legacy_driver_add(path->path); 630 delete path; 631 } 632 633 hash_iterator iterator; 634 hash_open(sDriverHash, &iterator); 635 legacy_driver *driver; 636 while (true) { 637 driver = (legacy_driver *)hash_next(sDriverHash, &iterator); 638 if (driver == NULL) 639 break; 640 if (!driver->binary_updated || driver->devices_used != 0) 641 continue; 642 643 // try to reload the driver 644 reload_driver(driver); 645 } 646 647 hash_close(sDriverHash, &iterator, false); 648 } 649 650 651 static void 652 driver_added(const char* path) 653 { 654 int32 priority = get_priority(path); 655 RecursiveLocker locker(sLock); 656 657 legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, 658 get_leaf(path)); 659 660 if (driver == NULL) { 661 // Add the driver to our list 662 path_entry* entry = new(std::nothrow) path_entry; 663 if (entry == NULL) 664 return; 665 666 strlcpy(entry->path, path, sizeof(entry->path)); 667 sDriversToAdd.Add(entry); 668 } else { 669 // Update the driver if it is affected by the new entry 670 if (priority < driver->priority) 671 return; 672 673 driver->binary_updated = true; 674 } 675 676 atomic_add(&sDriverEvents, 1); 677 } 678 679 680 static void 681 driver_removed(const char* path) 682 { 683 int32 priority = get_priority(path); 684 RecursiveLocker locker(sLock); 685 686 legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, 687 get_leaf(path)); 688 if (driver == NULL || priority < driver->priority) 689 return; 690 691 driver->binary_updated = true; 692 atomic_add(&sDriverEvents, 1); 693 } 694 695 696 // #pragma mark - DriverWatcher 697 698 699 DriverWatcher::DriverWatcher() 700 { 701 } 702 703 704 DriverWatcher::~DriverWatcher() 705 { 706 } 707 708 709 void 710 DriverWatcher::EventOccured(NotificationService& service, 711 const KMessage* event) 712 { 713 if (event->GetInt32("opcode", -1) != B_STAT_CHANGED 714 || (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0) 715 return; 716 717 RecursiveLocker locker(sLock); 718 719 legacy_driver* driver = find_driver(event->GetInt32("device", -1), 720 event->GetInt64("node", 0)); 721 if (driver == NULL) 722 return; 723 724 driver->binary_updated = true; 725 726 if (driver->devices_used == 0) { 727 // trigger a reload of the driver 728 atomic_add(&sDriverEvents, 1); 729 } else { 730 // driver is in use right now 731 dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name); 732 } 733 } 734 735 736 static int 737 dump_driver(int argc, char** argv) 738 { 739 if (argc < 2) { 740 // print list of all drivers 741 kprintf("address image used publ. pri name\n"); 742 hash_iterator iterator; 743 hash_open(sDriverHash, &iterator); 744 while (true) { 745 legacy_driver* driver = (legacy_driver*)hash_next(sDriverHash, 746 &iterator); 747 if (driver == NULL) 748 break; 749 750 kprintf("%p %5ld %3ld %5ld %c %3ld %s\n", driver, 751 driver->image < 0 ? -1 : driver->image, 752 driver->devices_used, driver->devices.Size(), 753 driver->binary_updated ? 'U' : ' ', driver->priority, 754 driver->name); 755 } 756 757 hash_close(sDriverHash, &iterator, false); 758 return 0; 759 } 760 761 if (!strcmp(argv[1], "--help")) { 762 kprintf("usage: %s [name]\n", argv[0]); 763 return 0; 764 } 765 766 legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, argv[1]); 767 if (driver == NULL) { 768 kprintf("Driver named \"%s\" not found.\n", argv[1]); 769 return 0; 770 } 771 772 kprintf("DEVFS DRIVER: %p\n", driver); 773 kprintf(" name: %s\n", driver->name); 774 kprintf(" path: %s\n", driver->path); 775 kprintf(" image: %ld\n", driver->image); 776 kprintf(" device: %ld\n", driver->device); 777 kprintf(" node: %Ld\n", driver->node); 778 kprintf(" last modified: %ld\n", driver->last_modified); 779 kprintf(" devs used: %ld\n", driver->devices_used); 780 kprintf(" devs published: %ld\n", driver->devices.Size()); 781 kprintf(" binary updated: %d\n", driver->binary_updated); 782 kprintf(" priority: %ld\n", driver->priority); 783 kprintf(" api version: %ld\n", driver->api_version); 784 kprintf(" hooks: find_device %p, publish_devices %p\n" 785 " uninit_driver %p, uninit_hardware %p\n", 786 driver->find_device, driver->publish_devices, driver->uninit_driver, 787 driver->uninit_hardware); 788 789 return 0; 790 } 791 792 793 // #pragma mark - 794 795 796 DirectoryIterator::DirectoryIterator(const char* path, const char* subPath, 797 bool recursive) 798 : 799 fDirectory(NULL), 800 fBasePath(NULL), 801 fCurrentName(NULL) 802 { 803 SetTo(path, subPath, recursive); 804 } 805 806 807 DirectoryIterator::~DirectoryIterator() 808 { 809 Unset(); 810 } 811 812 813 void 814 DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive) 815 { 816 Unset(); 817 fRecursive = recursive; 818 819 if (path == NULL) { 820 // add default paths 821 const directory_which whichPath[] = { 822 B_USER_ADDONS_DIRECTORY, 823 B_COMMON_ADDONS_DIRECTORY, 824 B_BEOS_ADDONS_DIRECTORY 825 }; 826 KPath pathBuffer; 827 828 bool disableUserAddOns = get_safemode_boolean( 829 B_SAFEMODE_DISABLE_USER_ADD_ONS, false); 830 831 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) { 832 if (i < 2 && disableUserAddOns) 833 continue; 834 835 if (find_directory(whichPath[i], gBootDevice, true, 836 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) { 837 pathBuffer.UnlockBuffer(); 838 pathBuffer.Append("kernel"); 839 AddPath(pathBuffer.Path(), subPath); 840 } 841 } 842 } else 843 AddPath(path, subPath); 844 } 845 846 847 status_t 848 DirectoryIterator::GetNext(KPath& path, struct stat& stat) 849 { 850 next_directory: 851 while (fDirectory == NULL) { 852 delete fBasePath; 853 fBasePath = NULL; 854 855 if (!fPaths.Pop(&fBasePath)) 856 return B_ENTRY_NOT_FOUND; 857 858 fDirectory = opendir(fBasePath->Path()); 859 } 860 861 next_entry: 862 struct dirent* dirent = readdir(fDirectory); 863 if (dirent == NULL) { 864 // get over to next directory on the stack 865 closedir(fDirectory); 866 fDirectory = NULL; 867 868 goto next_directory; 869 } 870 871 if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, ".")) 872 goto next_entry; 873 874 fCurrentName = dirent->d_name; 875 876 path.SetTo(fBasePath->Path()); 877 path.Append(fCurrentName); 878 879 if (::stat(path.Path(), &stat) != 0) 880 goto next_entry; 881 882 if (S_ISDIR(stat.st_mode) && fRecursive) { 883 KPath *nextPath = new(nothrow) KPath(path); 884 if (!nextPath) 885 return B_NO_MEMORY; 886 if (fPaths.Push(nextPath) != B_OK) 887 return B_NO_MEMORY; 888 889 goto next_entry; 890 } 891 892 return B_OK; 893 } 894 895 896 void 897 DirectoryIterator::Unset() 898 { 899 if (fDirectory != NULL) { 900 closedir(fDirectory); 901 fDirectory = NULL; 902 } 903 904 delete fBasePath; 905 fBasePath = NULL; 906 907 KPath *path; 908 while (fPaths.Pop(&path)) 909 delete path; 910 } 911 912 913 void 914 DirectoryIterator::AddPath(const char* basePath, const char* subPath) 915 { 916 KPath *path = new(nothrow) KPath(basePath); 917 if (!path) 918 panic("out of memory"); 919 if (subPath != NULL) 920 path->Append(subPath); 921 922 fPaths.Push(path); 923 } 924 925 926 // #pragma mark - 927 928 929 DirectoryWatcher::DirectoryWatcher() 930 { 931 } 932 933 934 DirectoryWatcher::~DirectoryWatcher() 935 { 936 } 937 938 939 void 940 DirectoryWatcher::EventOccured(NotificationService& service, 941 const KMessage* event) 942 { 943 int32 opcode = event->GetInt32("opcode", -1); 944 dev_t device = event->GetInt32("device", -1); 945 ino_t directory = event->GetInt64("directory", -1); 946 const char *name = event->GetString("name", NULL); 947 948 if (opcode == B_ENTRY_MOVED) { 949 // Determine wether it's a move within, out of, or into one 950 // of our watched directories. 951 ino_t from = event->GetInt64("from directory", -1); 952 ino_t to = event->GetInt64("to directory", -1); 953 if (sDirectoryNodeHash.Lookup(&from) == NULL) { 954 directory = to; 955 opcode = B_ENTRY_CREATED; 956 } else if (sDirectoryNodeHash.Lookup(&to) == NULL) { 957 directory = from; 958 opcode = B_ENTRY_REMOVED; 959 } else { 960 // Move within, don't do anything for now 961 // TODO: adjust driver priority if necessary 962 return; 963 } 964 } 965 966 KPath path(B_PATH_NAME_LENGTH + 1); 967 if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory, 968 name, path.LockBuffer(), path.BufferSize()) != B_OK) 969 return; 970 971 path.UnlockBuffer(); 972 973 dprintf("driver \"%s\" %s\n", path.Leaf(), 974 opcode == B_ENTRY_CREATED ? "added" : "removed"); 975 976 switch (opcode) { 977 case B_ENTRY_CREATED: 978 driver_added(path.Path()); 979 break; 980 case B_ENTRY_REMOVED: 981 driver_removed(path.Path()); 982 break; 983 } 984 } 985 986 987 // #pragma mark - 988 989 990 static void 991 start_watching(const char *base, const char *sub) 992 { 993 KPath path(base); 994 path.Append(sub); 995 996 // TODO: create missing directories? 997 struct stat stat; 998 if (::stat(path.Path(), &stat) != 0) 999 return; 1000 1001 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY, 1002 sDirectoryWatcher); 1003 1004 directory_node_entry *entry = new(std::nothrow) directory_node_entry; 1005 if (entry != NULL) { 1006 entry->node = stat.st_ino; 1007 sDirectoryNodeHash.Insert(entry); 1008 } 1009 } 1010 1011 1012 static struct driver_entry* 1013 new_driver_entry(const char* path, dev_t device, ino_t node) 1014 { 1015 driver_entry* entry = (driver_entry*)malloc(sizeof(driver_entry)); 1016 if (entry == NULL) 1017 return NULL; 1018 1019 entry->path = strdup(path); 1020 if (entry->path == NULL) { 1021 free(entry); 1022 return NULL; 1023 } 1024 1025 entry->device = device; 1026 entry->node = node; 1027 entry->busses = 0; 1028 return entry; 1029 } 1030 1031 1032 /*! Iterates over the given list and tries to load all drivers in that list. 1033 The list is emptied and freed during the traversal. 1034 */ 1035 static status_t 1036 try_drivers(DriverEntryList& list) 1037 { 1038 while (true) { 1039 driver_entry* entry = list.RemoveHead(); 1040 if (entry == NULL) 1041 break; 1042 1043 image_id image = load_kernel_add_on(entry->path); 1044 if (image >= 0) { 1045 // check if it's an old-style driver 1046 if (legacy_driver_add(entry->path) == B_OK) { 1047 // we have a driver 1048 dprintf("loaded driver %s\n", entry->path); 1049 } 1050 1051 unload_kernel_add_on(image); 1052 } 1053 1054 free(entry->path); 1055 free(entry); 1056 } 1057 1058 return B_OK; 1059 } 1060 1061 1062 static status_t 1063 probe_for_drivers(const char *type) 1064 { 1065 TRACE(("probe_for_drivers(type = %s)\n", type)); 1066 1067 if (gBootDevice < 0) 1068 return B_OK; 1069 1070 DriverEntryList drivers; 1071 1072 // build list of potential drivers for that type 1073 1074 DirectoryIterator iterator(NULL, type, false); 1075 struct stat stat; 1076 KPath path; 1077 1078 while (iterator.GetNext(path, stat) == B_OK) { 1079 if (S_ISDIR(stat.st_mode)) { 1080 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY, 1081 sDirectoryWatcher); 1082 1083 directory_node_entry *entry 1084 = new(std::nothrow) directory_node_entry; 1085 if (entry != NULL) { 1086 entry->node = stat.st_ino; 1087 sDirectoryNodeHash.Insert(entry); 1088 } 1089 1090 // We need to make sure that drivers in ie. "audio/raw/" can 1091 // be found as well - therefore, we must make sure that "audio" 1092 // exists on /dev. 1093 1094 size_t length = strlen("drivers/dev"); 1095 if (strncmp(type, "drivers/dev", length)) 1096 continue; 1097 1098 path.SetTo(type); 1099 path.Append(iterator.CurrentName()); 1100 devfs_publish_directory(path.Path() + length + 1); 1101 continue; 1102 } 1103 1104 driver_entry *entry = new_driver_entry(path.Path(), stat.st_dev, 1105 stat.st_ino); 1106 if (entry == NULL) 1107 return B_NO_MEMORY; 1108 1109 TRACE(("found potential driver: %s\n", path.Path())); 1110 drivers.Add(entry); 1111 } 1112 1113 if (drivers.IsEmpty()) 1114 return B_OK; 1115 1116 // ToDo: do something with the remaining drivers... :) 1117 try_drivers(drivers); 1118 return B_OK; 1119 } 1120 1121 1122 // #pragma mark - LegacyDevice 1123 1124 1125 LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path, 1126 device_hooks* hooks) 1127 : 1128 fDriver(driver), 1129 fRepublished(true) 1130 { 1131 fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info)); 1132 if (fDeviceModule != NULL) 1133 memset(fDeviceModule, 0, sizeof(device_module_info)); 1134 1135 fDeviceData = this; 1136 fPath = strdup(path); 1137 1138 SetHooks(hooks); 1139 } 1140 1141 1142 LegacyDevice::~LegacyDevice() 1143 { 1144 free(fDeviceModule); 1145 free((char*)fPath); 1146 } 1147 1148 1149 status_t 1150 LegacyDevice::InitCheck() const 1151 { 1152 return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY; 1153 } 1154 1155 1156 status_t 1157 LegacyDevice::InitDevice() 1158 { 1159 RecursiveLocker _(sLock); 1160 1161 if (fInitialized++ > 0) 1162 return B_OK; 1163 1164 if (fDriver != NULL && fDriver->devices_used == 0 1165 && (fDriver->image < 0 || fDriver->binary_updated)) { 1166 status_t status = reload_driver(fDriver); 1167 if (status < B_OK) 1168 return status; 1169 } 1170 1171 if (fDriver != NULL) 1172 fDriver->devices_used++; 1173 1174 return B_OK; 1175 } 1176 1177 1178 void 1179 LegacyDevice::UninitDevice() 1180 { 1181 RecursiveLocker _(sLock); 1182 1183 if (fInitialized-- > 1) 1184 return; 1185 1186 if (fDriver != NULL) 1187 fDriver->devices_used--; 1188 } 1189 1190 1191 void 1192 LegacyDevice::SetHooks(device_hooks* hooks) 1193 { 1194 // TODO: setup compatibility layer! 1195 fHooks = hooks; 1196 1197 fDeviceModule->close = hooks->close; 1198 fDeviceModule->free = hooks->free; 1199 fDeviceModule->control = hooks->control; 1200 fDeviceModule->read = hooks->read; 1201 fDeviceModule->write = hooks->write; 1202 1203 if (fDriver == NULL || fDriver->api_version >= 2) { 1204 // According to Be newsletter, vol II, issue 36, 1205 // version 2 added readv/writev, which we don't support, but also 1206 // select/deselect. 1207 if (hooks->select != NULL) { 1208 // Note we set the module's select to a non-null value to indicate 1209 // that we have select. HasSelect() will therefore return the 1210 // correct answer. As Select() is virtual our compatibility 1211 // version below is going to be called though, that redirects to 1212 // the proper select hook, so it is ok to set it to an invalid 1213 // address here. 1214 fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0; 1215 } 1216 1217 fDeviceModule->deselect = hooks->deselect; 1218 } 1219 } 1220 1221 1222 status_t 1223 LegacyDevice::Open(const char* path, int openMode, void** _cookie) 1224 { 1225 return Hooks()->open(path, openMode, _cookie); 1226 } 1227 1228 1229 status_t 1230 LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync) 1231 { 1232 return Hooks()->select(cookie, event, 0, sync); 1233 } 1234 1235 1236 // #pragma mark - kernel private API 1237 1238 1239 extern "C" void 1240 legacy_driver_add_preloaded(kernel_args* args) 1241 { 1242 // NOTE: This function does not exit in case of error, since it 1243 // needs to unload the images then. Also the return code of 1244 // the path operations is kept separate from the add_driver() 1245 // success, so that even if add_driver() fails for one driver, it 1246 // is still tried for the other drivers. 1247 // NOTE: The initialization success of the path objects is implicitely 1248 // checked by the immediately following functions. 1249 KPath basePath; 1250 status_t pathStatus = find_directory(B_BEOS_ADDONS_DIRECTORY, 1251 gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize()); 1252 if (pathStatus != B_OK) { 1253 dprintf("devfs_add_preloaded_drivers: find_directory() failed: " 1254 "%s\n", strerror(pathStatus)); 1255 } 1256 basePath.UnlockBuffer(); 1257 if (pathStatus == B_OK) 1258 pathStatus = basePath.Append("kernel"); 1259 if (pathStatus != B_OK) { 1260 dprintf("devfs_add_preloaded_drivers: constructing base driver " 1261 "path failed: %s\n", strerror(pathStatus)); 1262 } 1263 1264 struct preloaded_image* image; 1265 for (image = args->preloaded_images; image != NULL; image = image->next) { 1266 if (image->is_module || image->id < 0) 1267 continue; 1268 1269 KPath imagePath(basePath); 1270 if (pathStatus == B_OK) 1271 pathStatus = imagePath.Append(image->name); 1272 1273 // try to add the driver 1274 status_t addStatus = pathStatus; 1275 if (pathStatus == B_OK) 1276 addStatus = add_driver(imagePath.Path(), image->id); 1277 if (addStatus != B_OK) { 1278 dprintf("devfs_add_preloaded_drivers: Failed to add \"%s\"\n", 1279 image->name); 1280 unload_kernel_add_on(image->id); 1281 } 1282 } 1283 } 1284 1285 1286 extern "C" status_t 1287 legacy_driver_add(const char* path) 1288 { 1289 return add_driver(path, -1); 1290 } 1291 1292 1293 extern "C" status_t 1294 legacy_driver_publish(const char *path, device_hooks *hooks) 1295 { 1296 // we don't have a driver, just publish the hooks 1297 LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks); 1298 if (device == NULL) 1299 return B_NO_MEMORY; 1300 1301 status_t status = device->InitCheck(); 1302 if (status == B_OK) 1303 status = devfs_publish_device(path, device); 1304 1305 if (status != B_OK) 1306 delete device; 1307 1308 return status; 1309 } 1310 1311 1312 extern "C" status_t 1313 legacy_driver_rescan(const char* driverName) 1314 { 1315 RecursiveLocker locker(sLock); 1316 1317 legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, 1318 driverName); 1319 if (driver == NULL) 1320 return B_ENTRY_NOT_FOUND; 1321 1322 // Republish the driver's entries 1323 return republish_driver(driver); 1324 } 1325 1326 1327 extern "C" status_t 1328 legacy_driver_probe(const char* subPath) 1329 { 1330 TRACE(("legacy_driver_probe(type = %s)\n", subPath)); 1331 1332 char devicePath[64]; 1333 snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s", 1334 subPath[0] ? "/" : "", subPath); 1335 1336 if (!sWatching && gBootDevice > 0) { 1337 // We're probing the actual boot volume for the first time, 1338 // let's watch its driver directories for changes 1339 const directory_which whichPath[] = { 1340 B_USER_ADDONS_DIRECTORY, 1341 B_COMMON_ADDONS_DIRECTORY, 1342 B_BEOS_ADDONS_DIRECTORY 1343 }; 1344 KPath path; 1345 1346 new(&sDirectoryWatcher) DirectoryWatcher; 1347 1348 bool disableUserAddOns = get_safemode_boolean( 1349 B_SAFEMODE_DISABLE_USER_ADD_ONS, false); 1350 1351 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) { 1352 if (i < 2 && disableUserAddOns) 1353 continue; 1354 1355 if (find_directory(whichPath[i], gBootDevice, true, 1356 path.LockBuffer(), path.BufferSize()) == B_OK) { 1357 path.UnlockBuffer(); 1358 path.Append("kernel/drivers"); 1359 1360 start_watching(path.Path(), "dev"); 1361 start_watching(path.Path(), "bin"); 1362 } 1363 } 1364 1365 sWatching = true; 1366 } 1367 1368 return probe_for_drivers(devicePath); 1369 } 1370 1371 1372 extern "C" status_t 1373 legacy_driver_init(void) 1374 { 1375 sDriverHash = hash_init(DRIVER_HASH_SIZE, offsetof(legacy_driver, next), 1376 &driver_entry_compare, &driver_entry_hash); 1377 if (sDriverHash == NULL) 1378 return B_NO_MEMORY; 1379 1380 recursive_lock_init(&sLock, "legacy driver"); 1381 1382 new(&sDriverWatcher) DriverWatcher; 1383 new(&sDriversToAdd) DoublyLinkedList<path_entry>; 1384 1385 register_kernel_daemon(&handle_driver_events, NULL, 10); 1386 // once every second 1387 1388 add_debugger_command("legacy_driver", &dump_driver, 1389 "info about a legacy driver entry"); 1390 1391 return B_OK; 1392 } 1393