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