btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / device_manager / legacy_drivers.cpp
blobcbd792c2a6ed6d567f83e53950c926921a5ad2aa
1 /*
2 * Copyright 2002-2011, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "legacy_drivers.h"
9 #include <dirent.h>
10 #include <errno.h>
11 #include <new>
12 #include <stdio.h>
14 #include <FindDirectory.h>
15 #include <image.h>
16 #include <NodeMonitor.h>
18 #include <boot_device.h>
19 #include <boot/kernel_args.h>
20 #include <elf.h>
21 #include <find_directory_private.h>
22 #include <fs/devfs.h>
23 #include <fs/KPath.h>
24 #include <fs/node_monitor.h>
25 #include <Notifications.h>
26 #include <safemode.h>
27 #include <util/DoublyLinkedList.h>
28 #include <util/OpenHashTable.h>
29 #include <util/Stack.h>
30 #include <vfs.h>
32 #include "AbstractModuleDevice.h"
33 #include "devfs_private.h"
36 //#define TRACE_LEGACY_DRIVERS
37 #ifdef TRACE_LEGACY_DRIVERS
38 # define TRACE(x) dprintf x
39 #else
40 # define TRACE(x)
41 #endif
43 #define DRIVER_HASH_SIZE 16
46 namespace {
48 struct legacy_driver;
50 class LegacyDevice : public AbstractModuleDevice,
51 public DoublyLinkedListLinkImpl<LegacyDevice> {
52 public:
53 LegacyDevice(legacy_driver* driver,
54 const char* path, device_hooks* hooks);
55 virtual ~LegacyDevice();
57 status_t InitCheck() const;
59 virtual status_t InitDevice();
60 virtual void UninitDevice();
62 virtual void Removed();
64 void SetHooks(device_hooks* hooks);
66 legacy_driver* Driver() const { return fDriver; }
67 const char* Path() const { return fPath; }
68 device_hooks* Hooks() const { return fHooks; }
70 virtual status_t Open(const char* path, int openMode,
71 void** _cookie);
72 virtual status_t Select(void* cookie, uint8 event, selectsync* sync);
74 bool Republished() const { return fRepublished; }
75 void SetRepublished(bool republished)
76 { fRepublished = republished; }
78 void SetRemovedFromParent(bool removed)
79 { fRemovedFromParent = removed; }
81 private:
82 legacy_driver* fDriver;
83 const char* fPath;
84 device_hooks* fHooks;
85 bool fRepublished;
86 bool fRemovedFromParent;
89 typedef DoublyLinkedList<LegacyDevice> DeviceList;
91 struct legacy_driver {
92 legacy_driver* next;
93 const char* path;
94 const char* name;
95 dev_t device;
96 ino_t node;
97 timespec last_modified;
98 image_id image;
99 uint32 devices_used;
100 bool binary_updated;
101 int32 priority;
102 DeviceList devices;
104 // driver image information
105 int32 api_version;
106 device_hooks* (*find_device)(const char *);
107 const char** (*publish_devices)(void);
108 status_t (*uninit_driver)(void);
109 status_t (*uninit_hardware)(void);
113 enum driver_event_type {
114 kAddDriver,
115 kRemoveDriver,
116 kAddWatcher,
117 kRemoveWatcher
120 struct driver_event : DoublyLinkedListLinkImpl<driver_event> {
121 driver_event(driver_event_type _type) : type(_type) {}
123 struct ref {
124 dev_t device;
125 ino_t node;
128 driver_event_type type;
129 union {
130 char path[B_PATH_NAME_LENGTH];
131 ref node;
135 typedef DoublyLinkedList<driver_event> DriverEventList;
138 struct driver_entry : DoublyLinkedListLinkImpl<driver_entry> {
139 char* path;
140 dev_t device;
141 ino_t node;
142 int32 busses;
145 typedef DoublyLinkedList<driver_entry> DriverEntryList;
148 struct node_entry : DoublyLinkedListLinkImpl<node_entry> {
151 typedef DoublyLinkedList<node_entry> NodeList;
154 struct directory_node_entry {
155 directory_node_entry* hash_link;
156 ino_t node;
159 struct DirectoryNodeHashDefinition {
160 typedef ino_t* KeyType;
161 typedef directory_node_entry ValueType;
163 size_t HashKey(ino_t* key) const
164 { return _Hash(*key); }
165 size_t Hash(directory_node_entry* entry) const
166 { return _Hash(entry->node); }
167 bool Compare(ino_t* key, directory_node_entry* entry) const
168 { return *key == entry->node; }
169 directory_node_entry*&
170 GetLink(directory_node_entry* entry) const
171 { return entry->hash_link; }
173 uint32 _Hash(ino_t node) const
174 { return (uint32)(node >> 32) + (uint32)node; }
177 typedef BOpenHashTable<DirectoryNodeHashDefinition> DirectoryNodeHash;
179 class DirectoryIterator {
180 public:
181 DirectoryIterator(const char *path,
182 const char *subPath = NULL, bool recursive = false);
183 ~DirectoryIterator();
185 void SetTo(const char *path, const char *subPath = NULL,
186 bool recursive = false);
188 status_t GetNext(KPath &path, struct stat &stat);
189 const char* CurrentName() const { return fCurrentName; }
191 void Unset();
192 void AddPath(const char *path, const char *subPath = NULL);
194 private:
195 Stack<KPath*> fPaths;
196 bool fRecursive;
197 DIR* fDirectory;
198 KPath* fBasePath;
199 const char* fCurrentName;
203 class DirectoryWatcher : public NotificationListener {
204 public:
205 DirectoryWatcher();
206 virtual ~DirectoryWatcher();
208 virtual void EventOccurred(NotificationService& service,
209 const KMessage* event);
212 class DriverWatcher : public NotificationListener {
213 public:
214 DriverWatcher();
215 virtual ~DriverWatcher();
217 virtual void EventOccurred(NotificationService& service,
218 const KMessage* event);
222 struct DriverHash {
223 typedef const char* KeyType;
224 typedef legacy_driver ValueType;
226 size_t HashKey(KeyType key) const
228 return hash_hash_string(key);
231 size_t Hash(ValueType* driver) const
233 return HashKey(driver->name);
236 bool Compare(KeyType key, ValueType* driver) const
238 return strcmp(driver->name, key) == 0;
241 ValueType*& GetLink(ValueType* value) const
243 return value->next;
247 typedef BOpenHashTable<DriverHash> DriverTable;
250 } // unnamed namespace
253 static status_t unload_driver(legacy_driver *driver);
254 static status_t load_driver(legacy_driver *driver);
257 static DriverWatcher sDriverWatcher;
258 static int32 sDriverEventsPending;
259 static DriverEventList sDriverEvents;
260 static mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events");
261 // inner lock, protects the sDriverEvents list only
262 static DirectoryWatcher sDirectoryWatcher;
263 static DirectoryNodeHash sDirectoryNodeHash;
264 static recursive_lock sLock;
265 static bool sWatching;
267 static DriverTable* sDriverHash;
270 // #pragma mark - driver private
273 /*! Collects all published devices of a driver, compares them to what the
274 driver would publish now, and then publishes/unpublishes the devices
275 as needed.
276 If the driver does not publish any devices anymore, it is unloaded.
278 static status_t
279 republish_driver(legacy_driver* driver)
281 if (driver->image < 0) {
282 // The driver is not yet loaded - go through the normal load procedure
283 return load_driver(driver);
286 // mark all devices
287 DeviceList::Iterator iterator = driver->devices.GetIterator();
288 while (LegacyDevice* device = iterator.Next()) {
289 device->SetRepublished(false);
292 // now ask the driver for it's currently published devices
293 const char** devicePaths = driver->publish_devices();
295 int32 exported = 0;
296 for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
297 LegacyDevice* device;
299 iterator = driver->devices.GetIterator();
300 while ((device = iterator.Next()) != NULL) {
301 if (!strncmp(device->Path(), devicePaths[0], B_PATH_NAME_LENGTH)) {
302 // mark device as republished
303 device->SetRepublished(true);
304 exported++;
305 break;
309 device_hooks* hooks = driver->find_device(devicePaths[0]);
310 if (hooks == NULL)
311 continue;
313 if (device != NULL) {
314 // update hooks
315 device->SetHooks(hooks);
316 continue;
319 // the device was not present before -> publish it now
320 TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
321 device = new(std::nothrow) LegacyDevice(driver, devicePaths[0], hooks);
322 if (device != NULL && device->InitCheck() == B_OK
323 && devfs_publish_device(devicePaths[0], device) == B_OK) {
324 driver->devices.Add(device);
325 exported++;
326 } else
327 delete device;
330 // remove all devices that weren't republished
331 iterator = driver->devices.GetIterator();
332 while (LegacyDevice* device = iterator.Next()) {
333 if (device->Republished())
334 continue;
336 TRACE(("devfs: unpublishing no more present \"%s\"\n", device->Path()));
337 iterator.Remove();
338 device->SetRemovedFromParent(true);
340 devfs_unpublish_device(device, true);
343 if (exported == 0 && driver->devices_used == 0) {
344 TRACE(("devfs: driver \"%s\" does not publish any more nodes and is "
345 "unloaded\n", driver->path));
346 unload_driver(driver);
349 return B_OK;
353 static status_t
354 load_driver(legacy_driver *driver)
356 status_t (*init_hardware)(void);
357 status_t (*init_driver)(void);
358 status_t status;
360 driver->binary_updated = false;
362 // load the module
363 image_id image = driver->image;
364 if (image < 0) {
365 image = load_kernel_add_on(driver->path);
366 if (image < 0)
367 return image;
370 // For a valid device driver the following exports are required
372 int32 *apiVersion;
373 if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA,
374 (void **)&apiVersion) == B_OK) {
375 #if B_CUR_DRIVER_API_VERSION != 2
376 // just in case someone decides to bump up the api version
377 #error Add checks here for new vs old api version!
378 #endif
379 if (*apiVersion > B_CUR_DRIVER_API_VERSION) {
380 dprintf("devfs: \"%s\" api_version %" B_PRId32 " not handled\n",
381 driver->name, *apiVersion);
382 status = B_BAD_VALUE;
383 goto error1;
385 if (*apiVersion < 1) {
386 dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
387 status = B_BAD_VALUE;
388 goto error1;
391 driver->api_version = *apiVersion;
392 } else
393 dprintf("devfs: \"%s\" api_version missing\n", driver->name);
395 if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
396 (void **)&driver->publish_devices) != B_OK
397 || get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
398 (void **)&driver->find_device) != B_OK) {
399 dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n",
400 driver->name);
401 status = B_BAD_VALUE;
402 goto error1;
405 // Init the driver
407 if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
408 (void **)&init_hardware) == B_OK
409 && (status = init_hardware()) != B_OK) {
410 TRACE(("%s: init_hardware() failed: %s\n", driver->name,
411 strerror(status)));
412 status = ENXIO;
413 goto error1;
416 if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
417 (void **)&init_driver) == B_OK
418 && (status = init_driver()) != B_OK) {
419 TRACE(("%s: init_driver() failed: %s\n", driver->name,
420 strerror(status)));
421 status = ENXIO;
422 goto error2;
425 // resolve and cache those for the driver unload code
426 if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT,
427 (void **)&driver->uninit_driver) != B_OK)
428 driver->uninit_driver = NULL;
429 if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT,
430 (void **)&driver->uninit_hardware) != B_OK)
431 driver->uninit_hardware = NULL;
433 // The driver has successfully been initialized, now we can
434 // finally publish its device entries
436 driver->image = image;
437 return republish_driver(driver);
439 error2:
440 if (driver->uninit_hardware)
441 driver->uninit_hardware();
443 error1:
444 if (driver->image < 0) {
445 unload_kernel_add_on(image);
446 driver->image = status;
449 return status;
453 static status_t
454 unload_driver(legacy_driver *driver)
456 if (driver->image < 0) {
457 // driver is not currently loaded
458 return B_NO_INIT;
461 if (driver->uninit_driver)
462 driver->uninit_driver();
464 if (driver->uninit_hardware)
465 driver->uninit_hardware();
467 unload_kernel_add_on(driver->image);
468 driver->image = -1;
469 driver->binary_updated = false;
470 driver->find_device = NULL;
471 driver->publish_devices = NULL;
472 driver->uninit_driver = NULL;
473 driver->uninit_hardware = NULL;
475 return B_OK;
479 /*! Unpublishes all devices belonging to the \a driver. */
480 static void
481 unpublish_driver(legacy_driver *driver)
483 while (LegacyDevice* device = driver->devices.RemoveHead()) {
484 device->SetRemovedFromParent(true);
485 devfs_unpublish_device(device, true);
490 static void
491 change_driver_watcher(dev_t device, ino_t node, bool add)
493 if (device == -1)
494 return;
496 driver_event* event = new (std::nothrow) driver_event(
497 add ? kAddWatcher : kRemoveWatcher);
498 if (event == NULL)
499 return;
501 event->node.device = device;
502 event->node.node = node;
504 MutexLocker _(sDriverEventsLock);
505 sDriverEvents.Add(event);
507 atomic_add(&sDriverEventsPending, 1);
511 static int32
512 get_priority(const char* path)
514 // TODO: would it be better to initialize a static structure here
515 // using find_directory()?
516 const directory_which whichPath[] = {
517 B_BEOS_DIRECTORY,
518 B_SYSTEM_NONPACKAGED_DIRECTORY,
519 B_USER_DIRECTORY
521 KPath pathBuffer;
523 for (uint32 index = 0; index < sizeof(whichPath) / sizeof(whichPath[0]);
524 index++) {
525 if (__find_directory(whichPath[index], gBootDevice, false,
526 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
527 pathBuffer.UnlockBuffer();
528 if (!strncmp(pathBuffer.Path(), path, pathBuffer.BufferSize()))
529 return index;
530 } else
531 pathBuffer.UnlockBuffer();
534 return -1;
538 static const char *
539 get_leaf(const char *path)
541 const char *name = strrchr(path, '/');
542 if (name == NULL)
543 return path;
545 return name + 1;
549 static legacy_driver *
550 find_driver(dev_t device, ino_t node)
552 DriverTable::Iterator iterator(sDriverHash);
553 while (iterator.HasNext()) {
554 legacy_driver *driver = iterator.Next();
555 if (driver->device == device && driver->node == node)
556 return driver;
559 return NULL;
563 static status_t
564 add_driver(const char *path, image_id image)
566 // Check if we already know this driver
568 struct stat stat;
569 if (image >= 0) {
570 // The image ID should be a small number and hopefully the boot FS
571 // doesn't use small negative values -- if it is inode based, we should
572 // be relatively safe.
573 stat.st_dev = -1;
574 stat.st_ino = -1;
575 } else {
576 if (::stat(path, &stat) != 0)
577 return errno;
580 int32 priority = get_priority(path);
582 RecursiveLocker _(sLock);
584 legacy_driver *driver = sDriverHash->Lookup(get_leaf(path));
585 if (driver != NULL) {
586 // we know this driver
587 if (strcmp(driver->path, path) != 0) {
588 // TODO: do properly, but for now we just update the path if it
589 // isn't the same anymore so rescanning of drivers will work in
590 // case this driver was loaded so early that it has a boot module
591 // path and not a proper driver path
592 free((char*)driver->path);
593 driver->path = strdup(path);
594 driver->name = get_leaf(driver->path);
595 driver->binary_updated = true;
598 // TODO: check if this driver is a different one and has precendence
599 // (ie. common supersedes system).
600 //dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority);
601 if (priority >= driver->priority) {
602 driver->binary_updated = true;
603 return B_OK;
606 // TODO: test for changes here and/or via node monitoring and reload
607 // the driver if necessary
608 if (driver->image < B_OK)
609 return driver->image;
611 return B_OK;
614 // we don't know this driver, create a new entry for it
616 driver = (legacy_driver *)malloc(sizeof(legacy_driver));
617 if (driver == NULL)
618 return B_NO_MEMORY;
620 driver->path = strdup(path);
621 if (driver->path == NULL) {
622 free(driver);
623 return B_NO_MEMORY;
626 driver->name = get_leaf(driver->path);
627 driver->device = stat.st_dev;
628 driver->node = stat.st_ino;
629 driver->image = image;
630 driver->last_modified = stat.st_mtim;
631 driver->devices_used = 0;
632 driver->binary_updated = false;
633 driver->priority = priority;
635 driver->api_version = 1;
636 driver->find_device = NULL;
637 driver->publish_devices = NULL;
638 driver->uninit_driver = NULL;
639 driver->uninit_hardware = NULL;
640 new(&driver->devices) DeviceList;
642 sDriverHash->Insert(driver);
643 if (stat.st_dev > 0)
644 change_driver_watcher(stat.st_dev, stat.st_ino, true);
646 // Even if loading the driver fails - its entry will stay with us
647 // so that we don't have to go through it again
648 return load_driver(driver);
652 /*! This is no longer part of the public kernel API, so we just export the
653 symbol
655 extern "C" status_t load_driver_symbols(const char *driverName);
656 status_t
657 load_driver_symbols(const char *driverName)
659 // This is done globally for the whole kernel via the settings file.
660 // We don't have to do anything here.
662 return B_OK;
666 static status_t
667 reload_driver(legacy_driver *driver)
669 dprintf("devfs: reload driver \"%s\" (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
670 driver->name, driver->device, driver->node);
672 unload_driver(driver);
674 struct stat stat;
675 if (::stat(driver->path, &stat) == 0
676 && (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
677 // The driver file has been changed, so we need to update its listener
678 change_driver_watcher(driver->device, driver->node, false);
680 driver->device = stat.st_dev;
681 driver->node = stat.st_ino;
683 change_driver_watcher(driver->device, driver->node, true);
686 status_t status = load_driver(driver);
687 if (status != B_OK)
688 unpublish_driver(driver);
690 return status;
694 static void
695 handle_driver_events(void */*_fs*/, int /*iteration*/)
697 if (atomic_and(&sDriverEventsPending, 0) == 0)
698 return;
700 // something happened, let's see what it was
702 while (true) {
703 MutexLocker eventLocker(sDriverEventsLock);
705 driver_event* event = sDriverEvents.RemoveHead();
706 if (event == NULL)
707 break;
709 eventLocker.Unlock();
710 TRACE(("driver event %p, type %d\n", event, event->type));
712 switch (event->type) {
713 case kAddDriver:
715 // Add new drivers
716 RecursiveLocker locker(sLock);
717 TRACE((" add driver %p\n", event->path));
719 legacy_driver* driver = sDriverHash->Lookup(
720 get_leaf(event->path));
721 if (driver == NULL)
722 legacy_driver_add(event->path);
723 else if (get_priority(event->path) >= driver->priority)
724 driver->binary_updated = true;
725 break;
728 case kRemoveDriver:
730 // Mark removed drivers as updated
731 RecursiveLocker locker(sLock);
732 TRACE((" remove driver %p\n", event->path));
734 legacy_driver* driver = sDriverHash->Lookup(
735 get_leaf(event->path));
736 if (driver != NULL
737 && get_priority(event->path) >= driver->priority)
738 driver->binary_updated = true;
739 break;
742 case kAddWatcher:
743 TRACE((" add watcher %ld:%lld\n", event->node.device,
744 event->node.node));
745 add_node_listener(event->node.device, event->node.node,
746 B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
747 break;
749 case kRemoveWatcher:
750 TRACE((" remove watcher %ld:%lld\n", event->node.device,
751 event->node.node));
752 remove_node_listener(event->node.device, event->node.node,
753 sDriverWatcher);
754 break;
757 delete event;
760 // Reload updated drivers
762 RecursiveLocker locker(sLock);
764 DriverTable::Iterator iterator(sDriverHash);
765 while (iterator.HasNext()) {
766 legacy_driver *driver = iterator.Next();
768 if (!driver->binary_updated || driver->devices_used != 0)
769 continue;
771 // try to reload the driver
772 reload_driver(driver);
775 locker.Unlock();
779 // #pragma mark - DriverWatcher
782 DriverWatcher::DriverWatcher()
787 DriverWatcher::~DriverWatcher()
792 void
793 DriverWatcher::EventOccurred(NotificationService& service,
794 const KMessage* event)
796 int32 opcode = event->GetInt32("opcode", -1);
797 if (opcode != B_STAT_CHANGED
798 || (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
799 return;
801 RecursiveLocker locker(sLock);
803 legacy_driver* driver = find_driver(event->GetInt32("device", -1),
804 event->GetInt64("node", 0));
805 if (driver == NULL)
806 return;
808 driver->binary_updated = true;
810 if (driver->devices_used == 0) {
811 // trigger a reload of the driver
812 atomic_add(&sDriverEventsPending, 1);
813 } else {
814 // driver is in use right now
815 dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
820 static void
821 dump_driver(legacy_driver* driver)
823 kprintf("DEVFS DRIVER: %p\n", driver);
824 kprintf(" name: %s\n", driver->name);
825 kprintf(" path: %s\n", driver->path);
826 kprintf(" image: %" B_PRId32 "\n", driver->image);
827 kprintf(" device: %" B_PRIdDEV "\n", driver->device);
828 kprintf(" node: %" B_PRIdINO "\n", driver->node);
829 kprintf(" last modified: %" B_PRIdTIME ".%ld\n", driver->last_modified.tv_sec,
830 driver->last_modified.tv_nsec);
831 kprintf(" devs used: %" B_PRIu32 "\n", driver->devices_used);
832 kprintf(" devs published: %" B_PRId32 "\n", driver->devices.Count());
833 kprintf(" binary updated: %d\n", driver->binary_updated);
834 kprintf(" priority: %" B_PRId32 "\n", driver->priority);
835 kprintf(" api version: %" B_PRId32 "\n", driver->api_version);
836 kprintf(" hooks: find_device %p, publish_devices %p\n"
837 " uninit_driver %p, uninit_hardware %p\n",
838 driver->find_device, driver->publish_devices, driver->uninit_driver,
839 driver->uninit_hardware);
843 static int
844 dump_device(int argc, char** argv)
846 if (argc < 2 || !strcmp(argv[1], "--help")) {
847 kprintf("usage: %s [device]\n", argv[0]);
848 return 0;
851 LegacyDevice* device = (LegacyDevice*)parse_expression(argv[1]);
853 kprintf("LEGACY DEVICE: %p\n", device);
854 kprintf(" path: %s\n", device->Path());
855 kprintf(" hooks: %p\n", device->Hooks());
856 device_hooks* hooks = device->Hooks();
857 kprintf(" close() %p\n", hooks->close);
858 kprintf(" free() %p\n", hooks->free);
859 kprintf(" control() %p\n", hooks->control);
860 kprintf(" read() %p\n", hooks->read);
861 kprintf(" write() %p\n", hooks->write);
862 kprintf(" select() %p\n", hooks->select);
863 kprintf(" deselect() %p\n", hooks->deselect);
864 dump_driver(device->Driver());
866 return 0;
870 static int
871 dump_driver(int argc, char** argv)
873 if (argc < 2) {
874 // print list of all drivers
875 kprintf("address image used publ. pri name\n");
876 DriverTable::Iterator iterator(sDriverHash);
877 while (iterator.HasNext()) {
878 legacy_driver* driver = iterator.Next();
880 kprintf("%p %5" B_PRId32 " %3" B_PRIu32 " %5" B_PRId32 " %c "
881 "%3" B_PRId32 " %s\n", driver,
882 driver->image < 0 ? -1 : driver->image,
883 driver->devices_used, driver->devices.Count(),
884 driver->binary_updated ? 'U' : ' ', driver->priority,
885 driver->name);
888 return 0;
891 if (!strcmp(argv[1], "--help")) {
892 kprintf("usage: %s [name]\n", argv[0]);
893 return 0;
896 legacy_driver* driver = sDriverHash->Lookup(argv[1]);
897 if (driver == NULL) {
898 kprintf("Driver named \"%s\" not found.\n", argv[1]);
899 return 0;
902 dump_driver(driver);
903 return 0;
907 // #pragma mark -
910 DirectoryIterator::DirectoryIterator(const char* path, const char* subPath,
911 bool recursive)
913 fDirectory(NULL),
914 fBasePath(NULL),
915 fCurrentName(NULL)
917 SetTo(path, subPath, recursive);
921 DirectoryIterator::~DirectoryIterator()
923 Unset();
927 void
928 DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive)
930 Unset();
931 fRecursive = recursive;
933 if (path == NULL) {
934 // add default paths
935 const directory_which whichPath[] = {
936 B_USER_NONPACKAGED_ADDONS_DIRECTORY,
937 B_USER_ADDONS_DIRECTORY,
938 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
939 B_BEOS_ADDONS_DIRECTORY
941 KPath pathBuffer;
943 bool disableUserAddOns = get_safemode_boolean(
944 B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
946 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
947 if (i < 2 && disableUserAddOns)
948 continue;
950 if (__find_directory(whichPath[i], gBootDevice, true,
951 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
952 pathBuffer.UnlockBuffer();
953 pathBuffer.Append("kernel");
954 AddPath(pathBuffer.Path(), subPath);
955 } else
956 pathBuffer.UnlockBuffer();
958 } else
959 AddPath(path, subPath);
963 status_t
964 DirectoryIterator::GetNext(KPath& path, struct stat& stat)
966 next_directory:
967 while (fDirectory == NULL) {
968 delete fBasePath;
969 fBasePath = NULL;
971 if (!fPaths.Pop(&fBasePath))
972 return B_ENTRY_NOT_FOUND;
974 fDirectory = opendir(fBasePath->Path());
977 next_entry:
978 struct dirent* dirent = readdir(fDirectory);
979 if (dirent == NULL) {
980 // get over to next directory on the stack
981 closedir(fDirectory);
982 fDirectory = NULL;
984 goto next_directory;
987 if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
988 goto next_entry;
990 fCurrentName = dirent->d_name;
992 path.SetTo(fBasePath->Path());
993 path.Append(fCurrentName);
995 if (::stat(path.Path(), &stat) != 0)
996 goto next_entry;
998 if (S_ISDIR(stat.st_mode) && fRecursive) {
999 KPath *nextPath = new(nothrow) KPath(path);
1000 if (!nextPath)
1001 return B_NO_MEMORY;
1002 if (fPaths.Push(nextPath) != B_OK)
1003 return B_NO_MEMORY;
1005 goto next_entry;
1008 return B_OK;
1012 void
1013 DirectoryIterator::Unset()
1015 if (fDirectory != NULL) {
1016 closedir(fDirectory);
1017 fDirectory = NULL;
1020 delete fBasePath;
1021 fBasePath = NULL;
1023 KPath *path;
1024 while (fPaths.Pop(&path))
1025 delete path;
1029 void
1030 DirectoryIterator::AddPath(const char* basePath, const char* subPath)
1032 KPath *path = new(nothrow) KPath(basePath);
1033 if (!path)
1034 panic("out of memory");
1035 if (subPath != NULL)
1036 path->Append(subPath);
1038 fPaths.Push(path);
1042 // #pragma mark -
1045 DirectoryWatcher::DirectoryWatcher()
1050 DirectoryWatcher::~DirectoryWatcher()
1055 void
1056 DirectoryWatcher::EventOccurred(NotificationService& service,
1057 const KMessage* event)
1059 int32 opcode = event->GetInt32("opcode", -1);
1060 dev_t device = event->GetInt32("device", -1);
1061 ino_t directory = event->GetInt64("directory", -1);
1062 const char *name = event->GetString("name", NULL);
1064 if (opcode == B_ENTRY_MOVED) {
1065 // Determine whether it's a move within, out of, or into one
1066 // of our watched directories.
1067 ino_t from = event->GetInt64("from directory", -1);
1068 ino_t to = event->GetInt64("to directory", -1);
1069 if (sDirectoryNodeHash.Lookup(&from) == NULL) {
1070 directory = to;
1071 opcode = B_ENTRY_CREATED;
1072 } else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
1073 directory = from;
1074 opcode = B_ENTRY_REMOVED;
1075 } else {
1076 // Move within, don't do anything for now
1077 // TODO: adjust driver priority if necessary
1078 return;
1082 KPath path(B_PATH_NAME_LENGTH + 1);
1083 if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
1084 name, true, path.LockBuffer(), path.BufferSize()) != B_OK)
1085 return;
1087 path.UnlockBuffer();
1089 dprintf("driver \"%s\" %s\n", path.Leaf(),
1090 opcode == B_ENTRY_CREATED ? "added" : "removed");
1092 driver_event* driverEvent = new(std::nothrow) driver_event(
1093 opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
1094 if (driverEvent == NULL)
1095 return;
1097 strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
1099 MutexLocker _(sDriverEventsLock);
1100 sDriverEvents.Add(driverEvent);
1101 atomic_add(&sDriverEventsPending, 1);
1105 // #pragma mark -
1108 static void
1109 start_watching(const char *base, const char *sub)
1111 KPath path(base);
1112 path.Append(sub);
1114 // TODO: create missing directories?
1115 struct stat stat;
1116 if (::stat(path.Path(), &stat) != 0)
1117 return;
1119 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1120 sDirectoryWatcher);
1122 directory_node_entry *entry = new(std::nothrow) directory_node_entry;
1123 if (entry != NULL) {
1124 entry->node = stat.st_ino;
1125 sDirectoryNodeHash.Insert(entry);
1130 static struct driver_entry*
1131 new_driver_entry(const char* path, dev_t device, ino_t node)
1133 driver_entry* entry = (driver_entry*)malloc(sizeof(driver_entry));
1134 if (entry == NULL)
1135 return NULL;
1137 entry->path = strdup(path);
1138 if (entry->path == NULL) {
1139 free(entry);
1140 return NULL;
1143 entry->device = device;
1144 entry->node = node;
1145 entry->busses = 0;
1146 return entry;
1150 /*! Iterates over the given list and tries to load all drivers in that list.
1151 The list is emptied and freed during the traversal.
1153 static status_t
1154 try_drivers(DriverEntryList& list)
1156 while (true) {
1157 driver_entry* entry = list.RemoveHead();
1158 if (entry == NULL)
1159 break;
1161 image_id image = load_kernel_add_on(entry->path);
1162 if (image >= 0) {
1163 // check if it's an old-style driver
1164 if (legacy_driver_add(entry->path) == B_OK) {
1165 // we have a driver
1166 dprintf("loaded driver %s\n", entry->path);
1169 unload_kernel_add_on(image);
1172 free(entry->path);
1173 free(entry);
1176 return B_OK;
1180 static status_t
1181 probe_for_drivers(const char *type)
1183 TRACE(("probe_for_drivers(type = %s)\n", type));
1185 if (gBootDevice < 0)
1186 return B_OK;
1188 DriverEntryList drivers;
1190 // build list of potential drivers for that type
1192 DirectoryIterator iterator(NULL, type, false);
1193 struct stat stat;
1194 KPath path;
1196 while (iterator.GetNext(path, stat) == B_OK) {
1197 if (S_ISDIR(stat.st_mode)) {
1198 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1199 sDirectoryWatcher);
1201 directory_node_entry *entry
1202 = new(std::nothrow) directory_node_entry;
1203 if (entry != NULL) {
1204 entry->node = stat.st_ino;
1205 sDirectoryNodeHash.Insert(entry);
1208 // We need to make sure that drivers in ie. "audio/raw/" can
1209 // be found as well - therefore, we must make sure that "audio"
1210 // exists on /dev.
1212 size_t length = strlen("drivers/dev");
1213 if (strncmp(type, "drivers/dev", length))
1214 continue;
1216 path.SetTo(type);
1217 path.Append(iterator.CurrentName());
1218 devfs_publish_directory(path.Path() + length + 1);
1219 continue;
1222 driver_entry *entry = new_driver_entry(path.Path(), stat.st_dev,
1223 stat.st_ino);
1224 if (entry == NULL)
1225 return B_NO_MEMORY;
1227 TRACE(("found potential driver: %s\n", path.Path()));
1228 drivers.Add(entry);
1231 if (drivers.IsEmpty())
1232 return B_OK;
1234 // ToDo: do something with the remaining drivers... :)
1235 try_drivers(drivers);
1236 return B_OK;
1240 // #pragma mark - LegacyDevice
1243 LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path,
1244 device_hooks* hooks)
1246 fDriver(driver),
1247 fRepublished(true),
1248 fRemovedFromParent(false)
1250 fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info));
1251 if (fDeviceModule != NULL)
1252 memset(fDeviceModule, 0, sizeof(device_module_info));
1254 fDeviceData = this;
1255 fPath = strdup(path);
1257 SetHooks(hooks);
1261 LegacyDevice::~LegacyDevice()
1263 free(fDeviceModule);
1264 free((char*)fPath);
1268 status_t
1269 LegacyDevice::InitCheck() const
1271 return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY;
1275 status_t
1276 LegacyDevice::InitDevice()
1278 RecursiveLocker _(sLock);
1280 if (fInitialized++ > 0)
1281 return B_OK;
1283 if (fDriver != NULL && fDriver->devices_used == 0
1284 && (fDriver->image < 0 || fDriver->binary_updated)) {
1285 status_t status = reload_driver(fDriver);
1286 if (status < B_OK)
1287 return status;
1290 if (fDriver != NULL)
1291 fDriver->devices_used++;
1293 return B_OK;
1297 void
1298 LegacyDevice::UninitDevice()
1300 RecursiveLocker _(sLock);
1302 if (fInitialized-- > 1)
1303 return;
1305 if (fDriver != NULL) {
1306 if (--fDriver->devices_used == 0 && fDriver->devices.IsEmpty())
1307 unload_driver(fDriver);
1308 fDriver = NULL;
1313 void
1314 LegacyDevice::Removed()
1316 RecursiveLocker _(sLock);
1318 if (!fRemovedFromParent && fDriver != NULL)
1319 fDriver->devices.Remove(this);
1321 delete this;
1325 void
1326 LegacyDevice::SetHooks(device_hooks* hooks)
1328 // TODO: setup compatibility layer!
1329 fHooks = hooks;
1331 fDeviceModule->close = hooks->close;
1332 fDeviceModule->free = hooks->free;
1333 fDeviceModule->control = hooks->control;
1334 fDeviceModule->read = hooks->read;
1335 fDeviceModule->write = hooks->write;
1337 if (fDriver == NULL || fDriver->api_version >= 2) {
1338 // According to Be newsletter, vol II, issue 36,
1339 // version 2 added readv/writev, which we don't support, but also
1340 // select/deselect.
1341 if (hooks->select != NULL) {
1342 // Note we set the module's select to a non-null value to indicate
1343 // that we have select. HasSelect() will therefore return the
1344 // correct answer. As Select() is virtual our compatibility
1345 // version below is going to be called though, that redirects to
1346 // the proper select hook, so it is ok to set it to an invalid
1347 // address here.
1348 fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0;
1351 fDeviceModule->deselect = hooks->deselect;
1356 status_t
1357 LegacyDevice::Open(const char* path, int openMode, void** _cookie)
1359 return Hooks()->open(path, openMode, _cookie);
1363 status_t
1364 LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync)
1366 return Hooks()->select(cookie, event, 0, sync);
1370 // #pragma mark - kernel private API
1373 extern "C" void
1374 legacy_driver_add_preloaded(kernel_args* args)
1376 // NOTE: This function does not exit in case of error, since it
1377 // needs to unload the images then. Also the return code of
1378 // the path operations is kept separate from the add_driver()
1379 // success, so that even if add_driver() fails for one driver, it
1380 // is still tried for the other drivers.
1381 // NOTE: The initialization success of the path objects is implicitely
1382 // checked by the immediately following functions.
1383 KPath basePath;
1384 status_t status = __find_directory(B_BEOS_ADDONS_DIRECTORY,
1385 gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize());
1386 if (status != B_OK) {
1387 dprintf("legacy_driver_add_preloaded: find_directory() failed: "
1388 "%s\n", strerror(status));
1390 basePath.UnlockBuffer();
1391 if (status == B_OK)
1392 status = basePath.Append("kernel");
1393 if (status != B_OK) {
1394 dprintf("legacy_driver_add_preloaded: constructing base driver "
1395 "path failed: %s\n", strerror(status));
1396 return;
1399 struct preloaded_image* image;
1400 for (image = args->preloaded_images; image != NULL; image = image->next) {
1401 if (image->is_module || image->id < 0)
1402 continue;
1404 KPath imagePath(basePath);
1405 status = imagePath.Append(image->name);
1407 // try to add the driver
1408 TRACE(("legacy_driver_add_preloaded: adding driver %s\n",
1409 imagePath.Path()));
1411 if (status == B_OK)
1412 status = add_driver(imagePath.Path(), image->id);
1413 if (status != B_OK) {
1414 dprintf("legacy_driver_add_preloaded: Failed to add \"%s\": %s\n",
1415 (char *)image->name, strerror(status));
1416 unload_kernel_add_on(image->id);
1422 extern "C" status_t
1423 legacy_driver_add(const char* path)
1425 return add_driver(path, -1);
1429 extern "C" status_t
1430 legacy_driver_publish(const char *path, device_hooks *hooks)
1432 // we don't have a driver, just publish the hooks
1433 LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks);
1434 if (device == NULL)
1435 return B_NO_MEMORY;
1437 status_t status = device->InitCheck();
1438 if (status == B_OK)
1439 status = devfs_publish_device(path, device);
1441 if (status != B_OK)
1442 delete device;
1444 return status;
1448 extern "C" status_t
1449 legacy_driver_rescan(const char* driverName)
1451 RecursiveLocker locker(sLock);
1453 legacy_driver* driver = sDriverHash->Lookup(driverName);
1454 if (driver == NULL)
1455 return B_ENTRY_NOT_FOUND;
1457 // Republish the driver's entries
1458 return republish_driver(driver);
1462 extern "C" status_t
1463 legacy_driver_probe(const char* subPath)
1465 TRACE(("legacy_driver_probe(type = %s)\n", subPath));
1467 char devicePath[64];
1468 snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s",
1469 subPath[0] ? "/" : "", subPath);
1471 if (!sWatching && gBootDevice > 0) {
1472 // We're probing the actual boot volume for the first time,
1473 // let's watch its driver directories for changes
1474 const directory_which whichPath[] = {
1475 B_USER_NONPACKAGED_ADDONS_DIRECTORY,
1476 B_USER_ADDONS_DIRECTORY,
1477 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
1478 B_BEOS_ADDONS_DIRECTORY
1480 KPath path;
1482 new(&sDirectoryWatcher) DirectoryWatcher;
1484 bool disableUserAddOns = get_safemode_boolean(
1485 B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
1487 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
1488 if (i < 2 && disableUserAddOns)
1489 continue;
1491 if (__find_directory(whichPath[i], gBootDevice, true,
1492 path.LockBuffer(), path.BufferSize()) == B_OK) {
1493 path.UnlockBuffer();
1494 path.Append("kernel/drivers");
1496 start_watching(path.Path(), "dev");
1497 start_watching(path.Path(), "bin");
1498 } else
1499 path.UnlockBuffer();
1502 sWatching = true;
1505 return probe_for_drivers(devicePath);
1509 extern "C" status_t
1510 legacy_driver_init(void)
1512 sDriverHash = new DriverTable();
1513 if (sDriverHash == NULL || sDriverHash->Init(DRIVER_HASH_SIZE) != B_OK)
1514 return B_NO_MEMORY;
1516 recursive_lock_init(&sLock, "legacy driver");
1518 new(&sDriverWatcher) DriverWatcher;
1519 new(&sDriverEvents) DriverEventList;
1521 register_kernel_daemon(&handle_driver_events, NULL, 10);
1522 // once every second
1524 add_debugger_command("legacy_driver", &dump_driver,
1525 "info about a legacy driver entry");
1526 add_debugger_command("legacy_device", &dump_device,
1527 "info about a legacy device");
1529 return B_OK;