Работа с интерфейсами USB-устройства
В этой главе описываются, как разработать инструмент пространства пользователя, находящий и связывающийся с присоединенным USB-устройством и одним из его интерфейсов.
Используя интерфейсы USB-устройства
Приложения, работающие в OS X, заставляют доступ к USB-устройствам при помощи функций Набора I/O получать интерфейс устройства, тип плагина, указывающего функции, которые приложение может вызвать для передачи с устройством. Семья USB обеспечивает два типа интерфейса устройства:
IOUSBDeviceInterface
для связи с самим устройствомIOUSBInterfaceInterface
для связи с интерфейсом в устройстве
Оба интерфейса устройства определяются в /System/Library/Frameworks/IOKit.framework/Headers/usb/IOUSBLib.h
.
Когда необходимо установить или изменить его конфигурацию, связь с самим устройством обычно только необходима. Например, специфичные для поставщика устройства часто не конфигурируются, потому что нет никаких драйверов по умолчанию, устанавливающих определенную конфигурацию. В этом случае Ваше приложение должно использовать интерфейс устройства для устройства для установки конфигурации, этому нужны так интерфейсы, становятся доступными.
Процесс нахождения и связи с USB-устройством разделен на два набора шагов. Первые основы набора, как найти USB-устройство, получают интерфейс устройства типа IOUSBDeviceInterface
для него, и набор или изменение его конфигурация. Второй набор описывает, как найти интерфейс в устройстве, получить интерфейс устройства типа IOUSBInterfaceInterface
для него, и использование это для передачи с тем интерфейсом. Если необходимо связаться с несконфигурированным устройством или если необходимо изменить конфигурацию устройства, Вы следуете за обоими наборами шагов. Если необходимо связаться с устройством, уже сконфигурированным к спецификации, Вы следуете только за вторым набором шагов. Пример кода в Доступе к USB-устройству следует за обоими наборами шагов и расширяет их для включения установки уведомлений, которые это может получить, когда устройства динамично добавлены или демонтированы.
Следуйте за этим первым набором шагов только, чтобы установить или изменить конфигурацию устройства. Если устройство, которым Вы интересуетесь, уже сконфигурировано для Ваших потребностей, пропустите эти шаги и следуйте за вторым набором шагов.
Найдите
IOUSBDevice
объект, представляющий устройство в Реестре I/O. Это включает установку соответствующего словаря с ключом от USB Общая Спецификация Класса (см. USB-устройства Открытия и Интерфейсы). Пример кода использует основные элементыkUSBVendorName
иkUSBProductName
найти определенное USB-устройство (это - второй ключ, перечисленный в Таблице 1-2).Создайте интерфейс устройства типа
IOUSBDeviceInterface
для устройства. Этот интерфейс устройства обеспечивает функции, выполняющие задачи, такие как установка или изменение конфигурации устройства, получение информации об устройстве и сброса устройства.Исследуйте конфигурации устройства с
GetConfigurationDescriptorPtr
, выберите надлежащий и вызовSetConfiguration
установить конфигурацию устройства и инстанцироватьIOUSBInterface
объекты для той конфигурации.
Следуйте за этим вторым набором шагов, чтобы найти и выбрать интерфейс, получить интерфейс устройства для него и связаться с устройством.
Создайте интерфейсный итератор для итерации по доступным интерфейсам.
Создайте интерфейс устройства для каждого интерфейса, таким образом, можно исследовать его свойства и выбрать надлежащее. Чтобы сделать это, Вы создаете интерфейс устройства типа
IOUSBInterfaceInterface
. Этот интерфейс устройства обеспечивает функции, выполняющие задачи, такие как получение информации об интерфейсе, установка альтернативной установки интерфейса и доступ к ее каналам.Используйте
USBInterfaceOpen
функционируйте для открытия выбранного интерфейса. Это заставит каналы, связанные с интерфейсом быть инстанцированными так, можно исследовать свойства каждого и выбрать надлежащего.Свяжитесь с устройством через выбранный канал. Можно записать в и читать из канала синхронно или асинхронно — пример кода в Доступе к 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); |
} |