Работа с наследством интерфейсы устройства класса HID
Эта глава обеспечивает поэтапные инструкции, включая списки примера кода, для доступа к устройству класса HID на более старых версиях OS X. При разработке нового кода для OS X v10.5 или позже необходимо использовать APIs, описанный в Доступе к Устройству HID..
Эти примеры предполагают, что Вы уже получили итератор для списка устройств, соответствующих определенные свойства (например, при помощи MyFindHIDDevices
функция, Перечисление 3-3).
Теперь, когда у Вас есть итератор устройства, можно использовать его, чтобы исследовать каждое устройство и создать интерфейс устройства для него. Функции в этой главе демонстрируют этот процесс первым отображением свойств и затем созданием и тестированием интерфейса устройства для каждого устройства поочередно.
Эта глава содержит следующие разделы:
Для кода, чтобы помочь Вам определить местоположение устройств класса 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
функция выполняет следующие шаги:
Это получает интерфейс типа
IOCFPlugInInterface
для устройства класса HID, представленного переданнымhidDevice
объект.Для получения этого интерфейса это вызывает
IOCreatePlugInInterfaceForService
функция, передавая значениеkIOHIDDeviceUserClientTypeID
для сменного параметра типа и значенияkIOCFPlugInInterfaceID
для интерфейсного параметра типа.kIOHIDDeviceUserClientTypeID
определяется вIOHIDLib.h
иkIOCFPlugInInterfaceID
определяется вIOCFPlugIn.h
(расположенный вIOKit.framework
).Это получает интерфейс устройства класса HID путем вызова
QueryInterface
методIOCFPlugInInterface
объект. Для указания интерфейса устройства класса HID это использует следующий термин:CFUUIDGetUUIDBytes(
kIOHIDDeviceInterfaceID
)
Этот срок производит UUID (универсально уникальный идентификатор) для интерфейса устройства. UUIDs описаны в Базовой документации разработчика Основы, доступной в разделе Reference Library> Core Foundation веб-сайта документации разработчика.
kIOHIDDeviceInterfaceID
определяется вIOHIDLib.h
.Это выпускает
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; |
} |
} |