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