/** ****************************************************************************** * * @file usbmonitor_mac.cpp * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @addtogroup GCSPlugins GCS Plugins * @{ * @addtogroup RawHIDPlugin Raw HID Plugin * @{ * @brief Implements the USB monitor on Mac using XXXXX *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "usbmonitor.h" #include #include #include #include #include #include #define printf qDebug //! Local helper functions static bool HID_GetIntProperty(IOHIDDeviceRef dev, CFStringRef property, int * value); static bool HID_GetStrProperty(IOHIDDeviceRef dev, CFStringRef property, QString & value); /** Initialize the USB monitor here */ USBMonitor::USBMonitor(QObject *parent): QThread(parent) { hid_manager=NULL; CFMutableDictionaryRef dict; CFNumberRef num; IOReturn ret; m_instance = this; listMutex = new QMutex(); knowndevices.clear(); hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { if (hid_manager) CFRelease(hid_manager); Q_ASSERT(0); } // No matching filter IOHIDManagerSetDeviceMatching(hid_manager, NULL); // set up a callbacks for device attach & detach IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL); IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL); ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); if (ret != kIOReturnSuccess) { IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); CFRelease(hid_manager); return; } start(); } USBMonitor::~USBMonitor() { //if(hid_manager != NULL) // IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); quit(); } void USBMonitor::deviceEventReceived() { qDebug() << "Device event"; } USBMonitor* USBMonitor::instance() { return m_instance; } USBMonitor* USBMonitor::m_instance = 0; void USBMonitor::removeDevice(IOHIDDeviceRef dev) { for( int i = 0; i < knowndevices.length(); i++) { USBPortInfo port = knowndevices.at(i); if(port.dev_handle == dev) { QMutexLocker locker(listMutex); knowndevices.removeAt(i); emit deviceRemoved(port); return; } } } /** * @brief Static callback for the USB driver to indicate device removed */ void USBMonitor::detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) { instance()->removeDevice(dev); } void USBMonitor::addDevice(USBPortInfo info) { QMutexLocker locker(listMutex); knowndevices.append(info); emit deviceDiscovered(info); } void USBMonitor::attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) { bool got_properties = true; CFTypeRef prop; USBPortInfo deviceInfo; deviceInfo.dev_handle = dev; // Populate the device info structure got_properties &= HID_GetIntProperty(dev, CFSTR( kIOHIDVendorIDKey ), &deviceInfo.vendorID); got_properties &= HID_GetIntProperty(dev, CFSTR( kIOHIDProductIDKey ), &deviceInfo.productID); got_properties &= HID_GetIntProperty(dev, CFSTR( kIOHIDVersionNumberKey ), &deviceInfo.bcdDevice); got_properties &= HID_GetStrProperty(dev, CFSTR( kIOHIDSerialNumberKey ), deviceInfo.serialNumber); got_properties &= HID_GetStrProperty(dev, CFSTR( kIOHIDProductKey ), deviceInfo.product); got_properties &= HID_GetStrProperty(dev, CFSTR( kIOHIDManufacturerKey ), deviceInfo.manufacturer); // TOOD: Eventually want to take array of usages if devices start needing that got_properties &= HID_GetIntProperty(dev, CFSTR( kIOHIDPrimaryUsageKey ), &deviceInfo.Usage); got_properties &= HID_GetIntProperty(dev, CFSTR( kIOHIDPrimaryUsagePageKey ), &deviceInfo.UsagePage); // Currently only enumerating objects that have the complete list of properties if(got_properties) { instance()->addDevice(deviceInfo); } } /** Returns a list of all currently available devices */ QList USBMonitor::availableDevices() { //QMutexLocker locker(listMutex); return knowndevices; } /** * @brief Be a bit more picky and ask only for a specific type of device: * @param[in] vid VID to screen or -1 to ignore * @param[in] pid PID to screen or -1 to ignore * @param[in] bcdDeviceMSB MSB of bcdDevice to screen or -1 to ignore * @param[in] bcdDeviceLSB LSB of bcdDevice to screen or -1 to ignore * @return List of USBPortInfo that meet this criterion * @note * On OpenPilot, the bcdDeviceLSB indicates the run state: bootloader or running. * bcdDeviceMSB indicates the board model. */ QList USBMonitor::availableDevices(int vid, int pid, int bcdDeviceMSB, int bcdDeviceLSB) { QList allPorts = availableDevices(); QList thePortsWeWant; foreach (USBPortInfo port, allPorts) { if((port.vendorID==vid || vid==-1) && (port.productID==pid || pid==-1) && ((port.bcdDevice>>8)==bcdDeviceMSB || bcdDeviceMSB==-1) && ( (port.bcdDevice&0x00ff) ==bcdDeviceLSB || bcdDeviceLSB==-1)) thePortsWeWant.append(port); } return thePortsWeWant; } /** * @brief Helper function get get a HID integer property * @param[in] dev Device reference * @param[in] property The property to get (constants defined in IOKIT) * @param[out] value Pointer to integer to set * @return True if successful, false otherwise */ static bool HID_GetIntProperty(IOHIDDeviceRef dev, CFStringRef property, int * value) { CFTypeRef prop = IOHIDDeviceGetProperty(dev, property); if (prop) { if (CFNumberGetTypeID() == CFGetTypeID(prop)) { // if a number CFNumberGetValue((CFNumberRef) prop, kCFNumberSInt32Type, value); return true; } } return false; } /** * @brief Helper function get get a HID string property * @param[in] dev Device reference * @param[in] property The property to get (constants defined in IOKIT) * @param[out] value The QString to set * @return True if successful, false otherwise */ static bool HID_GetStrProperty(IOHIDDeviceRef dev, CFStringRef property, QString & value) { CFTypeRef prop = IOHIDDeviceGetProperty(dev, property); if (prop) { if (CFStringGetTypeID() == CFGetTypeID(prop)) { // if a string char buffer[2550]; bool success = CFStringGetCString ( (CFStringRef) prop, buffer, sizeof(buffer), kCFStringEncodingMacRoman ); value = QString(buffer); return success; } } return false; }