/* * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ #include "device_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bus.h" #define TRACE(a) dprintf a #define DEVICE_MANAGER_ROOT_NAME "system/devices_root/driver_v1" extern struct device_module_info gDeviceModuleInfo; extern struct driver_module_info gDriverModuleInfo; extern struct device_module_info gGenericVideoDeviceModuleInfo; extern struct driver_module_info gGenericVideoDriverModuleInfo; extern struct device_module_info gSpecificVideoDeviceModuleInfo; extern struct driver_module_info gSpecificVideoDriverModuleInfo; extern struct driver_module_info gBusModuleInfo; extern struct driver_module_info gBusDriverModuleInfo; extern "C" status_t _add_builtin_module(module_info *info); extern "C" status_t _get_builtin_dependencies(void); extern bool gDebugOutputEnabled; // from libkernelland_emu.so struct device_attr_private : device_attr, DoublyLinkedListLinkImpl { device_attr_private(); device_attr_private(const device_attr& attr); ~device_attr_private(); status_t InitCheck(); status_t CopyFrom(const device_attr& attr); static int Compare(const device_attr* attrA, const device_attr *attrB); private: void _Unset(); }; typedef DoublyLinkedList AttributeList; // I/O resource typedef struct io_resource_info { struct io_resource_info *prev, *next; device_node* owner; // associated node; NULL for temporary allocation io_resource resource; // info about actual resource } io_resource_info; class Device : public DoublyLinkedListLinkImpl { public: Device(device_node* node, const char* path, const char* moduleName); ~Device(); status_t InitCheck() const; device_node* Node() const { return fNode; } const char* Path() const { return fPath; } const char* ModuleName() const { return fModuleName; } status_t InitDevice(); void UninitDevice(); device_module_info* DeviceModule() const { return fDeviceModule; } void* DeviceData() const { return fDeviceData; } private: device_node* fNode; const char* fPath; const char* fModuleName; int32 fInitialized; device_module_info* fDeviceModule; void* fDeviceData; }; typedef DoublyLinkedList DeviceList; typedef DoublyLinkedList NodeList; struct device_node : DoublyLinkedListLinkImpl { device_node(const char* moduleName, const device_attr* attrs, const io_resource* resources); ~device_node(); status_t InitCheck() const; const char* ModuleName() const { return fModuleName; } device_node* Parent() const { return fParent; } AttributeList& Attributes() { return fAttributes; } const AttributeList& Attributes() const { return fAttributes; } status_t InitDriver(); bool UninitDriver(); void UninitUnusedDriver(); // The following two are only valid, if the node's driver is // initialized driver_module_info* DriverModule() const { return fDriver; } void* DriverData() const { return fDriverData; } void AddChild(device_node *node); void RemoveChild(device_node *node); const NodeList& Children() const { return fChildren; } void DeviceRemoved(); status_t Register(device_node* parent); status_t Probe(const char* devicePath, uint32 updateCycle); bool IsRegistered() const { return fRegistered; } bool IsInitialized() const { return fInitialized > 0; } uint32 Flags() const { return fFlags; } void Acquire(); bool Release(); const DeviceList& Devices() const { return fDevices; } void AddDevice(Device* device); void RemoveDevice(Device* device); int CompareTo(const device_attr* attributes) const; device_node* FindChild(const device_attr* attributes) const; device_node* FindChild(const char* moduleName) const; void Dump(int32 level = 0); private: status_t _RegisterFixed(uint32& registered); bool _AlwaysRegisterDynamic(); status_t _AddPath(Stack& stack, const char* path, const char* subPath = NULL); status_t _GetNextDriverPath(void*& cookie, KPath& _path); status_t _GetNextDriver(void* list, driver_module_info*& driver); status_t _FindBestDriver(const char* path, driver_module_info*& bestDriver, float& bestSupport, device_node* previous = NULL); status_t _RegisterPath(const char* path); status_t _RegisterDynamic(device_node* previous = NULL); status_t _RemoveChildren(); device_node* _FindCurrentChild(); void _ReleaseWaiting(); device_node* fParent; NodeList fChildren; int32 fRefCount; int32 fInitialized; bool fRegistered; uint32 fFlags; float fSupportsParent; uint32 fLastUpdateCycle; const char* fModuleName; driver_module_info* fDriver; void* fDriverData; DeviceList fDevices; AttributeList fAttributes; }; // flags in addition to those specified by B_DEVICE_FLAGS enum node_flags { NODE_FLAG_REGISTER_INITIALIZED = 0x00010000, NODE_FLAG_DEVICE_REMOVED = 0x00020000, NODE_FLAG_OBSOLETE_DRIVER = 0x00040000, NODE_FLAG_WAITING_FOR_DRIVER = 0x00080000, NODE_FLAG_PUBLIC_MASK = 0x0000ffff }; device_manager_info *gDeviceManager; static device_node *sRootNode; static recursive_lock sLock; static uint32 sDriverUpdateCycle = 1; // this is a *very* basic devfs emulation // #pragma mark - static device_attr_private* find_attr(const device_node* node, const char* name, bool recursive, type_code type) { do { AttributeList::ConstIterator iterator = node->Attributes().GetIterator(); while (iterator.HasNext()) { device_attr_private* attr = iterator.Next(); if (type != B_ANY_TYPE && attr->type != type) continue; if (!strcmp(attr->name, name)) return attr; } node = node->Parent(); } while (node != NULL && recursive); return NULL; } static void put_level(int32 level) { while (level-- > 0) dprintf(" "); } static void dump_attribute(device_attr* attr, int32 level) { if (attr == NULL) return; put_level(level + 2); dprintf("\"%s\" : ", attr->name); switch (attr->type) { case B_STRING_TYPE: dprintf("string : \"%s\"", attr->value.string); break; case B_INT8_TYPE: case B_UINT8_TYPE: dprintf("uint8 : %u (%#x)", attr->value.ui8, attr->value.ui8); break; case B_INT16_TYPE: case B_UINT16_TYPE: dprintf("uint16 : %u (%#x)", attr->value.ui16, attr->value.ui16); break; case B_INT32_TYPE: case B_UINT32_TYPE: dprintf("uint32 : %lu (%#lx)", attr->value.ui32, attr->value.ui32); break; case B_INT64_TYPE: case B_UINT64_TYPE: dprintf("uint64 : %Lu (%#Lx)", attr->value.ui64, attr->value.ui64); break; default: dprintf("raw data"); } dprintf("\n"); } static void uninit_unused() { puts("uninit unused"); RecursiveLocker _(sLock); sRootNode->UninitUnusedDriver(); } static status_t probe_path(const char* path) { printf("probe path \"%s\"\n", path); RecursiveLocker _(sLock); return sRootNode->Probe(path, sDriverUpdateCycle); } static void close_path(void* cookie) { Device* device = (Device*)cookie; if (device == NULL) return; printf("close path \"%s\" (node %p)\n", device->Path(), device->Node()); device->UninitDevice(); } static Device* get_device(device_node* node, const char* path) { DeviceList::ConstIterator iterator = node->Devices().GetIterator(); while (iterator.HasNext()) { Device* device = iterator.Next(); if (!strcmp(device->Path(), path)) { status_t status = device->InitDevice(); if (status != B_OK) { printf("opening path \"%s\" failed: %s\n", path, strerror(status)); return NULL; } printf("open path \"%s\" (node %p)\n", device->Path(), device->Node()); return device; } } // search in children NodeList::ConstIterator nodeIterator = node->Children().GetIterator(); while (nodeIterator.HasNext()) { device_node* child = nodeIterator.Next(); Device* device = get_device(child, path); if (device != NULL) return device; } return NULL; } static void* open_path(const char* path) { return get_device(sRootNode, path); } // #pragma mark - Device Manager module API static status_t rescan_node(device_node* node) { return B_ERROR; } static status_t register_node(device_node* parent, const char* moduleName, const device_attr* attrs, const io_resource* ioResources, device_node** _node) { if ((parent == NULL && sRootNode != NULL) || moduleName == NULL) return B_BAD_VALUE; if (parent != NULL && parent->FindChild(attrs) != NULL) { // A node like this one already exists for this parent return B_NAME_IN_USE; } // TODO: handle I/O resources! device_node *newNode = new(std::nothrow) device_node(moduleName, attrs, ioResources); if (newNode == NULL) return B_NO_MEMORY; TRACE(("%p: register node \"%s\", parent %p\n", newNode, moduleName, parent)); RecursiveLocker _(sLock); status_t status = newNode->InitCheck(); if (status != B_OK) goto err1; #if 0 // The following is done to reduce the stack usage of deeply nested // child device nodes. // There is no other need to delay the complete registration process // the way done here. This approach is also slightly different as // the registration might fail later than it used in case of errors. if (!parent->IsRegistered()) { // The parent has not been registered completely yet - child // registration is deferred to the parent registration return B_OK; } #endif status = newNode->Register(parent); if (status < B_OK) { parent->RemoveChild(newNode); goto err1; } if (_node) *_node = newNode; return B_OK; err1: newNode->Release(); return status; } /*! Unregisters the device \a node. If the node is currently in use, this function will return B_BUSY to indicate that the node hasn't been removed yet - it will still remove the node as soon as possible. */ static status_t unregister_node(device_node* node) { TRACE(("unregister_node(node %p)\n", node)); RecursiveLocker _(sLock); bool initialized = node->IsInitialized(); node->DeviceRemoved(); return initialized ? B_BUSY : B_OK; } static status_t get_driver(device_node* node, driver_module_info** _module, void** _data) { if (node->DriverModule() == NULL) return B_NO_INIT; if (_module != NULL) *_module = node->DriverModule(); if (_data != NULL) *_data = node->DriverData(); return B_OK; } static device_node* get_root_node(void) { if (sRootNode != NULL) sRootNode->Acquire(); return sRootNode; } static status_t get_next_child_node(device_node* parent, const device_attr* attributes, device_node** _node) { RecursiveLocker _(sLock); NodeList::ConstIterator iterator = parent->Children().GetIterator(); device_node* last = *_node; // skip those we already traversed while (iterator.HasNext() && last != NULL) { device_node* node = iterator.Next(); if (node != last) continue; } // find the next one that fits while (iterator.HasNext()) { device_node* node = iterator.Next(); if (!node->IsRegistered()) continue; if (!node->CompareTo(attributes)) { if (last != NULL) last->Release(); node->Acquire(); *_node = node; return B_OK; } } if (last != NULL) last->Release(); return B_ENTRY_NOT_FOUND; } static device_node* get_parent_node(device_node* node) { if (node == NULL) return NULL; RecursiveLocker _(sLock); device_node* parent = node->Parent(); parent->Acquire(); return parent; } static void put_node(device_node* node) { RecursiveLocker _(sLock); node->Release(); } static status_t publish_device(device_node *node, const char *path, const char *moduleName) { if (path == NULL || !path[0] || moduleName == NULL || !moduleName[0]) return B_BAD_VALUE; RecursiveLocker _(sLock); dprintf("publish device: node %p, path %s, module %s\n", node, path, moduleName); Device* device = new(std::nothrow) Device(node, path, moduleName); if (device == NULL) return B_NO_MEMORY; if (device->InitCheck() != B_OK) { delete device; return B_NO_MEMORY; } node->AddDevice(device); return B_OK; } static status_t unpublish_device(device_node *node, const char *path) { if (path == NULL) return B_BAD_VALUE; RecursiveLocker _(sLock); DeviceList::ConstIterator iterator = node->Devices().GetIterator(); while (iterator.HasNext()) { Device* device = iterator.Next(); if (!strcmp(device->Path(), path)) { node->RemoveDevice(device); delete device; return B_OK; } } return B_ENTRY_NOT_FOUND; } static status_t get_attr_uint8(const device_node* node, const char* name, uint8* _value, bool recursive) { if (node == NULL || name == NULL || _value == NULL) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_UINT8_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; *_value = attr->value.ui8; return B_OK; } static status_t get_attr_uint16(const device_node* node, const char* name, uint16* _value, bool recursive) { if (node == NULL || name == NULL || _value == NULL) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_UINT16_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; *_value = attr->value.ui16; return B_OK; } static status_t get_attr_uint32(const device_node* node, const char* name, uint32* _value, bool recursive) { if (node == NULL || name == NULL || _value == NULL) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_UINT32_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; *_value = attr->value.ui32; return B_OK; } static status_t get_attr_uint64(const device_node* node, const char* name, uint64* _value, bool recursive) { if (node == NULL || name == NULL || _value == NULL) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_UINT64_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; *_value = attr->value.ui64; return B_OK; } static status_t get_attr_string(const device_node* node, const char* name, const char** _value, bool recursive) { if (node == NULL || name == NULL || _value == NULL) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_STRING_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; *_value = attr->value.string; return B_OK; } static status_t get_attr_raw(const device_node* node, const char* name, const void** _data, size_t* _length, bool recursive) { if (node == NULL || name == NULL || (_data == NULL && _length == NULL)) return B_BAD_VALUE; device_attr_private* attr = find_attr(node, name, recursive, B_RAW_TYPE); if (attr == NULL) return B_NAME_NOT_FOUND; if (_data != NULL) *_data = attr->value.raw.data; if (_length != NULL) *_length = attr->value.raw.length; return B_OK; } static status_t get_next_attr(device_node* node, device_attr** _attr) { if (node == NULL) return B_BAD_VALUE; device_attr_private* next; device_attr_private* attr = *(device_attr_private**)_attr; if (attr != NULL) { // next attribute next = attr->GetDoublyLinkedListLink()->next; } else { // first attribute next = node->Attributes().First(); } *_attr = next; return next ? B_OK : B_ENTRY_NOT_FOUND; } static struct device_manager_info sDeviceManagerModule = { { B_DEVICE_MANAGER_MODULE_NAME, 0, NULL }, // device nodes rescan_node, register_node, unregister_node, get_driver, get_root_node, get_next_child_node, get_parent_node, put_node, // devices publish_device, unpublish_device, // attributes get_attr_uint8, get_attr_uint16, get_attr_uint32, get_attr_uint64, get_attr_string, get_attr_raw, get_next_attr, }; // #pragma mark - device_attr device_attr_private::device_attr_private() { name = NULL; type = 0; value.raw.data = NULL; value.raw.length = 0; } device_attr_private::device_attr_private(const device_attr& attr) { CopyFrom(attr); } device_attr_private::~device_attr_private() { _Unset(); } status_t device_attr_private::InitCheck() { return name != NULL ? B_OK : B_NO_INIT; } status_t device_attr_private::CopyFrom(const device_attr& attr) { name = strdup(attr.name); if (name == NULL) return B_NO_MEMORY; type = attr.type; switch (type) { case B_UINT8_TYPE: case B_UINT16_TYPE: case B_UINT32_TYPE: case B_UINT64_TYPE: value.ui64 = attr.value.ui64; break; case B_STRING_TYPE: if (attr.value.string != NULL) { value.string = strdup(attr.value.string); if (value.string == NULL) { _Unset(); return B_NO_MEMORY; } } else value.string = NULL; break; case B_RAW_TYPE: value.raw.data = malloc(attr.value.raw.length); if (value.raw.data == NULL) { _Unset(); return B_NO_MEMORY; } value.raw.length = attr.value.raw.length; memcpy((void*)value.raw.data, attr.value.raw.data, attr.value.raw.length); break; default: return B_BAD_VALUE; } return B_OK; } void device_attr_private::_Unset() { if (type == B_STRING_TYPE) free((char*)value.string); else if (type == B_RAW_TYPE) free((void*)value.raw.data); free((char*)name); name = NULL; value.raw.data = NULL; value.raw.length = 0; } /*static*/ int device_attr_private::Compare(const device_attr* attrA, const device_attr *attrB) { if (attrA->type != attrB->type) return -1; switch (attrA->type) { case B_UINT8_TYPE: return (int)attrA->value.ui8 - (int)attrB->value.ui8; case B_UINT16_TYPE: return (int)attrA->value.ui16 - (int)attrB->value.ui16; case B_UINT32_TYPE: if (attrA->value.ui32 > attrB->value.ui32) return 1; if (attrA->value.ui32 < attrB->value.ui32) return -1; return 0; case B_UINT64_TYPE: if (attrA->value.ui64 > attrB->value.ui64) return 1; if (attrA->value.ui64 < attrB->value.ui64) return -1; return 0; case B_STRING_TYPE: return strcmp(attrA->value.string, attrB->value.string); case B_RAW_TYPE: if (attrA->value.raw.length != attrB->value.raw.length) return -1; return memcmp(attrA->value.raw.data, attrB->value.raw.data, attrA->value.raw.length); } return -1; } // #pragma mark - Device Device::Device(device_node* node, const char* path, const char* moduleName) : fNode(node), fInitialized(0), fDeviceModule(NULL), fDeviceData(NULL) { fPath = strdup(path); fModuleName = strdup(moduleName); } Device::~Device() { free((char*)fPath); free((char*)fModuleName); } status_t Device::InitCheck() const { return fPath != NULL && fModuleName != NULL ? B_OK : B_NO_MEMORY; } status_t Device::InitDevice() { if ((fNode->Flags() & NODE_FLAG_DEVICE_REMOVED) != 0) { // TODO: maybe the device should be unlinked in devfs, too return ENODEV; } if ((fNode->Flags() & NODE_FLAG_WAITING_FOR_DRIVER) != 0) return B_BUSY; if (fInitialized++ > 0) { fNode->InitDriver(); // acquire another reference to our parent as well return B_OK; } status_t status = get_module(ModuleName(), (module_info**)&fDeviceModule); if (status == B_OK) { // our parent always has to be initialized status = fNode->InitDriver(); } if (status < B_OK) { fInitialized--; return status; } if (fDeviceModule->init_device != NULL) status = fDeviceModule->init_device(fNode->DriverData(), &fDeviceData); if (status < B_OK) { fNode->UninitDriver(); fInitialized--; put_module(ModuleName()); fDeviceModule = NULL; fDeviceData = NULL; } return status; } void Device::UninitDevice() { if (fInitialized-- > 1) { fNode->UninitDriver(); return; } TRACE(("uninit driver for node %p\n", this)); if (fDeviceModule->uninit_device != NULL) fDeviceModule->uninit_device(fDeviceData); fDeviceModule = NULL; fDeviceData = NULL; put_module(ModuleName()); fNode->UninitDriver(); } // #pragma mark - device_node /*! Allocate device node info structure; initially, ref_count is one to make sure node won't get destroyed by mistake */ device_node::device_node(const char* moduleName, const device_attr* attrs, const io_resource* resources) { fModuleName = strdup(moduleName); if (fModuleName == NULL) return; fParent = NULL; fRefCount = 1; fInitialized = 0; fRegistered = false; fFlags = 0; fSupportsParent = 0.0; fLastUpdateCycle = 0; fDriver = NULL; fDriverData = NULL; // copy attributes while (attrs != NULL && attrs->name != NULL) { device_attr_private* attr = new(std::nothrow) device_attr_private(*attrs); if (attr == NULL) break; fAttributes.Add(attr); attrs++; } get_attr_uint32(this, B_DEVICE_FLAGS, &fFlags, false); fFlags &= NODE_FLAG_PUBLIC_MASK; } device_node::~device_node() { TRACE(("delete node %p\n", this)); ASSERT(DriverModule() == NULL); if (Parent() != NULL) { if ((fFlags & NODE_FLAG_OBSOLETE_DRIVER) != 0) { // This driver has been obsoleted; another driver has been waiting // for us - make it available Parent()->_ReleaseWaiting(); } Parent()->RemoveChild(this); } // Delete children NodeList::Iterator nodeIterator = fChildren.GetIterator(); while (nodeIterator.HasNext()) { device_node* child = nodeIterator.Next(); nodeIterator.Remove(); delete child; } // Delete devices DeviceList::Iterator deviceIterator = fDevices.GetIterator(); while (deviceIterator.HasNext()) { Device* device = deviceIterator.Next(); deviceIterator.Remove(); // TODO: unpublish! delete device; } // Delete attributes AttributeList::Iterator attrIterator = fAttributes.GetIterator(); while (attrIterator.HasNext()) { device_attr_private* attr = attrIterator.Next(); attrIterator.Remove(); delete attr; } free((char*)fModuleName); } status_t device_node::InitCheck() const { return fModuleName != NULL ? B_OK : B_NO_MEMORY; } status_t device_node::InitDriver() { if (fInitialized++ > 0) { if (Parent() != NULL) { Parent()->InitDriver(); // acquire another reference to our parent as well } Acquire(); return B_OK; } status_t status = get_module(ModuleName(), (module_info**)&fDriver); if (status == B_OK && Parent() != NULL) { // our parent always has to be initialized status = Parent()->InitDriver(); } if (status < B_OK) { fInitialized--; return status; } if (fDriver->init_driver != NULL) status = fDriver->init_driver(this, &fDriverData); if (status < B_OK) { if (Parent() != NULL) Parent()->UninitDriver(); fInitialized--; put_module(ModuleName()); fDriver = NULL; fDriverData = NULL; return status; } Acquire(); return B_OK; } bool device_node::UninitDriver() { if (fInitialized-- > 1) { if (Parent() != NULL) Parent()->UninitDriver(); Release(); return false; } TRACE(("uninit driver for node %p\n", this)); if (fDriver->uninit_driver != NULL) fDriver->uninit_driver(fDriverData); fDriver = NULL; fDriverData = NULL; put_module(ModuleName()); if (Parent() != NULL) Parent()->UninitDriver(); Release(); return true; } void device_node::AddChild(device_node* node) { // we must not be destroyed as long as we have children Acquire(); node->fParent = this; fChildren.Add(node); } void device_node::RemoveChild(device_node* node) { node->fParent = NULL; fChildren.Remove(node); Release(); } /*! Registers this node, and all of its children that have to be registered. Also initializes the driver and keeps it that way on return in case it returns successfully. */ status_t device_node::Register(device_node* parent) { // make it public if (parent != NULL) parent->AddChild(this); else sRootNode = this; status_t status = InitDriver(); if (status != B_OK) return status; if ((fFlags & B_KEEP_DRIVER_LOADED) != 0) { // We keep this driver loaded by having it always initialized InitDriver(); } fFlags |= NODE_FLAG_REGISTER_INITIALIZED; // We don't uninitialize the driver - this is done by the caller // in order to save reinitializing during driver loading. uint32 registered; status = _RegisterFixed(registered); if (status != B_OK) { UninitUnusedDriver(); return status; } if (registered > 0) { fRegistered = true; return B_OK; } // Register the children the driver wants if (DriverModule()->register_child_devices != NULL) { status = DriverModule()->register_child_devices(this); if (status != B_OK) { UninitUnusedDriver(); return status; } if (!fChildren.IsEmpty()) { fRegistered = true; return B_OK; } } // Register all possible child device nodes status = _RegisterDynamic(); if (status == B_OK) fRegistered = true; else UninitUnusedDriver(); return status; } /*! Registers any children that are identified via the B_DRIVER_FIXED_CHILD attribute. If any of these children cannot be registered, this call will fail (we don't remove children we already registered up to this point in this case). */ status_t device_node::_RegisterFixed(uint32& registered) { AttributeList::Iterator iterator = fAttributes.GetIterator(); registered = 0; while (iterator.HasNext()) { device_attr_private* attr = iterator.Next(); if (strcmp(attr->name, B_DEVICE_FIXED_CHILD)) continue; driver_module_info* driver; status_t status = get_module(attr->value.string, (module_info**)&driver); if (status != B_OK) return status; if (driver->register_device != NULL) { status = driver->register_device(this); if (status == B_OK) registered++; } put_module(attr->value.string); if (status != B_OK) return status; } return B_OK; } status_t device_node::_AddPath(Stack& stack, const char* basePath, const char* subPath) { KPath* path = new(std::nothrow) KPath; if (path == NULL) return B_NO_MEMORY; status_t status = path->SetTo(basePath); if (status == B_OK && subPath != NULL && subPath[0]) status = path->Append(subPath); if (status == B_OK) status = stack.Push(path); if (status != B_OK) delete path; return status; } status_t device_node::_GetNextDriverPath(void*& cookie, KPath& _path) { Stack* stack = NULL; if (cookie == NULL) { // find all paths and add them stack = new(std::nothrow) Stack(); if (stack == NULL) return B_NO_MEMORY; StackDeleter stackDeleter(stack); uint16 type = 0; uint16 subType = 0; uint16 interface = 0; get_attr_uint16(this, B_DEVICE_TYPE, &type, false); get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false); get_attr_uint16(this, B_DEVICE_INTERFACE, &interface, false); // TODO: maybe make this extendible via settings file? switch (type) { case PCI_mass_storage: switch (subType) { case PCI_scsi: _AddPath(*stack, "busses", "scsi"); break; case PCI_ide: _AddPath(*stack, "busses", "ide"); break; case PCI_sata: _AddPath(*stack, "busses", "sata"); break; default: _AddPath(*stack, "busses", "disk"); break; } break; case PCI_serial_bus: switch (subType) { case PCI_firewire: _AddPath(*stack, "busses", "firewire"); break; case PCI_usb: _AddPath(*stack, "busses", "usb"); break; default: _AddPath(*stack, "busses"); break; } break; case PCI_network: _AddPath(*stack, "drivers", "net"); break; case PCI_display: _AddPath(*stack, "drivers", "graphics"); break; case PCI_multimedia: switch (subType) { case PCI_audio: case PCI_hd_audio: _AddPath(*stack, "drivers", "audio"); break; case PCI_video: _AddPath(*stack, "drivers", "video"); break; default: _AddPath(*stack, "drivers"); break; } break; default: if (sRootNode == this) { _AddPath(*stack, "busses/pci"); _AddPath(*stack, "bus_managers"); } else _AddPath(*stack, "drivers"); break; } stackDeleter.Detach(); cookie = (void*)stack; } else stack = static_cast*>(cookie); KPath* path; if (stack->Pop(&path)) { _path.Adopt(*path); delete path; return B_OK; } delete stack; return B_ENTRY_NOT_FOUND; } status_t device_node::_GetNextDriver(void* list, driver_module_info*& driver) { while (true) { char name[B_FILE_NAME_LENGTH]; size_t nameLength = sizeof(name); status_t status = read_next_module_name(list, name, &nameLength); if (status != B_OK) return status; if (!strcmp(fModuleName, name)) continue; if (get_module(name, (module_info**)&driver) != B_OK) continue; if (driver->supports_device == NULL || driver->register_device == NULL) { put_module(name); continue; } return B_OK; } } status_t device_node::_FindBestDriver(const char* path, driver_module_info*& bestDriver, float& bestSupport, device_node* previous) { if (bestDriver == NULL) bestSupport = previous != NULL ? previous->fSupportsParent : 0.0f; void* list = open_module_list_etc(path, "driver_v1"); driver_module_info* driver; while (_GetNextDriver(list, driver) == B_OK) { if (previous != NULL && driver == previous->DriverModule()) { put_module(driver->info.name); continue; } float support = driver->supports_device(this); if (support > bestSupport) { if (bestDriver != NULL) put_module(bestDriver->info.name); bestDriver = driver; bestSupport = support; continue; // keep reference to best module around } put_module(driver->info.name); } close_module_list(list); return bestDriver != NULL ? B_OK : B_ENTRY_NOT_FOUND; } status_t device_node::_RegisterPath(const char* path) { void* list = open_module_list_etc(path, "driver_v1"); driver_module_info* driver; uint32 count = 0; while (_GetNextDriver(list, driver) == B_OK) { float support = driver->supports_device(this); if (support > 0.0) { TRACE((" register module \"%s\", support %f\n", driver->info.name, support)); if (driver->register_device(this) == B_OK) count++; } put_module(driver->info.name); } close_module_list(list); return count > 0 ? B_OK : B_ENTRY_NOT_FOUND; } bool device_node::_AlwaysRegisterDynamic() { uint16 type = 0; uint16 subType = 0; get_attr_uint16(this, B_DEVICE_TYPE, &type, false); get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false); return type == PCI_serial_bus || type == PCI_bridge; // TODO: we may want to be a bit more specific in the future } status_t device_node::_RegisterDynamic(device_node* previous) { // If this is our initial registration, we honour the B_FIND_CHILD_ON_DEMAND // requirements if (!fRegistered && (fFlags & B_FIND_CHILD_ON_DEMAND) != 0 && !_AlwaysRegisterDynamic()) return B_OK; KPath path; if ((fFlags & B_FIND_MULTIPLE_CHILDREN) == 0) { // find the one driver driver_module_info* bestDriver = NULL; float bestSupport = 0.0; void* cookie = NULL; while (_GetNextDriverPath(cookie, path) == B_OK) { _FindBestDriver(path.Path(), bestDriver, bestSupport, previous); } if (bestDriver != NULL) { TRACE((" register best module \"%s\", support %f\n", bestDriver->info.name, bestSupport)); if (bestDriver->register_device(this) == B_OK) { // There can only be one node of this driver // (usually only one at all, but there might be a new driver // "waiting" for its turn) device_node* child = FindChild(bestDriver->info.name); if (child != NULL) { child->fSupportsParent = bestSupport; if (previous != NULL) { previous->fFlags |= NODE_FLAG_OBSOLETE_DRIVER; previous->Release(); child->fFlags |= NODE_FLAG_WAITING_FOR_DRIVER; } } // TODO: if this fails, we could try the second best driver, // and so on... } put_module(bestDriver->info.name); } } else { // register all drivers that match void* cookie = NULL; while (_GetNextDriverPath(cookie, path) == B_OK) { _RegisterPath(path.Path()); } } return B_OK; } void device_node::_ReleaseWaiting() { NodeList::Iterator iterator = fChildren.GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); child->fFlags &= ~NODE_FLAG_WAITING_FOR_DRIVER; } } status_t device_node::_RemoveChildren() { NodeList::Iterator iterator = fChildren.GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); child->Release(); } return fChildren.IsEmpty() ? B_OK : B_BUSY; } device_node* device_node::_FindCurrentChild() { NodeList::Iterator iterator = fChildren.GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); if ((child->Flags() & NODE_FLAG_WAITING_FOR_DRIVER) == 0) return child; } return NULL; } status_t device_node::Probe(const char* devicePath, uint32 updateCycle) { if ((fFlags & NODE_FLAG_DEVICE_REMOVED) != 0 || updateCycle == fLastUpdateCycle) return B_OK; status_t status = InitDriver(); if (status < B_OK) return status; MethodDeleter uninit(this, &device_node::UninitDriver); uint16 type = 0; uint16 subType = 0; if (get_attr_uint16(this, B_DEVICE_TYPE, &type, false) == B_OK && get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false) == B_OK) { // Check if this node matches the device path // TODO: maybe make this extendible via settings file? bool matches = false; if (!strcmp(devicePath, "disk")) { matches = type == PCI_mass_storage; } else if (!strcmp(devicePath, "audio")) { matches = type == PCI_multimedia && (subType == PCI_audio || subType == PCI_hd_audio); } else if (!strcmp(devicePath, "net")) { matches = type == PCI_network; } else if (!strcmp(devicePath, "graphics")) { matches = type == PCI_display; } else if (!strcmp(devicePath, "video")) { matches = type == PCI_multimedia && subType == PCI_video; } if (matches) { device_node* previous = NULL; fLastUpdateCycle = updateCycle; // This node will be probed in this update cycle if (!fChildren.IsEmpty() && (fFlags & B_FIND_MULTIPLE_CHILDREN) == 0) { // We already have a driver that claims this node; remove all // (unused) nodes, and evaluate it again _RemoveChildren(); previous = _FindCurrentChild(); if (previous != NULL) { // This driver is still active - give it back the reference // that was stolen by _RemoveChildren() - _RegisterDynamic() // will release it, if it really isn't needed anymore previous->Acquire(); } } return _RegisterDynamic(previous); } return B_OK; } NodeList::Iterator iterator = fChildren.GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); status = child->Probe(devicePath, updateCycle); if (status != B_OK) return status; } return B_OK; } /*! Uninitializes all temporary references to the driver. The registration process keeps the driver initialized to optimize the startup procedure; this function gives this reference away again. */ void device_node::UninitUnusedDriver() { // First, we need to go to the leaf, and go back from there NodeList::Iterator iterator = fChildren.GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); child->UninitUnusedDriver(); } if (!IsInitialized() || (fFlags & NODE_FLAG_REGISTER_INITIALIZED) == 0) return; fFlags &= ~NODE_FLAG_REGISTER_INITIALIZED; UninitDriver(); } /*! Calls device_removed() on this node and all of its children - starting with the deepest and last child. It will also remove the one reference that every node gets on its creation. */ void device_node::DeviceRemoved() { // notify children NodeList::ConstIterator iterator = Children().GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); child->DeviceRemoved(); } // notify devices DeviceList::ConstIterator deviceIterator = Devices().GetIterator(); while (deviceIterator.HasNext()) { Device* device = deviceIterator.Next(); if (device->DeviceModule() != NULL && device->DeviceModule()->device_removed != NULL) device->DeviceModule()->device_removed(device->DeviceData()); } fFlags |= NODE_FLAG_DEVICE_REMOVED; if (IsInitialized() && DriverModule()->device_removed != NULL) DriverModule()->device_removed(this); if ((fFlags & B_KEEP_DRIVER_LOADED) != 0) { // There is no point in keeping this driver loaded when its device // is gone UninitDriver(); } UninitUnusedDriver(); Release(); } void device_node::Acquire() { atomic_add(&fRefCount, 1); } bool device_node::Release() { if (atomic_add(&fRefCount, -1) > 1) return false; delete this; return true; } void device_node::AddDevice(Device* device) { fDevices.Add(device); } void device_node::RemoveDevice(Device* device) { fDevices.Remove(device); } int device_node::CompareTo(const device_attr* attributes) const { if (attributes == NULL) return -1; for (; attributes->name != NULL; attributes++) { // find corresponding attribute AttributeList::ConstIterator iterator = Attributes().GetIterator(); device_attr_private* attr = NULL; while (iterator.HasNext()) { attr = iterator.Next(); if (!strcmp(attr->name, attributes->name)) break; } if (!iterator.HasNext()) return -1; int compare = device_attr_private::Compare(attr, attributes); if (compare != 0) return compare; } return 0; } device_node* device_node::FindChild(const device_attr* attributes) const { if (attributes == NULL) return NULL; NodeList::ConstIterator iterator = Children().GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); // ignore nodes that are pending to be removed if ((child->Flags() & NODE_FLAG_DEVICE_REMOVED) == 0 && !child->CompareTo(attributes)) return child; } return NULL; } device_node* device_node::FindChild(const char* moduleName) const { if (moduleName == NULL) return NULL; NodeList::ConstIterator iterator = Children().GetIterator(); while (iterator.HasNext()) { device_node* child = iterator.Next(); if (!strcmp(child->ModuleName(), moduleName)) return child; } return NULL; } void device_node::Dump(int32 level = 0) { put_level(level); dprintf("(%ld) @%p \"%s\" (ref %ld, init %ld)\n", level, this, ModuleName(), fRefCount, fInitialized); AttributeList::Iterator attribute = Attributes().GetIterator(); while (attribute.HasNext()) { dump_attribute(attribute.Next(), level); } NodeList::ConstIterator iterator = Children().GetIterator(); while (iterator.HasNext()) { iterator.Next()->Dump(level + 1); } } // #pragma mark - root node static void init_root_node(void) { device_attr attrs[] = { {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Devices Root"}}, {B_DEVICE_BUS, B_STRING_TYPE, {string: "root"}}, {B_DEVICE_FLAGS, B_UINT32_TYPE, {ui32: B_FIND_MULTIPLE_CHILDREN | B_KEEP_DRIVER_LOADED }}, {NULL} }; if (register_node(NULL, DEVICE_MANAGER_ROOT_NAME, attrs, NULL, NULL) != B_OK) { dprintf("Cannot register Devices Root Node\n"); } } static driver_module_info sDeviceRootModule = { { DEVICE_MANAGER_ROOT_NAME, 0, NULL, }, NULL }; // #pragma mark - int main(int argc, char** argv) { _add_builtin_module((module_info*)&sDeviceManagerModule); _add_builtin_module((module_info*)&sDeviceRootModule); // bus _add_builtin_module((module_info*)&gBusModuleInfo); _add_builtin_module((module_info*)&gBusDriverModuleInfo); // sample driver _add_builtin_module((module_info*)&gDriverModuleInfo); _add_builtin_module((module_info*)&gDeviceModuleInfo); // generic video driver _add_builtin_module((module_info*)&gGenericVideoDriverModuleInfo); _add_builtin_module((module_info*)&gGenericVideoDeviceModuleInfo); gDeviceManager = &sDeviceManagerModule; status_t status = _get_builtin_dependencies(); if (status < B_OK) { fprintf(stderr, "device_manager: Could not initialize modules: %s\n", strerror(status)); return 1; } recursive_lock_init(&sLock, "device manager"); init_root_node(); sRootNode->Dump(); probe_path("net"); probe_path("graphics"); void* netHandle = open_path("net/sample/0"); uninit_unused(); puts("remove net driver"); device_node* busNode = sRootNode->FindChild(BUS_MODULE_NAME); bus_trigger_device_removed(busNode); close_path(netHandle); // the net nodes must be removed with this call void* graphicsHandle = open_path("graphics/generic/0"); // add specific video driver - ie. simulate installing it _add_builtin_module((module_info*)&gSpecificVideoDriverModuleInfo); _add_builtin_module((module_info*)&gSpecificVideoDeviceModuleInfo); sDriverUpdateCycle++; probe_path("graphics"); open_path("graphics/specific/0"); // this will fail close_path(graphicsHandle); // the graphics drivers must be switched with this call graphicsHandle = open_path("graphics/specific/0"); close_path(graphicsHandle); uninit_unused(); recursive_lock_destroy(&sLock); return 0; }