BPicture: Fix archive constructor.
[haiku.git] / src / system / kernel / device_manager / legacy_drivers.cpp
blob602e1a26c704eac2ed94f769b9458f16567f0c31
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 } // unnamed namespace
225 static status_t unload_driver(legacy_driver *driver);
226 static status_t load_driver(legacy_driver *driver);
229 static DriverWatcher sDriverWatcher;
230 static int32 sDriverEventsPending;
231 static DriverEventList sDriverEvents;
232 static mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events");
233 // inner lock, protects the sDriverEvents list only
234 static DirectoryWatcher sDirectoryWatcher;
235 static DirectoryNodeHash sDirectoryNodeHash;
236 static recursive_lock sLock;
237 static bool sWatching;
240 // #pragma mark - driver private
243 struct DriverHash {
244 typedef const char* KeyType;
245 typedef legacy_driver ValueType;
247 size_t HashKey(KeyType key) const
249 return hash_hash_string(key);
252 size_t Hash(ValueType* driver) const
254 return HashKey(driver->name);
257 bool Compare(KeyType key, ValueType* driver) const
259 return strcmp(driver->name, key) == 0;
262 ValueType*& GetLink(ValueType* value) const
264 return value->next;
268 typedef BOpenHashTable<DriverHash> DriverTable;
271 static DriverTable* sDriverHash;
274 /*! Collects all published devices of a driver, compares them to what the
275 driver would publish now, and then publishes/unpublishes the devices
276 as needed.
277 If the driver does not publish any devices anymore, it is unloaded.
279 static status_t
280 republish_driver(legacy_driver* driver)
282 if (driver->image < 0) {
283 // The driver is not yet loaded - go through the normal load procedure
284 return load_driver(driver);
287 // mark all devices
288 DeviceList::Iterator iterator = driver->devices.GetIterator();
289 while (LegacyDevice* device = iterator.Next()) {
290 device->SetRepublished(false);
293 // now ask the driver for it's currently published devices
294 const char** devicePaths = driver->publish_devices();
296 int32 exported = 0;
297 for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
298 LegacyDevice* device;
300 iterator = driver->devices.GetIterator();
301 while ((device = iterator.Next()) != NULL) {
302 if (!strncmp(device->Path(), devicePaths[0], B_PATH_NAME_LENGTH)) {
303 // mark device as republished
304 device->SetRepublished(true);
305 exported++;
306 break;
310 device_hooks* hooks = driver->find_device(devicePaths[0]);
311 if (hooks == NULL)
312 continue;
314 if (device != NULL) {
315 // update hooks
316 device->SetHooks(hooks);
317 continue;
320 // the device was not present before -> publish it now
321 TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
322 device = new(std::nothrow) LegacyDevice(driver, devicePaths[0], hooks);
323 if (device != NULL && device->InitCheck() == B_OK
324 && devfs_publish_device(devicePaths[0], device) == B_OK) {
325 driver->devices.Add(device);
326 exported++;
327 } else
328 delete device;
331 // remove all devices that weren't republished
332 iterator = driver->devices.GetIterator();
333 while (LegacyDevice* device = iterator.Next()) {
334 if (device->Republished())
335 continue;
337 TRACE(("devfs: unpublishing no more present \"%s\"\n", device->Path()));
338 iterator.Remove();
339 device->SetRemovedFromParent(true);
341 devfs_unpublish_device(device, true);
344 if (exported == 0 && driver->devices_used == 0) {
345 TRACE(("devfs: driver \"%s\" does not publish any more nodes and is "
346 "unloaded\n", driver->path));
347 unload_driver(driver);
350 return B_OK;
354 static status_t
355 load_driver(legacy_driver *driver)
357 status_t (*init_hardware)(void);
358 status_t (*init_driver)(void);
359 status_t status;
361 driver->binary_updated = false;
363 // load the module
364 image_id image = driver->image;
365 if (image < 0) {
366 image = load_kernel_add_on(driver->path);
367 if (image < 0)
368 return image;
371 // For a valid device driver the following exports are required
373 int32 *apiVersion;
374 if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA,
375 (void **)&apiVersion) == B_OK) {
376 #if B_CUR_DRIVER_API_VERSION != 2
377 // just in case someone decides to bump up the api version
378 #error Add checks here for new vs old api version!
379 #endif
380 if (*apiVersion > B_CUR_DRIVER_API_VERSION) {
381 dprintf("devfs: \"%s\" api_version %" B_PRId32 " not handled\n",
382 driver->name, *apiVersion);
383 status = B_BAD_VALUE;
384 goto error1;
386 if (*apiVersion < 1) {
387 dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
388 status = B_BAD_VALUE;
389 goto error1;
392 driver->api_version = *apiVersion;
393 } else
394 dprintf("devfs: \"%s\" api_version missing\n", driver->name);
396 if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
397 (void **)&driver->publish_devices) != B_OK
398 || get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
399 (void **)&driver->find_device) != B_OK) {
400 dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n",
401 driver->name);
402 status = B_BAD_VALUE;
403 goto error1;
406 // Init the driver
408 if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
409 (void **)&init_hardware) == B_OK
410 && (status = init_hardware()) != B_OK) {
411 TRACE(("%s: init_hardware() failed: %s\n", driver->name,
412 strerror(status)));
413 status = ENXIO;
414 goto error1;
417 if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
418 (void **)&init_driver) == B_OK
419 && (status = init_driver()) != B_OK) {
420 TRACE(("%s: init_driver() failed: %s\n", driver->name,
421 strerror(status)));
422 status = ENXIO;
423 goto error2;
426 // resolve and cache those for the driver unload code
427 if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT,
428 (void **)&driver->uninit_driver) != B_OK)
429 driver->uninit_driver = NULL;
430 if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT,
431 (void **)&driver->uninit_hardware) != B_OK)
432 driver->uninit_hardware = NULL;
434 // The driver has successfully been initialized, now we can
435 // finally publish its device entries
437 driver->image = image;
438 return republish_driver(driver);
440 error2:
441 if (driver->uninit_hardware)
442 driver->uninit_hardware();
444 error1:
445 if (driver->image < 0) {
446 unload_kernel_add_on(image);
447 driver->image = status;
450 return status;
454 static status_t
455 unload_driver(legacy_driver *driver)
457 if (driver->image < 0) {
458 // driver is not currently loaded
459 return B_NO_INIT;
462 if (driver->uninit_driver)
463 driver->uninit_driver();
465 if (driver->uninit_hardware)
466 driver->uninit_hardware();
468 unload_kernel_add_on(driver->image);
469 driver->image = -1;
470 driver->binary_updated = false;
471 driver->find_device = NULL;
472 driver->publish_devices = NULL;
473 driver->uninit_driver = NULL;
474 driver->uninit_hardware = NULL;
476 return B_OK;
480 /*! Unpublishes all devices belonging to the \a driver. */
481 static void
482 unpublish_driver(legacy_driver *driver)
484 while (LegacyDevice* device = driver->devices.RemoveHead()) {
485 device->SetRemovedFromParent(true);
486 devfs_unpublish_device(device, true);
491 static void
492 change_driver_watcher(dev_t device, ino_t node, bool add)
494 if (device == -1)
495 return;
497 driver_event* event = new (std::nothrow) driver_event(
498 add ? kAddWatcher : kRemoveWatcher);
499 if (event == NULL)
500 return;
502 event->node.device = device;
503 event->node.node = node;
505 MutexLocker _(sDriverEventsLock);
506 sDriverEvents.Add(event);
508 atomic_add(&sDriverEventsPending, 1);
512 static int32
513 get_priority(const char* path)
515 // TODO: would it be better to initialize a static structure here
516 // using find_directory()?
517 const directory_which whichPath[] = {
518 B_BEOS_DIRECTORY,
519 B_SYSTEM_NONPACKAGED_DIRECTORY,
520 B_USER_DIRECTORY
522 KPath pathBuffer;
524 for (uint32 index = 0; index < sizeof(whichPath) / sizeof(whichPath[0]);
525 index++) {
526 if (__find_directory(whichPath[index], gBootDevice, false,
527 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
528 pathBuffer.UnlockBuffer();
529 if (!strncmp(pathBuffer.Path(), path, pathBuffer.BufferSize()))
530 return index;
531 } else
532 pathBuffer.UnlockBuffer();
535 return -1;
539 static const char *
540 get_leaf(const char *path)
542 const char *name = strrchr(path, '/');
543 if (name == NULL)
544 return path;
546 return name + 1;
550 static legacy_driver *
551 find_driver(dev_t device, ino_t node)
553 DriverTable::Iterator iterator(sDriverHash);
554 while (iterator.HasNext()) {
555 legacy_driver *driver = iterator.Next();
556 if (driver->device == device && driver->node == node)
557 return driver;
560 return NULL;
564 static status_t
565 add_driver(const char *path, image_id image)
567 // Check if we already know this driver
569 struct stat stat;
570 if (image >= 0) {
571 // The image ID should be a small number and hopefully the boot FS
572 // doesn't use small negative values -- if it is inode based, we should
573 // be relatively safe.
574 stat.st_dev = -1;
575 stat.st_ino = -1;
576 } else {
577 if (::stat(path, &stat) != 0)
578 return errno;
581 int32 priority = get_priority(path);
583 RecursiveLocker _(sLock);
585 legacy_driver *driver = sDriverHash->Lookup(get_leaf(path));
586 if (driver != NULL) {
587 // we know this driver
588 if (strcmp(driver->path, path) != 0) {
589 // TODO: do properly, but for now we just update the path if it
590 // isn't the same anymore so rescanning of drivers will work in
591 // case this driver was loaded so early that it has a boot module
592 // path and not a proper driver path
593 free((char*)driver->path);
594 driver->path = strdup(path);
595 driver->name = get_leaf(driver->path);
596 driver->binary_updated = true;
599 // TODO: check if this driver is a different one and has precendence
600 // (ie. common supersedes system).
601 //dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority);
602 if (priority >= driver->priority) {
603 driver->binary_updated = true;
604 return B_OK;
607 // TODO: test for changes here and/or via node monitoring and reload
608 // the driver if necessary
609 if (driver->image < B_OK)
610 return driver->image;
612 return B_OK;
615 // we don't know this driver, create a new entry for it
617 driver = (legacy_driver *)malloc(sizeof(legacy_driver));
618 if (driver == NULL)
619 return B_NO_MEMORY;
621 driver->path = strdup(path);
622 if (driver->path == NULL) {
623 free(driver);
624 return B_NO_MEMORY;
627 driver->name = get_leaf(driver->path);
628 driver->device = stat.st_dev;
629 driver->node = stat.st_ino;
630 driver->image = image;
631 driver->last_modified = stat.st_mtim;
632 driver->devices_used = 0;
633 driver->binary_updated = false;
634 driver->priority = priority;
636 driver->api_version = 1;
637 driver->find_device = NULL;
638 driver->publish_devices = NULL;
639 driver->uninit_driver = NULL;
640 driver->uninit_hardware = NULL;
641 new(&driver->devices) DeviceList;
643 sDriverHash->Insert(driver);
644 if (stat.st_dev > 0)
645 change_driver_watcher(stat.st_dev, stat.st_ino, true);
647 // Even if loading the driver fails - its entry will stay with us
648 // so that we don't have to go through it again
649 return load_driver(driver);
653 /*! This is no longer part of the public kernel API, so we just export the
654 symbol
656 extern "C" status_t load_driver_symbols(const char *driverName);
657 status_t
658 load_driver_symbols(const char *driverName)
660 // This is done globally for the whole kernel via the settings file.
661 // We don't have to do anything here.
663 return B_OK;
667 static status_t
668 reload_driver(legacy_driver *driver)
670 dprintf("devfs: reload driver \"%s\" (%" B_PRIdDEV ", %" B_PRIdINO ")\n",
671 driver->name, driver->device, driver->node);
673 unload_driver(driver);
675 struct stat stat;
676 if (::stat(driver->path, &stat) == 0
677 && (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
678 // The driver file has been changed, so we need to update its listener
679 change_driver_watcher(driver->device, driver->node, false);
681 driver->device = stat.st_dev;
682 driver->node = stat.st_ino;
684 change_driver_watcher(driver->device, driver->node, true);
687 status_t status = load_driver(driver);
688 if (status != B_OK)
689 unpublish_driver(driver);
691 return status;
695 static void
696 handle_driver_events(void */*_fs*/, int /*iteration*/)
698 if (atomic_and(&sDriverEventsPending, 0) == 0)
699 return;
701 // something happened, let's see what it was
703 while (true) {
704 MutexLocker eventLocker(sDriverEventsLock);
706 driver_event* event = sDriverEvents.RemoveHead();
707 if (event == NULL)
708 break;
710 eventLocker.Unlock();
711 TRACE(("driver event %p, type %d\n", event, event->type));
713 switch (event->type) {
714 case kAddDriver:
716 // Add new drivers
717 RecursiveLocker locker(sLock);
718 TRACE((" add driver %p\n", event->path));
720 legacy_driver* driver = sDriverHash->Lookup(
721 get_leaf(event->path));
722 if (driver == NULL)
723 legacy_driver_add(event->path);
724 else if (get_priority(event->path) >= driver->priority)
725 driver->binary_updated = true;
726 break;
729 case kRemoveDriver:
731 // Mark removed drivers as updated
732 RecursiveLocker locker(sLock);
733 TRACE((" remove driver %p\n", event->path));
735 legacy_driver* driver = sDriverHash->Lookup(
736 get_leaf(event->path));
737 if (driver != NULL
738 && get_priority(event->path) >= driver->priority)
739 driver->binary_updated = true;
740 break;
743 case kAddWatcher:
744 TRACE((" add watcher %ld:%lld\n", event->node.device,
745 event->node.node));
746 add_node_listener(event->node.device, event->node.node,
747 B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
748 break;
750 case kRemoveWatcher:
751 TRACE((" remove watcher %ld:%lld\n", event->node.device,
752 event->node.node));
753 remove_node_listener(event->node.device, event->node.node,
754 sDriverWatcher);
755 break;
758 delete event;
761 // Reload updated drivers
763 RecursiveLocker locker(sLock);
765 DriverTable::Iterator iterator(sDriverHash);
766 while (iterator.HasNext()) {
767 legacy_driver *driver = iterator.Next();
769 if (!driver->binary_updated || driver->devices_used != 0)
770 continue;
772 // try to reload the driver
773 reload_driver(driver);
776 locker.Unlock();
780 // #pragma mark - DriverWatcher
783 DriverWatcher::DriverWatcher()
788 DriverWatcher::~DriverWatcher()
793 void
794 DriverWatcher::EventOccurred(NotificationService& service,
795 const KMessage* event)
797 int32 opcode = event->GetInt32("opcode", -1);
798 if (opcode != B_STAT_CHANGED
799 || (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
800 return;
802 RecursiveLocker locker(sLock);
804 legacy_driver* driver = find_driver(event->GetInt32("device", -1),
805 event->GetInt64("node", 0));
806 if (driver == NULL)
807 return;
809 driver->binary_updated = true;
811 if (driver->devices_used == 0) {
812 // trigger a reload of the driver
813 atomic_add(&sDriverEventsPending, 1);
814 } else {
815 // driver is in use right now
816 dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
821 static void
822 dump_driver(legacy_driver* driver)
824 kprintf("DEVFS DRIVER: %p\n", driver);
825 kprintf(" name: %s\n", driver->name);
826 kprintf(" path: %s\n", driver->path);
827 kprintf(" image: %" B_PRId32 "\n", driver->image);
828 kprintf(" device: %" B_PRIdDEV "\n", driver->device);
829 kprintf(" node: %" B_PRIdINO "\n", driver->node);
830 kprintf(" last modified: %" B_PRIdTIME ".%ld\n", driver->last_modified.tv_sec,
831 driver->last_modified.tv_nsec);
832 kprintf(" devs used: %" B_PRIu32 "\n", driver->devices_used);
833 kprintf(" devs published: %" B_PRId32 "\n", driver->devices.Count());
834 kprintf(" binary updated: %d\n", driver->binary_updated);
835 kprintf(" priority: %" B_PRId32 "\n", driver->priority);
836 kprintf(" api version: %" B_PRId32 "\n", driver->api_version);
837 kprintf(" hooks: find_device %p, publish_devices %p\n"
838 " uninit_driver %p, uninit_hardware %p\n",
839 driver->find_device, driver->publish_devices, driver->uninit_driver,
840 driver->uninit_hardware);
844 static int
845 dump_device(int argc, char** argv)
847 if (argc < 2 || !strcmp(argv[1], "--help")) {
848 kprintf("usage: %s [device]\n", argv[0]);
849 return 0;
852 LegacyDevice* device = (LegacyDevice*)parse_expression(argv[1]);
854 kprintf("LEGACY DEVICE: %p\n", device);
855 kprintf(" path: %s\n", device->Path());
856 kprintf(" hooks: %p\n", device->Hooks());
857 device_hooks* hooks = device->Hooks();
858 kprintf(" close() %p\n", hooks->close);
859 kprintf(" free() %p\n", hooks->free);
860 kprintf(" control() %p\n", hooks->control);
861 kprintf(" read() %p\n", hooks->read);
862 kprintf(" write() %p\n", hooks->write);
863 kprintf(" select() %p\n", hooks->select);
864 kprintf(" deselect() %p\n", hooks->deselect);
865 dump_driver(device->Driver());
867 return 0;
871 static int
872 dump_driver(int argc, char** argv)
874 if (argc < 2) {
875 // print list of all drivers
876 kprintf("address image used publ. pri name\n");
877 DriverTable::Iterator iterator(sDriverHash);
878 while (iterator.HasNext()) {
879 legacy_driver* driver = iterator.Next();
881 kprintf("%p %5" B_PRId32 " %3" B_PRIu32 " %5" B_PRId32 " %c "
882 "%3" B_PRId32 " %s\n", driver,
883 driver->image < 0 ? -1 : driver->image,
884 driver->devices_used, driver->devices.Count(),
885 driver->binary_updated ? 'U' : ' ', driver->priority,
886 driver->name);
889 return 0;
892 if (!strcmp(argv[1], "--help")) {
893 kprintf("usage: %s [name]\n", argv[0]);
894 return 0;
897 legacy_driver* driver = sDriverHash->Lookup(argv[1]);
898 if (driver == NULL) {
899 kprintf("Driver named \"%s\" not found.\n", argv[1]);
900 return 0;
903 dump_driver(driver);
904 return 0;
908 // #pragma mark -
911 DirectoryIterator::DirectoryIterator(const char* path, const char* subPath,
912 bool recursive)
914 fDirectory(NULL),
915 fBasePath(NULL),
916 fCurrentName(NULL)
918 SetTo(path, subPath, recursive);
922 DirectoryIterator::~DirectoryIterator()
924 Unset();
928 void
929 DirectoryIterator::SetTo(const char* path, const char* subPath, bool recursive)
931 Unset();
932 fRecursive = recursive;
934 if (path == NULL) {
935 // add default paths
936 const directory_which whichPath[] = {
937 B_USER_NONPACKAGED_ADDONS_DIRECTORY,
938 B_USER_ADDONS_DIRECTORY,
939 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
940 B_BEOS_ADDONS_DIRECTORY
942 KPath pathBuffer;
944 bool disableUserAddOns = get_safemode_boolean(
945 B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
947 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
948 if (i < 2 && disableUserAddOns)
949 continue;
951 if (__find_directory(whichPath[i], gBootDevice, true,
952 pathBuffer.LockBuffer(), pathBuffer.BufferSize()) == B_OK) {
953 pathBuffer.UnlockBuffer();
954 pathBuffer.Append("kernel");
955 AddPath(pathBuffer.Path(), subPath);
956 } else
957 pathBuffer.UnlockBuffer();
959 } else
960 AddPath(path, subPath);
964 status_t
965 DirectoryIterator::GetNext(KPath& path, struct stat& stat)
967 next_directory:
968 while (fDirectory == NULL) {
969 delete fBasePath;
970 fBasePath = NULL;
972 if (!fPaths.Pop(&fBasePath))
973 return B_ENTRY_NOT_FOUND;
975 fDirectory = opendir(fBasePath->Path());
978 next_entry:
979 struct dirent* dirent = readdir(fDirectory);
980 if (dirent == NULL) {
981 // get over to next directory on the stack
982 closedir(fDirectory);
983 fDirectory = NULL;
985 goto next_directory;
988 if (!strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "."))
989 goto next_entry;
991 fCurrentName = dirent->d_name;
993 path.SetTo(fBasePath->Path());
994 path.Append(fCurrentName);
996 if (::stat(path.Path(), &stat) != 0)
997 goto next_entry;
999 if (S_ISDIR(stat.st_mode) && fRecursive) {
1000 KPath *nextPath = new(nothrow) KPath(path);
1001 if (!nextPath)
1002 return B_NO_MEMORY;
1003 if (fPaths.Push(nextPath) != B_OK)
1004 return B_NO_MEMORY;
1006 goto next_entry;
1009 return B_OK;
1013 void
1014 DirectoryIterator::Unset()
1016 if (fDirectory != NULL) {
1017 closedir(fDirectory);
1018 fDirectory = NULL;
1021 delete fBasePath;
1022 fBasePath = NULL;
1024 KPath *path;
1025 while (fPaths.Pop(&path))
1026 delete path;
1030 void
1031 DirectoryIterator::AddPath(const char* basePath, const char* subPath)
1033 KPath *path = new(nothrow) KPath(basePath);
1034 if (!path)
1035 panic("out of memory");
1036 if (subPath != NULL)
1037 path->Append(subPath);
1039 fPaths.Push(path);
1043 // #pragma mark -
1046 DirectoryWatcher::DirectoryWatcher()
1051 DirectoryWatcher::~DirectoryWatcher()
1056 void
1057 DirectoryWatcher::EventOccurred(NotificationService& service,
1058 const KMessage* event)
1060 int32 opcode = event->GetInt32("opcode", -1);
1061 dev_t device = event->GetInt32("device", -1);
1062 ino_t directory = event->GetInt64("directory", -1);
1063 const char *name = event->GetString("name", NULL);
1065 if (opcode == B_ENTRY_MOVED) {
1066 // Determine whether it's a move within, out of, or into one
1067 // of our watched directories.
1068 ino_t from = event->GetInt64("from directory", -1);
1069 ino_t to = event->GetInt64("to directory", -1);
1070 if (sDirectoryNodeHash.Lookup(&from) == NULL) {
1071 directory = to;
1072 opcode = B_ENTRY_CREATED;
1073 } else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
1074 directory = from;
1075 opcode = B_ENTRY_REMOVED;
1076 } else {
1077 // Move within, don't do anything for now
1078 // TODO: adjust driver priority if necessary
1079 return;
1083 KPath path(B_PATH_NAME_LENGTH + 1);
1084 if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
1085 name, true, path.LockBuffer(), path.BufferSize()) != B_OK)
1086 return;
1088 path.UnlockBuffer();
1090 dprintf("driver \"%s\" %s\n", path.Leaf(),
1091 opcode == B_ENTRY_CREATED ? "added" : "removed");
1093 driver_event* driverEvent = new(std::nothrow) driver_event(
1094 opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
1095 if (driverEvent == NULL)
1096 return;
1098 strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
1100 MutexLocker _(sDriverEventsLock);
1101 sDriverEvents.Add(driverEvent);
1102 atomic_add(&sDriverEventsPending, 1);
1106 // #pragma mark -
1109 static void
1110 start_watching(const char *base, const char *sub)
1112 KPath path(base);
1113 path.Append(sub);
1115 // TODO: create missing directories?
1116 struct stat stat;
1117 if (::stat(path.Path(), &stat) != 0)
1118 return;
1120 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1121 sDirectoryWatcher);
1123 directory_node_entry *entry = new(std::nothrow) directory_node_entry;
1124 if (entry != NULL) {
1125 entry->node = stat.st_ino;
1126 sDirectoryNodeHash.Insert(entry);
1131 static struct driver_entry*
1132 new_driver_entry(const char* path, dev_t device, ino_t node)
1134 driver_entry* entry = (driver_entry*)malloc(sizeof(driver_entry));
1135 if (entry == NULL)
1136 return NULL;
1138 entry->path = strdup(path);
1139 if (entry->path == NULL) {
1140 free(entry);
1141 return NULL;
1144 entry->device = device;
1145 entry->node = node;
1146 entry->busses = 0;
1147 return entry;
1151 /*! Iterates over the given list and tries to load all drivers in that list.
1152 The list is emptied and freed during the traversal.
1154 static status_t
1155 try_drivers(DriverEntryList& list)
1157 while (true) {
1158 driver_entry* entry = list.RemoveHead();
1159 if (entry == NULL)
1160 break;
1162 image_id image = load_kernel_add_on(entry->path);
1163 if (image >= 0) {
1164 // check if it's an old-style driver
1165 if (legacy_driver_add(entry->path) == B_OK) {
1166 // we have a driver
1167 dprintf("loaded driver %s\n", entry->path);
1170 unload_kernel_add_on(image);
1173 free(entry->path);
1174 free(entry);
1177 return B_OK;
1181 static status_t
1182 probe_for_drivers(const char *type)
1184 TRACE(("probe_for_drivers(type = %s)\n", type));
1186 if (gBootDevice < 0)
1187 return B_OK;
1189 DriverEntryList drivers;
1191 // build list of potential drivers for that type
1193 DirectoryIterator iterator(NULL, type, false);
1194 struct stat stat;
1195 KPath path;
1197 while (iterator.GetNext(path, stat) == B_OK) {
1198 if (S_ISDIR(stat.st_mode)) {
1199 add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
1200 sDirectoryWatcher);
1202 directory_node_entry *entry
1203 = new(std::nothrow) directory_node_entry;
1204 if (entry != NULL) {
1205 entry->node = stat.st_ino;
1206 sDirectoryNodeHash.Insert(entry);
1209 // We need to make sure that drivers in ie. "audio/raw/" can
1210 // be found as well - therefore, we must make sure that "audio"
1211 // exists on /dev.
1213 size_t length = strlen("drivers/dev");
1214 if (strncmp(type, "drivers/dev", length))
1215 continue;
1217 path.SetTo(type);
1218 path.Append(iterator.CurrentName());
1219 devfs_publish_directory(path.Path() + length + 1);
1220 continue;
1223 driver_entry *entry = new_driver_entry(path.Path(), stat.st_dev,
1224 stat.st_ino);
1225 if (entry == NULL)
1226 return B_NO_MEMORY;
1228 TRACE(("found potential driver: %s\n", path.Path()));
1229 drivers.Add(entry);
1232 if (drivers.IsEmpty())
1233 return B_OK;
1235 // ToDo: do something with the remaining drivers... :)
1236 try_drivers(drivers);
1237 return B_OK;
1241 // #pragma mark - LegacyDevice
1244 LegacyDevice::LegacyDevice(legacy_driver* driver, const char* path,
1245 device_hooks* hooks)
1247 fDriver(driver),
1248 fRepublished(true),
1249 fRemovedFromParent(false)
1251 fDeviceModule = (device_module_info*)malloc(sizeof(device_module_info));
1252 if (fDeviceModule != NULL)
1253 memset(fDeviceModule, 0, sizeof(device_module_info));
1255 fDeviceData = this;
1256 fPath = strdup(path);
1258 SetHooks(hooks);
1262 LegacyDevice::~LegacyDevice()
1264 free(fDeviceModule);
1265 free((char*)fPath);
1269 status_t
1270 LegacyDevice::InitCheck() const
1272 return fDeviceModule != NULL && fPath != NULL ? B_OK : B_NO_MEMORY;
1276 status_t
1277 LegacyDevice::InitDevice()
1279 RecursiveLocker _(sLock);
1281 if (fInitialized++ > 0)
1282 return B_OK;
1284 if (fDriver != NULL && fDriver->devices_used == 0
1285 && (fDriver->image < 0 || fDriver->binary_updated)) {
1286 status_t status = reload_driver(fDriver);
1287 if (status < B_OK)
1288 return status;
1291 if (fDriver != NULL)
1292 fDriver->devices_used++;
1294 return B_OK;
1298 void
1299 LegacyDevice::UninitDevice()
1301 RecursiveLocker _(sLock);
1303 if (fInitialized-- > 1)
1304 return;
1306 if (fDriver != NULL) {
1307 if (--fDriver->devices_used == 0 && fDriver->devices.IsEmpty())
1308 unload_driver(fDriver);
1309 fDriver = NULL;
1314 void
1315 LegacyDevice::Removed()
1317 RecursiveLocker _(sLock);
1319 if (!fRemovedFromParent && fDriver != NULL)
1320 fDriver->devices.Remove(this);
1322 delete this;
1326 void
1327 LegacyDevice::SetHooks(device_hooks* hooks)
1329 // TODO: setup compatibility layer!
1330 fHooks = hooks;
1332 fDeviceModule->close = hooks->close;
1333 fDeviceModule->free = hooks->free;
1334 fDeviceModule->control = hooks->control;
1335 fDeviceModule->read = hooks->read;
1336 fDeviceModule->write = hooks->write;
1338 if (fDriver == NULL || fDriver->api_version >= 2) {
1339 // According to Be newsletter, vol II, issue 36,
1340 // version 2 added readv/writev, which we don't support, but also
1341 // select/deselect.
1342 if (hooks->select != NULL) {
1343 // Note we set the module's select to a non-null value to indicate
1344 // that we have select. HasSelect() will therefore return the
1345 // correct answer. As Select() is virtual our compatibility
1346 // version below is going to be called though, that redirects to
1347 // the proper select hook, so it is ok to set it to an invalid
1348 // address here.
1349 fDeviceModule->select = (status_t (*)(void*, uint8, selectsync*))~0;
1352 fDeviceModule->deselect = hooks->deselect;
1357 status_t
1358 LegacyDevice::Open(const char* path, int openMode, void** _cookie)
1360 return Hooks()->open(path, openMode, _cookie);
1364 status_t
1365 LegacyDevice::Select(void* cookie, uint8 event, selectsync* sync)
1367 return Hooks()->select(cookie, event, 0, sync);
1371 // #pragma mark - kernel private API
1374 extern "C" void
1375 legacy_driver_add_preloaded(kernel_args* args)
1377 // NOTE: This function does not exit in case of error, since it
1378 // needs to unload the images then. Also the return code of
1379 // the path operations is kept separate from the add_driver()
1380 // success, so that even if add_driver() fails for one driver, it
1381 // is still tried for the other drivers.
1382 // NOTE: The initialization success of the path objects is implicitely
1383 // checked by the immediately following functions.
1384 KPath basePath;
1385 status_t status = __find_directory(B_BEOS_ADDONS_DIRECTORY,
1386 gBootDevice, false, basePath.LockBuffer(), basePath.BufferSize());
1387 if (status != B_OK) {
1388 dprintf("legacy_driver_add_preloaded: find_directory() failed: "
1389 "%s\n", strerror(status));
1391 basePath.UnlockBuffer();
1392 if (status == B_OK)
1393 status = basePath.Append("kernel");
1394 if (status != B_OK) {
1395 dprintf("legacy_driver_add_preloaded: constructing base driver "
1396 "path failed: %s\n", strerror(status));
1397 return;
1400 struct preloaded_image* image;
1401 for (image = args->preloaded_images; image != NULL; image = image->next) {
1402 if (image->is_module || image->id < 0)
1403 continue;
1405 KPath imagePath(basePath);
1406 status = imagePath.Append(image->name);
1408 // try to add the driver
1409 TRACE(("legacy_driver_add_preloaded: adding driver %s\n",
1410 imagePath.Path()));
1412 if (status == B_OK)
1413 status = add_driver(imagePath.Path(), image->id);
1414 if (status != B_OK) {
1415 dprintf("legacy_driver_add_preloaded: Failed to add \"%s\": %s\n",
1416 (char *)image->name, strerror(status));
1417 unload_kernel_add_on(image->id);
1423 extern "C" status_t
1424 legacy_driver_add(const char* path)
1426 return add_driver(path, -1);
1430 extern "C" status_t
1431 legacy_driver_publish(const char *path, device_hooks *hooks)
1433 // we don't have a driver, just publish the hooks
1434 LegacyDevice* device = new(std::nothrow) LegacyDevice(NULL, path, hooks);
1435 if (device == NULL)
1436 return B_NO_MEMORY;
1438 status_t status = device->InitCheck();
1439 if (status == B_OK)
1440 status = devfs_publish_device(path, device);
1442 if (status != B_OK)
1443 delete device;
1445 return status;
1449 extern "C" status_t
1450 legacy_driver_rescan(const char* driverName)
1452 RecursiveLocker locker(sLock);
1454 legacy_driver* driver = sDriverHash->Lookup(driverName);
1455 if (driver == NULL)
1456 return B_ENTRY_NOT_FOUND;
1458 // Republish the driver's entries
1459 return republish_driver(driver);
1463 extern "C" status_t
1464 legacy_driver_probe(const char* subPath)
1466 TRACE(("legacy_driver_probe(type = %s)\n", subPath));
1468 char devicePath[64];
1469 snprintf(devicePath, sizeof(devicePath), "drivers/dev%s%s",
1470 subPath[0] ? "/" : "", subPath);
1472 if (!sWatching && gBootDevice > 0) {
1473 // We're probing the actual boot volume for the first time,
1474 // let's watch its driver directories for changes
1475 const directory_which whichPath[] = {
1476 B_USER_NONPACKAGED_ADDONS_DIRECTORY,
1477 B_USER_ADDONS_DIRECTORY,
1478 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
1479 B_BEOS_ADDONS_DIRECTORY
1481 KPath path;
1483 new(&sDirectoryWatcher) DirectoryWatcher;
1485 bool disableUserAddOns = get_safemode_boolean(
1486 B_SAFEMODE_DISABLE_USER_ADD_ONS, false);
1488 for (uint32 i = 0; i < sizeof(whichPath) / sizeof(whichPath[0]); i++) {
1489 if (i < 2 && disableUserAddOns)
1490 continue;
1492 if (__find_directory(whichPath[i], gBootDevice, true,
1493 path.LockBuffer(), path.BufferSize()) == B_OK) {
1494 path.UnlockBuffer();
1495 path.Append("kernel/drivers");
1497 start_watching(path.Path(), "dev");
1498 start_watching(path.Path(), "bin");
1499 } else
1500 path.UnlockBuffer();
1503 sWatching = true;
1506 return probe_for_drivers(devicePath);
1510 extern "C" status_t
1511 legacy_driver_init(void)
1513 sDriverHash = new DriverTable();
1514 if (sDriverHash == NULL || sDriverHash->Init(DRIVER_HASH_SIZE) != B_OK)
1515 return B_NO_MEMORY;
1517 recursive_lock_init(&sLock, "legacy driver");
1519 new(&sDriverWatcher) DriverWatcher;
1520 new(&sDriverEvents) DriverEventList;
1522 register_kernel_daemon(&handle_driver_events, NULL, 10);
1523 // once every second
1525 add_debugger_command("legacy_driver", &dump_driver,
1526 "info about a legacy driver entry");
1527 add_debugger_command("legacy_device", &dump_device,
1528 "info about a legacy device");
1530 return B_OK;