Работа с интерфейсами USB-устройства

В этой главе описываются, как разработать инструмент пространства пользователя, находящий и связывающийся с присоединенным USB-устройством и одним из его интерфейсов.

Используя интерфейсы USB-устройства

Приложения, работающие в OS X, заставляют доступ к USB-устройствам при помощи функций Набора I/O получать интерфейс устройства, тип плагина, указывающего функции, которые приложение может вызвать для передачи с устройством. Семья USB обеспечивает два типа интерфейса устройства:

Оба интерфейса устройства определяются в /System/Library/Frameworks/IOKit.framework/Headers/usb/IOUSBLib.h.

Когда необходимо установить или изменить его конфигурацию, связь с самим устройством обычно только необходима. Например, специфичные для поставщика устройства часто не конфигурируются, потому что нет никаких драйверов по умолчанию, устанавливающих определенную конфигурацию. В этом случае Ваше приложение должно использовать интерфейс устройства для устройства для установки конфигурации, этому нужны так интерфейсы, становятся доступными.

Процесс нахождения и связи с USB-устройством разделен на два набора шагов. Первые основы набора, как найти USB-устройство, получают интерфейс устройства типа IOUSBDeviceInterface для него, и набор или изменение его конфигурация. Второй набор описывает, как найти интерфейс в устройстве, получить интерфейс устройства типа IOUSBInterfaceInterface для него, и использование это для передачи с тем интерфейсом. Если необходимо связаться с несконфигурированным устройством или если необходимо изменить конфигурацию устройства, Вы следуете за обоими наборами шагов. Если необходимо связаться с устройством, уже сконфигурированным к спецификации, Вы следуете только за вторым набором шагов. Пример кода в Доступе к USB-устройству следует за обоими наборами шагов и расширяет их для включения установки уведомлений, которые это может получить, когда устройства динамично добавлены или демонтированы.

Следуйте за этим первым набором шагов только, чтобы установить или изменить конфигурацию устройства. Если устройство, которым Вы интересуетесь, уже сконфигурировано для Ваших потребностей, пропустите эти шаги и следуйте за вторым набором шагов.

  1. Найдите IOUSBDevice объект, представляющий устройство в Реестре I/O. Это включает установку соответствующего словаря с ключом от USB Общая Спецификация Класса (см. USB-устройства Открытия и Интерфейсы). Пример кода использует основные элементы kUSBVendorName и kUSBProductName найти определенное USB-устройство (это - второй ключ, перечисленный в Таблице 1-2).

  2. Создайте интерфейс устройства типа IOUSBDeviceInterface для устройства. Этот интерфейс устройства обеспечивает функции, выполняющие задачи, такие как установка или изменение конфигурации устройства, получение информации об устройстве и сброса устройства.

  3. Исследуйте конфигурации устройства с GetConfigurationDescriptorPtr, выберите надлежащий и вызов SetConfiguration установить конфигурацию устройства и инстанцировать IOUSBInterface объекты для той конфигурации.

Следуйте за этим вторым набором шагов, чтобы найти и выбрать интерфейс, получить интерфейс устройства для него и связаться с устройством.

  1. Создайте интерфейсный итератор для итерации по доступным интерфейсам.

  2. Создайте интерфейс устройства для каждого интерфейса, таким образом, можно исследовать его свойства и выбрать надлежащее. Чтобы сделать это, Вы создаете интерфейс устройства типа IOUSBInterfaceInterface. Этот интерфейс устройства обеспечивает функции, выполняющие задачи, такие как получение информации об интерфейсе, установка альтернативной установки интерфейса и доступ к ее каналам.

  3. Используйте USBInterfaceOpen функционируйте для открытия выбранного интерфейса. Это заставит каналы, связанные с интерфейсом быть инстанцированными так, можно исследовать свойства каждого и выбрать надлежащего.

  4. Свяжитесь с устройством через выбранный канал. Можно записать в и читать из канала синхронно или асинхронно — пример кода в Доступе к USB-устройству показывает, как сделать обоих.

Доступ к USB-устройству

Этот раздел обеспечивает отрывки примера кода, показывающие, как получить доступ к микросхеме EZ-USB Кипариса с 8 051 ядром микроконтроллера. Пример кода следует за первым набором шагов в разделе Using USB Device Interfaces, чтобы найти, что EZ-USB Кипариса вносит его значение по умолчанию, незапрограммированное состояние (также называемый «неструктурированным устройством»). Это тогда конфигурирует устройство и загружает встроенное микропрограммное обеспечение, предоставленное Кипарисом для программирования микросхемы для поведения как устройство, повторяющее всю информацию, которую это получает на ее объеме, передают по каналу к ее объему в канале.

Как только микросхема была запрограммирована, кусок устройства, представляющий значение по умолчанию, незапрограммированное устройство отсоединяется из Реестра I/O, и присоединяется новый кусок устройства, представляя запрограммированную микросхему. Для передачи с запрограммированной микросхемой (также называемый “объемным тестовым устройством”) пример кода должен выполнить первый набор шагов снова, чтобы найти устройство, создать интерфейс устройства для него и сконфигурировать его. Тогда это выполняет второй набор шагов, чтобы найти интерфейс, создать интерфейс устройства для него и протестировать устройство. Пример кода также показывает, как установить уведомления для динамического дополнения и демонтажа устройства.

Определения и глобальные переменные

Код в Примере Уведомления USB использует определения и глобальные переменные, показанные в Перечислении 2-1. Определение USE_ASYNC_IO позволяет Вам принимать решение использовать или синхронные или асинхронные вызовы, чтобы читать из и записать в микросхему путем комментирования строки или оставления его внутри, соответственно. Определение kTestMessage устанавливает простое сообщение для записи в устройство. Остающиеся определения являются определенными для микросхемы EZ-USB Кипариса.

  Определения перечисления 2-1 и глобальные переменные

#define USE_ASYNC_IO    //Comment this line out if you want to use
                        //synchronous calls for reads and writes
#define kTestMessage        "Bulk I/O Test"
#define k8051_USBCS         0x7f92
#define kOurVendorID        1351    //Vendor ID of the USB device
#define kOurProductID           8193    //Product ID of device BEFORE it
                                        //is programmed (raw device)
#define kOurProductIDBulkTest   4098    //Product ID of device AFTER it is
                                        //programmed (bulk test device)
 
//Global variables
static IONotificationPortRef    gNotifyPort;
static io_iterator_t            gRawAddedIter;
static io_iterator_t            gRawRemovedIter;
static io_iterator_t            gBulkTestAddedIter;
static io_iterator_t            gBulkTestRemovedIter;
static char                     gBuffer[64];

Основная Функция

main функция в проекте Уведомления USB В качестве примера (содержавшийся в файле main.c) выполняет следующие задачи.

  • Это устанавливает связь с Набором I/O и устанавливает соответствующий словарь для нахождения микросхемы EZ-USB Кипариса.

  • Это устанавливает асинхронное уведомление, которое вызовут, когда незапрограммированное (необработанное) устройство сначала присоединено к Реестру I/O и другому, чтобы быть вызванным, когда демонтировано устройство.

  • Это изменяет соответствующий словарь для нахождения запрограммированного (объемный тест) устройством.

  • Это устанавливает дополнительные уведомления, которые вызовут, когда объемное тестовое устройство сначала присоединено или демонтировано.

  • Это запускает цикл выполнения так уведомления, установленные, будет получен.

main функционируйте использует функции Набора I/O, чтобы установить и изменить соответствующий словарь и установить уведомления и Базовые функции Основы для установки цикла выполнения для получения уведомлений. Это вызывает следующие функции для доступа к и неструктурированному устройству и к объемному тестовому устройству.

  • RawDeviceAdded, показанный в Перечислении 2-3, выполняет итерации по набору согласующих устройств и создает интерфейс устройства для каждого. Это вызывает ConfigureDevice (показанный в Перечислении 2-5) для установки конфигурации устройства, и затем DownloadToDevice (показанный в Перечислении 2-6) для загрузки встроенного микропрограммного обеспечения для программирования его.

  • RawDeviceRemoved, показанный в Перечислении 2-4, выполняет итерации по набору согласующих устройств и выпускает каждого поочередно.

  • BulkTestDeviceAdded, показанный в Перечислении 2-7, выполняет итерации по новому набору согласующих устройств, создает интерфейс устройства для каждого и вызовы ConfigureDevice (показанный в Перечислении 2-5) для установки конфигурации устройства. Это тогда вызывает FindInterfaces (показанный в Перечислении 2-8) для получения доступа к интерфейсам на устройстве.

  • BulkTestDeviceRemoved выполняет итерации по новому набору согласующих устройств и выпускает каждого поочередно. Эта функция не показана в этой главе; посмотрите RawDeviceRemoved (Перечисление 2-4) для почти идентичной функции.

Перечисление 2-2  основная функция

int main (int argc, const char *argv[])
{
    mach_port_t             masterPort;
    CFMutableDictionaryRef  matchingDict;
    CFRunLoopSourceRef      runLoopSource;
    kern_return_t           kr;
    SInt32                  usbVendor = kOurVendorID;
    SInt32                  usbProduct = kOurProductID;
 
    // Get command line arguments, if any
    if (argc > 1)
        usbVendor = atoi(argv[1]);
    if (argc > 2)
        usbProduct = atoi(argv[2]);
 
    //Create a master port for communication with the I/O Kit
    kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
    if (kr || !masterPort)
    {
        printf("ERR: Couldn’t create a master I/O Kit port(%08x)\n", kr);
        return -1;
    }
    //Set up matching dictionary for class IOUSBDevice and its subclasses
    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    if (!matchingDict)
    {
        printf("Couldn’t create a USB matching dictionary\n");
        mach_port_deallocate(mach_task_self(), masterPort);
        return -1;
    }
 
    //Add the vendor and product IDs to the matching dictionary.
    //This is the second key in the table of device-matching keys of the
    //USB Common Class Specification
    CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorName),
                        CFNumberCreate(kCFAllocatorDefault,
                                     kCFNumberSInt32Type, &usbVendor));
    CFDictionarySetValue(matchingDict, CFSTR(kUSBProductName),
                        CFNumberCreate(kCFAllocatorDefault,
                                    kCFNumberSInt32Type, &usbProduct));
 
    //To set up asynchronous notifications, create a notification port and
    //add its run loop event source to the program’s run loop
    gNotifyPort = IONotificationPortCreate(masterPort);
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
                        kCFRunLoopDefaultMode);
 
    //Retain additional dictionary references because each call to
    //IOServiceAddMatchingNotification consumes one reference
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
 
    //Now set up two notifications: one to be called when a raw device
    //is first matched by the I/O Kit and another to be called when the
    //device is terminated
    //Notification of first match:
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOFirstMatchNotification, matchingDict,
                    RawDeviceAdded, NULL, &gRawAddedIter);
    //Iterate over set of matching devices to access already-present devices
    //and to arm the notification
    RawDeviceAdded(NULL, gRawAddedIter);
 
    //Notification of termination:
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOTerminatedNotification, matchingDict,
                    RawDeviceRemoved, NULL, &gRawRemovedIter);
    //Iterate over set of matching devices to release each one and to
    //arm the notification
    RawDeviceRemoved(NULL, gRawRemovedIter);
 
    //Now change the USB product ID in the matching dictionary to match
    //the one the device will have after the firmware has been downloaded
    usbProduct = kOurProductIDBulkTest;
    CFDictionarySetValue(matchingDict, CFSTR(kUSBProductName),
                        CFNumberCreate(kCFAllocatorDefault,
                                    kCFNumberSInt32Type, &usbProduct));
 
    //Now set up two notifications: one to be called when a bulk test device
    //is first matched by the I/O Kit and another to be called when the
    //device is terminated.
    //Notification of first match
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOFirstMatchNotification, matchingDict,
                    BulkTestDeviceAdded, NULL, &gBulkTestAddedIter);
    //Iterate over set of matching devices to access already-present devices
    //and to arm the notification
    BulkTestDeviceAdded(NULL, gBulkTestAddedIter);
 
    //Notification of termination
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOTerminatedNotification, matchingDict,
                    BulkTestDeviceRemoved, NULL, &gBulkTestRemovedIter);
    //Iterate over set of matching devices to release each one and to
    //arm the notification. NOTE: this function is not shown in this document.
    BulkTestDeviceRemoved(NULL, gBulkTestRemovedIter);
 
    //Finished with master port
    mach_port_deallocate(mach_task_self(), masterPort);
    masterPort = 0;
 
    //Start the run loop so notifications will be received
    CFRunLoopRun();
 
    //Because the run loop will run forever until interrupted,
    //the program should never reach this point
    return 0;
}

Работа с неструктурированным устройством

Теперь, когда Вы получили итератор для ряда согласующих устройств, можно использовать его, чтобы получить доступ к каждому неструктурированному устройству, сконфигурировать его и загрузить надлежащее встроенное микропрограммное обеспечение на него. Функция RawDeviceAdded (показанный в Перечислении 2-3), использует функции Набора I/O для создания интерфейса устройства для каждого устройства и затем вызывает следующие функции, чтобы сконфигурировать устройство и загрузить встроенное микропрограммное обеспечение на него.

  • ConfigureDevice, показанный в Перечислении 2-5, функции интерфейса устройства использования для получения числа конфигураций, исследуют первую и устанавливают конфигурацию устройства.

  • DownloadToDevice, показанный в Перечислении 2-6, загружает встроенное микропрограммное обеспечение в bulktest.c к устройству.

  Доступ перечисления 2-3 и программирование неструктурированного устройства

void RawDeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t               kr;
    io_service_t                usbDevice;
    IOCFPlugInInterface         **plugInInterface = NULL;
    IOUSBDeviceInterface        **dev = NULL;
    HRESULT                     result;
    SInt32                      score;
    UInt16                      vendor;
    UInt16                      product;
    UInt16                      release;
 
    while (usbDevice = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in
        kr = IOCreatePlugInInterfaceForService(usbDevice,
                    kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
                    &plugInInterface, &score);
        //Don’t need the device object after intermediate plug-in is created
        kr = IOObjectRelease(usbDevice);
        if ((kIOReturnSuccess != kr) || !plugInInterface)
        {
            printf("Unable to create a plug-in (%08x)\n", kr);
            continue;
        }
        //Now create the device interface
        result = (*plugInInterface)->QueryInterface(plugInInterface,
                        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                        (LPVOID *)&dev);
        //Don’t need the intermediate plug-in after device interface
        //is created
        (*plugInInterface)->Release(plugInInterface);
 
        if (result || !dev)
        {
            printf("Couldn’t create a device interface (%08x)\n",
                                                    (int) result);
            continue;
        }
 
        //Check these values for confirmation
        kr = (*dev)->GetDeviceVendor(dev, &vendor);
        kr = (*dev)->GetDeviceProduct(dev, &product);
        kr = (*dev)->GetDeviceReleaseNumber(dev, &release);
        if ((vendor != kOurVendorID) || (product != kOurProductID) ||
            (release != 1))
        {
            printf("Found unwanted device (vendor = %d, product = %d)\n",
                    vendor, product);
            (void) (*dev)->Release(dev);
            continue;
        }
 
        //Open the device to change its state
        kr = (*dev)->USBDeviceOpen(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to open device: %08x\n", kr);
            (void) (*dev)->Release(dev);
            continue;
        }
        //Configure device
        kr = ConfigureDevice(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to configure device: %08x\n", kr);
            (void) (*dev)->USBDeviceClose(dev);
            (void) (*dev)->Release(dev);
            continue;
        }
 
        //Download firmware to device
        kr = DownloadToDevice(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to download firmware to device: %08x\n", kr);
            (void) (*dev)->USBDeviceClose(dev);
            (void) (*dev)->Release(dev);
            continue;
        }
 
        //Close this device and release object
        kr = (*dev)->USBDeviceClose(dev);
        kr = (*dev)->Release(dev);
    }
}

Функция RawDeviceRemoved просто использует итератор, полученный из main функция (показанный в Перечислении 2-2) для выпуска каждого объекта устройства. Это также имеет эффект вооружения уведомления завершения неструктурированного устройства, таким образом, это уведомит программу будущих удалений устройства. RawDeviceRemoved показан в Перечислении 2-4.

Перечисление 2-4  , Выпускающее объекты неструктурированного устройства

void RawDeviceRemoved(void *refCon, io_iterator_t iterator)
{
    kern_return_t   kr;
    io_service_t    object;
 
    while (object = IOIteratorNext(iterator))
    {
        kr = IOObjectRelease(object);
        if (kr != kIOReturnSuccess)
        {
            printf("Couldn’t release raw device object: %08x\n", kr);
            continue;
        }
    }
}

Несмотря на то, что каждое USB-устройство имеет одну или более конфигураций, если устройство не является составным устройством класса, это было соответствующим AppleUSBComposite драйвер, автоматически устанавливающий первую конфигурацию, ни одну из тех конфигураций, возможно, был установлен. Поэтому Вашему приложению, вероятно, придется использовать функции интерфейса устройства, чтобы получить надлежащее значение конфигурации и использовать его для установки конфигурации устройства. В примере кода, функции ConfigureDevice (показанный в Перечислении 2-5), выполняет эту задачу. Фактически, это вызывают дважды: один раз RawDeviceAdded сконфигурировать неструктурированное устройство и снова BulkTestDeviceAdded (показанный в Перечислении 2-7) для конфигурирования объемного тестового устройства.

Перечисление 2-5  , Конфигурирующее USB-устройство

IOReturn ConfigureDevice(IOUSBDeviceInterface **dev)
{
    UInt8                           numConfig;
    IOReturn                        kr;
    IOUSBConfigurationDescriptorPtr configDesc;
 
    //Get the number of configurations. The sample code always chooses
    //the first configuration (at index 0) but your code may need a
    //different one
    kr = (*dev)->GetNumberOfConfigurations(dev, &numConfig);
    if (!numConfig)
        return -1;
 
    //Get the configuration descriptor for index 0
    kr = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &configDesc);
    if (kr)
    {
        printf("Couldn’t get configuration descriptor for index %d (err =
                %08x)\n", 0, kr);
        return -1;
    }
 
    //Set the device’s configuration. The configuration value is found in
    //the bConfigurationValue field of the configuration descriptor
    kr = (*dev)->SetConfiguration(dev, configDesc->bConfigurationValue);
    if (kr)
    {
        printf("Couldn’t set configuration to value %d (err = %08x)\n", 0,
                kr);
        return -1;
    }
    return kIOReturnSuccess;
}

Теперь, когда устройство сконфигурировано, можно загрузить встроенное микропрограммное обеспечение на него. Кипарис делает встроенное микропрограммное обеспечение доступным для программирования микросхемы EZ-USB для эмуляции различных устройств. Пример кода в этом документе использует встроенное микропрограммное обеспечение, программирующее микросхему, чтобы быть объемным тестовым устройством, устройством, берущим данные, которые это получает от его объема, передают по каналу, и повторяет его к его объему в канале. Встроенное микропрограммное обеспечение, содержавшееся в файле bulktest.c, массив INTEL_HEX_RECORD структуры (определенный в файле hex2c.h).

Функция DownloadToDevice использует функцию WriteToDevice (показанный вместе в Перечислении 2-6) для подготовки устройства, чтобы получить загрузку и затем записать информацию от каждой структуры до надлежащего адреса на устройстве. Когда все встроенное микропрограммное обеспечение было загружено, DownloadToDevice вызовы WriteToDevice в прошлый раз, который сообщит устройству, что загрузка завершена. В этой точке неструктурированное устройство отсоединяет себя от шины и повторно прикрепляет как объемное тестовое устройство. Это заставляет кусок устройства, представляющий неструктурированное устройство быть удаленным из Реестра I/O и нового куска устройства, представляя объемное тестовое устройство, быть присоединенным.

Перечисление 2-6  Две функции для загрузки встроенного микропрограммного обеспечения на неструктурированное устройство

IOReturn DownloadToDevice(IOUSBDeviceInterface **dev)
{
    int         i;
    UInt8       writeVal;
    IOReturn    kr;
 
    //Assert reset. This tells the device that the download is
    //about to occur
    writeVal = 1;   //For this device, a value of 1 indicates a download
    kr = WriteToDevice(dev, k8051_USBCS, 1, &writeVal);
    if (kr != kIOReturnSuccess)
    {
        printf("WriteToDevice reset returned err 0x%x\n", kr);
        (*dev)->USBDeviceClose(dev);
        (*dev)->Release(dev);
        return kr;
    }
 
    //Download firmware
    i = 0;
    while (bulktest[i].Type == 0)   //While bulktest[i].Type == 0, this is
    {                               //not the last firmware record to
                                    //download
        kr = WriteToDevice(dev, bulktest[i].Address,
                            bulktest[i].Length, bulktest[i].Data);
        if (kr != kIOReturnSuccess)
        {
            printf("WriteToDevice download %i returned err 0x%x\n", i,
                    kr);
            (*dev)->USBDeviceClose(dev);
            (*dev)->Release(dev);
            return kr;
        }
        i++;
    }
 
    //De-assert reset. This tells the device that the download is complete
    writeVal = 0;
    kr = WriteToDevice(dev, k8051_USBCS, 1, &writeVal);
    if (kr != kIOReturnSuccess)
        printf("WriteToDevice run returned err 0x%x\n", kr);
 
    return kr;
}
 
 
IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress,
                        UInt16 length, UInt8 writeBuffer[])
{
    IOUSBDevRequest     request;
 
    request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
                                                kUSBDevice);
    request.bRequest = 0xa0;
    request.wValue = deviceAddress;
    request.wIndex = 0;
    request.wLength = length;
    request.pData = writeBuffer;
 
    return (*dev)->DeviceRequest(dev, &request);
}

Работа с объемным тестовым устройством

После загрузки встроенного микропрограммного обеспечения на устройство неструктурированное устройство больше не присоединено к шине. Для получения доступа к объемному тестовому устройству Вы повторяете большинство тех же шагов, Вы раньше получали доступ к неструктурированному устройству.

  • Используйте итератор, полученный вызовом к IOServiceAddMatchingNotification в main функция (показанный в Перечислении 2-2) для итерации по ряду согласующих устройств.

  • Создайте интерфейс устройства для каждого устройства.

  • Сконфигурируйте устройство.

На сей раз, однако, следующий шаг должен найти интерфейсы на устройстве, таким образом, можно выбрать надлежащий и получить доступ к его каналам. Из-за общих черт этих задач, функции BulkTestDeviceAdded следует за той же схемой RawDeviceAdded функционируйте за исключением того, что вместо того, чтобы загрузить встроенное микропрограммное обеспечение на устройство, это вызывает FindInterfaces (показанный в Перечислении 2-8) для исследования доступных интерфейсов и их каналов. Код в Перечислении 2-7 заменяет большую часть BulkTestDeviceAdded код функции с комментариями, фокусирующимися на различиях между ним и RawDeviceAdded функция.

Перечисление 2-7  , Получающее доступ к объемному тестовому устройству

void BulkTestDeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t           kr;
    io_service_t            usbDevice;
    IOUSBDeviceInterface    **device=NULL;
 
    while (usbDevice = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in using the
        //IOCreatePlugInInterfaceForService function
 
        //Release the device object after getting the intermediate plug-in
 
        //Create the device interface using the QueryInterface function
 
        //Release the intermediate plug-in object
 
        //Check the vendor, product, and release number values to
        //confirm we’ve got the right device
 
        //Open the device before configuring it
        kr = (*device)->USBDeviceOpen(device);
 
        //Configure the device by calling ConfigureDevice
 
        //Close the device and release the device interface object if
        //the configuration is unsuccessful
 
        //Get the interfaces
        kr = FindInterfaces(device);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to find interfaces on device: %08x\n", kr);
            (*device)->USBDeviceClose(device);
            (*device)->Release(device);
            continue;
        }
 
//If using synchronous IO, close and release the device interface here
#ifndef USB_ASYNC_IO
        kr = (*device)->USBDeviceClose(device);
        kr = (*device)->Release(device);
#endif
    }
}

Функция BulkTestDeviceRemoved просто использует итератор, полученный из main функция (показанный в Перечислении 2-2) для выпуска каждого объекта устройства. Это также имеет эффект вооружения объемного тестового уведомления завершения устройства, таким образом, это уведомит программу будущих удалений устройства. BulkTestDeviceRemoved функция идентична RawDeviceRemoved функция (показанный в Перечислении 2-4), за исключением формулировки печатного ошибочного оператора.

Работа с интерфейсами

Теперь, когда Вы сконфигурировали устройство, у Вас есть доступ к его интерфейсам. FindInterfaces функция (показанный в Перечислении 2-8) создает итератор для итерации по всем интерфейсам на устройстве и затем создает интерфейс устройства для передачи с каждым. Для каждого найденного интерфейса функция открывает интерфейс, определяет, сколько конечных точек (или каналы) это имеет и распечатывает свойства каждого канала. Поскольку открытие интерфейса заставляет свои каналы быть инстанцированными, можно получить доступ к любому каналу при помощи его индекса канала. Индекс канала является числом канала в интерфейсе, в пределах от одного к числу конечных точек, возвращенных GetNumEndpoints. Можно связаться с каналом управления по умолчанию (описанный в Типах Передачи USB) от любого интерфейса при помощи индекса 0 канала, но обычно лучше использовать функции интерфейса устройства для самого устройства (см. использование IOUSBDeviceInterface функции в Перечислении 2-5).

Пример кода использует использование условной компиляции #ifdef и #ifndef продемонстрировать и синхронный и асинхронный I/O. Если Вы приняли решение протестировать синхронный I/O, FindInterfaces пишет тестовое сообщение (определенный в Перечислении 2-1) для передачи по каналу индекса 2 на устройстве и читает свое эхо перед возвратом. Для асинхронного I/O, FindInterfaces сначала создает источник события и добавляет его к циклу выполнения, создаваемому main функция (показанный в Перечислении 2-2). Это тогда устанавливает асинхронную запись, и считайте, что заставит уведомление быть отправленным после завершения. Функции завершения WriteCompletion и ReadCompletion показаны вместе в Перечислении 2-9.

  Открытие перечисления 2-8 взаимодействует через интерфейс на объемном тестовом устройстве

IOReturn FindInterfaces(IOUSBDeviceInterface **device)
{
    IOReturn                    kr;
    IOUSBFindInterfaceRequest   request;
    io_iterator_t               iterator;
    io_service_t                usbInterface;
    IOCFPlugInInterface         **plugInInterface = NULL;
    IOUSBInterfaceInterface     **interface = NULL;
    HRESULT                     result;
    SInt32                      score;
    UInt8                       interfaceClass;
    UInt8                       interfaceSubClass;
    UInt8                       interfaceNumEndpoints;
    int                         pipeRef;
 
#ifndef USE_ASYNC_IO
    UInt32                      numBytesRead;
    UInt32                      i;
#else
    CFRunLoopSourceRef          runLoopSource;
#endif
 
    //Placing the constant kIOUSBFindInterfaceDontCare into the following
    //fields of the IOUSBFindInterfaceRequest structure will allow you
    //to find all the interfaces
    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
 
    //Get an iterator for the interfaces on the device
    kr = (*device)->CreateInterfaceIterator(device,
                                        &request, &iterator);
    while (usbInterface = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in
        kr = IOCreatePlugInInterfaceForService(usbInterface,
                            kIOUSBInterfaceUserClientTypeID,
                            kIOCFPlugInInterfaceID,
                            &plugInInterface, &score);
        //Release the usbInterface object after getting the plug-in
        kr = IOObjectRelease(usbInterface);
        if ((kr != kIOReturnSuccess) || !plugInInterface)
        {
            printf("Unable to create a plug-in (%08x)\n", kr);
            break;
        }
 
        //Now create the device interface for the interface
        result = (*plugInInterface)->QueryInterface(plugInInterface,
                    CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
                    (LPVOID *) &interface);
        //No longer need the intermediate plug-in
        (*plugInInterface)->Release(plugInInterface);
 
        if (result || !interface)
        {
            printf("Couldn’t create a device interface for the interface
                    (%08x)\n", (int) result);
            break;
        }
 
        //Get interface class and subclass
        kr = (*interface)->GetInterfaceClass(interface,
                                                    &interfaceClass);
        kr = (*interface)->GetInterfaceSubClass(interface,
                                                &interfaceSubClass);
 
        printf("Interface class %d, subclass %d\n", interfaceClass,
                                                    interfaceSubClass);
 
        //Now open the interface. This will cause the pipes associated with
        //the endpoints in the interface descriptor to be instantiated
        kr = (*interface)->USBInterfaceOpen(interface);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to open interface (%08x)\n", kr);
            (void) (*interface)->Release(interface);
            break;
        }
 
        //Get the number of endpoints associated with this interface
        kr = (*interface)->GetNumEndpoints(interface,
                                        &interfaceNumEndpoints);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to get number of endpoints (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
 
        printf("Interface has %d endpoints\n", interfaceNumEndpoints);
        //Access each pipe in turn, starting with the pipe at index 1
        //The pipe at index 0 is the default control pipe and should be
        //accessed using (*usbDevice)->DeviceRequest() instead
        for (pipeRef = 1; pipeRef <= interfaceNumEndpoints; pipeRef++)
        {
            IOReturn        kr2;
            UInt8           direction;
            UInt8           number;
            UInt8           transferType;
            UInt16          maxPacketSize;
            UInt8           interval;
            char            *message;
 
            kr2 = (*interface)->GetPipeProperties(interface,
                                        pipeRef, &direction,
                                        &number, &transferType,
                                        &maxPacketSize, &interval);
            if (kr2 != kIOReturnSuccess)
                printf("Unable to get properties of pipe %d (%08x)\n",
                                        pipeRef, kr2);
            else
            {
                printf("PipeRef %d: ", pipeRef);
                switch (direction)
                {
                    case kUSBOut:
                        message = "out";
                        break;
                    case kUSBIn:
                        message = "in";
                        break;
                    case kUSBNone:
                        message = "none";
                        break;
                    case kUSBAnyDirn:
                        message = "any";
                        break;
                    default:
                        message = "???";
                }
                printf("direction %s, ", message);
 
                switch (transferType)
                {
                    case kUSBControl:
                        message = "control";
                        break;
                    case kUSBIsoc:
                        message = "isoc";
                        break;
                    case kUSBBulk:
                        message = "bulk";
                        break;
                    case kUSBInterrupt:
                        message = "interrupt";
                        break;
                    case kUSBAnyType:
                        message = "any";
                        break;
                    default:
                        message = "???";
                }
                printf("transfer type %s, maxPacketSize %d\n", message,
                                                    maxPacketSize);
            }
        }
 
#ifndef USE_ASYNC_IO    //Demonstrate synchronous I/O
        kr = (*interface)->WritePipe(interface, 2, kTestMessage,
                                            strlen(kTestMessage));
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform bulk write (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
 
        printf("Wrote \"%s\" (%ld bytes) to bulk endpoint\n", kTestMessage,
                                        (UInt32) strlen(kTestMessage));
 
        numBytesRead = sizeof(gBuffer) - 1; //leave one byte at the end
                                             //for NULL termination
        kr = (*interface)->ReadPipe(interface, 9, gBuffer,
                                            &numBytesRead);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform bulk read (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
 
        //Because the downloaded firmware echoes the one’s complement of the
        //message, now complement the buffer contents to get the original data
        for (i = 0; i < numBytesRead; i++)
            gBuffer[i] = ~gBuffer[i];
 
        printf("Read \"%s\" (%ld bytes) from bulk endpoint\n", gBuffer,
                    numBytesRead);
 
#else   //Demonstrate asynchronous I/O
        //As with service matching notifications, to receive asynchronous
        //I/O completion notifications, you must create an event source and
        //add it to the run loop
        kr = (*interface)->CreateInterfaceAsyncEventSource(
                                    interface, &runLoopSource);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to create asynchronous event source
                                    (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
                            kCFRunLoopDefaultMode);
        printf("Asynchronous event source added to run loop\n");
        bzero(gBuffer, sizeof(gBuffer));
        strcpy(gBuffer, kTestMessage);
        kr = (*interface)->WritePipeAsync(interface, 2, gBuffer,
                                    strlen(gBuffer),
                                    WriteCompletion, (void *) interface);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform asynchronous bulk write (%08x)\n",
                                                    kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
#endif
        //For this test, just use first interface, so exit loop
        break;
    }
    return kr;
}

Когда асинхронное действие записи завершено, WriteCompletion функция вызвана уведомлением. WriteCompletion тогда вызывает функцию интерфейса ReadPipeAsync выполнять асинхронное чтение от канала. Когда чтение завершено, управление передает ReadCompletion который просто распечатывает сообщения о состоянии и добавляет a NULL завершение к глобальному буферу, содержащему тестовое сообщение, читало из устройства. WriteCompletion и ReadCompletion функции показаны вместе в Перечислении 2-9.

Перечисление 2-9  Две асинхронных функции завершения I/O

void WriteCompletion(void *refCon, IOReturn result, void *arg0)
{
    IOUSBInterfaceInterface **interface = (IOUSBInterfaceInterface **) refCon;
    UInt32                  numBytesWritten = (UInt32) arg0;
    UInt32                  numBytesRead;
 
    printf("Asynchronous write complete\n");
    if (result != kIOReturnSuccess)
    {
        printf("error from asynchronous bulk write (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
    printf("Wrote \"%s\" (%ld bytes) to bulk endpoint\n", kTestMessage,
                                        numBytesWritten);
 
    numBytesRead = sizeof(gBuffer) - 1; //leave one byte at the end for
                                            //NULL termination
    result = (*interface)->ReadPipeAsync(interface, 9, gBuffer,
                                    numBytesRead, ReadCompletion, refCon);
    if (result != kIOReturnSuccess)
    {
        printf("Unable to perform asynchronous bulk read (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
}
 
 
void ReadCompletion(void *refCon, IOReturn result, void *arg0)
{
    IOUSBInterfaceInterface **interface = (IOUSBInterfaceInterface **) refCon;
    UInt32      numBytesRead = (UInt32) arg0;
    UInt32      i;
 
    printf("Asynchronous bulk read complete\n");
    if (result != kIOReturnSuccess) {
        printf("error from async bulk read (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
    //Check the complement of the buffer’s contents for original data
    for (i = 0; i < numBytesRead; i++)
        gBuffer[i] = ~gBuffer[i];
 
    printf("Read \"%s\" (%ld bytes) from bulk endpoint\n", gBuffer,
                                                    numBytesRead);
}