2017-07-18 6 views
0

J'essaie de découvrir des périphériques de stockage de masse USB sous MAC OSX. J'espérais obtenir la classe d'appareil, et, sur cette base, décider si l'appareil est un stockage de masse ou non. Mais, pour tous les lecteurs flash USB que j'ai, je reçois la classe de périphérique == 0, qui semble être un périphérique composite. S'il vous plaît, aidez-moi à comprendre, ce que je fais mal, ou, peut-être, quelle est la manière fiable de découvrir les périphériques de stockage de masse USB (j'ai besoin d'obtenir PID, VID et point de montage). Voici mon code:OS X, découverte de stockage de masse USB

#import <iostream> 
#import <IOKit/IOkitLib.h> 
#import <IOKit/usb/IOUSBLib.h> 
#import <IOKit/IOCFPlugIn.h> 
#import <IOKit/usb/USBSpec.h> 
#import <CoreFoundation/CoreFoundation.h> 
#import <Foundation/Foundation.h> 

int main(int argc, const char * argv[]) { 
CFMutableDictionaryRef matchingDictionary = NULL; 
io_iterator_t foundIterator = 0; 
io_service_t usbDevice; 
matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &foundIterator); 
for(usbDevice = IOIteratorNext(foundIterator); usbDevice; usbDevice = IOIteratorNext(foundIterator)) 
{ 
    IOCFPlugInInterface** plugin = NULL; 
    SInt32 theScore=0; 
    IOReturn err; 

    err = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &theScore); 
    if (err!= 0){ 
     std::cout<<"error, error code: "<<err_get_code(err) <<std::endl; 
    } 
    else if (plugin && *plugin) 
    { 
     IOUSBDeviceInterface182** usbInterface = NULL; 
     (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),(LPVOID*)&usbInterface); 
     (*plugin)->Release(plugin); 
     if (usbInterface && *usbInterface) 
     { 
      UInt8 devClass; 
      UInt8 devSubClass; 
      UInt16 productId; 
      UInt16 vendorID; 

      //here I'm getting 0 for all my USB flash cards 
      (*usbInterface)->GetDeviceClass(usbInterface,&devClass); 
      (*usbInterface)->GetDeviceVendor(usbInterface, &vendorID); 
      (*usbInterface)->GetDeviceProduct(usbInterface, &productId); 
      (*usbInterface)->GetDeviceSubClass(usbInterface, &devSubClass); 
      std::cout<<"device class: "<<+devClass<<std::endl; 
      std::cout<<"device sub class: "<<+devSubClass<<std::endl; 
      std::cout<<"vendor ID: "<<vendorID<<std::endl; 
      std::cout<<"product ID: "<<productId<<std::endl; 
     } 
    } 
    IOObjectRelease(usbDevice); 
} 
IOObjectRelease(foundIterator); 
return 0; 

}

Répondre

0

Pour moi, la façon suivante à itérer USB travaillé sur OSX:

void scanUsbMassStorage() 
{ 
CFMutableDictionaryRef matchingDictionary = IOServiceMatching(kIOUSBInterfaceClassName); 
//now specify class and subclass to iterate only through USB mass storage devices: 
CFNumberRef cfValue; 
SInt32 deviceClassNum = kUSBMassStorageInterfaceClass; 
cfValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &deviceClassNum); 
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceClass), cfValue); 
CFRelease(cfValue); 
    SInt32 deviceSubClassNum = kUSBMassStorageSCSISubClass; 
cfValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &deviceSubClassNum); 
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceSubClass), cfValue); 
CFRelease(cfValue); 
//NOTE: if you will specify only device class and will not specify subclass, it will return an empty iterator, 
//and I don't know how to say that we need any subclass. 
//BUT: all the devices I've check had kUSBMassStorageSCSISubClass 
io_iterator_t foundIterator = 0; 
io_service_t usbInterface; 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &foundIterator); 
//iterate through USB mass storage devices 
for(usbInterface = IOIteratorNext(foundIterator); usbInterface; usbInterface = IOIteratorNext(foundIterator)) 
{ 
    CFTypeRef bsdName = IORegistryEntrySearchCFProperty(usbInterface, 
                 kIOServicePlane, 
                 CFSTR(kIOBSDNameKey), 
                 kCFAllocatorDefault, 
                 kIORegistryIterateRecursively); 

    CFTypeRef serial = IORegistryEntrySearchCFProperty(usbInterface, 
                 kIOServicePlane, 
                 CFSTR(kUsbSerialPropertyName), 
                 kCFAllocatorDefault, 
                 kIORegistryIterateRecursively|kIORegistryIterateParents); 

    CFTypeRef pid = IORegistryEntrySearchCFProperty(usbInterface, 
                kIOServicePlane, 
                CFSTR(kUsbPidPropertyName), 
                kCFAllocatorDefault, 
                kIORegistryIterateRecursively|kIORegistryIterateParents); 

    CFTypeRef vid = IORegistryEntrySearchCFProperty(usbInterface, 
                kIOServicePlane, 
                CFSTR(kUsbVidPropertyName), 
                kCFAllocatorDefault, 
                kIORegistryIterateRecursively|kIORegistryIterateParents); 

    //now we can perform checks and casts from CFTypeRef like this: 
    std::string filePathStr; 
    std::string serialStr; 
    uint16_t pidInt; 
    uint16_t vidInt; 
    //getMountPathByBSDName - see below 
    bool stillOk = getMountPathByBSDName(bsdName, filePath); 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2AsciiString(serial, serialStr); 
    } 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2uint16(pid, pidInt); 
    } 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2uint16(vid, vidInt); 
    } 
    if (stillOK) 
    { 
     //can do something with the values here 
    } 
} 

Obtenir chemin de montage du nom BSD, cependant, était étonnamment difficile pour moi, et si quelqu'un connaît un meilleur moyen, s'il vous plaît, partagez-le. Voici ma méthode. Il y a beaucoup de code, mais au moins cela fonctionne dans plusieurs versions OSX:

bool getMountPathByBSDName(CFTypeRef bsdName, std::string& dest) 
{ 
std::list<std::string> bsdNames; 
//for getChildBsdNames - see below =) 
getChildBsdNames(bsdName, bsdNames); 
DASessionRef session = DASessionCreate(kCFAllocatorDefault); 
if (!session) 
{ 
    return false; 
} 
for (const auto& bsdNameStr : bsdNames) 
{ 
    DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, bsdNameStr.c_str()); 
    if (disk) 
    { 
     CFDictionaryRef diskInfo = DADiskCopyDescription(disk); 
     if (diskInfo) 
     { 
      char buf[1024]; 
      CFURLRef fspath = (CFURLRef)CFDictionaryGetValue(diskInfo, kDADiskDescriptionVolumePathKey); 
      if (CFURLGetFileSystemRepresentation(fspath, false, (UInt8 *)buf, 1024)) 
      { 
       //for now, return the first found partition 
       dest = std::string(buf); 
       return true; 
      } 
     } 
    } 
} 
return false; 
} 

et enfin - fonction getChildBsdNames:

void getChildBsdNames(CFTypeRef bsdName, std::list<std::string>& tgtList) 
{ 
std::string bsdNameStr; 
if(!CFTypeRef2AsciiString(bsdName, bsdNameStr)) 
{ 
    return; 
} 
CFDictionaryRef matchingDictionary = IOBSDNameMatching(kIOMasterPortDefault, 0, bsdNameStr.c_str()); 
io_iterator_t it; 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &it); 
io_object_t service; 
while ((service = IOIteratorNext(it))) 
{ 
    io_iterator_t children; 
    io_registry_entry_t child; 

    IORegistryEntryGetChildIterator(service, kIOServicePlane, &children); 
    while((child = IOIteratorNext(children))) 
    { 
     CFTypeRef bsdNameChild = IORegistryEntrySearchCFProperty(child, 
                   kIOServicePlane, 
                   CFSTR (kIOBSDNameKey), 
                   kCFAllocatorDefault, 
                   kIORegistryIterateRecursively); 
     std::string bsdNameChildStr; 
     if (CFTypeRef2AsciiString(bsdNameChild, bsdNameChildStr)) 
     { 
      tgtList.push_back(bsdNameChildStr); 
     } 
    } 
} 
/** 
* The device could get name 'disk1s1, or just 'disk1'. In first case, the original bsd name would be 
* 'disk1', and the child bsd name would be 'disk1s1'. In second case, there would be no child bsd names, 
* but the original one is valid for further work (obtaining various properties). 
*/ 
if (tgtList.empty()) 
{ 
    tgtList.push_back(bsdNameStr); 
} 
} 

post-scriptum Il y a un mécanisme basé sur les événements, mais pour certaines raisons, ce n'était pas une solution. J'ai choisi l'interrogation, mais il n'est pas si difficile de modifier ce code pour le rendre basé sur les événements. Méfiez-vous, cependant, que le même pourrait venir plus tôt que le dispositif serait monté.