VirtualBox

Changeset 49904 in vbox


Ignore:
Timestamp:
Dec 14, 2013 6:36:13 PM (11 years ago)
Author:
vboxsync
Message:

OS X host: HID LEDs sync: update HID device notification subscription (now should work on both Mountain Lion and Mavericks); new device added into the blacklist.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp

    r49850 r49904  
    4141# include <VBox/sup.h>
    4242# include <IOKit/IOMessage.h>
     43# include <IOKit/usb/IOUSBLib.h>
     44# include <IOKit/IOMessage.h>
    4345#endif
    4446
     
    4648# include <mach/mach.h>
    4749# include <mach/mach_error.h>
    48 # include <IOKit/IOKitLib.h>
    49 # include <IOKit/IOCFPlugIn.h>
    5050# include <IOKit/hid/IOHIDUsageTables.h>
    5151# include <CoreFoundation/CoreFoundation.h>
    5252#endif
    53 # include <IOKit/usb/USB.h>
     53
     54#include <IOKit/IOKitLib.h>
     55#include <IOKit/IOCFPlugIn.h>
     56#include <IOKit/usb/USB.h>
    5457#include <IOKit/hid/IOHIDLib.h>
    5558#include <ApplicationServices/ApplicationServices.h>
     
    316319    CFIndex           idxPosition;          /** Position in global storage (used to simplify CFArray navigation when removing detached device) */
    317320    uint64_t          cCapsLockTimeout;     /** KBD CAPS LOCK key hold timeout (some Apple keyboards only) */
    318 
    319     io_object_t           notification;         /** General interest notification stuff: notification reference */
    320     IONotificationPortRef notificationPortRef;  /** General interest notification stuff: notification port reference */
    321 
     321    uint32_t          idLocation;           /** HID Location ID: unique for an USB device registered in the system */
    322322} VBoxKbdState_t;
    323323
     
    337337    CFMutableArrayRef   pFifoEventQueue;    /** This queue will be appended in IOKit input callback. Carbon input callback will extract data from it */
    338338    RTSEMMUTEX          fifoEventQueueLock; /** Lock for pFifoEventQueue */
     339
     340    io_iterator_t         pUsbHidDeviceMatchNotify;     /** IOService notification reference: USB HID device matching */
     341    io_iterator_t         pUsbHidGeneralInterestNotify; /** IOService notification reference: USB HID general interest
     342                                                            notifications (IOService messages) */
     343    IONotificationPortRef pNotificationPrortRef;        /** IOService notification port reference: used for both notification
     344                                                            types - device match and device general interest message */
    339345
    340346    /* Carbon events data */
     
    14531459}
    14541460
     1461/** Get HID Location ID */
     1462static uint32_t darwinHidLocationId(IOHIDDeviceRef pHidDeviceRef)
     1463{
     1464    return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDLocationIDKey));
     1465}
     1466
    14551467/** Some keyboard devices might freeze after LEDs manipulation. We filter out such devices here.
    14561468 * In the list below, devices that known to have such issues. If you want to add new device,
     
    14671479    {
    14681480        if (productId == 0x8001) /* GK-04008/C keyboard */
     1481            fSupported = false;
     1482    }
     1483    if (vendorId == 0xE6A)       /* Megawin Technology */
     1484    {
     1485        if (productId == 0x6001) /* Japanese flexible keyboard */
    14691486            fSupported = false;
    14701487    }
     
    17231740}
    17241741
    1725 /** IOHID Device general interest notification callback. We are interested in kIOMessageDeviceHasPoweredOn message.
    1726  * When we receive it, we do silently resync kbd which was just resumed. The rest of messages are for debugging
    1727  * purpose. */
    1728 static void darwinHidNotificationCb(void *pData, io_service_t unused1, natural_t msg, void *unused2)
     1742/** Helper function to obtain interface for IOUSBInterface IOService. */
     1743static IOUSBDeviceInterface ** darwinQueryUsbHidInterfaceInterface(io_service_t service)
     1744{
     1745    kern_return_t         rc;
     1746    IOCFPlugInInterface **ppPluginInterface = NULL;
     1747    int32_t               iScore;
     1748
     1749    rc = IOCreatePlugInInterfaceForService(service, kIOUSBInterfaceUserClientTypeID,
     1750                                           kIOCFPlugInInterfaceID, &ppPluginInterface, &iScore);
     1751
     1752    if (rc == kIOReturnSuccess && ppPluginInterface != NULL)
     1753    {
     1754        IOUSBDeviceInterface **ppUsbDeviceInterface = NULL;
     1755
     1756        rc = (*ppPluginInterface)->QueryInterface(ppPluginInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
     1757                                                  (LPVOID *)&ppUsbDeviceInterface);
     1758        IODestroyPlugInInterface(ppPluginInterface);
     1759
     1760        if (rc == kIOReturnSuccess && ppUsbDeviceInterface != NULL)
     1761            return ppUsbDeviceInterface;
     1762        else
     1763            LogRel2(("Failed to query plugin interface for USB device\n"));
     1764
     1765    }
     1766    else
     1767        LogRel2(("Failed to create plugin interface for USB device\n"));
     1768
     1769    return NULL;
     1770}
     1771
     1772/** Helper function for IOUSBInterface IOService general interest notification callback: resync LEDs. */
     1773static void darwinUsbHidResyncLeds(VBoxKbdState_t *pKbd)
     1774{
     1775    AssertReturnVoid(pKbd);
     1776
     1777    VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
     1778    CFDictionaryRef  elementMatchingDict = darwinQueryLedElementMatchingDictionary();
     1779    if (elementMatchingDict)
     1780    {
     1781        LogRel2(("Do HID device resync at location 0x%X \n", pKbd->idLocation));
     1782        (void)darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict,
     1783            pHidState->guestState.fNumLockOn, pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
     1784        CFRelease(elementMatchingDict);
     1785    }
     1786}
     1787
     1788/** IOUSBInterface IOService general interest notification callback. When we receive it, we do
     1789 * silently resync kbd which has just changed its state. */
     1790static void darwinUsbHidGeneralInterestCb(void *pData, io_service_t unused1, natural_t msg, void *unused2)
    17291791{
    17301792    NOREF(unused1);
     
    17361798    switch (msg)
    17371799    {
    1738         case kIOMessageDeviceHasPoweredOn:
    1739             {
    1740                 LogRel2(("HID general interest notification kIOMessageDeviceHasPoweredOn for KBD %d\n", (int)(pKbd->idxPosition)));
    1741 
    1742                 VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
    1743                 CFDictionaryRef  elementMatchingDict = darwinQueryLedElementMatchingDictionary();
    1744                 if (elementMatchingDict)
    1745                 {
    1746                     (void)darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict,
    1747                         pHidState->guestState.fNumLockOn, pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
    1748                     CFRelease(elementMatchingDict);
    1749                 }
     1800        case kIOUSBMessagePortHasBeenSuspended:
     1801            {
     1802                LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenSuspended for KBD %d (Location ID: 0x%X)\n",
     1803                         (int)(pKbd->idxPosition), pKbd->idLocation));
    17501804                break;
    17511805            }
    17521806
    1753         case kIOMessageDeviceWillPowerOff:
    1754             {
    1755                 LogRel2(("HID general interest notification kIOMessageDeviceWillPowerOff for KBD %d\n", (int)(pKbd->idxPosition)));
     1807        case kIOUSBMessagePortHasBeenResumed:
     1808            {
     1809                LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenResumed for KBD %d (Location ID: 0x%X)\n",
     1810                         (int)(pKbd->idxPosition), pKbd->idLocation));
     1811                darwinUsbHidResyncLeds(pKbd);
    17561812                break;
    17571813            }
    17581814
    1759         case kIOMessageCanDevicePowerOff:
    1760             {
    1761                 LogRel2(("HID general interest notification kIOMessageCanDevicePowerOff for KBD %d\n", (int)(pKbd->idxPosition)));
     1815        case kIOUSBMessagePortHasBeenReset:
     1816            {
     1817                LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenReset for KBD %d (Location ID: 0x%X)\n",
     1818                         (int)(pKbd->idxPosition), pKbd->idLocation));
     1819                darwinUsbHidResyncLeds(pKbd);
    17621820                break;
    17631821            }
    17641822
     1823        case kIOUSBMessageCompositeDriverReconfigured:
     1824            {
     1825                LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessageCompositeDriverReconfigured for KBD %d (Location ID: 0x%X)\n",
     1826                         (int)(pKbd->idxPosition), pKbd->idLocation));
     1827                darwinUsbHidResyncLeds(pKbd);
     1828                break;
     1829            }
     1830
     1831        case kIOMessageServiceWasClosed:
     1832            {
     1833                LogRel2(("IOUSBInterface IOService general interest notification kIOMessageServiceWasClosed for KBD %d (Location ID: 0x%X)\n",
     1834                         (int)(pKbd->idxPosition), pKbd->idLocation));
     1835                break;
     1836            }
     1837
    17651838        default:
    1766             LogRel2(("HID general interest notification 0x%X for KBD %d\n", (int)msg, (int)(pKbd->idxPosition)));
    1767     }
    1768 }
    1769 
    1770 /** Battle against automatic power management for KBD devices. Register general interest notification
    1771  * callback in order to recync KBD device when its driver report that it has been resumed from auto-sleep. */
    1772 static int darwinHidSubscribeInterestNotification(VBoxKbdState_t *pKbd)
    1773 {
    1774     AssertReturn(pKbd, kIOReturnError);
    1775     AssertReturn(pKbd->pDevice, kIOReturnError);
    1776 
    1777     io_service_t  service;
    1778     kern_return_t rc = kIOReturnError;
    1779 
    1780     service = IOHIDDeviceGetService(pKbd->pDevice);
    1781     if (service)
    1782     {
    1783         pKbd->notificationPortRef = IONotificationPortCreate(kIOMasterPortDefault);
    1784         if (pKbd->notificationPortRef)
    1785         {
    1786             rc = IOServiceAddInterestNotification(pKbd->notificationPortRef, service, kIOGeneralInterest, darwinHidNotificationCb, pKbd, &(pKbd->notification));
    1787             if (rc == kIOReturnSuccess)
    1788             {
    1789                 LogRel2(("Interest notification has been registeted for KBD %d\n", (int)(pKbd->idxPosition)));
    1790                 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pKbd->notificationPortRef), kCFRunLoopDefaultMode);
    1791                 return 0;
     1839            LogRel2(("IOUSBInterface IOService general interest notification 0x%X for KBD %d (Location ID: 0x%X)\n",
     1840                     msg, (int)(pKbd->idxPosition), pKbd->idLocation));
     1841    }
     1842}
     1843
     1844/** Get pre-cached KBD device by its Location ID. */
     1845static VBoxKbdState_t * darwinUsbHidQueryKbdByLocationId(uint32_t idLocation, VBoxHidsState_t *pHidState)
     1846{
     1847    AssertReturn(pHidState, NULL);
     1848
     1849    for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
     1850    {
     1851        VBoxKbdState_t *pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
     1852        if (pKbd && pKbd->idLocation == idLocation)
     1853            return pKbd;
     1854    }
     1855
     1856    return NULL;
     1857}
     1858
     1859/** IOUSBInterface IOService match notification callback: issued when IOService instantinates.
     1860 * We subscribe to general interest notifications for available IOServices here. */
     1861static void darwinUsbHidDeviceMatchCb(void *pData, io_iterator_t iter)
     1862{
     1863    AssertReturnVoid(pData);
     1864
     1865    io_service_t     service;
     1866    VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
     1867
     1868    while ((service = IOIteratorNext(iter)))
     1869    {
     1870        kern_return_t         rc;
     1871
     1872        IOUSBDeviceInterface **ppUsbDeviceInterface = darwinQueryUsbHidInterfaceInterface(service);
     1873
     1874        if (ppUsbDeviceInterface)
     1875        {
     1876            uint8_t  idDeviceClass, idDeviceSubClass;
     1877            uint32_t idLocation;
     1878
     1879            rc = (*ppUsbDeviceInterface)->GetLocationID    (ppUsbDeviceInterface,  &idLocation);       AssertMsg(rc == 0, ("Failed to get Location ID"));
     1880            rc = (*ppUsbDeviceInterface)->GetDeviceClass   (ppUsbDeviceInterface,  &idDeviceClass);    AssertMsg(rc == 0, ("Failed to get Device Class"));
     1881            rc = (*ppUsbDeviceInterface)->GetDeviceSubClass(ppUsbDeviceInterface,  &idDeviceSubClass); AssertMsg(rc == 0, ("Failed to get Device Subclass"));
     1882
     1883            if (idDeviceClass == kUSBHIDInterfaceClass && idDeviceSubClass == kUSBHIDBootInterfaceSubClass)
     1884            {
     1885                VBoxKbdState_t *pKbd = darwinUsbHidQueryKbdByLocationId(idLocation, pHidState);
     1886
     1887                rc = IOServiceAddInterestNotification(pHidState->pNotificationPrortRef, service, kIOGeneralInterest,
     1888                    darwinUsbHidGeneralInterestCb, pKbd, &pHidState->pUsbHidGeneralInterestNotify);
     1889
     1890                AssertMsg(rc == 0, ("Failed to add general interest notification"));
     1891
     1892                LogRel2(("Found HID device at location 0x%X: class 0x%X, subclass 0x%X\n", idLocation, idDeviceClass, idDeviceSubClass));
    17921893            }
    1793         }
    1794     }
     1894
     1895            rc = (*ppUsbDeviceInterface)->Release(ppUsbDeviceInterface); AssertMsg(rc == 0, ("Failed to release USB device interface"));
     1896        }
     1897
     1898        IOObjectRelease(service);
     1899    }
     1900}
     1901
     1902/** Register IOUSBInterface IOService match notification callback in order to recync KBD
     1903 * device when it reports state change. */
     1904static int darwinUsbHidSubscribeInterestNotifications(VBoxHidsState_t *pHidState)
     1905{
     1906    AssertReturn(pHidState, kIOReturnBadArgument);
     1907
     1908    int rc = kIOReturnNoMemory;
     1909    CFDictionaryRef pDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
     1910
     1911    if (pDictionary)
     1912    {
     1913        pHidState->pNotificationPrortRef = IONotificationPortCreate(kIOMasterPortDefault);
     1914        if (pHidState->pNotificationPrortRef)
     1915        {
     1916            CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
     1917
     1918            rc = IOServiceAddMatchingNotification(pHidState->pNotificationPrortRef, kIOMatchedNotification,
     1919                pDictionary, darwinUsbHidDeviceMatchCb, pHidState, &pHidState->pUsbHidDeviceMatchNotify);
     1920
     1921            if (rc == kIOReturnSuccess && &pHidState->pUsbHidDeviceMatchNotify != NULL)
     1922            {
     1923                darwinUsbHidDeviceMatchCb(pHidState, pHidState->pUsbHidDeviceMatchNotify);
     1924                LogRel2(("Successfully subscribed to IOUSBInterface IOService match notifications\n"));
     1925            }
     1926            else
     1927                LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: subscription error 0x%X\n", rc));
     1928        }
     1929        else
     1930            LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: unable to create notification port\n"));
     1931    }
     1932    else
     1933        LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: no memory\n"));
    17951934
    17961935    return rc;
    17971936}
    17981937
    1799 /** Remove general interest notification subscription. */
    1800 static void darwinHidUnsubscribeInterestNotification(VBoxKbdState_t *pKbd)
    1801 {
    1802     AssertReturnVoid(pKbd);
    1803     AssertReturnVoid(pKbd->notificationPortRef);
    1804 
    1805     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pKbd->notificationPortRef), kCFRunLoopDefaultMode);
    1806     IONotificationPortDestroy(pKbd->notificationPortRef);
    1807     pKbd->notificationPortRef = 0;
    1808 
    1809     LogRel2(("Interest notification has been removed for KBD %d\n", (int)(pKbd->idxPosition)));
     1938/** Remove IOUSBInterface IOService match notification subscription. */
     1939static void darwinUsbHidUnsubscribeInterestNotifications(VBoxHidsState_t *pHidState)
     1940{
     1941    AssertReturnVoid(pHidState);
     1942
     1943    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
     1944    IONotificationPortDestroy(pHidState->pNotificationPrortRef);
     1945    pHidState->pNotificationPrortRef = NULL;
     1946
     1947    LogRel2(("Successfully un-subscribed from IOUSBInterface IOService match notifications\n"));
    18101948}
    18111949
     
    18251963    //if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
    18261964    //    return ;
    1827 
    1828     darwinHidUnsubscribeInterestNotification(pKbd);
    18291965
    18301966    CFArrayRemoveValueAtIndex(pHidState->pDeviceCollection, pKbd->idxPosition);
     
    18662002                pKbd->pParentContainer = (void *)pHidState;
    18672003                pKbd->idxPosition = CFArrayGetCount(pHidState->pDeviceCollection);
     2004                pKbd->idLocation = darwinHidLocationId(pDevice);
    18682005
    18692006                /* Some Apple keyboards have CAPS LOCK key timeout. According to corresponding
     
    18912028                    }
    18922029
    1893                     rc = darwinHidSubscribeInterestNotification(pKbd);
    1894                     if (rc == 0)
     2030                    /* Register per-device removal callback */
     2031                    IOHIDDeviceRegisterRemovalCallback(pKbd->pDevice, darwinHidRemovalCallback, (void *)pKbd);
     2032
     2033                    /* Register per-device input callback */
     2034                    IOHIDDeviceRegisterInputValueCallback(pKbd->pDevice, darwinHidInputCallback, (void *)pKbd);
     2035                    IOHIDDeviceScheduleWithRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
     2036
     2037                    CFArrayAppendValue(pHidState->pDeviceCollection, (void *)pKbd);
     2038
     2039                    LogRel2(("Saved LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
     2040                        (int)pKbd->idxPosition, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
     2041                        VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
     2042
     2043                    if (fApplyLedState)
    18952044                    {
    1896                         /* Register per-device removal callback */
    1897                         IOHIDDeviceRegisterRemovalCallback(pKbd->pDevice, darwinHidRemovalCallback, (void *)pKbd);
    1898 
    1899                         /* Register per-device input callback */
    1900                         IOHIDDeviceRegisterInputValueCallback(pKbd->pDevice, darwinHidInputCallback, (void *)pKbd);
    1901                         IOHIDDeviceScheduleWithRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    1902 
    1903                         CFArrayAppendValue(pHidState->pDeviceCollection, (void *)pKbd);
    1904 
    1905                         LogRel2(("Saved LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
    1906                             (int)pKbd->idxPosition, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
    1907                             VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
    1908 
    1909                         if (fApplyLedState)
    1910                         {
    1911                             rc = darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict, pHidState->guestState.fNumLockOn,
    1912                                                           pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
    1913                             if (rc != 0)
    1914                                 LogRel2(("Unable to apply guest state to newly attached device\n"));
    1915                         }
    1916 
    1917                         CFRelease(elementMatchingDict);
    1918                         return;
     2045                        rc = darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict, pHidState->guestState.fNumLockOn,
     2046                                                      pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
     2047                        if (rc != 0)
     2048                            LogRel2(("Unable to apply guest state to newly attached device\n"));
    19192049                    }
    1920                     else
    1921                         LogRel2(("Unable to subscribe to IOService interest notification for KBD %d. Disable sync for this keyboard.\n", (int)(pKbd->idxPosition)));
     2050
     2051                    CFRelease(elementMatchingDict);
     2052                    return;
    19222053                }
    19232054
     
    20672198                        pHidState->guestState.fScrollLockOn = false;
    20682199
    2069                         return pHidState;
     2200                        /* Finally, subscribe to USB HID notifications in order to prevent LED artifacts on
     2201                           automatic power management */
     2202                        if (darwinUsbHidSubscribeInterestNotifications(pHidState) == 0)
     2203                            return pHidState;
    20702204                    }
    20712205                }
     
    21042238
    21052239    AssertReturn(pHidState, kIOReturnError);
     2240
     2241    darwinUsbHidUnsubscribeInterestNotifications(pHidState);
    21062242
    21072243    /* Need to unregister Carbon stuff first */
     
    21362272                     (int)i, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
    21372273                     VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
    2138 
    2139                 darwinHidUnsubscribeInterestNotification(pKbd);
    21402274
    21412275                free(pKbd);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette