2 * Copyright 2002-2011, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include "legacy_drivers.h"
14 #include <FindDirectory.h>
16 #include <NodeMonitor.h>
18 #include <boot_device.h>
19 #include <boot/kernel_args.h>
21 #include <find_directory_private.h>
24 #include <fs/node_monitor.h>
25 #include <Notifications.h>
27 #include <util/DoublyLinkedList.h>
28 #include <util/OpenHashTable.h>
29 #include <util/Stack.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
43 #define DRIVER_HASH_SIZE 16
50 class LegacyDevice
: public AbstractModuleDevice
,
51 public DoublyLinkedListLinkImpl
<LegacyDevice
> {
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
,
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
; }
82 legacy_driver
* fDriver
;
86 bool fRemovedFromParent
;
89 typedef DoublyLinkedList
<LegacyDevice
> DeviceList
;
91 struct legacy_driver
{
97 timespec last_modified
;
104 // driver image information
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
{
120 struct driver_event
: DoublyLinkedListLinkImpl
<driver_event
> {
121 driver_event(driver_event_type _type
) : type(_type
) {}
128 driver_event_type type
;
130 char path
[B_PATH_NAME_LENGTH
];
135 typedef DoublyLinkedList
<driver_event
> DriverEventList
;
138 struct driver_entry
: DoublyLinkedListLinkImpl
<driver_entry
> {
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
;
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
{
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
; }
192 void AddPath(const char *path
, const char *subPath
= NULL
);
195 Stack
<KPath
*> fPaths
;
199 const char* fCurrentName
;
203 class DirectoryWatcher
: public NotificationListener
{
206 virtual ~DirectoryWatcher();
208 virtual void EventOccurred(NotificationService
& service
,
209 const KMessage
* event
);
212 class DriverWatcher
: public NotificationListener
{
215 virtual ~DriverWatcher();
217 virtual void EventOccurred(NotificationService
& service
,
218 const KMessage
* event
);
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
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
276 If the driver does not publish any devices anymore, it is unloaded.
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
);
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();
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);
309 device_hooks
* hooks
= driver
->find_device(devicePaths
[0]);
313 if (device
!= NULL
) {
315 device
->SetHooks(hooks
);
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
);
330 // remove all devices that weren't republished
331 iterator
= driver
->devices
.GetIterator();
332 while (LegacyDevice
* device
= iterator
.Next()) {
333 if (device
->Republished())
336 TRACE(("devfs: unpublishing no more present \"%s\"\n", device
->Path()));
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
);
354 load_driver(legacy_driver
*driver
)
356 status_t (*init_hardware
)(void);
357 status_t (*init_driver
)(void);
360 driver
->binary_updated
= false;
363 image_id image
= driver
->image
;
365 image
= load_kernel_add_on(driver
->path
);
370 // For a valid device driver the following exports are required
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!
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
;
385 if (*apiVersion
< 1) {
386 dprintf("devfs: \"%s\" api_version invalid\n", driver
->name
);
387 status
= B_BAD_VALUE
;
391 driver
->api_version
= *apiVersion
;
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",
401 status
= B_BAD_VALUE
;
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
,
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
,
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
);
440 if (driver
->uninit_hardware
)
441 driver
->uninit_hardware();
444 if (driver
->image
< 0) {
445 unload_kernel_add_on(image
);
446 driver
->image
= status
;
454 unload_driver(legacy_driver
*driver
)
456 if (driver
->image
< 0) {
457 // driver is not currently loaded
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
);
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
;
479 /*! Unpublishes all devices belonging to the \a driver. */
481 unpublish_driver(legacy_driver
*driver
)
483 while (LegacyDevice
* device
= driver
->devices
.RemoveHead()) {
484 device
->SetRemovedFromParent(true);
485 devfs_unpublish_device(device
, true);
491 change_driver_watcher(dev_t device
, ino_t node
, bool add
)
496 driver_event
* event
= new (std::nothrow
) driver_event(
497 add
? kAddWatcher
: kRemoveWatcher
);
501 event
->node
.device
= device
;
502 event
->node
.node
= node
;
504 MutexLocker
_(sDriverEventsLock
);
505 sDriverEvents
.Add(event
);
507 atomic_add(&sDriverEventsPending
, 1);
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
[] = {
518 B_SYSTEM_NONPACKAGED_DIRECTORY
,
523 for (uint32 index
= 0; index
< sizeof(whichPath
) / sizeof(whichPath
[0]);
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()))
531 pathBuffer
.UnlockBuffer();
539 get_leaf(const char *path
)
541 const char *name
= strrchr(path
, '/');
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
)
564 add_driver(const char *path
, image_id image
)
566 // Check if we already know this driver
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.
576 if (::stat(path
, &stat
) != 0)
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;
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
;
614 // we don't know this driver, create a new entry for it
616 driver
= (legacy_driver
*)malloc(sizeof(legacy_driver
));
620 driver
->path
= strdup(path
);
621 if (driver
->path
== NULL
) {
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
);
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
655 extern "C" status_t
load_driver_symbols(const char *driverName
);
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.
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
);
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
);
688 unpublish_driver(driver
);
695 handle_driver_events(void */
*_fs*/
, int /*iteration*/)
697 if (atomic_and(&sDriverEventsPending
, 0) == 0)
700 // something happened, let's see what it was
703 MutexLocker
eventLocker(sDriverEventsLock
);
705 driver_event
* event
= sDriverEvents
.RemoveHead();
709 eventLocker
.Unlock();
710 TRACE(("driver event %p, type %d\n", event
, event
->type
));
712 switch (event
->type
) {
716 RecursiveLocker
locker(sLock
);
717 TRACE((" add driver %p\n", event
->path
));
719 legacy_driver
* driver
= sDriverHash
->Lookup(
720 get_leaf(event
->path
));
722 legacy_driver_add(event
->path
);
723 else if (get_priority(event
->path
) >= driver
->priority
)
724 driver
->binary_updated
= true;
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
));
737 && get_priority(event
->path
) >= driver
->priority
)
738 driver
->binary_updated
= true;
743 TRACE((" add watcher %ld:%lld\n", event
->node
.device
,
745 add_node_listener(event
->node
.device
, event
->node
.node
,
746 B_WATCH_STAT
| B_WATCH_NAME
, sDriverWatcher
);
750 TRACE((" remove watcher %ld:%lld\n", event
->node
.device
,
752 remove_node_listener(event
->node
.device
, event
->node
.node
,
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)
771 // try to reload the driver
772 reload_driver(driver
);
779 // #pragma mark - DriverWatcher
782 DriverWatcher::DriverWatcher()
787 DriverWatcher::~DriverWatcher()
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)
801 RecursiveLocker
locker(sLock
);
803 legacy_driver
* driver
= find_driver(event
->GetInt32("device", -1),
804 event
->GetInt64("node", 0));
808 driver
->binary_updated
= true;
810 if (driver
->devices_used
== 0) {
811 // trigger a reload of the driver
812 atomic_add(&sDriverEventsPending
, 1);
814 // driver is in use right now
815 dprintf("devfs: changed driver \"%s\" is still in use\n", driver
->name
);
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
);
844 dump_device(int argc
, char** argv
)
846 if (argc
< 2 || !strcmp(argv
[1], "--help")) {
847 kprintf("usage: %s [device]\n", argv
[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());
871 dump_driver(int argc
, char** argv
)
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
,
891 if (!strcmp(argv
[1], "--help")) {
892 kprintf("usage: %s [name]\n", argv
[0]);
896 legacy_driver
* driver
= sDriverHash
->Lookup(argv
[1]);
897 if (driver
== NULL
) {
898 kprintf("Driver named \"%s\" not found.\n", argv
[1]);
910 DirectoryIterator::DirectoryIterator(const char* path
, const char* subPath
,
917 SetTo(path
, subPath
, recursive
);
921 DirectoryIterator::~DirectoryIterator()
928 DirectoryIterator::SetTo(const char* path
, const char* subPath
, bool recursive
)
931 fRecursive
= recursive
;
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
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
)
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
);
956 pathBuffer
.UnlockBuffer();
959 AddPath(path
, subPath
);
964 DirectoryIterator::GetNext(KPath
& path
, struct stat
& stat
)
967 while (fDirectory
== NULL
) {
971 if (!fPaths
.Pop(&fBasePath
))
972 return B_ENTRY_NOT_FOUND
;
974 fDirectory
= opendir(fBasePath
->Path());
978 struct dirent
* dirent
= readdir(fDirectory
);
979 if (dirent
== NULL
) {
980 // get over to next directory on the stack
981 closedir(fDirectory
);
987 if (!strcmp(dirent
->d_name
, "..") || !strcmp(dirent
->d_name
, "."))
990 fCurrentName
= dirent
->d_name
;
992 path
.SetTo(fBasePath
->Path());
993 path
.Append(fCurrentName
);
995 if (::stat(path
.Path(), &stat
) != 0)
998 if (S_ISDIR(stat
.st_mode
) && fRecursive
) {
999 KPath
*nextPath
= new(nothrow
) KPath(path
);
1002 if (fPaths
.Push(nextPath
) != B_OK
)
1013 DirectoryIterator::Unset()
1015 if (fDirectory
!= NULL
) {
1016 closedir(fDirectory
);
1024 while (fPaths
.Pop(&path
))
1030 DirectoryIterator::AddPath(const char* basePath
, const char* subPath
)
1032 KPath
*path
= new(nothrow
) KPath(basePath
);
1034 panic("out of memory");
1035 if (subPath
!= NULL
)
1036 path
->Append(subPath
);
1045 DirectoryWatcher::DirectoryWatcher()
1050 DirectoryWatcher::~DirectoryWatcher()
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
) {
1071 opcode
= B_ENTRY_CREATED
;
1072 } else if (sDirectoryNodeHash
.Lookup(&to
) == NULL
) {
1074 opcode
= B_ENTRY_REMOVED
;
1076 // Move within, don't do anything for now
1077 // TODO: adjust driver priority if necessary
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
)
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
)
1097 strlcpy(driverEvent
->path
, path
.Path(), sizeof(driverEvent
->path
));
1099 MutexLocker
_(sDriverEventsLock
);
1100 sDriverEvents
.Add(driverEvent
);
1101 atomic_add(&sDriverEventsPending
, 1);
1109 start_watching(const char *base
, const char *sub
)
1114 // TODO: create missing directories?
1116 if (::stat(path
.Path(), &stat
) != 0)
1119 add_node_listener(stat
.st_dev
, stat
.st_ino
, B_WATCH_DIRECTORY
,
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
));
1137 entry
->path
= strdup(path
);
1138 if (entry
->path
== NULL
) {
1143 entry
->device
= device
;
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.
1154 try_drivers(DriverEntryList
& list
)
1157 driver_entry
* entry
= list
.RemoveHead();
1161 image_id image
= load_kernel_add_on(entry
->path
);
1163 // check if it's an old-style driver
1164 if (legacy_driver_add(entry
->path
) == B_OK
) {
1166 dprintf("loaded driver %s\n", entry
->path
);
1169 unload_kernel_add_on(image
);
1181 probe_for_drivers(const char *type
)
1183 TRACE(("probe_for_drivers(type = %s)\n", type
));
1185 if (gBootDevice
< 0)
1188 DriverEntryList drivers
;
1190 // build list of potential drivers for that type
1192 DirectoryIterator
iterator(NULL
, type
, false);
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
,
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"
1212 size_t length
= strlen("drivers/dev");
1213 if (strncmp(type
, "drivers/dev", length
))
1217 path
.Append(iterator
.CurrentName());
1218 devfs_publish_directory(path
.Path() + length
+ 1);
1222 driver_entry
*entry
= new_driver_entry(path
.Path(), stat
.st_dev
,
1227 TRACE(("found potential driver: %s\n", path
.Path()));
1231 if (drivers
.IsEmpty())
1234 // ToDo: do something with the remaining drivers... :)
1235 try_drivers(drivers
);
1240 // #pragma mark - LegacyDevice
1243 LegacyDevice::LegacyDevice(legacy_driver
* driver
, const char* path
,
1244 device_hooks
* hooks
)
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
));
1255 fPath
= strdup(path
);
1261 LegacyDevice::~LegacyDevice()
1263 free(fDeviceModule
);
1269 LegacyDevice::InitCheck() const
1271 return fDeviceModule
!= NULL
&& fPath
!= NULL
? B_OK
: B_NO_MEMORY
;
1276 LegacyDevice::InitDevice()
1278 RecursiveLocker
_(sLock
);
1280 if (fInitialized
++ > 0)
1283 if (fDriver
!= NULL
&& fDriver
->devices_used
== 0
1284 && (fDriver
->image
< 0 || fDriver
->binary_updated
)) {
1285 status_t status
= reload_driver(fDriver
);
1290 if (fDriver
!= NULL
)
1291 fDriver
->devices_used
++;
1298 LegacyDevice::UninitDevice()
1300 RecursiveLocker
_(sLock
);
1302 if (fInitialized
-- > 1)
1305 if (fDriver
!= NULL
) {
1306 if (--fDriver
->devices_used
== 0 && fDriver
->devices
.IsEmpty())
1307 unload_driver(fDriver
);
1314 LegacyDevice::Removed()
1316 RecursiveLocker
_(sLock
);
1318 if (!fRemovedFromParent
&& fDriver
!= NULL
)
1319 fDriver
->devices
.Remove(this);
1326 LegacyDevice::SetHooks(device_hooks
* hooks
)
1328 // TODO: setup compatibility layer!
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
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
1348 fDeviceModule
->select
= (status_t (*)(void*, uint8
, selectsync
*))~0;
1351 fDeviceModule
->deselect
= hooks
->deselect
;
1357 LegacyDevice::Open(const char* path
, int openMode
, void** _cookie
)
1359 return Hooks()->open(path
, openMode
, _cookie
);
1364 LegacyDevice::Select(void* cookie
, uint8 event
, selectsync
* sync
)
1366 return Hooks()->select(cookie
, event
, 0, sync
);
1370 // #pragma mark - kernel private API
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.
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();
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
));
1399 struct preloaded_image
* image
;
1400 for (image
= args
->preloaded_images
; image
!= NULL
; image
= image
->next
) {
1401 if (image
->is_module
|| image
->id
< 0)
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",
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
);
1423 legacy_driver_add(const char* path
)
1425 return add_driver(path
, -1);
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
);
1437 status_t status
= device
->InitCheck();
1439 status
= devfs_publish_device(path
, device
);
1449 legacy_driver_rescan(const char* driverName
)
1451 RecursiveLocker
locker(sLock
);
1453 legacy_driver
* driver
= sDriverHash
->Lookup(driverName
);
1455 return B_ENTRY_NOT_FOUND
;
1457 // Republish the driver's entries
1458 return republish_driver(driver
);
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
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
)
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");
1499 path
.UnlockBuffer();
1505 return probe_for_drivers(devicePath
);
1510 legacy_driver_init(void)
1512 sDriverHash
= new DriverTable();
1513 if (sDriverHash
== NULL
|| sDriverHash
->Init(DRIVER_HASH_SIZE
) != B_OK
)
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");