2015-12-25 11 views
1

Здравствуйте, я новичок в программировании xCode и Mac OS и немного потерял работу с USB и последовательными устройствами.Составьте список USB-устройств с определенным ID

На самом деле я пытаюсь запрограммировать внешний интерфейс max/msp, который отображает последовательность последовательных BSD USB-устройств, которые соответствуют определенному идентификатору продукта, идентификатору поставщика и имени.

Насколько я иду, я получил эту программу, что список Серийного BSD Пути:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/ioctl.h> 
#include <errno.h> 
#include <paths.h> 
#include <termios.h> 
#include <sysexits.h> 
#include <sys/param.h> 
#include <sys/select.h> 
#include <sys/time.h> 
#include <time.h> 
#include <CoreFoundation/CoreFoundation.h> 
#include <IOKit/IOKitLib.h> 
#include <IOKit/serial/IOSerialKeys.h> 
#include <IOKit/serial/ioss.h> 
#include <IOKit/IOBSD.h> 


// Function prototypes 
static kern_return_t findModems(io_iterator_t *matchingServices); 
static kern_return_t getModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize); 

// Returns an iterator across all known modems. Caller is responsible for 
// releasing the iterator when iteration is complete. 
static kern_return_t findModems(io_iterator_t *matchingServices) 
{ 
    kern_return_t   kernResult; 
    CFMutableDictionaryRef classesToMatch; 

    // Serial devices are instances of class IOSerialBSDClient. 
    // Create a matching dictionary to find those instances. 
    classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); 
    if (classesToMatch == NULL) { 
     printf("IOServiceMatching returned a NULL dictionary.\n"); 
    } 
    else { 
     // Look for devices that claim to be modems. 
     CFDictionarySetValue(classesToMatch, 
          CFSTR(kIOSerialBSDTypeKey), 
          CFSTR(kIOSerialBSDAllTypes)); 
    } 

    // Get an iterator across all matching devices. 
    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); 
    if (KERN_SUCCESS != kernResult) { 
     printf("IOServiceGetMatchingServices returned %d\n", kernResult); 
     goto exit; 
    } 

exit: 
    return kernResult; 
} 

static kern_return_t getModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize) 
{ 
    io_object_t  modemService; 
    kern_return_t kernResult = KERN_FAILURE; 
    Boolean   modemFound = false; 

    // Initialize the returned path 
    *bsdPath = '\0'; 

    // Iterate across all modems found. In this example, we bail after finding the first modem. 

    while ((modemService = IOIteratorNext(serialPortIterator))) { 
     CFTypeRef bsdPathAsCFString; 

     bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, 
                  CFSTR(kIOCalloutDeviceKey), 
                  kCFAllocatorDefault, 
                  0); 
     if (bsdPathAsCFString) { 
      Boolean result; 
      result = CFStringGetCString(bsdPathAsCFString, 
             bsdPath, 
             maxPathSize, 
             kCFStringEncodingUTF8); 
      CFRelease(bsdPathAsCFString); 

      if (result) { 
       printf("Modem found with BSD path: %s", bsdPath); 
       modemFound = true; 
       kernResult = KERN_SUCCESS; 
      } 
     } 

     printf("\n"); 

     // Release the io_service_t now that we are done with it. 
     (void) IOObjectRelease(modemService); 
    } 

    return kernResult; 
} 

int main(int argc, const char * argv[]) 
{ 
    kern_return_t kernResult; 
    io_iterator_t serialPortIterator; 
    char   bsdPath[MAXPATHLEN]; 

    kernResult = findModems(&serialPortIterator); 
    if (KERN_SUCCESS != kernResult) { 
     printf("No modems were found.\n"); 
    } 

    kernResult = getModemPath(serialPortIterator, bsdPath, sizeof(bsdPath)); 
    if (KERN_SUCCESS != kernResult) { 
     printf("Could not get path for modem.\n"); 
    } 

    IOObjectRelease(serialPortIterator); 

    return EX_OK; 
} 

и этот другой код, который поиск по USB с конкретными идентификаторами:

#include <CoreFoundation/CoreFoundation.h> 
#include <IOKit/IOKitLib.h> 
#include <IOKit/IOMessage.h> 
#include <IOKit/IOCFPlugIn.h> 
#include <IOKit/usb/IOUSBLib.h> 

// Change these two constants to match your device's idVendor and idProduct. 
// Or, just pass your idVendor and idProduct as command line arguments when running this sample. 
#define kMyVendorID   0x04D8 
#define kMyProductID  0x000A 

typedef struct MyPrivateData { 
    io_object_t    notification; 
    IOUSBDeviceInterface **deviceInterface; 
    CFStringRef    deviceName; 
    unsigned int   locationID; 
} MyPrivateData; 

static IONotificationPortRef gNotifyPort; 
static io_iterator_t   gAddedIter; 
static CFRunLoopRef    gRunLoop; 

//================================================================================================ 
// 
// DeviceNotification 
// 
// This routine will get called whenever any kIOGeneralInterest notification happens. We are 
// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other 
// messages are defined in IOMessage.h. 
// 
//================================================================================================ 
void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) 
{ 
    kern_return_t kr; 
    MyPrivateData *privateDataRef = (MyPrivateData *) refCon; 

    if (messageType == kIOMessageServiceIsTerminated) { 
     fprintf(stderr, "Device removed.\n"); 

     // Dump our private data to stderr just to see what it looks like. 
     fprintf(stderr, "privateDataRef->deviceName: "); 
     CFShow(privateDataRef->deviceName); 
     fprintf(stderr, "privateDataRef->locationID: 0x%x.\n\n", privateDataRef->locationID); 

     // Free the data we're no longer using now that the device is going away 
     CFRelease(privateDataRef->deviceName); 

     if (privateDataRef->deviceInterface) { 
      kr = (*privateDataRef->deviceInterface)->Release(privateDataRef->deviceInterface); 
     } 

     kr = IOObjectRelease(privateDataRef->notification); 

     free(privateDataRef); 
    } 
} 

//================================================================================================ 
// 
// DeviceAdded 
// 
// This routine is the callback for our IOServiceAddMatchingNotification. When we get called 
// we will look at all the devices that were added and we will: 
// 
// 1. Create some private data to relate to each device (in this case we use the service's name 
//  and the location ID of the device 
// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device, 
//  using the refCon field to store a pointer to our private data. When we get called with 
//  this interest notification, we can grab the refCon and access our private data. 
// 
//================================================================================================ 
void DeviceAdded(void *refCon, io_iterator_t iterator) 
{ 
    kern_return_t  kr; 
    io_service_t  usbDevice; 
    IOCFPlugInInterface **plugInInterface = NULL; 
    SInt32    score; 
    HRESULT    res; 

    while ((usbDevice = IOIteratorNext(iterator))) { 
     io_name_t  deviceName; 
     CFStringRef  deviceNameAsCFString; 
     MyPrivateData *privateDataRef = NULL; 
     unsigned int locationID; 

     printf("Device added.\n"); 

     // Add some app-specific information about this device. 
     // Create a buffer to hold the data. 
     privateDataRef = malloc(sizeof(MyPrivateData)); 
     bzero(privateDataRef, sizeof(MyPrivateData)); 

     // Get the USB device's name. 
     kr = IORegistryEntryGetName(usbDevice, deviceName); 
     if (KERN_SUCCESS != kr) { 
      deviceName[0] = '\0'; 
     } 

     deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, 
                 kCFStringEncodingASCII); 

     // Dump our data to stderr just to see what it looks like. 
     fprintf(stderr, "deviceName: "); 
     CFShow(deviceNameAsCFString); 

     // Save the device's name to our private data.   
     privateDataRef->deviceName = deviceNameAsCFString; 

     // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface 
     // for our device. This will create the necessary connections between our userland application and the 
     // kernel object for the USB Device. 
     kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, 
               &plugInInterface, &score); 

     if ((kIOReturnSuccess != kr) || !plugInInterface) { 
      fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr); 
      continue; 
     } 

     // Use the plugin interface to retrieve the device interface. 
     res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), 
               (LPVOID*) &privateDataRef->deviceInterface); 

     // Now done with the plugin interface. 
     (*plugInInterface)->Release(plugInInterface); 

     if (res || privateDataRef->deviceInterface == NULL) { 
      fprintf(stderr, "QueryInterface returned %d.\n", (int) res); 
      continue; 
     } 

     // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h. 
     // In this case, fetch the locationID. The locationID uniquely identifies the device 
     // and will remain the same, even across reboots, so long as the bus topology doesn't change. 

     kr = (*privateDataRef->deviceInterface)->GetLocationID(privateDataRef->deviceInterface, &locationID); 
     if (KERN_SUCCESS != kr) { 
      fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr); 
      continue; 
     } 
     else { 
      fprintf(stderr, "Location ID: 0x%x\n\n", locationID); 
     } 

     privateDataRef->locationID = locationID; 

     // Register for an interest notification of this device being removed. Use a reference to our 
     // private data as the refCon which will be passed to the notification callback. 
     kr = IOServiceAddInterestNotification(gNotifyPort,      // notifyPort 
               usbDevice,      // service 
               kIOGeneralInterest,    // interestType 
               DeviceNotification,    // callback 
               privateDataRef,     // refCon 
               &(privateDataRef->notification) // notification 
              ); 

     if (KERN_SUCCESS != kr) { 
      printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr); 
     } 

     // Done with this USB device; release the reference added by IOIteratorNext 
     kr = IOObjectRelease(usbDevice); 
    } 
} 

//================================================================================================ 
// 
// SignalHandler 
// 
// This routine will get called when we interrupt the program (usually with a Ctrl-C from the 
// command line). 
// 
//================================================================================================ 
void SignalHandler(int sigraised) 
{ 
    fprintf(stderr, "\nInterrupted.\n"); 

    exit(0); 
} 

//================================================================================================ 
// main 
//================================================================================================ 
int main(int argc, const char *argv[]) 
{ 
    CFMutableDictionaryRef matchingDict; 
    CFRunLoopSourceRef  runLoopSource; 
    CFNumberRef    numberRef; 
    kern_return_t   kr; 
    long     usbVendor = kMyVendorID; 
    long     usbProduct = kMyProductID; 
    sig_t     oldHandler; 

    // Set up a signal handler so we can clean up when we're interrupted from the command line 
    // Otherwise we stay in our run loop forever. 
    oldHandler = signal(SIGINT, SignalHandler); 
    if (oldHandler == SIG_ERR) { 
     fprintf(stderr, "Could not establish new signal handler."); 
    } 

    fprintf(stderr, "Looking for devices matching vendor ID=%ld and product ID=%ld.\n", usbVendor, usbProduct); 

    // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow 
    // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7. 
    // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X" 
    // <http://developer.apple.com/qa/qa2001/qa1076.html>. 
    // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching 
    // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will 
    // consume this dictionary reference, so there is no need to release it later on. 

    matchingDict = IOServiceMatching(kIOUSBDeviceClassName); // Interested in instances of class 
                   // IOUSBDevice and its subclasses 
    if (matchingDict == NULL) { 
     fprintf(stderr, "IOServiceMatching returned NULL.\n"); 
     return -1; 
    } 

    // We are interested in all USB devices (as opposed to USB interfaces). The Common Class Specification 
    // tells us that we need to specify the idVendor, idProduct, and bcdDevice fields, or, if we're not interested 
    // in particular bcdDevices, just the idVendor and idProduct. Note that if we were trying to match an 
    // IOUSBInterface, we would need to set more values in the matching dictionary (e.g. idVendor, idProduct, 
    // bInterfaceNumber and bConfigurationValue. 

    // Create a CFNumber for the idVendor and set the value in the dictionary 
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbVendor); 
    CFDictionarySetValue(matchingDict, 
         CFSTR(kUSBVendorID), 
         numberRef); 
    CFRelease(numberRef); 

    // Create a CFNumber for the idProduct and set the value in the dictionary 
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbProduct); 
    CFDictionarySetValue(matchingDict, 
         CFSTR(kUSBProductID), 
         numberRef); 
    CFRelease(numberRef); 
    numberRef = NULL; 

    // Create a notification port and add its run loop event source to our run loop 
    // This is how async notifications get set up. 

    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); 
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); 

    gRunLoop = CFRunLoopGetCurrent(); 
    CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode); 

    // Now set up a notification to be called when a device is first matched by I/O Kit. 
    kr = IOServiceAddMatchingNotification(gNotifyPort,     // notifyPort 
              kIOFirstMatchNotification, // notificationType 
              matchingDict,     // matching 
              DeviceAdded,     // callback 
              NULL,       // refCon 
              &gAddedIter     // notification 
             );   

    // Iterate once to get already-present devices and arm the notification  
    DeviceAdded(NULL, gAddedIter); 

    // Start the run loop. Now we'll receive notifications. 
    fprintf(stderr, "Starting run loop.\n\n"); 
    CFRunLoopRun(); 

    // We should never get here 
    fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n"); 
    return 0; 
} 

Но я не слияние обоих. Любая помощь будет очень признательна.

ответ

3

Хорошо, поэтому с дальнейшим поиском я пришел через IORegistryExplorer, который вы можете скачать как часть оборудования IO Tools с веб-сайта Apple Developer. Насколько я понял, первый код изолирует узел IOSerialBSDClient, который является дочерним элементом USB-устройств, обнаруженным вторым кодом. Поэтому мне нужно найти решение для доступа к дочернему ресурсу моего USB-устройства или родительского свойства моего узла BSD.

Вот что я, наконец, нашел в открытом источнике этих двух библиотек: libserialport & libusb.

На MacOS следующего код сделать трюк:

// Variable declaration 
int pid, vid; 
CFTypeRef cf_vendor, cf_product;  

// Search properties among parents of the current modemService 
cf_vendor = IORegistryEntrySearchCFProperty(modemService, kIOServicePlane, 
              CFSTR("idVendor"), 
              kCFAllocatorDefault, 
              kIORegistryIterateRecursively 
              | kIORegistryIterateParents); 

cf_product = IORegistryEntrySearchCFProperty(modemService, kIOServicePlane, 
              CFSTR("idProduct"), 
              kCFAllocatorDefault, 
              kIORegistryIterateRecursively 
              | kIORegistryIterateParents); 

// Decode & print VID & PID 
if (cf_vendor && cf_product && 
    CFNumberGetValue(cf_vendor , kCFNumberIntType, &vid) && 
    CFNumberGetValue(cf_product, kCFNumberIntType, &pid)) { 
    printf("\nFound matching USB VID: %04X PID: %04X", vid, pid); 
} 

// Release CFTypeRef 
if (cf_vendor) CFRelease(cf_vendor); 
if (cf_product) CFRelease(cf_product); 

Надеется, что это поможет будущим людям.