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