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