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

Эта глава обеспечивает поэтапные инструкции, включая списки примера кода, для доступа к устройству класса HID на более старых версиях OS X. При разработке нового кода для OS X v10.5 или позже необходимо использовать APIs, описанный в Доступе к Устройству HID..

Эти примеры предполагают, что Вы уже получили итератор для списка устройств, соответствующих определенные свойства (например, при помощи MyFindHIDDevices функция, Перечисление 3-3).

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

Эта глава содержит следующие разделы:

  1. Печать свойств устройства класса HID

  2. Создание интерфейса устройства класса HID

  3. Получение cookie HID

  4. Выполнение операций на устройстве класса HID

  5. Используя обратные вызовы очереди

Для кода, чтобы помочь Вам определить местоположение устройств класса HID, посмотрите Наследство Обзор Доступа HID.

Печать свойств устройства класса HID

Так как свойства устройства класса HID часто содержат вложенные словари элемента, функции, получающие доступ к элементам, должны сделать так рекурсивным способом. В примере кода, MyShowHIDProperties функционируйте использует CFShow для печати словаря.

Перечисление 4-1  функция, показывающая все свойства для устройства класса HID

static void MyShowHIDProperties(io_registry_entry_t hidDevice)
{
        kern_return_t                                   result;
        CFMutableDictionaryRef                          properties = 0;
        char                                            path[512];
 
        result = IORegistryEntryGetPath(hidDevice, kIOServicePlane, path);
        if (result == KERN_SUCCESS)
                printf("[ %s ]", path);
 
        //Create a CF dictionary representation of the I/O
        //Registry entry's properties
        result = IORegistryEntryCreateCFProperties(hidDevice, &properties,
                kCFAllocatorDefault, kNilOptions);
        if ((result == KERN_SUCCESS) && properties)
        {
                CFShow(properties);
                /* Some common properties of interest include:
                    kIOHIDTransportKey, kIOHIDVendorIDKey,
                    kIOHIDProductIDKey, kIOHIDVersionNumberKey,
                    kIOHIDManufacturerKey, kIOHIDProductKey,
                    kIOHIDSerialNumberKey, kIOHIDLocationIDKey,
                    kIOHIDPrimaryUsageKey, kIOHIDPrimaryUsagePageKey,
                    and kIOHIDElementKey.
                */
 
                //Release the properties dictionary
                CFRelease(properties);
        }
        printf("\n\n");
}

MyShowHIDProperties берет устройство класса HID, найденное в Реестре I/O и использовании IORegistryEntryCreateCFProperties создать представление словаря CF свойств устройства. Это тогда вызывает CFShow, чтобы отформатировать информацию и распечатать его на экран.

Создание интерфейса устройства класса HID

Интерфейс устройства обеспечивает функции, которые Ваше приложение или другой код, работающий на OS X, могут использовать для доступа к устройству. MyCreateHIDDeviceInterface функция, показанная в Перечислении 4-2, создает и возвращает интерфейс устройства класса HID на основе переданного объекта из Реестра I/O. Это также демонстрирует, как получить имя класса переданного объекта путем вызывания функции Набора I/O IOObjectGetClass.

Создать интерфейс устройства класса HID, MyCreateHIDDeviceInterface функция выполняет следующие шаги:

  1. Это получает интерфейс типа IOCFPlugInInterface для устройства класса HID, представленного переданным hidDevice объект.

    Для получения этого интерфейса это вызывает IOCreatePlugInInterfaceForService функция, передавая значение kIOHIDDeviceUserClientTypeID для сменного параметра типа и значения kIOCFPlugInInterfaceID для интерфейсного параметра типа. kIOHIDDeviceUserClientTypeID определяется в IOHIDLib.h и kIOCFPlugInInterfaceID определяется в IOCFPlugIn.h (расположенный в IOKit.framework).

  2. Это получает интерфейс устройства класса HID путем вызова QueryInterface метод IOCFPlugInInterface объект. Для указания интерфейса устройства класса HID это использует следующий термин:

    CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID)

    Этот срок производит UUID (универсально уникальный идентификатор) для интерфейса устройства. UUIDs описаны в Базовой документации разработчика Основы, доступной в разделе Reference Library> Core Foundation веб-сайта документации разработчика. kIOHIDDeviceInterfaceID определяется в IOHIDLib.h.

  3. Это выпускает IOCFPlugInInterface возразите и возвращает интерфейс устройства класса HID в hidDeviceInterface параметр.

Перечисление 4-2  функция, создающая и возвращающая интерфейс устройства класса HID на основе переданного объекта из Реестра I/O

static void MyCreateHIDDeviceInterface(io_object_t hidDevice,
                            IOHIDDeviceInterface ***hidDeviceInterface)
{
    io_name_t               className;
    IOCFPlugInInterface     **plugInInterface = NULL;
    HRESULT                 plugInResult = S_OK;
    SInt32                  score = 0;
    IOReturn                ioReturnValue = kIOReturnSuccess;
 
    ioReturnValue = IOObjectGetClass(hidDevice, className);
 
    print_errmsg_if_io_err(ioReturnValue, "Failed to get class name.");
 
    printf("Found device type %s\n", className);
 
    ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
                            kIOHIDDeviceUserClientTypeID,
                            kIOCFPlugInInterfaceID,
                            &plugInInterface,
                            &score);
 
    if (ioReturnValue == kIOReturnSuccess)
    {
        //Call a method of the intermediate plug-in to create the device
        //interface
        plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
                            CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
                            (LPVOID *) hidDeviceInterface);
        print_errmsg_if_err(plugInResult != S_OK, "Couldn't create HID class device interface");
 
        (*plugInInterface)->Release(plugInInterface);
    }
}

Получение cookie HID

Следующая функция, getHIDCookies (показанный в Перечислении 4-3), определенные cookie мест, связанные с осью X устройства класса HID, кнопкой 1, кнопкой 2 и кнопкой 3 в структуру данных, таким образом, очередь, обрабатывающая функции, может добавить эти элементы к очереди.

Перечисление 4-3  функция, хранящая выбранные cookie в глобальных переменных для более позднего использования

typedef struct cookie_struct
{
        IOHIDElementCookie gXAxisCookie;
        IOHIDElementCookie gButton1Cookie;
        IOHIDElementCookie gButton2Cookie;
        IOHIDElementCookie gButton3Cookie;
} *cookie_struct_t;
 
cookie_struct_t getHIDCookies(IOHIDDeviceInterface122 **handle)
{
        cookie_struct_t cookies = memset(malloc(sizeof(*cookies)), 0,
            sizeof(*cookies));
        CFTypeRef                               object;
        long                                    number;
        IOHIDElementCookie                      cookie;
        long                                    usage;
        long                                    usagePage;
        CFArrayRef                              elements; //
        CFDictionaryRef                         element;
        IOReturn                                success;
 
        if (!handle || !(*handle)) return cookies;
 
        // Copy all elements, since we're grabbing most of the elements
        // for this device anyway, and thus, it's faster to iterate them
        // ourselves. When grabbing only one or two elements, a matching
        // dictionary should be passed in here instead of NULL.
        success = (*handle)->copyMatchingElements(handle, NULL, &elements);
 
        printf("LOOKING FOR ELEMENTS.\n");
        if (success == kIOReturnSuccess) {
                CFIndex i;
                printf("ITERATING...\n");
                for (i=0; i<CFArrayGetCount(elements); i++)
                {
                        element = CFArrayGetValueAtIndex(elements, i);
                        // printf("GOT ELEMENT.\n");
 
                        //Get cookie
                        object = (CFDictionaryGetValue(element,
                            CFSTR(kIOHIDElementCookieKey)));
                        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
                            continue;
                        if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
                            &number))
                                continue;
                        cookie = (IOHIDElementCookie) number;
 
                        //Get usage
                        object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey));
                        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
                            continue;
                        if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
                            &number))
                                continue;
                        usage = number;
 
                        //Get usage page
                        object = CFDictionaryGetValue(element,
                            CFSTR(kIOHIDElementUsagePageKey));
                        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
                            continue;
                        if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
                            &number))
                                continue;
                        usagePage = number;
 
                        //Check for x axis
                        if (usage == 0x30 && usagePage == 0x01)
                            cookies->gXAxisCookie = cookie;
                        //Check for buttons
                        else if (usage == 0x01 && usagePage == 0x09)
                            cookies->gButton1Cookie = cookie;
                        else if (usage == 0x02 && usagePage == 0x09)
                            cookies->gButton2Cookie = cookie;
                        else if (usage == 0x03 && usagePage == 0x09)
                            cookies->gButton3Cookie = cookie;
                }
                printf("DONE.\n");
        } else {
                printf("copyMatchingElements failed with error %d\n", success);
        }
 
        return cookies;
}

Выполнение операций на устройстве класса HID

Как только Вы нашли устройство класса HID в Реестре I/O и создали интерфейс устройства для него, можно выполнить операции, такие как:

Пример кода в этом разделе выполняет эти типы операций через тестовую функцию MyTestHIDDeviceInterface, показанный в Перечислении 4-4. Эта функция передается интерфейс устройства класса HID, и она сначала вызывает функцию интерфейса устройства open открыть устройство. Затем, это вызывает MyTestQueues, показанный в Перечислении 4-5, чтобы создать и управлять очередью. Затем это вызывает MyTestHIDInterface получить различные значения элемента и распечатать их. Наконец, это закрывает устройство путем вызывания функции интерфейса устройства close.

Перечисление 4-4  функция, выполняющая тестирование в переданном интерфейсе устройства класса HID

static void MyTestHIDDeviceInterface(IOHIDDeviceInterface **hidDeviceInterface, cookie_struct_t cookies)
{
        IOReturn ioReturnValue = kIOReturnSuccess;
 
        //open the device
        ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, 0);
        printf("Open result = %d\n", ioReturnValue);
 
        //test queue interface
        MyTestQueues(hidDeviceInterface, cookies);
 
        //test the interface
        MyTestHIDInterface(hidDeviceInterface, cookies);
 
        //close the device
        if (ioReturnValue == KERN_SUCCESS)
                ioReturnValue = (*hidDeviceInterface)->close(hidDeviceInterface);
 
        //release the interface
        (*hidDeviceInterface)->Release(hidDeviceInterface);
}

MyTestQueues функция (показанный в Перечислении 4-5) первые вызовы функция интерфейса устройства HID allocQueue выделить очередь. Затем, это использует очередь, обрабатывающую функции addElement, hasElement, и removeElement чтобы добавить элементы к очереди, проверьте, чтобы видеть, находится ли элемент в очереди, и удалите элемент, соответственно. Наконец, MyTestQueues моделирует игровой цикл путем вызова start функция очереди для запуска поставки данных очереди и затем неоднократно вызова getNextEvent функция очереди для получения значений для элементов в очереди. Вся очередь, обрабатывающая функции для устройств класса HID, определяется в IOHIDLib.h.

Перечисление 4-5  функция для тестирования устройства класса HID соединяет интерфейсом с очередью, обрабатывающей функции

static void MyTestQueues(IOHIDDeviceInterface **hidDeviceInterface, cookie_struct_t cookies)
{
        HRESULT                                                 result;
        IOHIDQueueInterface **                                          queue;
        Boolean                                         hasElement;
        long                                            index;
        IOHIDEventStruct                                                event;
 
        queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
 
        if (queue)
        {
                printf("Queue allocated: %lx\n", (long) queue);
                //create the queue
                result = (*queue)->create(queue, 0, 8);
                    /*  depth (8): maximum number of elements in queue before oldest
                        elements in queue begin to be lost.
                     */
                printf("Queue create result: %lx\n", result);
 
                //add elements to the queue
                result = (*queue)->addElement(queue, cookies->gXAxisCookie, 0);
                printf("Queue added x axis result: %lx\n", result);
                result = (*queue)->addElement(queue, cookies->gButton1Cookie, 0);
                printf("Queue added button 1 result: %lx\n", result);
                result = (*queue)->addElement(queue, cookies->gButton2Cookie, 0);
                printf("Queue added button 2 result: %lx\n", result);
                result = (*queue)->addElement(queue, cookies->gButton3Cookie, 0);
                printf("Queue added button 3 result: %lx\n", result);
 
 
                //check to see if button 3 is in queue
                hasElement = (*queue)->hasElement(queue,cookies->gButton3Cookie);
                printf("Queue has button 3 result: %s\n", hasElement ? "true" :
                    "false");
 
                //remove button 3 from queue
                result = (*queue)->removeElement(queue, cookies->gButton3Cookie);
                printf("Queue remove button 3 result: %lx\n",result);
 
                //start data delivery to queue
                result = (*queue)->start(queue);
                printf("Queue start result: %lx\n", result);
 
                //check queue a few times to see accumulated events
                sleep(1);
                printf("Checking queue\n");
                for (index = 0; index < 10; index++)
                {
                        AbsoluteTime                            zeroTime = {0,0};
 
                        result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
                        if (result)
                                printf("Queue getNextEvent result: %lx\n", result);
                        else
                                printf("Queue: event:[%lx] %ld\n",
                                                                (unsigned long) event.elementCookie,
                                                                event.value);
 
                        sleep(1);
                }
 
                //stop data delivery to queue
                result = (*queue)->stop(queue);
                printf("Queue stop result: %lx\n", result);
 
                //dispose of queue
                result = (*queue)->dispose(queue);
                printf("Queue dispose result: %lx\n", result);
 
                //release the queue we allocated
                (*queue)->Release(queue);
        }
}

MyTestHIDInterface функционируйте использует переданный - в интерфейсе устройства HID для вызова интерфейса устройства getElement функция для связывания значений элемента с cookie, сохраненными в глобальных переменных gXAxisCookie, gButton1Cookie, gButton2Cookie, и gButton3Cookie getHIDCookies (показанный в Перечислении 4-3). Пример кода моделирует игровой цикл путем повторного вызова getElementValue в a for цикл.

Перечисление 4-6  функция, использующая функции интерфейса устройства класса HID, чтобы получить и отображать выбранные значения элемента в течение долгого времени

static void MyTestHIDInterface(IOHIDDeviceInterface ** hidDeviceInterface, cookie_struct_t cookies)
{
        HRESULT                                 result;
        IOHIDEventStruct                                        hidEvent;
        long                                    index;
 
        printf("X Axis (%lx), Button 1 (%lx), Button 2 (%lx), Button 3 ($lx)\n",
                        (long) cookies->gXAxisCookie, (long) cookies->gButton1Cookie,
                        (long) cookies->gButton2Cookie, (long) cookies->gButton3Cookie);
 
        for (index = 0; index < 10; index++)
        {
                long xAxis, button1, button2, button3;
 
                //Get x axis
                result = (*hidDeviceInterface)->getElementValue(hidDeviceInterface,
                    cookies->gXAxisCookie, &hidEvent);
                if (result)
                     printf("getElementValue error = %lx", result);
                xAxis = hidEvent.value;
 
                //Get button 1
                result = (*hidDeviceInterface)->getElementValue(hidDeviceInterface,
                    cookies->gButton1Cookie, &hidEvent);
                if (result)
                     printf("getElementValue error = %lx", result);
                button1 = hidEvent.value;
 
                //Get button 2
                result = (*hidDeviceInterface)->getElementValue(hidDeviceInterface,
                    cookies->gButton2Cookie, &hidEvent);
                if (result)
                     printf("getElementValue error = %lx", result);
                button2 = hidEvent.value;
 
                //Get button 3
                result = (*hidDeviceInterface)->getElementValue(hidDeviceInterface,
                    cookies->gButton3Cookie, &hidEvent);
                if (result)
                     printf("getElementValue error = %lx", result);
                button3 = hidEvent.value;
 
                //Print values
                printf("%ld %s%s%s\n", xAxis, button1 ? "button1 " : "",
                                button2 ? "button2 " : "", button3 ? "button3 " : "");
 
                sleep(1);
        }
}

Используя обратные вызовы очереди

Существует два основных способа использовать очереди при взаимодействии с устройствами HID: опрос и обратные вызовы. В Перечислении 4-5 к очередям получают доступ опрошенным способом. Проект в качестве примера «hidexample» использует этот механизм.

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

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

Обратный вызов очереди передается два void * параметры, callbackRefcon и callbackTarget, оба из которых могут содержать указатели на произвольные данные. С этим проектом та же функция обратного вызова может воздействовать на многократные очереди, подавая данные к многократным экземплярам класса, просто путем передачи различной очереди и указателей класса на setEventCallout функция.

В перечислении 4-7, callbackTarget параметр будет передан NULL, и callbackRefcon параметр будет передан динамично выделенный объект containining hidQueueInterface указатель (только для усмешек).

Перечисление 4-7  , добавляющее обратный вызов очереди

bool addQueueCallbacks(IOHIDQueueInterface **hidQueueInterface)
{
    IOReturn ret;
    CFRunLoopSourceRef eventSource;
    /*  We could use any data structure here. This data structure
        will be passed to the callback, and should probably
        include some information about the queue, assuming your
        program deals with more than one. */
    IOHIDQueueInterface ***myPrivateData = malloc(sizeof(*myPrivateData));
    *myPrivateData = hidQueueInterface;
 
    // In the calling function, we did something like:
    // hidQueueInterface = (*hidDeviceInterface)->
    //      allocQueue(hidDeviceInterface);
    // (*hidQueueInterface)->create(hidQueueInterface, 0, 8);
    ret = (*hidQueueInterface)->
        createAsyncEventSource(hidQueueInterface,
        &eventSource);
    if ( ret != kIOReturnSuccess )
        return false;
    ret = (*hidQueueInterface)->
        setEventCallout(hidQueueInterface,
        QueueCallbackFunction, NULL, myPrivateData);
    if ( ret != kIOReturnSuccess )
        return false;
    CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource,
        kCFRunLoopDefaultMode);
    return true;
}

Заключительный пример в этом разделе взят от примера «HidTestTool». Этот отрывок показывает, как обработать обратный вызов очереди. Этот код структурно несколько отличается в этом, структура данных передается как refcon параметр. Это позволяет ему передавать и в информации об очереди и в информации об элементах, поддерживаемых данным устройством HID.

Перечисление 4-8  основная функция обратного вызова очереди

static void QueueCallbackFunction(
                            void *          target,
                            IOReturn            result,
                            void *          refcon,
                            void *          sender)
{
    HIDDataRef          hidDataRef      = (HIDDataRef)refcon;
    AbsoluteTime    zeroTime    = {0,0};
    CFNumberRef     number      = NULL;
    CFMutableDataRef    element     = NULL;
    HIDElementRef   tempHIDElement  = NULL;//(HIDElementRef)refcon;
    IOHIDEventStruct    event;
    bool                change;
    bool                stateChange = false;
 
    if ( !hidDataRef || ( sender != hidDataRef->hidQueueInterface))
        return;
 
    while (result == kIOReturnSuccess)
    {
        result = (*hidDataRef->hidQueueInterface)->getNextEvent(
                                        hidDataRef->hidQueueInterface,
                                        &event,
                                        zeroTime,
                                        0);
 
        if ( result != kIOReturnSuccess )
            continue;
 
        // Only intersted in 32 values right now
        if ((event.longValueSize != 0) && (event.longValue != NULL))
        {
            free(event.longValue);
            continue;
        }
 
        number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType,
            &event.elementCookie);
        if ( !number )  continue;
        element = (CFMutableDataRef)CFDictionaryGetValue(hidDataRef->
            hidElementDictionary, number);
        CFRelease(number);
 
        if ( !element ||
            !(tempHIDElement = (HIDElement *)CFDataGetMutableBytePtr(element)))
            continue;
 
        change = (tempHIDElement->currentValue != event.value);
        tempHIDElement->currentValue = event.value;
 
    }
 
}