nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : OVR_DeviceImpl.h nuclear@1: Content : Partial back-end independent implementation of Device interfaces nuclear@1: Created : October 10, 2012 nuclear@1: Authors : Michael Antonov nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #ifndef OVR_DeviceImpl_h nuclear@1: #define OVR_DeviceImpl_h nuclear@1: nuclear@1: #include "OVR_Device.h" nuclear@1: #include "Kernel/OVR_Atomic.h" nuclear@1: #include "Kernel/OVR_Log.h" nuclear@1: #include "Kernel/OVR_System.h" nuclear@1: nuclear@1: #include "Kernel/OVR_Threads.h" nuclear@1: #include "OVR_ThreadCommandQueue.h" nuclear@1: #include "OVR_HIDDevice.h" nuclear@1: nuclear@1: namespace OVR { nuclear@1: nuclear@1: class DeviceManagerImpl; nuclear@1: class DeviceFactory; nuclear@1: nuclear@1: enum nuclear@1: { nuclear@1: Oculus_VendorId = 0x2833 nuclear@1: }; nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // Globally shared Lock implementation used for MessageHandlers. nuclear@1: nuclear@1: class SharedLock nuclear@1: { nuclear@1: public: nuclear@1: SharedLock() : UseCount(0) {} nuclear@1: nuclear@1: Lock* GetLockAddRef(); nuclear@1: void ReleaseLock(Lock* plock); nuclear@1: nuclear@1: private: nuclear@1: Lock* toLock() { return (Lock*)Buffer; } nuclear@1: nuclear@1: // UseCount and max alignment. nuclear@1: volatile int UseCount; nuclear@1: UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)]; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: // Wrapper for MessageHandler that includes synchronization logic. nuclear@1: // References to MessageHandlers are organized in a list to allow for them to nuclear@1: // easily removed with MessageHandler::RemoveAllHandlers. nuclear@1: class MessageHandlerRef : public ListNode nuclear@1: { nuclear@1: public: nuclear@1: MessageHandlerRef(DeviceBase* device); nuclear@1: ~MessageHandlerRef(); nuclear@1: nuclear@1: void SetHandler(MessageHandler* hander); nuclear@1: nuclear@1: // Not-thread-safe version nuclear@1: void SetHandler_NTS(MessageHandler* hander); nuclear@1: nuclear@1: void Call(const Message& msg) nuclear@1: { nuclear@1: Lock::Locker lockScope(pLock); nuclear@1: if (pHandler) nuclear@1: pHandler->OnMessage(msg); nuclear@1: } nuclear@1: nuclear@1: Lock* GetLock() const { return pLock; } nuclear@1: nuclear@1: // GetHandler() is not thread safe if used out of order across threads; nothing can be done nuclear@1: // about that. nuclear@1: MessageHandler* GetHandler() const { return pHandler; } nuclear@1: DeviceBase* GetDevice() const { return pDevice; } nuclear@1: nuclear@1: private: nuclear@1: Lock* pLock; // Cached global handler lock. nuclear@1: DeviceBase* pDevice; nuclear@1: MessageHandler* pHandler; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: // DeviceManagerLock is a synchronization lock used by DeviceManager for Devices nuclear@1: // and is allocated separately for potentially longer lifetime. nuclear@1: // nuclear@1: // DeviceManagerLock is used for all of the following: nuclear@1: // - Adding/removing devices nuclear@1: // - Reporting manager lifetime (pManager != 0) for DeviceHandles nuclear@1: // - Protecting device creation/shutdown. nuclear@1: nuclear@1: class DeviceManagerLock : public RefCountBase nuclear@1: { nuclear@1: public: nuclear@1: Lock CreateLock; nuclear@1: DeviceManagerImpl* pManager; nuclear@1: nuclear@1: DeviceManagerLock() : pManager(0) { } nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: // DeviceCreateDesc provides all of the information needed to create any device, a derived nuclear@1: // instance of this class is created by DeviceFactory during enumeration. nuclear@1: // - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0). nuclear@1: // - Referenced and kept alive by DeviceHandle. nuclear@1: nuclear@1: class DeviceCreateDesc : public ListNode, public NewOverrideBase nuclear@1: { nuclear@1: void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning. nuclear@1: public: nuclear@1: DeviceCreateDesc(DeviceFactory* factory, DeviceType type) nuclear@1: : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true) nuclear@1: { nuclear@1: pNext = pPrev = 0; nuclear@1: } nuclear@1: nuclear@1: virtual ~DeviceCreateDesc() nuclear@1: { nuclear@1: OVR_ASSERT(!pDevice); nuclear@1: if (pNext) nuclear@1: RemoveNode(); nuclear@1: } nuclear@1: nuclear@1: DeviceManagerImpl* GetManagerImpl() const { return pLock->pManager; } nuclear@1: Lock* GetLock() const { return &pLock->CreateLock; } nuclear@1: nuclear@1: // DeviceCreateDesc reference counting is tied to Devices list management, nuclear@1: // see comments for HandleCount. nuclear@1: void AddRef(); nuclear@1: void Release(); nuclear@1: nuclear@1: nuclear@1: // *** Device creation/matching Interface nuclear@1: nuclear@1: nuclear@1: // Cloning copies us to an allocated object when new device is enumerated. nuclear@1: virtual DeviceCreateDesc* Clone() const = 0; nuclear@1: // Creates a new device instance without Initializing it; the nuclear@1: // later is done my Initialize()/Shutdown() methods of the device itself. nuclear@1: virtual DeviceBase* NewDeviceInstance() = 0; nuclear@1: // Override to return device-specific info. nuclear@1: virtual bool GetDeviceInfo(DeviceInfo* info) const = 0; nuclear@1: nuclear@1: nuclear@1: enum MatchResult nuclear@1: { nuclear@1: Match_None, nuclear@1: Match_Found, nuclear@1: Match_Candidate nuclear@1: }; nuclear@1: nuclear@1: // Override to return Match_Found if descriptor matches our device. nuclear@1: // Match_Candidate can be returned, with pcandicate update, if this may be a match nuclear@1: // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called. nuclear@1: virtual MatchResult MatchDevice(const DeviceCreateDesc& other, nuclear@1: DeviceCreateDesc** pcandidate) const = 0; nuclear@1: nuclear@1: // Called for matched candidate after all potential matches are iterated. nuclear@1: // Used to update HMDevice creation arguments from Sensor. nuclear@1: // Optional return param 'newDeviceFlag' will be set to true if the nuclear@1: // 'desc' refers to a new device; false, otherwise. nuclear@1: // Return 'false' to create new object, 'true' if done with this argument. nuclear@1: virtual bool UpdateMatchedCandidate( nuclear@1: const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL) nuclear@1: { OVR_UNUSED2(desc, newDeviceFlag); return false; } nuclear@1: nuclear@1: // Matches HID device to the descriptor. nuclear@1: virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; } nuclear@1: nuclear@1: // Matches device by path. nuclear@1: virtual bool MatchDevice(const String& /*path*/) { return false; } nuclear@1: //protected: nuclear@1: DeviceFactory* const pFactory; nuclear@1: const DeviceType Type; nuclear@1: nuclear@1: // List in which this descriptor lives. pList->CreateLock required if added/removed. nuclear@1: Ptr pLock; nuclear@1: nuclear@1: // Strong references to us: Incremented by Device, DeviceHandles & Enumerators. nuclear@1: // May be 0 if device not created and there are no handles. nuclear@1: // Following transitions require pList->CreateLock: nuclear@1: // {1 -> 0}: May delete & remove handle if no longer available. nuclear@1: // {0 -> 1}: Device creation is only possible if manager is still alive. nuclear@1: AtomicInt HandleCount; nuclear@1: // If not null, points to our created device instance. Modified during lock only. nuclear@1: DeviceBase* pDevice; nuclear@1: // True if device is marked as available during enumeration. nuclear@1: bool Enumerated; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: nuclear@1: // Common data present in the implementation of every DeviceBase. nuclear@1: // Injected by DeviceImpl. nuclear@1: class DeviceCommon nuclear@1: { nuclear@1: public: nuclear@1: AtomicInt RefCount; nuclear@1: Ptr pCreateDesc; nuclear@1: Ptr pParent; nuclear@1: MessageHandlerRef HandlerRef; nuclear@1: nuclear@1: DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent) nuclear@1: : RefCount(1), pCreateDesc(createDesc), pParent(parent), HandlerRef(device) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: // Device reference counting delegates to Manager thread to actually kill devices. nuclear@1: void DeviceAddRef(); nuclear@1: void DeviceRelease(); nuclear@1: nuclear@1: Lock* GetLock() const { return pCreateDesc->GetLock(); } nuclear@1: nuclear@1: virtual bool Initialize(DeviceBase* parent) = 0; nuclear@1: virtual void Shutdown() = 0; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // DeviceImpl address DeviceRecord implementation to a device base class B. nuclear@1: // B must be derived form DeviceBase. nuclear@1: nuclear@1: template nuclear@1: class DeviceImpl : public B, public DeviceCommon nuclear@1: { nuclear@1: public: nuclear@1: DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent) nuclear@1: : DeviceCommon(createDesc, getThis(), parent) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: // Convenience method to avoid manager access typecasts. nuclear@1: DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; } nuclear@1: nuclear@1: // Inline to avoid warnings. nuclear@1: DeviceImpl* getThis() { return this; } nuclear@1: nuclear@1: // Common implementation delegate to avoid virtual inheritance and dynamic casts. nuclear@1: virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; } nuclear@1: nuclear@1: /* nuclear@1: virtual void AddRef() { pCreateDesc->DeviceAddRef(); } nuclear@1: virtual void Release() { pCreateDesc->DeviceRelease(); } nuclear@1: virtual DeviceBase* GetParent() const { return pParent.GetPtr(); } nuclear@1: virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;} nuclear@1: virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); } nuclear@1: virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); } nuclear@1: virtual DeviceType GetType() const { return pCreateDesc->Type; } nuclear@1: virtual DeviceType GetType() const { return pCreateDesc->Type; } nuclear@1: */ nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** DeviceFactory nuclear@1: nuclear@1: // DeviceFactory is maintained in DeviceManager for each separately-enumerable nuclear@1: // device type; factories allow separation of unrelated enumeration code. nuclear@1: nuclear@1: class DeviceFactory : public ListNode, public NewOverrideBase nuclear@1: { nuclear@1: public: nuclear@1: nuclear@1: DeviceFactory() : pManager(0) nuclear@1: { nuclear@1: pNext = pPrev = 0; nuclear@1: } nuclear@1: virtual ~DeviceFactory() { } nuclear@1: nuclear@1: DeviceManagerImpl* GetManagerImpl() { return pManager; } nuclear@1: nuclear@1: // Notifiers called when we are added to/removed from a device. nuclear@1: virtual bool AddedToManager(DeviceManagerImpl* manager) nuclear@1: { nuclear@1: OVR_ASSERT(pManager == 0); nuclear@1: pManager = manager; nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: virtual void RemovedFromManager() nuclear@1: { nuclear@1: pManager = 0; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // *** Device Enumeration/Creation Support nuclear@1: nuclear@1: // Passed to EnumerateDevices to be informed of every device detected. nuclear@1: class EnumerateVisitor nuclear@1: { nuclear@1: public: nuclear@1: virtual void Visit(const DeviceCreateDesc& createDesc) = 0; nuclear@1: }; nuclear@1: nuclear@1: // Enumerates factory devices by notifying EnumerateVisitor about every nuclear@1: // device that is present. nuclear@1: virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0; nuclear@1: nuclear@1: // Matches vendorId/productId pair with the factory; returns 'true' nuclear@1: // if the factory can handle the device. nuclear@1: virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const nuclear@1: { nuclear@1: OVR_UNUSED2(vendorId, productId); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Detects the HID device and adds the DeviceCreateDesc into Devices list, if nuclear@1: // the device belongs to this factory. Returns 'false', if not. nuclear@1: virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) nuclear@1: { nuclear@1: OVR_UNUSED2(pdevMgr, desc); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: protected: nuclear@1: DeviceManagerImpl* pManager; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** DeviceManagerImpl nuclear@1: nuclear@1: // DeviceManagerImpl is a partial default DeviceManager implementation that nuclear@1: // maintains a list of devices and supports their enumeration. nuclear@1: nuclear@1: class DeviceManagerImpl : public DeviceImpl, public ThreadCommandQueue nuclear@1: { nuclear@1: public: nuclear@1: DeviceManagerImpl(); nuclear@1: ~DeviceManagerImpl(); nuclear@1: nuclear@1: // Constructor helper function to create Descriptor and manager lock during initialization. nuclear@1: static DeviceCreateDesc* CreateManagerDesc(); nuclear@1: nuclear@1: // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must nuclear@1: // be called by the platform-specific derived class. nuclear@1: virtual bool Initialize(DeviceBase* parent); nuclear@1: virtual void Shutdown(); nuclear@1: nuclear@1: nuclear@1: // Every DeviceManager has an associated profile manager, which is used to store nuclear@1: // user settings that may affect device behavior. nuclear@1: virtual ProfileManager* GetProfileManager() const { return pProfileManager.GetPtr(); } nuclear@1: nuclear@1: // Override to return ThreadCommandQueue implementation used to post commands nuclear@1: // to the background device manager thread (that must be created by Initialize). nuclear@1: virtual ThreadCommandQueue* GetThreadQueue() = 0; nuclear@1: nuclear@1: // Returns the thread id of the DeviceManager. nuclear@1: virtual ThreadId GetThreadId() const = 0; nuclear@1: nuclear@1: virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); nuclear@1: nuclear@1: nuclear@1: // nuclear@1: void AddFactory(DeviceFactory* factory) nuclear@1: { nuclear@1: // This lock is only needed if we call AddFactory after manager thread creation. nuclear@1: Lock::Locker scopeLock(GetLock()); nuclear@1: Factories.PushBack(factory); nuclear@1: factory->AddedToManager(this); nuclear@1: } nuclear@1: nuclear@1: void CallOnDeviceAdded(DeviceCreateDesc* desc) nuclear@1: { nuclear@1: HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc))); nuclear@1: } nuclear@1: void CallOnDeviceRemoved(DeviceCreateDesc* desc) nuclear@1: { nuclear@1: HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc))); nuclear@1: } nuclear@1: nuclear@1: // Helper to access Common data for a device. nuclear@1: static DeviceCommon* GetDeviceCommon(DeviceBase* device) nuclear@1: { nuclear@1: return device->getDeviceCommon(); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Background-thread callbacks for DeviceCreation/Release. These nuclear@1: DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0); nuclear@1: Void ReleaseDevice_MgrThread(DeviceBase* device); nuclear@1: nuclear@1: nuclear@1: // Calls EnumerateDevices() on all factories nuclear@1: virtual Void EnumerateAllFactoryDevices(); nuclear@1: // Enumerates devices for a particular factory. nuclear@1: virtual Void EnumerateFactoryDevices(DeviceFactory* factory); nuclear@1: nuclear@1: virtual HIDDeviceManager* GetHIDDeviceManager() const nuclear@1: { nuclear@1: return HidDeviceManager; nuclear@1: } nuclear@1: nuclear@1: // Adds device (DeviceCreateDesc*) into Devices. Returns NULL, nuclear@1: // if unsuccessful or device is already in the list. nuclear@1: virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc); nuclear@1: nuclear@1: // Finds a device descriptor by path and optional type. nuclear@1: Ptr FindDevice(const String& path, DeviceType = Device_None); nuclear@1: nuclear@1: // Finds HID device by HIDDeviceDesc. nuclear@1: Ptr FindHIDDevice(const HIDDeviceDesc&); nuclear@1: void DetectHIDDevice(const HIDDeviceDesc&); nuclear@1: nuclear@1: // Manager Lock-protected list of devices. nuclear@1: List Devices; nuclear@1: nuclear@1: // Factories used to detect and manage devices. nuclear@1: List Factories; nuclear@1: nuclear@1: protected: nuclear@1: Ptr HidDeviceManager; nuclear@1: Ptr pProfileManager; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: } // namespace OVR nuclear@1: nuclear@1: #endif