Пользование библиотеками интерфейса устройства FireWire
Эта глава использует пример кода от FireWire SDK, чтобы проиллюстрировать, как пользоваться библиотеками интерфейса устройства FireWire. (Последняя версия SDK доступна для скачивания в http://developer .apple.com/hardwaredrivers/download.) Первый раздел, Используя IOFireWireLib, описывает два демонстрационных проекта. Первое устанавливает изохронную связь с устройством и вторыми наборами псевдоадресное пространство в Macintosh для обработки пакетов FireWire. Второй раздел, Используя IOFireWireSBP2Lib, представляет демонстрационный проект, создающий драйвер пространства пользователя для доступа к устройству SBP-2. Наконец, Используя IOFireWireAVCLib описывает проект, получающий доступ к модулю AV/C с интерфейсами IOFireWireAVCLib.
Примеры кода в этой главе опускают большинство шагов, требуемых создать соответствие словарей, искать Реестр I/O и получить интерфейсы устройства. Для примеров кода, показывающих, как выполнить эти задачи, посмотрите Доступ Устройства FireWire Из Приложений или проектов кода в FireWire SDK.
Используя IOFireWireLib
IOFireWireLib обеспечивает самые низкие интерфейсы уровня, доступные для передачи непосредственно с устройством FireWire из приложения. Это также обеспечивает интерфейсы для установки изохронной связи с устройством FireWire.
Установка изохронной коммуникации
Изохронная коммуникация имеет место на каналах, которые могут каждый иметь самое большее одного говорящего и любое число слушателей. Семья IOFireWire абстрагирует эти понятия в объекты канала и объекты порта. Объекты канала соответствуют каналам шины FireWire, и объекты порта являются или удаленными или локальными и соответствуют или говорящим или слушателям. Удаленные порты соответствуют внешним устройствам и имеют методы, которые можно переопределить для поддержки устройства. Локальный порт соответствует Macintosh и не имеет методов, которые можно переопределить. Вы используете эти объекты в своем приложении для планирования потока изохронной коммуникации, прежде чем начнется любая фактическая передача данных.
Для управления изохронными данными устройство отправляет и получает, Вы создаете язык управления потока данных (или DCL) программа. Программа DCL является связанным списком команд DCL, указывающих, куда поместить каждый полученный пакет и где найти, что отправляют данные. Программа DCL может работать и линейно, с начала до конца, и нелинейно, путем спрыгивания с одной команды другому. Можно даже установить программу DCL для изменения ее переходов во время выполнения, создав динамично изменяемую программу. Семья IOFireWire делает запись программами DCL проще путем обеспечения функции, позволяющей Вам создавать пул готовых к употреблению объектов команды DCL и функции, заполняющие объекты команды DCL с Вашими параметрами.
Проект IOFireWireLibIsochTest создает удаленный порт и локальный порт, выделяет изохронный канал, устанавливает удаленный порт как слушателя и локальный порт как говорящий, и затем запускает канал. Программа включает простую программу DCL, управляющую пакетами. IOFireWireLibIsochTest соответствует на локальном узле (сам Macintosh), таким образом, можно выполнить его, даже если в настоящее время не включаются никакие внешние устройства FireWire.
Для начала проект IOFireWireLibIsochTest получает порт Маха, создает соответствующий словарь для локального узла и получает объект IOFireWireDeviceInterface для него. Эти шаги близко следуют за описанными в Нахождении Устройств FireWire, таким образом, этот раздел не повторяет их.
Фрагменты кода и списки в этом разделе используют переменные в Перечислении 3-1.
Определения переменной перечисления 3-1 для IOFireWireLibIsochTest
//global declarations |
IOFireWireLibDCLCommandPoolRef gCommandPool; |
UInt8 gBuf[1024]; |
UInt8 gBuf2[1024]; |
//local declarations in the main function |
IOReturn result; |
IOFireWireLibNubRef localNode; |
IOFireWireLibLocalIsochPortRef localIsochPort; |
IOFireWireLibRemoteIsochPortRef remoteIsochPort; |
IOFireWireLibIsochChannelRef isochChannel; |
К исключительно открытому устройство (в этом случае, Macintosh), IOFireWireLibIsochTest main
вызовы функции функция IOFireWireLibDeviceInterface open
:
result = (*localNode)->Open( localNode ); |
Затем, это создает пул структур команды DCL. Функция CreateDCLCommandPool
вызывает функцию IOFireWireLibDeviceInterface того же имени, создавая объект пула команды DCL и возвращая интерфейс ему, который программа может использовать для создания программы DCL. Перечисление 3-2 показывает вызов функции, сопровождаемый функциональным определением, исключая проверку ошибок.
Перечисление 3-2 , Создающее пул команды DCL
//Call to CreateDCLCommandPool from main. |
result = CreateDCLCommandPool( localNode, &gCommandPool ); |
//... |
//CreateDCLCommandPool function. |
IOReturn CreateDCLCommandPool( IOFireWireLibNubRef inNub, |
IOFireWireLibDCLCommandPoolRef* outCommandPool ) { |
//Create a DCL command pool. |
*outCommandPool = (*inNub)->CreateDCLCommandPool( inNub, 0x1000, |
CFUUIDGetUUIDBytes( kIOFireWireDCLCommandPoolInterfaceID ) ); |
} |
Прежде чем это сможет запустить изохронный канал, IOFireWireLibIsochTest main
функция сначала создает удаленные и локальные объекты порта и изохронный объект канала. IOFireWireLib содержит интерфейсы для каждого из этих объектов, обеспечивающих функции для управления ими.
Перечисление 3-3 показывает CreateRemoteIsochPort
функция (вместе с ее вызовом от main
) который включает много функций обратного вызова, можно переопределить для обеспечения специфичных для устройства функций, такой как, как запустить и остановить порт и который образовывает канал поддержки устройства.
Перечисление 3-3 , Создающее удаленный изохронный порт
//Call to CreateRemoteIsochPort from main. |
result = CreateRemoteIsochPort( localNode, &remoteIsochPort ); |
//... |
//CreateRemoteIsochPort function. |
IOReturn CreateRemoteIsochPort( IOFireWireLibNubRef inNub, |
IOFireWireLibRemoteIsochPortRef* outPort ) { |
IOFireWireLibRemoteIsochPortRef port; |
//Call the IOFireWireDeviceInterface function CreateRemoteIsochPort to |
//create a remote port object and return an interface to it. The “false” |
//parameter indicates that this port will not be a talker. |
port = (*inNub)->CreateRemoteIsochPort( inNub, false, CFUUIDGetUUIDBytes |
(kIOFireWireRemoteIsochPortInterfaceID ) ); |
(*port)->SetGetSupportedHandler( port, &RemotePort_GetSupported ); |
(*port)->SetAllocatePortHandler( port, &RemotePort_AllocatePort ); |
(*port)->SetReleasePortHandler( port, &RemotePort_ReleasePort ); |
(*port)->SetStartHandler( port, &RemotePort_Start ); |
(*port)->SetStopHandler( port, &RemotePort_Stop ); |
*outPort = port; |
} |
В IOFireWireLibIsochTest, RemotePort_GetSupported
функция указывает, что поддерживает все каналы, и остающиеся функции обратного вызова просто распечатывают сообщения. Необходимо добавить собственный код к этим функциям обратного вызова для поддержки устройства.
Создать локальный изохронный порт, IOFireWireLibIsochTest main
вызовы функции функция CreateLocalIsochPort
. Эта функция использует функцию IOFireWireDeviceInterface того же имени, чтобы создать локальный изохронный объект порта с определенной программой DCL и возвратить интерфейс ему. Перечисление 3-4 показывает CreateLocalIsochPort
функция, вместе с ее вызовом от main
.
Перечисление 3-4 , Создающее локальный изохронный порт
//Call to CreateLocalIsochPort from main. |
result = CreateLocalIsochPort( localNode, &localIsochPort ); |
//... |
//CreateLocalIsochPort function. |
IOReturn CreateLocalIsochPort( IOFireWireLibNubRef inNub, |
IOFireWireLibLocalIsochPortRef* outPort ) { |
//Set the pointer dclProgram to a simple DCL program created in |
//the IOFireWireLibIsochTest function WriteTalkingDCLProgram (not shown). |
DCLCommandStruct* dclProgram = WriteTalkingDCLProgram(); |
//Use the IOFireWireDeviceInterface function PrintDCLProgram |
//to print the contents of the DCL program. |
(*inNub)->PrintDCLProgram( inNub, dclProgram, 6 ); |
//Create a local isochronous port object and return an interface |
//for it. The “true” parameter indicates that this port will be a talker. |
*outPort = (*inNub)->CreateLocalIsochPort( inNub, true, dclProgram, 0, 0, |
0, nil, 0, nil, 0, CFUUIDGetUUIDBytes( |
kIOFireWireLocalIsochPortInterfaceID ) ); |
return *outPort; |
} |
Теперь IOFireWireLibIsochTest main
функционируйте использует функции канального интерфейса для установки изохронной коммуникации. Во-первых, это определяет удаленный порт как слушателя и локальный порт как говорящий:
result = (*isochChannel)->AddListener( isochChannel, |
(IOFireWireLibIsochPortRef) remoteIsochPort ); |
//... |
result = (*isochChannel)->SetTalker( isochChannel, |
(IOFireWireLibIsochPortRef) localIsochPort ); |
Затем это использует другую функцию канального интерфейса для выделения канала:
result = (*isochChannel)->AllocateChannel( isochChannel ); |
Функция канального интерфейса AllocateChannel
вызовы GetSupported
методы на всех портах для обнаружения, какие каналы они поддерживают, согласовывают ответы и запрашивают определенный канал и пропускную способность от шины FireWire.
После того, как канал выделяется, IOFireWireLibIsochTest main
вызовы функции канальный интерфейс Start
функция, вызывающая Start
функции на всех портах:
result = (*isochChannel)->Start( isochChannel ); |
Для проверки обратные вызовы вызывают, main
функция тогда вызывает функцию IOFireWireDeviceInterface для добавления изохронного диспетчера обратного вызова к циклу выполнения программы:
(*localNode)->AddIsochCallbackDispatcherToRunLoop( localNode, |
CFRunLoopGetCurrent() ); |
В целях тестирования, main
вызовы функции Базовая функция Основы CFRunLoopRunInMode
выполнить цикл выполнения и инициировать функции обратного вызова в течение 15 секунд. После того времени, main
функционируйте тогда вызывает канальный интерфейс Stop
функция, вызывающая _Stop
функции на всех портах:
result = (*isochChannel)->Stop( isochChannel ); |
Наконец, IOFireWireLibIsochTest main
функционируйте выпускает интерфейсы, которые это получило. Это сначала выпускает изохронный объект канала с функцией канального интерфейса ReleaseChannel
и затем это использует функцию IOCFPlugInInterface Release
выпускать сам интерфейс:
result = (*isochChannel)->ReleaseChannel( isochChannel ); |
//... |
(*isochChannel)->Release( isochChannel ); |
main
функционируйте так же выпускает интерфейсы локального и удаленного порта, затем вызывает функцию IOFireWireDeviceInterface Close
на локальном узле прежде, чем выпустить его интерфейс и выпуски исходный CFPlugInInterface:
IODestroyPlugInInterface( gCFPlugInInterface ); |
Установка обрабатывающего пакет проекта
Семья IOFireWire определяет два типа адресного пространства на Macintosh: Физическое адресное пространство и псевдоадресное пространство. IOFireWireDeviceInterface обеспечивает объекты IOFireWirePhysicalAddressSpace, выделяющиеся в диапазоне поддержанных аппаратными средствами адресов и объектов IOFireWirePseudoAddressSpace, которые выделяются в диапазоне адресов вне физического диапазона и доступны только через программное обеспечение.
Как упомянуто в Обзоре FireWire, FireWire определяет 64-разрядные адреса, из которых более низкие 48 битов для специфичного для устройства использования. Диапазон физического адресного пространства составляет более низкие 32 бита специфичного для устройства диапазона, отображенного на 32 разрядных Macintosh RAM. Таким образом, с точки зрения адресов FireWire, объект IOFireWirePhysicalAddressSpace является выделенным поддиапазоном в диапазоне 0 000,00000000$ к 0000$. FFFFFFFF. Объект IOFireWirePseudoAddressSpace является выделенным поддиапазоном адресов FireWire 1,00000000$ и выше.
Чип контроллера FireWire может записать непосредственно в и читать непосредственно из физического адресного пространства без любого вмешательства программного обеспечения. Поэтому можно передать большие объемы данных и от этого адресного пространства эффективно. Поскольку нет никакого участия программного обеспечения в такой передаче, однако, нет никакой индикации относительно того, когда передача завершена или если это перестало работать. Для обработки этого необходимо также выделить псевдоадресное пространство для служения в качестве обменивающейся сообщениями области. Ваше удаленное устройство тогда передает свои данные физическому адресному пространству и отправляет сообщение успешности или неуспешности к псевдоадресному пространству, предупреждающему Ваше программное обеспечение.
Проект IOFireWirePacketQueueTest использует функции IOFireWireDeviceInterface для создания объекта интерфейса IOFireWirePseudoAddressSpace, представляющего псевдоадресное пространство на локальном узле. Проект тогда использует функции интерфейса IOFireWirePseudoAddressSpace, чтобы получить адрес FireWire адресного пространства и установить подпрограммы обратного вызова для записи и чтения к адресному пространству. Это также устанавливает подпрограмму обратного вызова для обработки пакетов, которые должны быть отброшены, когда очередь пакетов, входящих в псевдоадресное пространство, заполняется слишком быстро.
main
функция IOFireWirePacketQueueTest.cpp
первые вызовы GetIOFireWireDevices
функционируйте для помещения всех объектов устройства IOFireWireLocalNode в массив. Затем это использует стандартную Базовую Основу и функции Набора I/O для получения IOFireWireDeviceInterface для первого объекта в массиве (см. Получение Интерфейсов Устройства FireWire для примера кода, иллюстрирующего этот процесс). После использования IOFireWireDeviceInterface функционирует, чтобы открыть устройство и установить диспетчера обратного вызова, main
функция тогда создает объект псевдоадресного пространства и получает интерфейс к нему, поскольку Перечисление 3-5 показывает.
Перечисление 3-5 , получающее IOFireWirePseudoAddressSpaceInterface
Ptr gBuf = 0; //Buffer for backing store. |
//... |
IOFireWireLibDeviceRef interface; |
//... |
IOFireWireLibPseudoAddressSpaceRef addressSpace = 0; |
IOFireWireLibPhysicalAddressSpaceRefphysAddressSpace = 0; |
//Allocate backing store and put some text into gBuf for testing. |
gBuf = (Ptr) new char[40960]; |
sprintf( gBuf, “Testing...” ); |
//Use IOFireWireDeviceInterface function CreatePseudoAddressSpace to |
//create a pseudo-address space object and get an interface to it. |
//In the call to CreatePseudoAddressSpace, 40960 is the size of the |
//address space, addressSpace is the reference value passed to all |
//callback functions, and 4096 is the size of the queue that |
//receives packets from the bus. |
addressSpace = (*interface)->CreatePseudoAddressSpace( interface, |
40960, (void*) addressSpace, 4096, gBuf, kFWAddressSpaceAutoCopyOnWrite, |
CFUUIDGetUUIDBytes( kIOFireWirePseudoAddressSpaceInterfaceID ) ); |
Затем, main
функционируйте использует функции IOFireWirePseudoAddressSpaceInterface для установки подпрограмм обработчика обратного вызова для чтения и записи в псевдоадресное пространство и обработки пропущенных пакетов. Когда a write
к псевдоадресному пространству происходит, PacketWriteHandler
подпрограмма распечатывает информацию о псевдоадресном пространстве и пакетах, которые это получило. Точно так же, когда a read
происходит, PacketReadHandler
подпрограмма обрабатывает read
и затем распечатывает информацию о read
запрос. SkippedPacketHandler
подпрограмма просто распечатывает число пропущенных пакетов. Все три подпрограммы обработчика заканчиваются вызовом к функции IOFireWirePseudoAddressSpaceInterface ClientCommandIsComplete
, который уведомляет псевдоадресное пространство, что пакетный обработчик уведомления завершил свою работу, как в этом примере от SkippedPacketHandler
:
//In the following function call, commandID is the same ID that was |
//passed to the packet notification handler and kIOReturnSuccess is |
//the completion status of the packet handler. |
(*addressSpace)->ClientCommandIsComplete( addressSpace, |
commandID, kIOReturnSuccess ); |
main
функция тогда приводит все в движение путем вызова CFRunLoopRun
. Наконец, это выпускает все интерфейсы, которые это получило и концы.
Используя IOFireWireSBP2Lib
SBP2SampleProject в FireWire SDK добавляет другой уровень сложности к стандартному доступу прикладного уровня устройства FireWire. Вместо того, чтобы получить интерфейсы от IOFireWireSBP2Lib и использовать их для передачи с устройством SBP2SampleProject обеспечивает плагин драйвера пространства пользователя, существующий между приложением и IOFireWireSBP2Lib. Приложение в SBP2SampleProject создает соответствующий словарь и получает интерфейс, но это - интерфейс к драйверу пространства пользователя, не к самому объекту устройства. Драйвер пространства пользователя тогда получает интерфейс от IOFireWireSBP2Lib и использует его для доступа к службам SBP-2 в ядре. Этот проект позволяет многократным приложениям использовать тот же плагин драйвера пространства пользователя для доступа к устройству.
SBP2SampleProject является многопоточным проектом. Для примера того же проекта в однопоточной среде см. проект SBP2SampleProject-SingleThread.
Доступ к устройствам кратких обзоров драйвера SBP2SampleProject на два логических уровня. Нижний уровень является транспортным уровнем, обрабатывающим такие задачи как обслуживание объединения в цепочку и входа в систему ORB SBP-2. Верхний уровень является протокольным уровнем, получающим команды от приложения и создающим и представляющим надлежащий ORBs транспортному уровню для выполнения их. Эта архитектура позволяет Вам изменять верхний, протокольный уровень для удовлетворения потребностей устройства, не имея необходимость изменять большую часть транспортного уровня драйвера.
Этот раздел представляет основные функции SBP2SampleProject и указывает, где можно изменить код для использования его в качестве основы для собственного проекта. Чтобы быть универсально применимым, доступы SBP2SampleProject жесткий диск компьютера. Для тестирования неизмененного проекта на Macintosh следуйте инструкциям, включенным в FireWire SDK, чтобы отключить встроенный драйвер жесткого диска и позволить демонстрационному драйверу присоединять.
Несмотря на то, что отключение встроенного драйвера, чтобы позволить Вашему драйверу пространства пользователя присоединять является приемлемой процедурой тестирования, необходимо обеспечить собственное расширение ядра (или KEXT), который может успешно конкурировать с другими драйверами. Это не только гарантирует, что Набор I/O находит и загружает Ваш KEXT для Вашего устройства, но также и предоставляет информацию CFPlugIn, идентифицирующую Ваш плагин драйвера пространства пользователя. В целом, когда драйвер является плагином пространства пользователя что загрузки приложения с помощью функции, необходимо обеспечить собственный KEXT IOCreatePlugInInterfaceForService
или когда могут быть другие драйверы, конкурирующие за Ваше устройство. Исследуйте настройки пакета SBP2SampleDriver.kext
видеть, как идентифицируется плагин драйвера. Для получения дополнительной информации о разработке KEXT см. Руководство по проектированию Драйвера устройства IOKit.
Установка приложения
Часть приложения SBP2SampleProject записана в Objective C с помощью платформы Какао и содержит два файла кода: SelectorController.m
и LUNController.m
. В дополнение к связанным с GUI задачам реализация класса SelectorController ответственна за:
Получение порта Маха
Создание соответствующего словаря для объектов IOFireWireSBP2LUN
Получение итератора по набору соответствующих объектов
Инстанцирование LUNController возражает для создания строки имени для каждого соответствия объект IOFireWireSBP2LUN
Заполнение массива с соответствием объектов IOFireWireSBP2LUN
Используя объект LUNController получить интерфейс к плагину драйвера для каждого IOFireWireSBP2LUN возражают, что пользователь выбирает
Выпуск порта Маха
Основная задача реализации класса LUNController состоит в том, чтобы получить интерфейс плагина драйвера и использовать его для передачи с устройством. Экземпляр класса LUNController:
Использует ссылку на объект устройства от экземпляра SelectorController для получения свойств устройства от Реестра I/O
Добирается интерфейс плагина драйвера для устройства возражают, и устанавливает обратные вызовы на текущем цикле выполнения
Выполняет устройство
login
иlogout
Выполняет a
read
из первых четырех блоков устройстваВыпускает интерфейс плагина драйвера
Реализация класса LUNController использует те же Базовые функции Основы для получения интерфейса к плагину драйвера, как описано в Получении Интерфейсов Устройства FireWire. Например,
//Get CFPlugIn interface. |
status = IOCreatePlugInInterfaceForService( fLUNReference, |
kSBP2SampleDriverTypeID, kIOCFPlugInInterfaceID, &fCFPlugInInterface, |
&score ); |
//... |
//Get driver plug-in interface. |
result = (*fCFPlugInInterface)->QueryInterface( fCFPlugInInterface, |
CFUUIDGetUUIDBytes( kSBP2SampleDriverInterfaceID ), (LPVOID *) |
&fDriverInterface ); |
Единственная разница находится в параметрах, идентифицирующих тип интерфейса. Вместо использования kIOFireWireSBP2LibTypeID
, это использует kSBP2SampleDriverTypeID
получить IOCFPlugInInterface. Затем это вызывает QueryInterface
с kSBP2SampleDriverInterfaceID
получить интерфейс к плагину драйвера. Оба kSBP2SampleDriverTypeID
и kSBP2SampleDriverInterfaceID
определяются в SBP2SampleDriverInterface.h
.
Экземпляр класса LUNController выполняет функции устройства (login
, logout
, и read
) использование функций интерфейса плагина драйвера. login
и logout
функции являются прямыми вызовами к интерфейсу плагина драйвера:
(*fDriverInterface)->loginToDevice( fDriverInterface ); |
//... |
(*fDriverInterface)->logoutOfDevice( fDriverInterface ); |
Экземпляр LUNController использует отдельный поток для выполнения read
функция:
[NSThread detachNewThreadSelector:@selector(runReadTransaction:) |
toTarget:self withObject:nil]; |
Затем в runReadWorker
метод, экземпляр LUNController использует интерфейс плагина драйвера для отправки read
команда:
(*fDriverInterface)->readBlock( fDriverInterface, transactionID, 1, &block ); |
Установка протокольного уровня
Классы SBP2SampleDriver и SBP2SampleDriverPlugInGlue SBP2SampleProject определяют объекты, включающие протокольный уровень. Протокольный уровень сначала создает транспортный уровень, затем создает ORBs, заливки и ставит их в очередь и связывается с транспортным уровнем, чтобы представить им. В свою очередь, СОСТОЯНИЕ ORB реле транспортного уровня и сообщения о состоянии входа в систему к протокольному уровню так объект SBP2SampleDriver могут реагировать соответственно.
«Связующее звено» в SBP2SampleDriverPlugInGlue в основном относится для кодирования, который предоставляет методы SBP2SAMPLEDRIVER, поскольку CFPlugIn функционирует (другой код обрабатывает особенности C++, такие как предоставление статических методов, которые могут вызвать виртуальные методы на объектах). Вспомните, что приложение использует стандартные функции CFPlugIn, такой как QueryInterface
, получить и использовать интерфейс плагина драйвера. Если Ваше приложение получает IOFireWireLib или интерфейсы IOFireWireSBP2Lib непосредственно, библиотеки обеспечивают это «связующее звено» негласно. SBP2SampleProject, однако, должен явно обеспечить этот код, таким образом, приложение может использовать интерфейс плагина драйвера, как будто это был интерфейс устройства семьи IOFireWire.
Класс SBP2SampleDriver реализует многие из тех же методов, которые в драйвере ядра делает, такой как start
, stop
, и probe
. В start
метод, драйвер вызывает метод класса SBP2SampleORBTransport для создания транспортного уровня и использует возвращенную ссылку, чтобы сказать уровню запускаться.
Затем, это использует транспортный уровень createORB
метод для создания a TEST_UNIT_READY
ORB. TEST_UNIT_READY
ORB является частью процесса конфигурации для жестких дисков SBP-2 (см. Конфигурацию Загрузочно-разгрузочного устройства и Реконфигурирование для получения дополнительной информации о процессе конфигурации устройства). Ваше устройство может иметь различные потребности конфигурации. Наконец, это использует createORB
метод снова для создания пула свободного ORBs для рисования из позже и Базовая функция Основы CFArrayCreateMutable
создать массив для содержания происходящего ORBs.
stop
метод класса SBP2SampleDriver выпускает пул ORB и происходящий массив ORB и вызывает stop
метод класса SBP2SampleORBTransport для инициирования завершения работы транспортного уровня. probe
метод просто проверяет, чтобы быть уверенным, что объект устройства, на который ссылаются, имеет тип IOFireWireSBP2LUN.
Реализация класса SBP2SampleDriver использует Базовые функции обработки массива Основы, чтобы создать и управлять ее очередью происходящего ORBs, как в этом примере:
CFMutableArrayRef fInProgressORBQueue; |
//... |
//kSBP2SampleFreeORBCount is defined in SBP2SampleDriver.h |
fInProgressORBQueue = CFArrayCreateMutable( kCFAllocatorDefault, |
kSBP2SampleFreeORBCount, SBP2SampleORB::getCFArrayCallbacks() ); |
Поскольку проект SBP2SampleDriver многопоточен, возможно, что один поток мог бы удалить ORB из свободного пула ORB одновременно, другой поток добавляет тот. Для защиты от этого методы SBP2SampleDriver, обрабатывающие свободный пул ORB, получают блокировку на взаимном исключении прежде, чем удалить или добавить ORBs и выпускают блокировку впоследствии, поскольку Перечисление 3-6 показывает.
Перечисление 3-6 getORBFromFreePool метод класса SBP2SampleDriver
SBP2SampleORB * SBP2SampleDriver::getORBFromFreePool( void ) |
{ |
CFIndex freeORBCount; |
pthread_mutex_lock( &fFreePoolLock ); |
while ( ( freeORBCount = CFArrayGetCount( fFreeORBPool ) ) == 0 ) { |
//If there are no free ORBs, wait for one. |
pthread_cond_wait( &fFreePoolCondition, &fFreePoolLock ); |
} |
//Remove the ORB from the pool. |
SBP2SampleORB * orb = (SBP2SampleORB*) CFArrayGetValueAtIndex( |
fFreeORBPool, 0 ); |
orb->retain(); |
CFArrayRemoveValueAtIndex( fFreeORBPool, 0 ); |
pthread_mutex_unlock( &fFreePoolLock ); |
return orb; |
} |
Если необходимо настроить демонстрационные водительские механизмы организации очередей, исследовать следующие методы, в которых класс SBP2SampleDriver выполняет большую часть своей ОБРАБАТЫВАЮЩЕЙ ORB работы:
getORBFromFreePool
надевает блокировкуfFreePoolLock
взаимное исключение и ожидает свободного ORB, который оно удаляет из пула и возвратов.addORBToFreePool
надевает блокировкуfFreePoolLock
взаимное исключение, добавляет переданный - в ORB к свободному пулу и отправляет сигнал, будящий потоки, ожидающие свободного ORBs.appendORBToInProgressQueue
использует Базовую функцию Основы для добавления переданного - в ORB очереди происходящего ORBs.removeORBFromInProgressQueue
Основа Ядра использования функционирует для нахождения индекса переданного - в ORB в очереди происходящего ORBs и удаляет его.
Состояние ORBs прослежено методами, отправляющими отчеты между протокольными и транспортными уровнями. Демонстрационный драйвер использует транспортный уровень submitORB
метод для отправки ORB в устройство. Транспортный уровень отвечает путем вызова одного из следующих методов класса SBP2SampleDriver:
completeORB
если устройство получило ORBsuspendORBs
если устройство отключается, когда сброс шины происходит илиresumeORBs
если снова зарегистрировано устройствоloginLost
если потеряно соединение с устройством
Установка транспортного уровня
Транспортный уровень SBP2SampleProject ответственен за получение надлежащих интерфейсов IOFireWireSBP2Lib, позволяющих прямой связи с устройством и использующий те интерфейсы отправлять ORBs и управлять состоянием входа в систему устройства.
Транспортный уровень включает четыре класса, перечисленные в порядке близости к объекту устройства:
SBP2SampleSBP2LibGlue
SBP2SampleLoginController
SBP2SampleORB
SBP2SampleORBTransport
Когда протокольный уровень запускает транспортный уровень, start
метод класса SBP2SampleSBP2LibGlue выполняется, получая IOFireWireSBP2LibLUNInterface. Используя этот интерфейс, экземпляр SBP2SampleSBP2LibGlue открывает LUN и получает IOFireWireSBP2LibLoginInterface. IOFireWireSBP2LibLoginInterface предоставляет APIs для выполнения обслуживания и команды входа в систему.
Класс SBP2SampleSBP2LibGlue использует IOFireWireSBP2LibLoginInterface для регистрации обратных вызовов состояния что использование служб SBP-2 в ядре для уведомления драйвера, такой как setLoginCallback
и setStatusNotify
. Кроме того, класс использует этот интерфейс для создания объекта IOFireWireSBP2LibMgmtORBInterface, предоставляющего Вам доступ к управлению SBP-2 ORB. Этот объект позволяет Вам выполнять команды такой как QueryLogin
, AbortTask
, и LogicalUnitReset
. В этом проекте класс SBP2SampleSBP2LibGlue использует функции объекта IOFireWireSBP2LibMgmtORBInterface для установки команды, которая будет управляема управлением ORB (с setManageeLogin
), установите функцию управления ORB (с setCommandFunction
), и набор подпрограмма завершения ORB (с setORBCompleteCallback
).
Класс SBP2SampleLoginController использует IOFireWireSBP2LibLoginInterface для отправки login
и logout
команды к устройству. loginCompletion
метод проверяет параметры состояния и пытается войти в систему снова при необходимости. Можно добавить, что код здесь для проверки на состояние оценивает определенный для устройства. Класс SBP2SampleLoginController также обрабатывает сообщения об обратном вызове от устройства, описывающего состояние соединения устройства. Можно разделить на подклассы методы, обрабатывающие состояния входа в систему (потерянный, приостановленный и возобновленный) для выполнения специфичных для устройства задач.
Реализация класса SBP2SampleORB ответственна за сам объект ORB. Методы в этом классе инициализируют новый объект SBP2SampleORB, представляющий ORB, и обеспечьте доступ к различным полям в объектах IOFireWireSBP2LibORBInterface, таких как идентификация транзакции и максимальный размер полезной нагрузки ORB.
Класс SBP2SampleORBTransport абстрагирует транспорт ORBs от протокольного уровня до самых низких уровней транспортного уровня. Когда класс SBP2SampleDriver инициирует транспортный уровень, он вызывает метод фабрики класса SBP2SampleORBTransport. В start
метод, класс SBP2SampleORBTransport инстанцирует класса SBP2SampleSBP2LibGlue, получающего интерфейсы IOFireWireSBP2Lib, которые это должно передать с устройством. Класс SBP2SampleORBTransport также реализует submitORB
метод для протокольного уровня, с помощью функции IOFireWireSBP2LibLoginInterface submitORB
.
Класс SBP2SampleORBTransport уведомляет протокольный уровень состояния ORB с вызванным методом statusNotify
. Перечисление 3-7 показывает statusNotify
метод. Это не показывает метод parseStatus
это интерпретирует состояние ORB’s и возвращает одно из значений, перечисленных в начале реализации класса SBP2SampleORBTransport или сообщений statusNotify
отправляет к FWLog
(макрос, определенный в FWDebugging.h
).
Перечисление 3-7 statusNotify метод класса SBP2SampleORBTransport
void SBP2SampleORBTransport::statusNotify( FWSBP2NotifyParams * params ) |
{ |
SBP2SampleORB * orb = ( SBP2SampleORB *)params->refCon; |
UInt32 event = parseStatus( params ); |
switch( event ) |
{ |
case kStatusDeadBitSet: |
//Suspend protocol layer. |
fDriverLayer->suspendORBs(); |
//Complete this ORB. |
completeORB( orb, kIOReturnError ); |
//Reset the fetch agent. |
(*fSBP2LoginInterface)->submitFetchAgentReset( |
fSBP2LoginInterface ); |
break; |
case kStatusDummyORBComplete: |
if ( orb == fDummyORB ) { |
// All is initialized so resume protocol layer. |
fDriverLayer->resumeORBs(); |
} |
else { |
//This must be an aborted ORB. |
completeORB( orb, kIOReturnError ); |
} |
break; |
case kStatusORBComplete: |
// Complete the protocol layer’s ORBs. |
completeORB( orb, kIOReturnSuccess ); |
break; |
case kStatusORBError: |
completeORB( orb, kIOReturnIOError ); |
break; |
case kStatusORBReset: |
//This is a command reset so tell the protocol layer (it |
//should already be suspended at this point). |
completeORB( orb, kIOReturnNotReady ); |
break; |
case kStatusORBTimeout: |
//Suspend the protocol layer. |
fDriverLayer->suspendORBs(); |
//Complete this ORB. |
completeORB( orb, kIOReturnError ); |
//Reset the LUN. |
(*fLUNResetORBInterface)->submitORB( fLUNResetORBInterface |
); |
break; |
} |
} |
В перечислении 3-7, completeORB
метод относится к методу класса SBP2SampleORBTransport, только если рассматриваемым ORB является фиктивный ORB (описанный в Конфигурации Загрузочно-разгрузочного устройства и Реконфигурировании); иначе, это относится к методу класса SBP2SampleDriver.
Конфигурация загрузочно-разгрузочного устройства и реконфигурирование
И транспортные и протокольные уровни должны выполнить некоторую конфигурацию, прежде чем они смогут принять команды из приложения. Когда приложение запускает и после сброса шины, это происходит. Транспортный уровень добавляет фиктивный ORB (ORB без команды) к специальному регистру на устройстве SBP-2 вызвал агент выборки, содержащий адрес ORB, устройство в настоящее время продолжает работать. При входе в систему или после сброса шины, необходимо записать адрес первого ORB непосредственно к регистру агента выборки. Можно тогда объединить весь последующий ORBS В ЦЕПОЧКУ на тот первый ORB. В этом случае фиктивной функцией ORB’s должен всегда быть первый ORB, выполняемый при входе в систему или после того, как шина сбросит.
Поскольку проект записан для доступа к устройству массового хранения, протокольный уровень реконфигурировал устройство, когда приложение запускает и после того, как каждая шина сбросила. Протокольный уровень отправляет a TEST_UNIT_READY
ORB, потому что устройство является жестким диском — Ваше устройство, мог бы потребовать другого типа ORB. Протокольный уровень должен ожидать, пока транспортный уровень не выполнил фиктивный ORB, прежде чем это сможет отправить TEST_UNIT_READY
ORB. Приложение должно ожидать, пока оба этих ORBs не выполнился, прежде чем оно сможет начать отправлять свой собственный ORBs.
Когда проект сначала запускает, экземпляр класса LUNController вызывает loginToDevice
метод класса SBP2SampleDriver в ответ на ввод данных пользователем. Протокольные и транспортные уровни тогда выполняют следующие шаги, чтобы инициализировать устройство и подготовить его для принятия ORBs из приложения:
Экземпляр SBP2SampleDriver вызывает
loginToDevice
метод класса SBP2SampleLoginController.Экземпляр SBP2SampleLoginController использует функцию IOFireWireSBP2LibLoginInterface
submitLogin
входить в систему устройства.Когда вход в систему завершен, он инициировал метод обратного вызова SBP2SampleLoginController
loginCompletion
который вызывает класс SBP2SampleORBTransportloginResumed
метод.Экземпляр SBP2SampleORBTransport представляет фиктивный ORB.
Когда фиктивный ORB завершается,
statusNotify
метод SBP2SampleORBTransport вызывает SBP2SAMPLEDRIVERresumeORBs
метод, устанавливающийfSuspended
флаг кfalse
и представляет aTEST_UNIT_READY
ORB к транспортному уровню.Экземпляр SBP2SampleORBTransport подчиняется
TEST_UNIT_READY
ШАР.Когда
TEST_UNIT_READY
ORB завершается, SBP2SampleORBTransportstatusNotify
вызовы методаcompleteORB
метод класса SBP2SampleDriver.SBP2SampleDriver
completeORB
метод проверяет, является ли ORB, просто завершенный, aTEST_UNIT_READY
ORB и, если это, это устанавливает флагfDeviceReady
кtrue
и доходы для представления ORBs из приложения.
После сброса шины шаги подобны:
Экземпляр SBP2SampleLoginController получает a
kIOMessageServiceIsSuspended
обменивайтесь сообщениями и, если это в настоящее время зарегистрировано к устройству, это устанавливаетfLoggedIn
флаг кfalse
.Экземпляр SBP2SampleDriver выполняет
suspendORBs
метод, устанавливающийfSuspended
флаг кtrue
иfDeviceReady
флаг кfalse
.Когда устройство успешно повторно соединяется, экземпляр SBP2SampleLoginController получает a
kIOMessageFWSBP2ReconnectComplete
сообщение и наборыfLoggedIn
флаг кtrue
.Экземпляр SBP2SampleORBTransport тогда вызывает класс SBP2SampleLoginController
loginResumed
метод (который можно изменить для выполнения специфичных для устройства операций) и представляет фиктивный ORB.Когда фиктивный ORB завершается,
statusNotify
метод SBP2SampleORBTransport вызывает SBP2SAMPLEDRIVERresumeORBs
метод, устанавливающийfSuspended
флаг кfalse
и представляет aTEST_UNIT_READY
ORB к транспортному уровню.Экземпляр SBP2SampleORBTransport подчиняется
TEST_UNIT_READY
ШАР.Когда
TEST_UNIT_READY
ORB завершается, SBP2SampleORBTransportstatusNotify
вызовы методаcompleteORB
метод класса SBP2SampleDriver.SBP2SampleDriver
completeORB
метод проверяет, является ли ORB, просто завершенный, aTEST_UNIT_READY
ORB и, если это, это устанавливает флагfDeviceReady
кtrue
и доходы для представления ORBs из приложения.
Используя IOFireWireAVCLib
IOFireWireAVCLib обеспечивает два интерфейса, которые можно использовать друг независимо от друга. IOFireWireAVCLibProtocolInterface позволяет Вам обрабатывать Macintosh как модуль AV/C, настраивая локальные разъемы и получая запросы AV/C. IOFireWireAVCLibUnitInterface предоставляет методы, отправляющие команды AV/C во внешний модуль AV/C. IOFireWireAVCLibUnitInterface также обеспечивает метод для получения ссылки сеанса, таким образом, можно одновременно заставить IOFireWireLibDeviceInterface получать доступ к внешнему устройству и читать его регистры управления разъема.
Проект AVCBrowser показывает, как использовать оба интерфейса. Во-первых, это использует IOFireWireAVCLibProtocolInterface, чтобы выделить и считать разъемы ввода и вывода на Macintosh и получить запросы AV/C. Затем это в настоящее время находит все модули AV/C в Реестре I/O и получает IOFireWireAVCLibUnitInterface для каждого. С этим интерфейсом проект открывает модуль и отправляет ему команды. Наконец, проект получает IOFireWireDeviceInterface для объекта устройства лежание в основе объекта IOFireWireAVCUnit и читает его регистры управления разъема.
AVCBrowser записан в Objective C с помощью платформы Какао и, как таковой, содержит несколько методов, обрабатывающих пользовательский интерфейс. Ради краткости этот документ фокусируется на только тех методах, получающих и использующих интерфейсы IOFireWireAVCLib.
AVCBrowser содержит четыре класса:
DevicesController реализует объект DevicesController, содержащий массив модулей AV/C, которые он находит в Реестре I/O и включает метод экземпляра открыть информацию об устройстве и дисплее о ее разъемах ввода и вывода.
AVCDevice реализует объект AVCDevice, содержащий информацию об объекте IOFireWireAVCUnit, включая дескриптор к его IOFireWireAVCLibUnitInterface и IOFireWireDeviceInterface для его базового объекта IOFireWireDevice.
PlugBrowserController реализует объект PlugBrowserController, реагирующий на пользовательские запросы информации о разъемах ввода и вывода определенного модуля AV/C.
Разъем реализует объект Разъема для каждого разъема на модуле AV/C, включающем информацию о ее широковещательном состоянии и канале.
Реализация DevicesController init
метод использует стандартные функции Набора I/O, чтобы получить порт Маха и искать Реестр I/O объекты IOFireWireLocalNode (если существует больше чем одна шина FireWire, существует больше чем один объект IOFireWireLocalNode представление Macintosh). Для каждого объекта это находит, init
метод получает IOFireWireAVCLibProtocolInterface, и он использует функцию интерфейса setMessageCallback
получить сброс шины и сообщения переподключения. Это также устанавливает подпрограмму для обрабатывания запросов, отправленных в Macintosh от устройства AV/C (в этой выборке, видеокамере), с помощью функции интерфейса setAVCRequestCallback
.
DevicesController init
метод тогда устанавливает разъемы ввода и вывода на локальном узле и читает их использование значений функции IOFireWireAVCLibProtocolInterface, поскольку Перечисление 3-8 показывает.
Перечисление 3-8 , Настраивающее разъемы на локальном узле
IOFireWireAVCLibProtocolInterface ** avcInterface; |
UInt32 inputPlug, outputPlug; |
kern_return_t result; |
//Not shown in this listing: acquisition of ‘avcInterface’ and error |
//checking using the value of ‘result’. |
if ( (*avcInterface)->allocateInputPlug ) |
//In the call to allocateInputPlug, writePlug is the callback function |
//called when a successful lock transition plug has been performed |
//and inputPlug is set to the plug number upon successful allocation. |
result = (*avcInterface)->allocateInputPlug( avcInterface, self, |
writePlug, &inputPlug ) |
if ( (*avcInterface)->readInputPlug ) { |
UInt32 val; |
val = (*avcInterface)->readInputPlug( avcInterface, inputPlug ); |
if ( (*avcInterface)->updateInputPlug ) |
(*avcInterface)->updateInputPlug( avcInterface, inputPlug, val, |
val+1 ); |
val = (*avcInterface)->readInputPlug( avcInterface, inputPlug ); |
} |
if ( (*avcInterface)->freeInputPlug ) |
(*avcInterface)->freeInputPlug( avcInterface, inputPlug ); |
if ( (*avcInterface)->allocateOutputPlug ) |
(*avcInterface)->allocateOutputPlug( avcInterface, self, writePlug, |
&outputPlug ); |
if ( (*avcInterface)->readOutputPlug ) |
(*avcInterface)->readOutputPlug( avcInterface, outputPlug ); |
Устанавливать диспетчера для ядра обменивается сообщениями к программе, init
метод использует функцию IOFireWireAVCLibProtocolInterface addCallbackDispatcherToRunLoop
, как в этом примере:
(*avcInterface)->addCallbackDispatcherToRunLoop( avcInterface, |
CFRunLoopGetCurrent() ); |
После установки IOFireWireAVCLibProtocolInterface для локального узла, init
метод тогда регистрируется для уведомления о новых объектах IOFireWireAVCUnit в Реестре I/O, с помощью функций, описанных в Нахождении Устройств FireWire. Уже чтобы счесть все объекты IOFireWireAVCUnit существующими в Реестре I/O и вооружить уведомление, класс DevicesController вызывает свою функцию serviceMatchingCallback
. Эта функция инстанцирует объекта AVCDevice (объявленный в AVCDevice.h
) поскольку каждый IOFireWireAVCUnit возражает, что находит.
Реализация метода класса AVCDevice withIOService:
использование Набор I/O и Базовая Основа функционирует для извлечения объекта IOFireWireAVCUnit GUID
(глобально уникальный идентификатор), Unit_Type
, и FireWire Product Name
свойства. Можно использовать эти свойства (или любое из свойств объекта IOFireWireAVCUnit, перечисленного в Таблице 2-2) для создания более определенного словаря соответствия. withIOService:
вызовы метода закрытая функция findPluginForDevice
создать словарь, соответствующий на объектах IOFireWireAVCUnit с переданным - в GUID и создать интерфейс типа IOCFPlugInInterface для него.
withIOService:
метод запрашивает IOCFPlugInInterface от findPluginForDevice
получить IOFireWireAVCLibUnitInterface. Это тогда использует функции IOFireWireAVCLibUnitInterface, чтобы открыть модуль, установить подпрограмму обратного вызова и диспетчера, и отправить команду в модуль, поскольку Перечисление 3-9 показывает.
Перечисление 3-9 , Связывающееся с внешним устройством AV/C
IOReturn result; |
IOFireWireAVCLibUnitInterface **avcInterface; |
AVCDevice *theDevice; |
UInt32 size; |
UInt8 cmd[8], response[8]; |
cmd[kAVCCommandResponse] = kAVCStatusInquiryCommand; |
cmd[kAVCAddress] = kAVCUnitAddress; |
cmd[kAVCOpcode] = kAVCUnitInfoOpcode; |
cmd[3] = cmd[4] = cmd[5] = cmd[6] = cmd[7] = 0xff; |
size = 8; |
//Error-checking by examining ‘result’ is not shown here. |
result = (*avcInterface)->open( avcInterface ); |
//In the call to setMessageCallback, avcMessage is the completion routine |
//that forwards AV/C bus status messages from the I/O Kit. |
(*avcInterface)->setMessageCallback( avcInterface, theDevice, avcMessage ); |
(*avcInterface)->addCallbackDispatcherToRunLoop( avcInterface, |
CFRunLoopGetCurrent() ); |
result = (*avcInterface)->AVCCommand( avcInterface, cmd, 8, response, &size |
); |
Затем, withIOService:
метод передает свойство GUID объекта IOFireWireAVCUnit и тип службы IOFireWireDevice к findPluginForDevice
функция для получения IOCFPlugInInterface для соответствующего объекта устройства. Это тогда получает IOFireWireDeviceInterface для объекта устройства и использует функции интерфейса, чтобы добавить диспетчера обратного вызова к текущему циклу выполнения и открыть устройство, как в этом примере:
IOFireWireLibDeviceRef resultInterface; |
(*resultInterface)->AddCallbackDispatcherToRunLoop( resultInterface, |
CFRunLoopGetCurrent() ); |
(*resultInterface)->Open( resultInterface ); |
Наконец, withIOService:
метод использует метод экземпляра readQuad:
вызывать функцию IOFireWireDeviceInterface ReadQuadlet
с адресами основных выходных и входных разъемов, поскольку Перечисление 3-10 показывает.
Перечисление 3-10 Читая регистры управления разъема внешнего устройства AV/C
//... |
AVCDevice *theDevice = [[self alloc] init]; |
//... |
FWAddress addr; |
UInt32 master; |
addr.addressHi = 0xffff; |
addr.addressLo = 0xf0000900; |
//Read the master output plug. |
master = [theDevice readQuad:addr]; |
//Place results in the AVCDevice object ‘theDevice’. |
[theDevice setOutMax: [NSNumber numberWithUnsignedInt:(1 << (master >> 30)) |
* 100]]; |
[theDevice setOutBase: [NSNumber numberWithUnsignedInt:(master >> 24 & |
0x3f)]]; |
[theDevice setOutNum: [NSNumber numberWithUnsignedInt:master & 0x1f]]; |
addr.addressLo = 0xf0000980; |
//Read the master input plug. |
master = [theDevice readQuad:addr]; |
//Place results in the AVCDevice object ‘theDevice’. |
[theDevice setInMax: [NSNumber numberWithUnsignedInt:(1 << (master >> 30)) * |
100]]; |
[theDevice setInMax: [NSNumber numberWithUnsignedInt:(master >> 24 & 0x3f)]]; |
[theDevice setInMax: [NSNumber numberWithUnsignedInt:master & 0x1f]]; |
//... |
//The readQuad: instance method. |
-(UInt32)readQuad:(FWAddress)addr |
{ |
UInt32 val = 0xdeadbeef; |
IOReturn status; |
status = (*_interface)->ReadQuadlet( _interface, _device, &addr, &val, |
kFWDontFailOnReset, 0 ); |
return val; |
} |
Когда проект AVCBrowser работает, пользователь может принять решение открыть определенное устройство AV/C и просмотреть его тип, пакетную скорость и число разъемов ввода и вывода. Для каждого разъема пользователь может просмотреть информацию об этом, такой, как будто это является онлайновым и широковещательным.