VirtualBox

Ignore:
Timestamp:
Jan 18, 2016 11:49:16 AM (9 years ago)
Author:
vboxsync
Message:

Main/iokit.cpp: Correctly determine the USB device state on El Capitan and newer without breaking older releases and capturing USB devices in general

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-server/darwin/iokit.cpp

    r59332 r59374  
    542542} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
    543543
    544 /**
    545  * Returns the correct class name to identify USB devices. El Capitan
    546  * introduced a reworked USb stack with changed names.
    547  * The old names are still available but the objects don't reveal all the
    548  * information required.
    549  */
    550 DECLINLINE(const char *)darwinGetUsbDeviceClassName(void)
    551 {
    552     return   g_uMajorDarwin >= VBOX_OSX_EL_CAPTIAN_VER
    553            ? kIOUSBHostDeviceClassName
    554            : kIOUSBDeviceClassName;
    555 }
    556544
    557545/**
     
    656644            kern_return_t rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
    657645                                                                kIOPublishNotification,
    658                                                                 IOServiceMatching(darwinGetUsbDeviceClassName()),
     646                                                                IOServiceMatching(kIOUSBDeviceClassName),
    659647                                                                darwinUSBAttachNotification1,
    660648                                                                pNotify,
     
    665653                rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
    666654                                                      kIOMatchedNotification,
    667                                                       IOServiceMatching(darwinGetUsbDeviceClassName()),
     655                                                      IOServiceMatching(kIOUSBDeviceClassName),
    668656                                                      darwinUSBAttachNotification2,
    669657                                                      pNotify,
     
    674662                    rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
    675663                                                          kIOTerminatedNotification,
    676                                                           IOServiceMatching(darwinGetUsbDeviceClassName()),
     664                                                          IOServiceMatching(kIOUSBDeviceClassName),
    677665                                                          darwinUSBDetachNotification,
    678666                                                          pNotify,
     
    800788}
    801789
    802 
    803 /**
    804  * Worker function for DarwinGetUSBDevices() that tries to figure out
    805  * what state the device is in and set enmState.
    806  *
    807  * This is mostly a matter of distinguishing between devices that nobody
    808  * uses, devices that can be seized and devices that cannot be grabbed.
    809  *
    810  * @param   pCur        The USB device data.
    811  * @param   USBDevice   The USB device object.
    812  * @param   PropsRef    The USB device properties.
    813  */
    814 static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef /* PropsRef */)
     790/**
     791 * Finds the matching IOUSBHostDevice registry entry for the given legacy USB device interface (IOUSBDevice).
     792 *
     793 * @returns kern_return_t error code.
     794 * @param   USBDeviceLegacy    The legacy device I/O Kit object.
     795 * @param   pUSBDevice         Where to store the IOUSBHostDevice object on success.
     796 */
     797static kern_return_t darwinGetUSBHostDeviceFromLegacyDevice(io_object_t USBDeviceLegacy, io_object_t *pUSBDevice)
     798{
     799    kern_return_t krc = KERN_SUCCESS;
     800    uint64_t uIoRegEntryId = 0;
     801
     802    *pUSBDevice = 0;
     803
     804    /* Get the registry entry ID to match against. */
     805    krc = IORegistryEntryGetRegistryEntryID(USBDeviceLegacy, &uIoRegEntryId);
     806    if (krc != KERN_SUCCESS)
     807        return krc;
     808
     809    /*
     810     * Create a matching dictionary for searching for USB Devices in the IOKit.
     811     */
     812    CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBHostDeviceClassName);
     813    AssertReturn(RefMatchingDict, KERN_FAILURE);
     814
     815    /*
     816     * Perform the search and get a collection of USB Device back.
     817     */
     818    io_iterator_t USBDevices = NULL;
     819    IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
     820    AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), KERN_FAILURE);
     821    RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
     822
     823    /*
     824     * Walk the devices and check for the matching alternate registry entry ID.
     825     */
     826    io_object_t USBDevice;
     827    while ((USBDevice = IOIteratorNext(USBDevices)) != 0)
     828    {
     829        DARWIN_IOKIT_DUMP_OBJ(USBDevice);
     830
     831        CFMutableDictionaryRef PropsRef = 0;
     832        krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
     833        if (krc == KERN_SUCCESS)
     834        {
     835            uint64_t uAltRegId = 0;
     836            if (   darwinDictGetU64(PropsRef,  CFSTR("AppleUSBAlternateServiceRegistryID"), &uAltRegId)
     837                && uAltRegId == uIoRegEntryId)
     838            {
     839                *pUSBDevice = USBDevice;
     840                CFRelease(PropsRef);
     841                break;
     842            }
     843
     844            CFRelease(PropsRef);
     845        }
     846        IOObjectRelease(USBDevice);
     847    }
     848    IOObjectRelease(USBDevices);
     849
     850    return krc;
     851}
     852
     853static bool darwinUSBDeviceIsGrabbedDetermineState(PUSBDEVICE pCur, io_object_t USBDevice)
     854{
     855    /*
     856     * Iterate the interfaces (among the children of the IOUSBDevice object).
     857     */
     858    io_iterator_t Interfaces;
     859    kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
     860    if (krc != KERN_SUCCESS)
     861        return false;
     862
     863    bool fHaveOwner = false;
     864    RTPROCESS Owner = NIL_RTPROCESS;
     865    bool fHaveClient = false;
     866    RTPROCESS Client = NIL_RTPROCESS;
     867    io_object_t Interface;
     868    while ((Interface = IOIteratorNext(Interfaces)) != 0)
     869    {
     870        io_name_t szName;
     871        krc = IOObjectGetClass(Interface, szName);
     872        if (    krc == KERN_SUCCESS
     873            &&  !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
     874        {
     875            CFMutableDictionaryRef PropsRef = 0;
     876            krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
     877            if (krc == KERN_SUCCESS)
     878            {
     879                fHaveOwner = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
     880                fHaveClient = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
     881                CFRelease(PropsRef);
     882            }
     883        }
     884
     885        IOObjectRelease(Interface);
     886    }
     887    IOObjectRelease(Interfaces);
     888
     889    /*
     890     * Calc the status.
     891     */
     892    if (fHaveOwner)
     893    {
     894        if (Owner == RTProcSelf())
     895            pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
     896                           ? USBDEVICESTATE_HELD_BY_PROXY
     897                           : USBDEVICESTATE_USED_BY_GUEST;
     898        else
     899            pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
     900    }
     901
     902    return fHaveOwner;
     903}
     904
     905/**
     906 * Worker for determining the USB device state for devices which are not captured by the VBoxUSB driver
     907 * Works for both, IOUSBDevice (legacy on release >= El Capitan) and IOUSBHostDevice (available on >= El Capitan).
     908 *
     909 * @returns nothing.
     910 * @param   pCur      The USB device data.
     911 * @param   USBDevice I/O Kit USB device object (either IOUSBDevice or IOUSBHostDevice).
     912 */
     913static void darwinDetermineUSBDeviceStateWorker(PUSBDEVICE pCur, io_object_t USBDevice)
    815914{
    816915    /*
     
    822921        return;
    823922
    824     bool fHaveOwner = false;
    825     RTPROCESS Owner = NIL_RTPROCESS;
    826     bool fHaveClient = false;
    827     RTPROCESS Client = NIL_RTPROCESS;
    828923    bool fUserClientOnly = true;
    829924    bool fConfigured = false;
     
    831926    bool fSeizable = true;
    832927    io_object_t Interface;
    833     while ((Interface = IOIteratorNext(Interfaces)))
     928    while ((Interface = IOIteratorNext(Interfaces)) != 0)
    834929    {
    835930        io_name_t szName;
    836931        krc = IOObjectGetClass(Interface, szName);
    837932        if (    krc == KERN_SUCCESS
    838             &&  !strcmp(szName, "IOUSBInterface"))
     933            &&  (   !strcmp(szName, "IOUSBInterface")
     934                 || !strcmp(szName, "IOUSBHostInterface")))
    839935        {
    840936            fConfigured = true;
     
    849945            {
    850946                io_object_t Child1;
    851                 while ((Child1 = IOIteratorNext(Children1)))
     947                while ((Child1 = IOIteratorNext(Children1)) != 0)
    852948                {
    853949                    krc = IOObjectGetClass(Child1, szName);
     
    883979            }
    884980        }
    885         /*
    886          * Not an interface, could it be VBoxUSBDevice?
    887          * If it is, get the owner and client properties.
    888          */
    889         else if (    krc == KERN_SUCCESS
    890                  &&  !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
    891         {
    892             CFMutableDictionaryRef PropsRef = 0;
    893             krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
    894             if (krc == KERN_SUCCESS)
    895             {
    896                 fHaveOwner = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
    897                 fHaveClient = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
    898                 CFRelease(PropsRef);
    899             }
    900         }
    901981
    902982        IOObjectRelease(Interface);
     
    907987     * Calc the status.
    908988     */
    909     if (fHaveOwner)
    910     {
    911         if (Owner == RTProcSelf())
    912             pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
    913                            ? USBDEVICESTATE_HELD_BY_PROXY
    914                            : USBDEVICESTATE_USED_BY_GUEST;
    915         else
    916             pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
    917     }
    918     else if (fUserClientOnly)
    919         /** @todo how to detect other user client?!? - Look for IOUSBUserClient! */
    920         pCur->enmState = !fConfigured
    921                        ? USBDEVICESTATE_UNUSED
    922                        : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
    923     else if (!fInUse)
     989    if (!fInUse)
    924990        pCur->enmState = USBDEVICESTATE_UNUSED;
    925991    else
     
    929995}
    930996
     997/**
     998 * Worker function for DarwinGetUSBDevices() that tries to figure out
     999 * what state the device is in and set enmState.
     1000 *
     1001 * This is mostly a matter of distinguishing between devices that nobody
     1002 * uses, devices that can be seized and devices that cannot be grabbed.
     1003 *
     1004 * @param   pCur        The USB device data.
     1005 * @param   USBDevice   The USB device object.
     1006 * @param   PropsRef    The USB device properties.
     1007 */
     1008static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef /* PropsRef */)
     1009{
     1010
     1011    if (!darwinUSBDeviceIsGrabbedDetermineState(pCur, USBDevice))
     1012    {
     1013        /*
     1014         * The USB stack was completely reworked on El Capitan and the IOUSBDevice and IOUSBInterface
     1015         * are deprecated and don't return the information required for the additional checks below.
     1016         * We also can't directly make use of the new classes (IOUSBHostDevice and IOUSBHostInterface)
     1017         * because VBoxUSB only exposes the legacy interfaces. Trying to use the new classes results in errors
     1018         * because the I/O Kit USB library wants to use the new interfaces. The result is us losing the device
     1019         * form the list when VBoxUSB has attached to the USB device.
     1020         *
     1021         * To make the checks below work we have to get hold of the IOUSBHostDevice and IOUSBHostInterface
     1022         * instances for the current device. Fortunately the IOUSBHostDevice instance contains a
     1023         * "AppleUSBAlternateServiceRegistryID" which points to the legacy class instance for the same device.
     1024         * So just iterate over the list of IOUSBHostDevice instances and check whether the
     1025         * AppleUSBAlternateServiceRegistryID property matches with the legacy instance.
     1026         *
     1027         * The upside is that we can keep VBoxUSB untouched and still compatible with older OS X releases.
     1028         */
     1029        if (g_uMajorDarwin >= VBOX_OSX_EL_CAPTIAN_VER)
     1030        {
     1031            io_object_t IOUSBDeviceNew = 0;
     1032
     1033            io_object_t krc = darwinGetUSBHostDeviceFromLegacyDevice(USBDevice, &IOUSBDeviceNew);
     1034            if (   krc == KERN_SUCCESS
     1035                && IOUSBDeviceNew != 0)
     1036            {
     1037                darwinDetermineUSBDeviceStateWorker(pCur, IOUSBDeviceNew);
     1038                IOObjectRelease(IOUSBDeviceNew);
     1039            }
     1040        }
     1041        else
     1042            darwinDetermineUSBDeviceStateWorker(pCur, USBDevice);
     1043    }
     1044}
     1045
    9311046
    9321047/**
     
    9441059     * Create a matching dictionary for searching for USB Devices in the IOKit.
    9451060     */
    946     CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(darwinGetUsbDeviceClassName());
     1061    CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    9471062    AssertReturn(RefMatchingDict, NULL);
    9481063
     
    10191134                darwinDictGetU8(PropsRef, CFSTR("PortNum"), &pCur->bPort); /* Not present in 10.11 beta 3, so ignore failure. (Is set to zero.) */
    10201135                uint8_t bSpeed;
    1021                 AssertBreak(darwinDictGetU8(PropsRef,
    1022                                               g_uMajorDarwin >= VBOX_OSX_EL_CAPTIAN_VER
    1023                                             ? CFSTR("USBSpeed")
    1024                                             : CFSTR(kUSBDevicePropertySpeed),
    1025                                             &bSpeed));
    1026                 Assert(bSpeed <= 4);
    1027                 pCur->enmSpeed = bSpeed == 4 ? USBDEVICESPEED_SUPER /** @todo: Check what 4 really means (USB 3.1 perhaps?), seen on El Capitan. */
    1028                                : bSpeed == 3 ? USBDEVICESPEED_SUPER
     1136                AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDevicePropertySpeed), &bSpeed));
     1137                Assert(bSpeed <= 3);
     1138                pCur->enmSpeed = bSpeed == 3 ? USBDEVICESPEED_SUPER
    10291139                               : bSpeed == 2 ? USBDEVICESPEED_HIGH
    10301140                               : bSpeed == 1 ? USBDEVICESPEED_FULL
     
    11771287     */
    11781288
    1179     CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(darwinGetUsbDeviceClassName());
     1289    CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    11801290    AssertReturn(RefMatchingDict, NULL);
    11811291
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