Пользование библиотеками интерфейса устройства 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 если устройство получило ORB

  • suspendORBs если устройство отключается, когда сброс шины происходит или

  • 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 который вызывает класс SBP2SampleORBTransport loginResumed метод.

  • Экземпляр SBP2SampleORBTransport представляет фиктивный ORB.

  • Когда фиктивный ORB завершается, statusNotify метод SBP2SampleORBTransport вызывает SBP2SAMPLEDRIVER resumeORBs метод, устанавливающий fSuspended флаг к false и представляет a TEST_UNIT_READY ORB к транспортному уровню.

  • Экземпляр SBP2SampleORBTransport подчиняется TEST_UNIT_READY ШАР.

  • Когда TEST_UNIT_READY ORB завершается, SBP2SampleORBTransport statusNotify вызовы метода completeORB метод класса SBP2SampleDriver.

  • SBP2SampleDriver completeORB метод проверяет, является ли ORB, просто завершенный, a TEST_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 вызывает SBP2SAMPLEDRIVER resumeORBs метод, устанавливающий fSuspended флаг к false и представляет a TEST_UNIT_READY ORB к транспортному уровню.

  • Экземпляр SBP2SampleORBTransport подчиняется TEST_UNIT_READY ШАР.

  • Когда TEST_UNIT_READY ORB завершается, SBP2SampleORBTransport statusNotify вызовы метода completeORB метод класса SBP2SampleDriver.

  • SBP2SampleDriver completeORB метод проверяет, является ли ORB, просто завершенный, a TEST_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 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 и просмотреть его тип, пакетную скорость и число разъемов ввода и вывода. Для каждого разъема пользователь может просмотреть информацию об этом, такой, как будто это является онлайновым и широковещательным.