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