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