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
);
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
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
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
277 If the driver does not publish any devices anymore, it is unloaded.
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
);
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();
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);
310 device_hooks
* hooks
= driver
->find_device(devicePaths
[0]);
314 if (device
!= NULL
) {
316 device
->SetHooks(hooks
);
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
);
331 // remove all devices that weren't republished
332 iterator
= driver
->devices
.GetIterator();
333 while (LegacyDevice
* device
= iterator
.Next()) {
334 if (device
->Republished())
337 TRACE(("devfs: unpublishing no more present \"%s\"\n", device
->Path()));
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
);
355 load_driver(legacy_driver
*driver
)
357 status_t (*init_hardware
)(void);
358 status_t (*init_driver
)(void);
361 driver
->binary_updated
= false;
364 image_id image
= driver
->image
;
366 image
= load_kernel_add_on(driver
->path
);
371 // For a valid device driver the following exports are required
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!
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
;
386 if (*apiVersion
< 1) {
387 dprintf("devfs: \"%s\" api_version invalid\n", driver
->name
);
388 status
= B_BAD_VALUE
;
392 driver
->api_version
= *apiVersion
;
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",
402 status
= B_BAD_VALUE
;
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
,
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
,
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
);
441 if (driver
->uninit_hardware
)
442 driver
->uninit_hardware();
445 if (driver
->image
< 0) {
446 unload_kernel_add_on(image
);
447 driver
->image
= status
;
455 unload_driver(legacy_driver
*driver
)
457 if (driver
->image
< 0) {
458 // driver is not currently loaded
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
);
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
;
480 /*! Unpublishes all devices belonging to the \a driver. */
482 unpublish_driver(legacy_driver
*driver
)
484 while (LegacyDevice
* device
= driver
->devices
.RemoveHead()) {
485 device
->SetRemovedFromParent(true);
486 devfs_unpublish_device(device
, true);
492 change_driver_watcher(dev_t device
, ino_t node
, bool add
)
497 driver_event
* event
= new (std::nothrow
) driver_event(
498 add
? kAddWatcher
: kRemoveWatcher
);
502 event
->node
.device
= device
;
503 event
->node
.node
= node
;
505 MutexLocker
_(sDriverEventsLock
);
506 sDriverEvents
.Add(event
);
508 atomic_add(&sDriverEventsPending
, 1);
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
[] = {
519 B_SYSTEM_NONPACKAGED_DIRECTORY
,
524 for (uint32 index
= 0; index
< sizeof(whichPath
) / sizeof(whichPath
[0]);
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()))
532 pathBuffer
.UnlockBuffer();
540 get_leaf(const char *path
)
542 const char *name
= strrchr(path
, '/');
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
)
565 add_driver(const char *path
, image_id image
)
567 // Check if we already know this driver
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.
577 if (::stat(path
, &stat
) != 0)
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;
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
;
615 // we don't know this driver, create a new entry for it
617 driver
= (legacy_driver
*)malloc(sizeof(legacy_driver
));
621 driver
->path
= strdup(path
);
622 if (driver
->path
== NULL
) {
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
);
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
656 extern "C" status_t
load_driver_symbols(const char *driverName
);
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.
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
);
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
);
689 unpublish_driver(driver
);
696 handle_driver_events(void */
*_fs*/
, int /*iteration*/)
698 if (atomic_and(&sDriverEventsPending
, 0) == 0)
701 // something happened, let's see what it was
704 MutexLocker
eventLocker(sDriverEventsLock
);
706 driver_event
* event
= sDriverEvents
.RemoveHead();
710 eventLocker
.Unlock();
711 TRACE(("driver event %p, type %d\n", event
, event
->type
));
713 switch (event
->type
) {
717 RecursiveLocker
locker(sLock
);
718 TRACE((" add driver %p\n", event
->path
));
720 legacy_driver
* driver
= sDriverHash
->Lookup(
721 get_leaf(event
->path
));
723 legacy_driver_add(event
->path
);
724 else if (get_priority(event
->path
) >= driver
->priority
)
725 driver
->binary_updated
= true;
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
));
738 && get_priority(event
->path
) >= driver
->priority
)
739 driver
->binary_updated
= true;
744 TRACE((" add watcher %ld:%lld\n", event
->node
.device
,
746 add_node_listener(event
->node
.device
, event
->node
.node
,
747 B_WATCH_STAT
| B_WATCH_NAME
, sDriverWatcher
);
751 TRACE((" remove watcher %ld:%lld\n", event
->node
.device
,
753 remove_node_listener(event
->node
.device
, event
->node
.node
,
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)
772 // try to reload the driver
773 reload_driver(driver
);
780 // #pragma mark - DriverWatcher
783 DriverWatcher::DriverWatcher()
788 DriverWatcher::~DriverWatcher()
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)
802 RecursiveLocker
locker(sLock
);
804 legacy_driver
* driver
= find_driver(event
->GetInt32("device", -1),
805 event
->GetInt64("node", 0));
809 driver
->binary_updated
= true;
811 if (driver
->devices_used
== 0) {
812 // trigger a reload of the driver
813 atomic_add(&sDriverEventsPending
, 1);
815 // driver is in use right now
816 dprintf("devfs: changed driver \"%s\" is still in use\n", driver
->name
);
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
);
845 dump_device(int argc
, char** argv
)
847 if (argc
< 2 || !strcmp(argv
[1], "--help")) {
848 kprintf("usage: %s [device]\n", argv
[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());
872 dump_driver(int argc
, char** argv
)
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
,
892 if (!strcmp(argv
[1], "--help")) {
893 kprintf("usage: %s [name]\n", argv
[0]);
897 legacy_driver
* driver
= sDriverHash
->Lookup(argv
[1]);
898 if (driver
== NULL
) {
899 kprintf("Driver named \"%s\" not found.\n", argv
[1]);
911 DirectoryIterator::DirectoryIterator(const char* path
, const char* subPath
,
918 SetTo(path
, subPath
, recursive
);
922 DirectoryIterator::~DirectoryIterator()
929 DirectoryIterator::SetTo(const char* path
, const char* subPath
, bool recursive
)
932 fRecursive
= recursive
;
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
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
)
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
);
957 pathBuffer
.UnlockBuffer();
960 AddPath(path
, subPath
);
965 DirectoryIterator::GetNext(KPath
& path
, struct stat
& stat
)
968 while (fDirectory
== NULL
) {
972 if (!fPaths
.Pop(&fBasePath
))
973 return B_ENTRY_NOT_FOUND
;
975 fDirectory
= opendir(fBasePath
->Path());
979 struct dirent
* dirent
= readdir(fDirectory
);
980 if (dirent
== NULL
) {
981 // get over to next directory on the stack
982 closedir(fDirectory
);
988 if (!strcmp(dirent
->d_name
, "..") || !strcmp(dirent
->d_name
, "."))
991 fCurrentName
= dirent
->d_name
;
993 path
.SetTo(fBasePath
->Path());
994 path
.Append(fCurrentName
);
996 if (::stat(path
.Path(), &stat
) != 0)
999 if (S_ISDIR(stat
.st_mode
) && fRecursive
) {
1000 KPath
*nextPath
= new(nothrow
) KPath(path
);
1003 if (fPaths
.Push(nextPath
) != B_OK
)
1014 DirectoryIterator::Unset()
1016 if (fDirectory
!= NULL
) {
1017 closedir(fDirectory
);
1025 while (fPaths
.Pop(&path
))
1031 DirectoryIterator::AddPath(const char* basePath
, const char* subPath
)
1033 KPath
*path
= new(nothrow
) KPath(basePath
);
1035 panic("out of memory");
1036 if (subPath
!= NULL
)
1037 path
->Append(subPath
);
1046 DirectoryWatcher::DirectoryWatcher()
1051 DirectoryWatcher::~DirectoryWatcher()
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
) {
1072 opcode
= B_ENTRY_CREATED
;
1073 } else if (sDirectoryNodeHash
.Lookup(&to
) == NULL
) {
1075 opcode
= B_ENTRY_REMOVED
;
1077 // Move within, don't do anything for now
1078 // TODO: adjust driver priority if necessary
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
)
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
)
1098 strlcpy(driverEvent
->path
, path
.Path(), sizeof(driverEvent
->path
));
1100 MutexLocker
_(sDriverEventsLock
);
1101 sDriverEvents
.Add(driverEvent
);
1102 atomic_add(&sDriverEventsPending
, 1);
1110 start_watching(const char *base
, const char *sub
)
1115 // TODO: create missing directories?
1117 if (::stat(path
.Path(), &stat
) != 0)
1120 add_node_listener(stat
.st_dev
, stat
.st_ino
, B_WATCH_DIRECTORY
,
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
));
1138 entry
->path
= strdup(path
);
1139 if (entry
->path
== NULL
) {
1144 entry
->device
= device
;
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.
1155 try_drivers(DriverEntryList
& list
)
1158 driver_entry
* entry
= list
.RemoveHead();
1162 image_id image
= load_kernel_add_on(entry
->path
);
1164 // check if it's an old-style driver
1165 if (legacy_driver_add(entry
->path
) == B_OK
) {
1167 dprintf("loaded driver %s\n", entry
->path
);
1170 unload_kernel_add_on(image
);
1182 probe_for_drivers(const char *type
)
1184 TRACE(("probe_for_drivers(type = %s)\n", type
));
1186 if (gBootDevice
< 0)
1189 DriverEntryList drivers
;
1191 // build list of potential drivers for that type
1193 DirectoryIterator
iterator(NULL
, type
, false);
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
,
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"
1213 size_t length
= strlen("drivers/dev");
1214 if (strncmp(type
, "drivers/dev", length
))
1218 path
.Append(iterator
.CurrentName());
1219 devfs_publish_directory(path
.Path() + length
+ 1);
1223 driver_entry
*entry
= new_driver_entry(path
.Path(), stat
.st_dev
,
1228 TRACE(("found potential driver: %s\n", path
.Path()));
1232 if (drivers
.IsEmpty())
1235 // ToDo: do something with the remaining drivers... :)
1236 try_drivers(drivers
);
1241 // #pragma mark - LegacyDevice
1244 LegacyDevice::LegacyDevice(legacy_driver
* driver
, const char* path
,
1245 device_hooks
* hooks
)
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
));
1256 fPath
= strdup(path
);
1262 LegacyDevice::~LegacyDevice()
1264 free(fDeviceModule
);
1270 LegacyDevice::InitCheck() const
1272 return fDeviceModule
!= NULL
&& fPath
!= NULL
? B_OK
: B_NO_MEMORY
;
1277 LegacyDevice::InitDevice()
1279 RecursiveLocker
_(sLock
);
1281 if (fInitialized
++ > 0)
1284 if (fDriver
!= NULL
&& fDriver
->devices_used
== 0
1285 && (fDriver
->image
< 0 || fDriver
->binary_updated
)) {
1286 status_t status
= reload_driver(fDriver
);
1291 if (fDriver
!= NULL
)
1292 fDriver
->devices_used
++;
1299 LegacyDevice::UninitDevice()
1301 RecursiveLocker
_(sLock
);
1303 if (fInitialized
-- > 1)
1306 if (fDriver
!= NULL
) {
1307 if (--fDriver
->devices_used
== 0 && fDriver
->devices
.IsEmpty())
1308 unload_driver(fDriver
);
1315 LegacyDevice::Removed()
1317 RecursiveLocker
_(sLock
);
1319 if (!fRemovedFromParent
&& fDriver
!= NULL
)
1320 fDriver
->devices
.Remove(this);
1327 LegacyDevice::SetHooks(device_hooks
* hooks
)
1329 // TODO: setup compatibility layer!
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
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
1349 fDeviceModule
->select
= (status_t (*)(void*, uint8
, selectsync
*))~0;
1352 fDeviceModule
->deselect
= hooks
->deselect
;
1358 LegacyDevice::Open(const char* path
, int openMode
, void** _cookie
)
1360 return Hooks()->open(path
, openMode
, _cookie
);
1365 LegacyDevice::Select(void* cookie
, uint8 event
, selectsync
* sync
)
1367 return Hooks()->select(cookie
, event
, 0, sync
);
1371 // #pragma mark - kernel private API
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.
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();
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
));
1400 struct preloaded_image
* image
;
1401 for (image
= args
->preloaded_images
; image
!= NULL
; image
= image
->next
) {
1402 if (image
->is_module
|| image
->id
< 0)
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",
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
);
1424 legacy_driver_add(const char* path
)
1426 return add_driver(path
, -1);
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
);
1438 status_t status
= device
->InitCheck();
1440 status
= devfs_publish_device(path
, device
);
1450 legacy_driver_rescan(const char* driverName
)
1452 RecursiveLocker
locker(sLock
);
1454 legacy_driver
* driver
= sDriverHash
->Lookup(driverName
);
1456 return B_ENTRY_NOT_FOUND
;
1458 // Republish the driver's entries
1459 return republish_driver(driver
);
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
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
)
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");
1500 path
.UnlockBuffer();
1506 return probe_for_drivers(devicePath
);
1511 legacy_driver_init(void)
1513 sDriverHash
= new DriverTable();
1514 if (sDriverHash
== NULL
|| sDriverHash
->Init(DRIVER_HASH_SIZE
) != B_OK
)
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");