В этой статье приведен перевод раздела "Device Driver Manager" из документации "VisualDSP++ 5.0 Device Drivers and System Services Manual for Blackfin® Processors" [1]. Описывается использование модели Драйвера Устройства компании Analog Devices, которая применяется для управления устройствами - как внутренними, встроенными в процессор Blackfin, так и внешними. К внутренним устройствам могут относиться, к примеру, последовательные интерфейсы SPORT или параллельный интерфейс PPI. К внешним устройствам относятся устройства, подключаемые к процессору снаружи, например АЦП, ЦАП, микросхемы кодеков, расширитель ввода/вывода и т. д.
Рассматриваются вопросы, распределенные по отдельным разделам:
Обзор модели Драйвера Устройства. Предоставлено общее описание функционала, предоставленного моделью, и краткое описание всей архитектуры Драйвера Устройства.
Использование Менеджера Устройств. Описывается, как приложения примеряют модель Драйвера Устройства и взаимодействуют с ней, включая методы организации потоков данных, поддерживаемые моделью.
Дизайн Менеджера Устройств. Описывается, как работает Менеджер Устройств и его интерфейс программирования API. Приведено описание ответов Менеджера Устройств на вызовы API, и как он взаимодействует с физическими драйверами.
Дизайн драйвера физического устройства. Описано, как можно самому написать драйвер физического устройства, чтобы он был совместим с моделью Драйвера Устройств, и как драйвер физического устройства взаимодействует с Менеджером Устройств.
Интерфейс программирования (API) Менеджера Устройств. Описание функций API Менеджера Устройств.
Публичные типы данных и перечисления, используемые Менеджером Устройств. Предоставлены таблицы с описанием всех структур данных и перечислений.
Интерфейс программирования (API) драйвера физического устройства. Описание функций API, которые используются для взаимодействия между Менеджером Устройств и драйвером физического устройства.
В составе системы программирования VisualDSP++ предоставлены примеры кода для реализации драйвера PPI и драйвера UART. Примеры показывают, как использовать модель Драйвера Устройства, как и драйверы устройств компании Analog Devices вместе с драйвером устройства и системными службами. Примеры приложений, использующих драйверы устройств, см. в директории Blackfin/EZ-Kits. Исходный код для всех предоставленных компанией Analog Devices драйверов устройств см. в директории Blackfin/Lib/Src/Driver.
√ Система программирования VisualDSP++ поставляется вместе с готовыми реализациями драйверов для внутренних устройств процессора (и даже для некоторых внешних устройств) и примерами их использования. Подробное описание по использованию отдельных драйверов устройств, включая идентификаторы команд (command ID), коды событий (event codes), коды возврата (return codes), примеры использования и т. д. см. в отдельных файлах документации, находящихся в директории Blackfin/docs/drivers корневого каталога инсталляции VisualDSP++.
Интерфейс между приложением и драйвером устройства (API) прост, непротиворечив и знаком большинству программистов, которые ранее использовали библиотеки компании Analog Device (например, библиотеку Системных Служб [2]). Хотя имеются некоторые накладные расходы для обеспечения стандартизации API, выигрыш от унифицированной модели далеко перевешивает все незначительные связанные с ней неудобства. Модель делает относительно простой процедуру создания нового драйвера устройства, позволяет изолировать приложение от специфики реализации драйвера устройства, и позволяет драйверам устройства максимально использовать аппаратные возможности железа.
Не ожидается, что эта модель будет полностью универсальной. Возможно, что появятся устройства, которые не удастся уложить в рамки предлагаемой модели, или приложению нужно работать с устройством неким уникальным образом, и т. д. Целью разработки модели Менеджера Устройств и драйверов физических устройств было предоставление простого, эффективного рабочего окружения, который будет работать на большинстве приложений, независимо от применяемого процессора. Такая система должна облегчить портируемость приложения на различные модели процессоров.
Весь исходный код модели драйвера устройств прикладывается различным дистрибутивам системы разработки VisualDSP. Вовсе не обязательно, что этот исходный код должен быть как-то модифицирован при использовании, однако его предоставление дает пользователю возможность лучше разобраться, как работает код.
Термины "Менеджер Устройств" и "драйвер физического устройства" относятся к соответствующим программным компонентам библиотеки компании Analog Devices. Термин "Драйвер Устройства" в этой документации относится к комбинации драйвера физического устройства и Менеджера Устройств.
[Обзор модели Драйвера Устройства]
Модель Драйвера Устройства построена на основе иерархии. На рис. 10-1 показаны различные компоненты дизайна системы.
Рис. 10-1. Дизайн и иерархия системы Драйвера Устройства.
На рисунке 10-1 показаны следующие компоненты:
Приложение. Хотя это обычно программа пользователя, этот блок может относиться к любому программному компоненту, который можно рассматривать как клиент служб Менеджера Устройств (это может быть также поток приложения VDK). Обратите внимание, что клиент не имеет единого функционального блока. Менеджер Устройств может поддерживать любое количество клиентов. Т. е. клиентом может быть как единственное приложение пользователя, так и потоки (задачи) RTOS-систем (например VDK).
RTOS. Некоторые разработки используют службы операционных систем реального времени (real-time operating system, RTOS). Модель Драйвера Устройства не относится к какой-то определенной RTOS, и также она не требует присутствия RTOS. Модель Драйвера Устройства не требует никакого функционала или сервисов от RTOS. Некоторые операционные системы реального времени требуют от приложений, чтобы они работали с драйверами устройств через API-функции RTOS. В этих системах RTOS просто рассматривается как клиент для Менеджера Устройств.
Device Manager (Менеджер Устройств). Этот программный компонент предоставляет одну точку доступа к модели Драйвера Устройства. Менеджер Устройств предоставляет для этой цели свой API. Все взаимодействие между клиентом и Драйверами Устройств осуществляется через Менеджер Устройств. В дополнение к предоставленному API, Менеджер Устройств гарантирует, что клиент делает вызовы к этому API в правильной последовательности, выполняются все необходимые синхронизации между службами, и правильно управляется подсистема DMA для всех периферийных устройств (через системные службы Менеджера DMA [3]) для тех периферийных устройств, которые используют DMA.
Физические драйверы. Драйверы физических устройств предоставляют функционал для управления физическим устройством (например, любое конфигурирование настроек регистров, установка параметров устройства и т. д.). Физические драйверы отвечают за привязку обработчиков прерываний ошибки для своих устройств и соответствующую обработку ошибок. Если устройство не поддерживается системой DMA для периферийных устройств, то физический драйвер должен предоставить механизм программируемого ввода/вывода или некий подобный механизм, чтобы обеспечить обмен данными с устройством.
System Services (Системные Службы). Компонент Драйвера Устройства жестко полагается на функционал, предоставляемый библиотекой Системных Служб. Например, Менеджер Устройств используется Менеджер Прерываний [4], и если это необходимо, также Менеджер DMA [3] и Менеджер DCB [5] (службы отложенный функций обратного вызова). Функциональность Системных Служб также доступна для использования физическими драйверами. Например, драйвер UART должен знать частоту тактов SCLK, чтобы UART работал на нужной скорости (baud rate). Через службу управления питанием (power management service), физический драйвер UART может запрашивать текущую тактовую частоту SCLK.
И модель Драйвера Устройства, и Системные Службы разработаны как портируемые программные компоненты. Они написаны главным образом на языке C, с применением кода ассемблера для некоторых критических секций алгоритма. Так что программа, взаимодействующая с моделью Драйвера Устройства и Системными Службами, должна придерживаться модели программирования кода C (C run-time model), соответствующих соглашений о вызовах, передачи параметров и т. п. Приложения и физические драйверы могут быть написаны на языке C или ассемблере. Везде, где это возможно, устранялась зависимость от тулчейна для генерации исполняемого кода. Система включает в себя файлы, которые не требуются, но обслуживают модель программирования библиотек применяемого тулчейна (run-time libraries). Модель Драйвера Устройства и Системные Службы могут быть собраны и запущены в любых известных системах сборки кода (тулчейн), однако главным образом подразумевается использование системы программирования VisualDSP++.
Ни моделью Драйвера Устройства, ни Системными Службами не используется динамическое выделение памяти (malloc). Статическое выделение памяти сведено к минимуму, и необходимая выделенная память передаются в модель Драйверов Устройств и Системные Службы через вызов инициализирующих функций в клиенте (потоке приложения). Это позволяет пользователю определить количество выделяемой памяти (и из какой области памяти она должна быть выделена) для модели Драйвера Устройства и Системных Служб.
[Использование Менеджера Устройств]
Менеджер Устройств предоставляет точку доступа к модели Драйвера Устройства (т. е. к связке Менеджер Устройств + драйвер физического устройства). Для этой цели Менеджер Устройств предоставляет API для клиента (под клиентом подразумевается код приложения или код потока приложения).
Обзор Менеджера Устройств. API Менеджера Устройств состоит из следующих функций:
adi_dev_Init. Предоставляет данные для Менеджера Устройства (память) и инициализирует его. adi_dev_Terminate. Освобождает данные и закрывает Менеджер Устройства. adi_dev_Open. Открывает устройство для использования. adi_dev_Control. Устанавливает и определяет параметры устройства. adi_dev_Read. Читает данные из устройства или ставит в очередь буферы приема для устройства. adi_dev_Write. Записывает данные в устройство или ставит в очередь буферы передачи для устройства. adi_dev_Close. Закрывает устройство.
В дополнение к использованию функций API Менеджера Устройств, приложение предоставляет функцию обратного вызова для Менеджера Устройств. Часто Менеджер Устройства или физический драйвер имеет дело с (асинхронными) событиями, которые нужно передать в приложение пользователя. Событие может быть ожидаемым, как например событие завершения обработки буфера драйвером устройства, или может быть неожиданным, как например генерация устройством ошибки. О возникновении всех событий приложению сообщает вызов callback-функции (так называемая функция обратного вызова). Callback-функция это просто функция в приложении пользователя, которую Менеджер Устройства вызывает для передачи ей информации о событии.
Теория функционирования. Модель Драйвера Устройства построена вокруг концепции, что устройство предназначено для ввода данных в систему и/или для вывода данных из системы. В большинстве систем некое устройство используется для ввода данных в систему (где эти данные должны быть обработаны), и другое устройство выводит обработанные данные из системы. Также часто несколько устройств работают в системе одновременно. Менеджер Устройств предоставляет простой и единообразный интерфейс, не зависящий от количества активных устройств в любой момент времени, и от подробностей реализации обработки взаимодействия с каждым устройством.
Данные, который вдвигаются через устройство или выдвигаются через устройство наружу, инкапсулируются в буфере. API Менеджера Устройств определяет 3 разные типа буфера:
Из-за того, что физическое перемещение данных связано со значительными вычислительными затратами, между программными компонентами передаются только указатели на буферы. API Менеджера Устройств определяет тип данных ADI_DEV_BUFFER как указатель на объединение (union) одномерного буфера, двумерного буфера и кольцевого буфера. Хотя каждый из этих типов обрабатывается по-разному, нет значительных отличий в обработке, так что в этой документации они все вместе упоминаются просто как буфер.
Обычно приложения предоставляют буферы через API-функции Менеджера Устройств, где буферы обрабатываются и затем снова делаются доступными для приложения. Функция adi_dev_Write предоставляет устройству буферы, которые содержат данные для отправки через устройство. Соответственно функция adi_dev_Read предоставляет устройству буферы, которые устройство заполняет приходящими данными.
Буферы обрабатываются в том порядке, в котором они были получены. Буферы, предоставленные для определенного устройства, не должны быть какого-то универсального размера; каждый индивидуальный буфер может иметь любой произвольный размер. Кроме того, для одного устройства могут быть предоставлены как одномерные, так и двумерные буферы. Кольцевые буферы более сложные (подробнее см. врезку "Предоставление буферов для устройства").
Перед использованием устройства приложение (или клиент) должно сначала инициализировать Менеджер Устройств. Клиент делает это вызовом функции adi_dev_Init, в которую передается блок памяти, который Менеджер Устройств может использовать для своей внутренней обработки. Клиент решает, какое количество памяти (и какая область памяти используется, SRAM или SDRAM) следует предоставить Менеджеру Устройств; чем больше памяти предоставлено, тем большее количество физических устройств может быть открыто одновременно.
Менеджер Устройств требует для себя непрерывный блок памяти, которую делит внутри себя на 2 части. Одна часть это базовая память, необходимая Менеджеру Устройств для инициации самого себя, и другая часть памяти требуется для поддержки n одновременно открытых драйверов устройств. Предоставляются макросы (ADI_DEV_BASE_MEMORY и ADI_DEV_DEVICE_MEMORY), чтобы определить количество памяти (в байтах), требуемое соответственно для базовой памяти и для открытых добавляемых драйверов устройств.
Например, если клиент хочет инициализировать Менеджер Устройств, и нужно в любой момент времени обеспечить одновременное открытие 4 драйверов устройств, то требуемое количество памяти в байтах будет вычисляется следующим образом:
ADI_DEV_BASE_MEMORY+(ADI_DEV_DEVICE_MEMORY*4))
Будучи вызванной, функция adi_dev_Init(), инициализирует предоставленную память. Как и все функции Менеджера Устройств, функция инициализации вернет код, который покажет специфическую ошибку, возникшую при вызове функции. Все API-функции Менеджера Устройств возвратят код ADI_DEV_RESULT_SUCCESS, если вызов функции был завершен успешно. Все коды возврата по ошибке имеют имена вида ADI_DEV_RESULT_XXXX.
В дополнение к коду возврата функция adi_dev_Init() возвратит количество драйверов устройств, которое может быть поддержано одновременно, и хендл на Менеджер Устройств. Количество устройств может быть проверено программно, чтобы убедиться в том, что Менеджер Устройств действительно может управлять запрошенным количеством драйверов устройств.
Другой параметр, передаваемый в функцию adi_dev_Init(), это параметр критического региона. Когда необходимо защитить критический регион кода, Менеджер Устройства и все физические драйверы полагаются на системную службу Менеджера Прерываний [4], чтобы защитить критический код. Параметр критического региона передается в adi_dev_Init(), после чего он внутренне передается в функцию adi_int_EnterCriticalRegion(). Подробнее см. врезку с описанием функции adi_int_Init на страничке [4].
Примечание: текущая реализация библиотеки системных служб и VDK не использует вложенные критические регионы, поэтому в параметре критического кода может быть передан любой параметр. Т. е. обычное приложение VisualDSP или VDK приложение должно вызвать adi_int_Init с параметром NULL для критической секции кода. Если же используется RTOS, отличающаяся от VDK, или в приложении используются вложенные критическое области, то значение в этом параметре зависит от использования критических секций кода в этой RTOS или приложении.
Когда Менеджер Устройств больше не нужен, клиент может завершить его вызовом функции adi_dev_Terminate(). В эту функцию передается хендл Менеджера Устройств, который был ранее получен вызовом функции adi_dev_Init(). Менеджер Устройств закрывает любые открытые физические устройства и затем делает возврат. После выхода и функции adi_dev_Terminate() клиент может повторно использовать ту память, которая была предоставлена для Менеджера Устройства вызовом функции adi_dev_Init(). После того, как Менеджер Устройств был завершен, для использования API Менеджера Устройств он должен быть инициализирован повторно.
√ Во многих встраиваемых системах работа Менеджера Устройств никогда не завершается.
После того, как Менеджер Устройств был инициализирован, для использования устройства клиент должен сначала вызвать API-функцию adi_dev_Open() для открытия устройства. Клиент передает в неё параметры, показывающие драйвер устройства, который надо открыть (параметр pEntryPoint), номер устройства, которое надо открыть (параметр DevNumber; он нужен, потому что однотипных устройств в процессоре может быть несколько, так что они нумеруются по порядку, начиная с 0 - например SPI0, SPI1 и т. д.), направление потока данных (inbound, outbound или both), и т. д. Клиент также передает хендл на службу DMA для использования Менеджером Устройств и физическим драйвером. Этот параметр может быть равен NULL, если клиент знает, что это устройство не использует DMA.
Параметр pDeviceHandle указывает на место в памяти, куда Менеджер Устройства сохранит хендл на открываемый драйвер устройства. Все последующие вызовы API для этого устройства должны использовать этот хендл. ClientHandle это параметр, который Менеджер Устройств передает обратно клиенту при каждом вызове callback-функции клиента.
В функцию adi_dev_Open() еще передаются 2 других параметра. Они также связаны с функцией callback. Параметр DCBHandle это хендл на службу отложенных функций обратного вызова (Менеджер DCB [5]), которая должна использоваться для вызова клиентской callback-функции. Если DCBHandle не NULL, то драйвер устройства использует указанную службу отложенных функций обратного вызова для всех запусков callback этого устройства. Если же указано DCBHandle равным NULL, все вызовы callback будут немедленными (live); это означают, что их выполнение не будет отложено на менее приоритетный код, и они будут сразу выполняться в контексте обработчика аппаратного прерывания (обычно это ISR, работающий на каком-то уровне приоритета IVG). Параметр ClientCallback указывает на callback-функцию клиента.
Callback-функция вызывается в ответ на возникновение асинхронных событий, с которыми имеет дело драйвер устройства. Как уже опоминалось, некоторые события могут быть ожидаемыми, как например опустошение буфера передачи, и некоторые события могут быть неожиданными, как например возникновение ошибки с устройством. Независимо от типа события Менеджер Устройства вызывает callback-функцию для оповещения клиента о возникновении события.
Поток данных не начинается с вызовом функции adi_dev_Open(). Эта функция просто открывает устройство для использования; для устройства может потребоваться некое конфигурирование, чтобы мог начаться поток данных через устройство.
Функция adi_dev_Control() используется для конфигурирования и разрешения/запрета потока данных через устройство. При открытии большинство драйверов инициализируют устройство некоторыми настройками по умолчанию. Если эти настройки подходят для приложения, то со стороны приложения потребуются совсем незначительные настройки, или они совсем не потребуются. В других случаях, когда настройки драйвера по умолчанию не соответствуют потребностям приложения, для устройства требуется некоторое конфигурирование. Для того, чтобы выполнить настройки, и определить, какие настройки сейчас используются, предназначена функция adi_dev_Control().
Эта функция получает в качестве параметров DeviceHandle (указывающий на открытое устройство, см. врезку "Открытие устройства"), идентификатор команды (command ID, который задает действие - установить параметры устройства или их прочитать), и указатель на область памяти, где содержится значение устанавливаемого параметра (или куда должно быть записано значение считанного параметра). Менеджер Устройств определяет некоторые стандартные параметры; однако физические драйверы свободны в добавлении своих собственных идентификаторов команд помимо определенных Менеджером Устройств. Например, драйвер физического устройства может создать command ID, чтобы установить уровень громкости на выходе.
√ Разработчик приложения должен проверить документацию по физическому драйверу, чтобы определить, какие параметры можно конфигурировать, и какие конфигурации можно выбрать.
Независимо от того, какие нужно сделать изменения конфигурации устройства для клиента, он должен сделать (как минимум) 2 вызова функции adi_dev_Control(). Эти вызовы устанавливают метод работы потока данных (см. врезку ниже), и разрешают поток данных для устройства.
Возможные методы потока данных определены в перечислении ADI_DEV_MODE, которое находится в файле adi_dev.h (папка Blackfin\include\drivers в каталоге установки VisualDSP++ 5.0):
typedefenum { /* Методы потока данных (Dataflow) */
ADI_DEV_MODE_UNDEFINED, /* не определено */
ADI_DEV_MODE_CIRCULAR, /* кольцевой буфер */
ADI_DEV_MODE_CHAINED, /* цепочка буферов */
ADI_DEV_MODE_CHAINED_LOOPBACK, /* цепочка с автопереходом на начало цепочки */
ADI_DEV_MODE_SEQ_CHAINED, /* последовательность цепочек буферов */
ADI_DEV_MODE_SEQ_CHAINED_LOOPBACK /* последовательность цепочек с переходом на начало */
} ADI_DEV_MODE;
Менеджер Устройств поддерживает 3 метода потока данных: circular (кольцевой или автобуфер), chained (соединение буферов в цепочку) и chained with loopback (цепочка буферов с возвратом на начало цепочки). Перед предоставлением для Менеджера Устройств каких-либо буферов или разрешением потока данных, приложение должно информировать Менеджер Устройства об используемом методе работы потока данных вызовом функции adi_dev_Control() с идентификатором команды ADI_DEV_CMD_SET_DATAFLOW_METHOD. После того, как метод потока данных был определен, клиент должен может предоставить для устройства входящие буферы (вызовом функции adi_dev_Read) или исходящие буферы (вызовом функции adi_dev_Write).
Как показано на рис. 10-2, метод кольцевого потока использует один предоставляемый для Менеджера Устройств кольцевой буфер, при этом предполагается, что устройство было открыто для однонаправленного трафика.
Рис. 10-2. Работа кольцевого буфера (автобуфер).
Когда Менеджеру Устройств предоставляется кольцевой буфер, приложение информирует Менеджер Устройств о том, сколько находится подбуферов внутри кольцевого буфера; два подбуфера используется для традиционной схемы двойной буферизации (ping-pong), хотя процессоры Blackfin поддерживают любое количество подбуферов. Приложение также говорит Менеджеру Устройств, когда оно хочет вызвать callback во время обработки кольцевого буфера.
Предоставляется 3 опции для организации оповещения приложения об обработке буфера: никогда не вызывать callback, callback после обработки каждого подбуфера и callback после обработки всего буфера. Менеджер Устройства начинает обработку от начала буфера. Установленным способом Менеджер Устройств оповещает приложение путем вызова callback-функции, когда завершается обработка каждого подбуфера, или когда был обработан весь буфер целиком. После того, как был достигнут конец буфера, Менеджер Устройств автоматически перезапускает обработку с начала буфера, и процесс циклически продолжается.
На рис. 10-3 показан метод потока данных с соединением буферов в цепочку (chained dataflow), где для Менеджера Устройств предоставлен один или большее количество одномерных и/или двумерных буферов. Может быть предоставлено любое количество буферов; буферы могут быть любых размеров, для одного и того же устройства могут быть предоставлены как одномерные, так и двумерные буферы. Только один из всех буферов, или все буферы, или никакой из буферов могут быть помечены для генерации вызова callback-функции приложения, когда буфер был обработан. Дополнительные буферы могут быть предоставлены в любой момент времени - либо до, либо после того, как поток данных было разрешен. Менеджер Устройств гарантирует обработку буферов в том порядке, в каком они были предоставлены для Менеджера Устройств.
Рис. 10-3. Соединенные в цепочку буферы (chained dataflow).
Когда используется метод chained dataflow, приложение может дать команду Менеджеру Устройств работать в синхронном режиме. Обычно Менеджер Устройств работает в асинхронном режиме, когда функции adi_dev_Read и adi_dev_Write сразу делают возврат в приложение до того, как были обработаны предоставленные ими буферы. В синхронном режиме функции adi_dev_Read и adi_dev_Write не возвратят управление, пока не будут обработаны все предоставленные ими буферы. Хотя синхронный режим редко когда используется в системах реального времени, Менеджер Устройств все-таки предоставляет такую возможность.
На рис. 10-4 показан метод потока данных с соединением буферов в цепочку, когда после обработки последнего буфера происходит возврат на начало цепочки (chained dataflow with loopback). Он похож на метод обработки chained dataflow, отличие только в том, что Менеджер Устройств после обработки последнего буфера возвращается к обработке первого буфера в цепочке, предоставленной устройству. Такой метод потока данных эффективно создает бесконечный цикл из последовательности буферов. При использовании метода chained with loopback приложение может предоставить буферы во время инициализации, позволяя Менеджеру Устройств обрабатывать буферы, после чего не предоставлять Менеджеру Устройств никакие буферы. При таком методе работы, так же и как с методом chained dataflow, для каждого одного буфера в цепочке, для всех буферов цепочки, или ни для одного буфера цепочки может быть задано запускать вызов функции callback для оповещения приложения.
Рис. 10-4. Соединенные в цепочку буферы с возвратом на начало (Loopback).
После того, как был определен метод работы потока данных, правильно подготовлены все предоставленные для устройства буферы (см. врезку "Предоставление буферов для устройства"), приложение разрешает поток данных вызовом функции adi_dev_Control с идентификатором команды ADI_DEV_CMD_SET_DATAFLOW. Поток данных стартует немедленно, так что приложение должно гарантировать, что если не используется синхронный режим, что устройства были открыты для входящих или двунаправленных данных с предоставленными буферами, или в противном случае данные могут быть потеряны.
Буферы для устройства предоставляются через вызовы API-функций adi_dev_Read и adi_dev_Write. Функция adi_dev_Read предоставляет буферы для входящих данных, adi_dev_Write для исходящих данных. Каким образом клиент предоставляет буферы для устройства через эти вызовы API зависит с небольшими различиями от выбранного метода работы потока данных (см. врезку "Методы работы потока данных").
Когда устройство сконфигурировано для использования кольцевого метода потока данных (circular dataflow), приложение предоставляет драйверу устройства один и только один буфер для входящих данных и/или один и только один буфер для исходящих данных. Данные предоставленных буферов указывают на непрерывные участки памяти, соответствующие однако нескольким подбуферам, которые приложение хочет использовать.
Например, предположим, что приложение хочет обработать данные в инкрементах по 512 байт, и хочет работать традиционным способом "ping-pong" (2 подбуфера). Тогда приложение предоставляет драйверу устройства 1 буфер данных размером 1024 байта, состоящий из 2 подбуферов, каждый по 512 байт. Таким способом одновременно могут быть использованы два подбуфера - с одним подбуфером из 512 байт работает приложение, а с другим подбуфером из 512 байт работает драйвер.
Другой пример - приложение хочет обработать стандартный кадр видео формата NTSC (525 строк, на каждую по 1716 байт). Буфер, предоставленный для Менеджера Устройств, должен быть непрерывным куском памяти размером 900900 байт (525 * 1716). В этом случае количество подбуферов будет 525. Независимо от того, сколько предоставлено подбуферов, с методом кольцевого потока данных (circular dataflow), для драйвера устройства предоставлен один буфер, приложению никогда не нужно предоставлять устройству другой буфер, поскольку один и тот же буфер используется бесконечно.
Когда устройство сконфигурировано для использования метода потока данных с цепочкой буферов (chained dataflow), для устройства может быть предоставлено любое количество одномерных и двумерных буферов. Буферы могут быть предоставлены для устройства либо по одному, либо сразу несколько, через один вызова adi_dev_Read и/или один вызов adi_dev_Write. Приложение может предоставить Драйверу Устройства дополнительные буферы в любое время до (или даже после) момента разрешения потока данных. Предположим, что Драйвер Устройства работает в асинхронном режиме, тогда любой один из буферов, или никакой из буферов, или все буферы, или только некоторые из них, могут быть помечены для генерации callback, когда драйвер устройства завершил его обработку. Каждый буфер может быть индивидуального размера, и для одного и того же устройства может быть предоставлены как одномерные, так и двумерные буферы.
Предоставление буферов для метода потока с цепочкой буферов и переходом от конца к началу цепочки (chained-with-loopback dataflow) идентично предоставлению буферов для устройств, использующих метод chained dataflow, за исключением того, что буферы могут быть предоставлены только когда поток данных запрещен.
Когда устройство больше не нужно для клиента, то оно закрывается вызовом API-функции adi_dev_Close. Она завершает поток данных, если он был разрешен, и освобождает все ресурсы, включая DMA и другие, используемые Драйвером Устройства. Если приложению нужно опять использовать устройство после того, как оно было закрыто, оно может быть открыто повторно вызовом API-функции adi_dev_Open.
Менеджер Устройств вызывает callback-функцию приложения, чтобы оповестить клиента о происходящих событиях. События бывают ожидаемые (такие как завершение обработки буфера) или неожиданные (такие как ошибка на аппаратуре устройства). Обычно callback-функция клиента организована как эквивалентный оператор switch на языке C, вовлекая подходящую обработку, какая требуется для имеющегося типа события. Менеджер Устройств определяет несколько событий, и физические драйверы могут добавить дополнительные события, которые требуются для управляемого ими устройства.
Поскольку Менеджер Устройств и физические драйверы полагаются на системные сервисы, то эти сервисы должны быть инициализированы перед открытием Драйвера Устройства. Например, когда открывается Драйвер Устройства, то Менеджер Устройств нуждается в хендлах к службе отложенных функций обратного вызова (хендл Менеджера DCB [5]) и к службам DMA (хендл Менеджера DMA) - если предположить, что используются и DCB, и DMA. Таким образом, хорошей практикой будет инициализировать и открыть системные службы перед открытием любого Драйвера Устройства. См. "Инициализация" и "Завершение" в статье [2] для дополнительной информации по драйверам устройств и системным службам.
Могут быть созданы такие драйверы, которые вызывают другие драйверы. Например, плата разработчика Blackfin EZ-KIT Lite содержит аудиокодек AD1836. Этот кодек имеет интерфейс управления и статуса, который подходит для подключения к порту SPI, в то время как аудиоданные AD1836 представляются устройству использованием высокоскоростной последовательной линии связи (в этом случае периферийное устройство SPORT). Если система разработана так, что кодек AD1836 это только одно устройство, подключенное к процессору, то один физический драйвер должен быть написан для управления и обслуживания SPI и SPORT.
Альтернативно драйвер AD1836 может быть иерархически находиться над драйверами SPI и SPORT, делая вызовы в эти физические драйверы по мере необходимости. Это особенно верно, если другие периферийные устройства также совместно используют порт SPI (например, разные драйверы SPI и SPORT могут управляться драйвером AD1836). Таким методом в виде стека могут быть созданы более сложные драйверы, такие как драйвер AD1836 или драйвер стандартного стека TCP/IP, находящегося поверх драйвера контроллера Ethernet.
Принятие решения о выборе метода для потока данных. При использовании Драйверов Устройств нужно вдумчиво выбирать метод для потока данных, используемый каждым Драйвером Устройства. Некоторым типам данных удобнее использовать один метод потока данных, и для некоторых - другой метод потока данных. Как общее грубое руководство Вы можете рассмотреть следующие ситуации, которые помогут выбрать подходящий метод потока данных, используемый для Драйвера Устройства.
Цепочка буферов без перехода в начало цепочки. Метод потока chained-without-loopback подходит для данных, основанных на пакетах, которые могут быть отправлены и/или приняты прерывисто (не периодически, не постоянно), или "быстрыми" пересылками (burst). Например, данные от терминала UART, данные Ethernet, и трафик USB часто используют метод потока данных с цепочкой буферов без перехода на начало (chained-without-loopback).
Цепочка буферов с переходом в начало цепочки. Метод потока chained-with-loopback подходит для установившихся, продолжительных и периодических пересылок данных. Например, приложения с передачей аудио и видео часто используют метод потока данных chained-with-loopback. С этим методом буферы предоставляются во время инициализации приложения и Драйверов Устройств. Из-за того, что используется переход в начало, приложению никогда больше не требуется дополнительно предоставлять драйверу буферы, поскольку драйвер автоматически прокручивает один и тот же буфер.
Кольцевой поток данных без цепочки. При передаче аудио или видео настоятельно рекомендуется использовать кольцевой режим, чтобы избежать щелчков и пропаданий звука или искажений изображения. Метод потока данных по кольцу без цепочки подходит для постоянных, продолжительных передачи, где все данные умещаются в непрерывный блок памяти размером 64 килобайта максимум. Особенно это подходит для данных аудио при условии размера буфера до 64 килобайт, что избавляет от накладных расходов применения метода цепочки буферов (chained-with-loopback). Обычно данные видео не подходят для кольцевого потока без цепочки, потому что видеоданные для кадра часто не умещаются в буфер размером 64 килобайта.
Последовательная передача с переходом на начало и без перехода на начало. Такие методы подходят только для устройств, реализующих полудуплексные, последовательные протоколы связи. Например, драйвер устройства для интерфейса I2C (TWI) использует последовательные методы потока, чтобы точно планировать чтения и записи в правильном порядке.
Структура данных ADI_DEV_1D_BUFFER используется для описания линейного массива данных, который обрабатывает Драйвер Устройства. Приложения заполняют различные поля в буфере, полностью описывающие данные для Драйвера Устройства. Для одномерных буферов приложения заполняют следующие поля в структуре ADI_DEV_1D_BUFFER:
Data. Если буфер предоставлен функции adi_dev_Write(), то это поле содержит начальный адрес данных (Data это указатель), отправляемых наружу из устройства. Если буфер был предоставлен для функции adi_dev_Read(), то это поле содержит начальный адрес области памяти, куда Драйвер Устройства будет сохранять данные, принятые устройством снаружи.
ElementCount. Это поле показывает количество элементов, на которое указывает поле Data.
ElementWidth. Это поле показывает ширину (в байтах) каждого элемента данных.
CallbackParameter. Это указатель на параметр функции обратного вызова приложения. Если здесь указано значение NULL, то Драйвер Устройства не будет использовать вызов callback после обработки буфера. Если же здесь не NULL, то Драйвер Устройства запустит callback-функцию приложения после того, как буфер был обработан драйвером, при этом в callback-функцию третьим параметром будет передано значение CallbackParameter.
pNext. Это поле указывает на следующий одномерный буфер в цепочке, если таковой имеется. Если здесь NULL, то этот буфер последний в цепочке, предоставленный для функций adi_dev_Read() или adi_dev_Write(). Если не NULL, то это поле содержит адрес следующего одномерного буфера в цепочке буферов, переданной в функцию adi_dev_Read() или adi_dev_Write().
pAdditionalInfo. Это поле содержит значение, зависящее от драйвера. Оно не используется большинством Драйверов Устройств. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
Когда буферы были предоставлены для Драйвера Устройства через функции adi_dev_Read() или adi_dev_Write(), некоторые Драйверы Устройства не требуют заполнения следующих полей:
ProcessedFlag. Некоторые Драйверы Устройств устанавливают это значение в TRUE после обработки буфера. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
ProcessedElementCount. Некоторые Драйверы Устройств устанавливают это значение в количество элементов, обработанных драйвером для этого буфера. Например, если сетевому драйверу предоставлен буфер размером 100 байт функцией adi_dev_Read(), для входящего пакета, который содержит только 75 байта данных, драйвер может установить это значение в число 75. Это должно означать, что хотя было запрошено 100 байт, имеется в наличии всего лишь 75 байт. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
Например, следующий фрагмент кода подготавливает и предоставляет одиночный буфер из 128 элементов, каждый размером 16 бит для функции adi_dev_Read(). Драйвер вызовет callback-функцию приложения, когда буфер будет обработан, передавая значение 0x12345678 в качестве параметра callback-функции.
#define SAMPLES_PER_BUFFER (128) // количество выборок (элементов)// в буфере данных
static u16 Data[SAMPLES_PER_BUFFER];// хранилище для данных
// создание буфера для обработки драйвером
Buffer.Data = Data;
Buffer.ElementCount = SAMPLES_PER_BUFFER;
Buffer.ElementWidth =2;
Buffer.CallbackParameter = (void*)0x12345678; // параметр pArg = 0x12345678 для// callback-функции
Buffer.pNext =NULL; // только один буфер в цепочке
// предоставление буфера для драйвера, чтобы он заполнил его данными:
Result = adi_dev_Read(Handle, ADI_DEV_1D, (ADI_DEV_BUFFER*)&Buffer);
Следующий фрагмент кода подготавливает и предоставляет для функции adi_dev_Read() цепочку из 4 буферов, каждый из которых описан как 128 элементов, каждый по 32 бита данных. Драйвер запустит callback-функцию приложения после обработки каждого буфера, передавая адрес буфера, который был обработан, в качестве параметра для callback-функции.
#define NUM_BUFFERS (4) // количество используемых буферов
#define SAMPLES_PER_BUFFER (128) // количество выборок (элементов) в буфере данных
static u32 Data[NUM_BUFFERS*SAMPLES_PER_BUFFER];
// хранилище для актуальных буферов
static ADI_DEV_1D_BUFFER Buffer[NUM_BUFFERS];
// создание буферов для обработки драйвером:
for (u32 i =0; i < NUM_BUFFERS; i++)
{
Buffer[i].Data =&Data[i * SAMPLES_PER_BUFFER];
Buffer[i].ElementCoun = SAMPLES_PER_BUFFER;
Buffer[i].ElementWid =4;
Buffer[i].CallbackParameter =&Buffer[i]; // при запуске callback-функции её
Buffer[i].pNext =&Buffer[i+1]; // параметр, pArg будет равен адресу// буфера в цепочке
}
Buffer[NUM_BUFFERS -1].pNext =NULL; // завершение цепочки буферов
// предоставление буферов для драйвера, чтобы он заполнил их данными:
Result = adi_dev_Read(Handle, ADI_DEV_1D, (ADI_DEV_BUFFER*)Buffer);
Структура данных ADI_DEV_2D_BUFFER используется для описания двумерного массива данных, который обрабатывает Драйвер Устройства. Приложение заполняет различные параметры этой структуры, чтобы полностью описать данные для Драйвера Устройства. Для двумерных буферов приложение заполняет следующие поля структуры ADI_DEV_2D_BUFFER:
Data. Если буфер предоставлен функции adi_dev_Write(), то это поле содержит начальный адрес данных (Data это указатель), отправляемых наружу из устройства. Если буфер был предоставлен для функции adi_dev_Read(), то это поле содержит начальный адрес области памяти, куда Драйвер Устройства будет сохранять данные, принятые устройством снаружи.
ElementWidth. Это поле показывает ширину (в байтах) каждого элемента данных.
XCount. Это поле задает количество элементов - столбцов.
XModify. Это поле задает байтовый инкремент адреса (переход по буферу) после передачи каждого столбца.
YCount. Это поле задает количество элементов - строк.
YModify. Это поле задает байтовый инкремент адреса (переход по буферу) после передачи каждой строки.
CallbackParameter. Это указатель на параметр функции обратного вызова приложения. Если здесь указано значение NULL, то Драйвер Устройства не будет использовать вызов callback после обработки буфера. Если же здесь не NULL, то Драйвер Устройства запустит callback-функцию приложения после того, как буфер был обработан драйвером, при этом в callback-функцию третьим параметром будет передано значение CallbackParameter.
pNext. Это поле указывает на следующий одномерный буфер в цепочке, если таковой имеется. Если здесь NULL, то этот буфер последний в цепочке, предоставленный для функций adi_dev_Read() или adi_dev_Write(). Если не NULL, то это поле содержит адрес следующего двумерного буфера в цепочке буферов, переданной в функцию adi_dev_Read() или adi_dev_Write().
pAdditionalInfo. Это поле содержит значение, зависящее от драйвера. Оно не используется большинством Драйверов Устройств. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
Когда буферы были предоставлены для Драйвера Устройства через функции adi_dev_Read() или adi_dev_Write(), некоторые Драйверы Устройства не требуют заполнения следующих полей:
ProcessedFlag. Некоторые Драйверы Устройств устанавливают это значение в TRUE после обработки буфера. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
ProcessedElementCount. Некоторые Драйверы Устройств устанавливают это значение в количество элементов, обработанных драйвером для этого буфера. Например, если сетевому драйверу предоставлен буфер размером 100 байт функцией adi_dev_Read(), для входящего пакета, который содержит только 75 байта данных, драйвер может установить это значение в число 75. Это должно означать, что хотя было запрошено 100 байт, имеется в наличии всего лишь 75 байт. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
Например, следующий фрагмент кода подготавливает и предоставляет для функции adi_dev_Write() пару двумерных буферов, чтобы они были переданы драйвером наружу. Каждый буфер описывает данные для кадра видео формата NTSC ITU-656. Каждый кадр содержит 525 строк, в каждой строке имеется 1716 байт данных. Драйвер вызовет callback-функцию приложения, когда будет обработан каждый буфер, передавая адрес буфера, который был только что обработан, в качестве параметра callback-функции.
#define NUM_BUFFERS (2) // количество буферов
#define NUM_FRAMES (2) // количество кадров
#define COLUMNS (1716) // количество столбцов
#define ROWS (525) // количество строк
static u8 Frames[NUM_FRAMES][COLUMNS * ROWS]; // хранилище для данных
Структура ADI_DEV_CIRCULAR_BUFFER используется для описания данных, который драйвер обрабатывает кольцевым методом. Приложения заполняют различные поля в этой структуре, чтобы полностью описать данные буфера для Драйвера Устройства.
Для кольцевых буферов приложение заполняет следующие поля структуры ADI_DEV_CIRCULAR_BUFFER:
Data. Если буфер предоставлен функции adi_dev_Write(), то это поле содержит начальный адрес данных (Data это указатель), отправляемых наружу из устройства. Если буфер был предоставлен для функции adi_dev_Read(), то это поле содержит начальный адрес области памяти, куда Драйвер Устройства будет сохранять данные, принятые устройством снаружи.
ElementWidth. Это поле показывает ширину (в байтах) каждого элемента данных.
SubBufferCount. Это поле задает количество подбуферов, на которые поделены данные буфера.
SubBufferElementCount. Это поле задает количество элементов в каждом подбуфере.
CallbackType. Это поле задает, как должна вызываться callback-функция приложения, и в нем могут быть следующие значения:
• ADI_DEV_CIRC_NO_CALLBACK - нет вызовов callback-функции. • ADI_DEV_CIRC_SUB_BUFFER - вызов callback-функции после обработки каждого подбуфера. • ADI_DEV_CIRC_FULL_BUFFER - вызов callback-функции только после обработки всего буфера целиком.
pAdditionalInfo. Это поле содержит значение, зависящее от драйвера. Оно не используется большинством Драйверов Устройств. Информацию о том, как это поле используется каждым отдельным драйвером, можно получить из документации по этому драйверу (документация находится в подкаталоге .../Blackfin/docs).
Например, следующий фрагмент кода подготавливает и предоставляет для функции adi_dev_Write() кольцевой буфер для передачи наружу через Драйвер Устройства. Буфер описан как непрерывный блок памяти из 1024 байт, поделенный на 8 подбуферов размером по 128 байт каждый. Драйвер вызовет callback-функцию приложения после обработки каждого подбуфера. Как только поток данных разрешен, драйвер с повторениями обрабатывает кольцевой буфер, пока не будет остановлен поток данных.
#define SUB_BUFFERS (8)
#define SUB_BUFFER_ELEMENTS (128)
static u8 Data[SUB_BUFFERS * SUB_BUFFER_ELEMENTS]; // хранилище для данных
Структура ADI_DEV_SEQ_1D_BUFFER используется для описания последовательного, одномерного (линейного) массива данных, который обрабатывает Драйвер Устройства. Подобно стандартным одномерным буферам, приложение заполняет различные поля структуры, чтобы полностью описать данные для Драйвера Устройства. Фактически последовательный одномерный буфер это урезанный вариант стандартного одномерного буфера, с полем направления, добавленном в конец стандартного буфера. Для последовательных одномерных буферов приложение заполняет следующие поля структуры ADI_DEV_SEQ_1D_BUFFER:
Buffer. Указатель на буфер, идентичный полю Data стандартного одномерного буфера, который заполняется точно так же (см. врезку "Создание одномерных буферов (1D)").
Direction. Это поле заполняется следующими значениями по выбору:
• ADI_DEV_DIRECTION_INBOUND – соответствует буферу для данных, принимаемых устройством. • ADI_DEV_DIRECTION_OUTBOUND – соответствует буферу для данных, передаваемых устройством.
Следующий фрагмент кода предоставляет цепочку из 2 буферов. Первый и третий буферы описывают данные, передаваемые устройством, и второй и четвертый буферы описывают данные, которые принимаются устройством. Драйвер вызовет callback-функцию приложения после того, как будет обработан последний буфер в цепочке, передавая адрес буфера, который был только что обработан, в качестве параметра для callback-функции.
#define ELEMENT_WIDTH (1) // ширина элемента данных в байтах
#define NUM_BUFFERS (2) // количество буферов
#define INBOUND_ELEMENTS (64) // количество читаемых элементов
#define OUTBOUND_ELEMENTS (2) // количество записываемых элементов
static u32 InboundData[INBOUND_ELEMENTS]; // входящие данные
static u32 OutboundData[OUTBOUND_ELEMENTS]; // исходящие данные
// создание исходящего буфера для обработки драйвером:
SeqBuffer[0].Buffer.Data = OutboundData;
SeqBuffer[0].Buffer.ElementCount = OUTBOUND_ELEMENTS;
SeqBuffer[0].Buffer.ElementWidth = ELEMENT_WIDTH;
SeqBuffer[0].Buffer.CallbackParameter =NULL; // нет вызова callback-функции
SeqBuffer[0].Buffer.pNext = (ADI_DEV_1D_BUFFER*)&SeqBuffer[1];
SeqBuffer[0].Direction = ADI_DEV_DIRECTION_OUTBOUND;
// создание входящего буфера для обработки драйвером:
SeqBuffer[1].Buffer.Data = InboundData;
SeqBuffer[1].Buffer.ElementCount = INBOUND_ELEMENTS;
SeqBuffer[1].Buffer.ElementWidth = ELEMENT_WIDTH;
SeqBuffer[1].Buffer.CallbackParameter =&SeqBuffer[1]; // callback
SeqBuffer[1].Buffer.pNext =NULL; // конец цепочки
SeqBuffer[1].Direction = ADI_DEV_DIRECTION_INBOUND;
// предоставление буферов для драйвера:
Result = adi_dev_SequentialIO(Handle, ADI_DEV_SEQ_1D, (ADI_DEV_BUFFER *)SeqBuffer);
[Дизайн Менеджера Устройств]
Менеджер Устройств предоставляет одну точку для доступа к модели Драйвера Устройства. Менеджер Устройств предоставляет приложению API для работы с Драйверами Устройств. Все взаимодействие между клиентом и Драйверами Устройств происходят через Менеджер Устройств - приложение никогда не работает напрямую с физическим драйвером. Менеджер Устройств также предоставляет все для управления DMA - постановка в очередь, зацикливание и т. д. - для тех периферийных устройств, которые поддерживают DMA.
Пользователям обычно не нужно понимать подробности по разработке и реализации Менеджера Устройств. В этом разделе дана информация для тех пользователей, которые хотели бы все-таки глубже разобраться в дизайне. Этот раздел будет частично полезен для тех, кто пишет физические драйверы, и понадобилась дополнительная информация при разработке физических драйверов.
Макросы, определения и структуры данных, созданные для API Менеджера Устройств, являются ключом к пониманию его дизайна. API для Менеджера Устройств описано в файле adi_dev.h, который находится в директории Blackfin/Include/Drivers каталога установки VisualDSP++.
Раздел "Публичные типы данных и перечисления, используемые Менеджером Устройств" содержит таблицы для всех структур данных и перечислений, используемых Менеджером Устройств.
Макросы для использования памяти. В первой секции файла adi_dev.h находятся макросы, которые определяют количество используемой памяти Менеджером Устройств. Эти макросы могут использоваться клиентом, чтобы определить, сколько памяти надо выделить для Менеджера Устройств с помощью функции adi_dev_Init.
Макрос ADI_DEV_BASE_MEMORY определяет количество байт, которое требуется для Менеджера Устройств. Макрос ADI_DEV_DEVICE_MEMORY определяет количество байт, которое требуется Менеджеру Устройств для управления каждым физическим драйвером. Когда память предоставляется для Менеджера Устройств, клиент предоставляет следующее количество памяти (n равно максимальному количеству одновременно открытых в системе физических драйверов):
ADI_DEV_BASE_MEMORY + (n * ADI_DEV_DEVICE_MEMORY)
Хендлы. Далее в файле adi_dev.h находятся операторы typedef для различных handle-типов, используемых Менеджером Устройств. Обычно хендлы это указатели на структуры данных, используемые внутри Менеджера Устройств. Они используются для смысловой идентификации данных, относящихся к устройству, чтобы они могли быть быстро обработаны.
Перечисления для определения метода потока данных. Далее в файле adi_dev.h file имеются перечисления для различных поддерживаемых Менеджером Устройств методов потоков данных, и перечисления, показывающие направление потока данных. Эти перечисления не расширяются физическими драйверами.
Идентификаторы команд (Command ID). В следующей секции файла adi_dev.h перечислены command ID, которые определены Менеджером Устройств. Эти command ID передаются в Менеджер Устройств через функцию adi_dev_Control.
Физические драйверы могут добавить любое количество дополнительных command ID, которые будут относиться к их определенному устройству. Физически драйверы начинают добавлять свои command ID, начиная от стартового значения перечисления кодов для драйвера.
Также в этой секции добавлена структура данных, определяющая пару команд конфигурации. Это предоставлено просто для удобства, что позволяет клиентам передавать таблицу команд в функцию adi_dev_Control, вместо того, чтобы вызывать adi_dev_Control для каждой отдельной команды (см. врезку с описанием функции adi_dev_Control).
События callback. Следующая секция в файле adi_dev.h содержит перечисления для событий callback. Когда происходит событие, запускается callback-функция клиента, и туда передается значение из перечисления, характеризующее событие.
Менеджер Устройств определяет некоторые общие события. Как и с идентификаторами команд (command ID), физические драйверы могут добавить свои собственные события callback, начиная от стартового значения для кодов драйвера.
Коды возврата. Следующая секция файла adi_dev.h содержит перечисления для кодов возврата. Все функции API в Менеджере Устройств возвратят код, показывающий какая ошибка произошла при вызове функции (если возвращен код, равный 0, то вызов API-функции считается успешным). Менеджер Устройств определяет некоторый общий набор кодов возврата. Точно так же, как и для command ID и событий callback, физические драйверы могут добавить свои собственные коды возврата, начиная от стартового значения для кодов драйвера.
Опции callback кольцевых буферов. Следующая секция файла adi_dev.h содержит перечисления для типа callback запросов клиента, когда Менеджер Устройств использует метод потока данных с кольцевым буфером.
Предоставлены перечисления, показывающие Менеджеру Устройств, что не нужно делать вызовы callback, делать вызов callback по завершению подбуфера, или делать callback по завершению обработки всего буфера.
Типы буфера данных. Структуры данных ADI_DEV_1D_BUFFER, ADI_DEV_2D_BUFFER и ADI_DEV_CIRCULAR_BUFFER используются для предоставления буферов данных для драйвера. В верхней части этих структур данных есть зарезервированная область, которая позволяет Драйверам Устройства получить доступ к маленькой области памяти, подключенной к каждому буферу. Как (если это требуется) Драйвер Устройства использует эту зарезервированную область - зависит от реализации.
Обратите внимание, что если физический драйвер устройства поддерживается DMA периферии, то Менеджер Устройства использует эту зарезервированную область для создания дескриптора DMA, описывающего буфер. Этот дескриптор, в свою очередь, передается Менеджеру DMA системных служб, чтобы использовать DMA для перемещения данных, как описано в структуре буфера.
Если физический драйвер не поддерживается периферийным DMA, то физический драйвер может использовать эту зарезервированную область для любых целей - например, для управления очередью или для какого-то механизма физического драйвера, используемого при перемещении данных.
Также в этой секции имеется структура данных ADI_DEV_BUFFER, которая представляет объединение одномерного, двумерного и кольцевого буферов. Этот тип данных используется как удобный метод ссылаться на буфер общим методом, без необходимости знать, какого именно типа этот буфер. API-функции adi_dev_Read и adi_dev_Write используют тип данных ADI_DEV_BUFFER, когда буфер передается в эти функции.
Точка входа в физический драйвер. Следующая секция файла adi_dev.h содержит структуру данных, которая описывает точку входа в физический драйвер. Структура ADI_DEV_PDD_ENTRY_POINT это просто тип данных, который указывает на функции в физическом драйвере, которые вызываются Менеджером Устройств.
Определения API-функций. Последняя секция файла adi_dev.h описывает вызовы API в Менеджер Устройств. Каждая функция здесь декларируется с соответствующими параметрами для каждого вызова. Каждая функция описана подробно в разделе "Интерфейс программирования (API) Менеджера Устройств".
Весь код Менеджера Устройств сосредоточен в файле adi_dev.c. Этот файл находится в директории Blackfin/Lib/Src/Drivers.
Структуры данных. Здесь определены только дополнительные структуры данных ADI_DEV_MANAGER и ADI_DEV_DEVICE. Эти структуры содержат все необходимы данные, которые нужны для работы самого Менеджера Устройств и управления физическим драйвером.
Статические данные. Менеджер Устройств использует единственное место для размещения статических данных. Элемент InitialDeviceSettings копируется в структур ADI_DEV_DEVICE, когда открывается устройство. Эт опредоставляет быстрый и и эффективный способ для инициализации структуры без индивидуального заполнения каждого поля.
Декларации статических функций. Эта секция декларирует статические функции, которые используются в Менеджере Устройств. Каждая из них описана в следующих секциях. Глобально декларируются только функции API; все другие функции являются для Менеджера Устройств статическими.
[Функциональное описание API]
В этой секции приведено описание функциональности, выполняемой каждой из API-функций Менеджера Устройств. Вот эти функции:
Для подробной информацией по параметрам функций и их прототипам см. раздел "Интерфейс программирования (API) Менеджера Устройств".
adi_dev_Init. Эта функция используется для инициализации Менеджера Устройств. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Init.
Обработка начинается с проверки, достаточно ли предоставленное количество памяти для работы Менеджера Устройств. Затем функция определяет, сколько физических устройств можно поддерживать оставшейся предоставленной памятью.
После этого сохраняются указатели критического региона, и структура данных для каждого устройства, которое может поддерживаться как доступное для использования. После этого функция возвращает управление в вызывающий код.
adi_dev_Open. Эта функция используется для открытия устройства, чтобы можно было его использовать. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Open.
Обработка начинается с нахождения свободной структуры ADI_DEV_DEVICE, используемой для управления устройством. Адрес структуры сохраняется в предоставленной клиентом ячейке памяти как хендл на устройство.
Структура ADI_DEV_DEVICE инициализируется и заполняется информацией, описывающей устройство.
Как только структура ADI_DEV_DEVICE была инициализирована, Менеджер Устройств вызывает функцию adi_pdd_Open физического драйвера. Запускается код физического драйвера, который делает все необходимое, чтобы открыть управляемое им устройство. Если физический драйвер не может открыть устройство, то Менеджер Устройств освобождает структуру ADI_DEV_DEVICE, и передает соответствующий код возврата в приложение. Обратите внимание, что из-за того, что коды возврата могут быть расширены физическим драйвером, код возврата может быть специфическим, описывающим какие-либо возможные причины ошибки при открытии устройства.
Если физическое устройство было корректно открыто, то Менеджер Устройств взаимодействует с физическим устройством, чтобы определить, поддерживается ли периферийный DMA. Менеджер Устройства сохраняет эту информацию в структуре ADI_DEV_DEVICE.
adi_dev_Close. Эта функция вызывается приложением, когда устройство больше не нужно. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Close.
После того, как хендл устройства был проверен, подразумевая, что разрешена проверка на ошибку, функция вызывает функцию adi_pdd_Control физического драйвера чтобы остановить поток данных. Как только поток данных был остановлен, закрываются любые открытые устройством каналы DMA. Затем вызывается функция adi_pdd_Close физического драйвера, чтобы выключить устройство и освободить любые ресурсы, используемые физическим устройством. И в завершение структура ADI_DEV_DEVICE помечается как закрытая, так что она может быть впоследствии использована повторно.
adi_dev_Read. Эта функция вызывается приложением, чтобы предоставить устройству буферы, куда могут быть помещены приходящие данные. Предположительно проверка ошибок разрешена, тогда в этой функции обработка начинается проверкой хендла устройства на предмет того, что устройство действительно открыто для входящего (или для двунаправленного) трафика, и что определен метод потока данных. Если метод потока данных еще не задан, то Менеджеру Устройств не предоставлено достаточно информации для того, чтобы определить, что делать дальше с этим буфером. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Read.
Параметр pBuffer, передаваемый в эту функцию, может указывать на одиночный буфер или цепочку буферов. Кроме того, если устройство поддерживается периферийным DMA, то зарезервированная область данных в структуре данных буфера должна быть соответствующим образом сконфигурирована. Обо всех этих подробностях заботится статическая функция PrepareBufferList, описанная ниже.
Как только список буферов подготовлен, проверяется поддерживается ли устройство периферийным DMA. Если да, то вызывается Менеджер DMA, чтобы поставить в очередь буферы на подходящем канале DMA, используя соответствующий метод организации потока данных - связанные в цепочку дескрипторы передаются Менеджеру DMA через вызов функции adi_dma_Queue, и кольцевые буферы передаются через функцию adi_dma_Buffer. Если периферийный DMA не поддерживается, то буферы передаются напрямую к физическому драйверу с использованием функции adi_pdd_Read. Обратите внимание, что когда устройство поддерживается периферийным DMA, физический драйвер экстремально прост, потому что Менеджер Устройств обрабатывает все буферы данных для физического устройства.
В завершении делается проверка, чтобы посмотреть, как работает устройство - в синхронном или асинхронном режиме. Если работа осуществляется в асинхронном режиме (наиболее частый случай для RTOS и быстродействующих приложений), то функция adi_dev_Read немедленно возвратит управление в вызывающий код приложения. Если же работа осуществляется в синхронном режиме, то функция adi_dev_Read ждет, пока буфер (или последний буфер в списке буферов, если в качестве параметра задано несколько буферов) не будет обработан, и только после этого возвращает управление в приложение. И опять, у физического драйвера нет знаний о том, какой используется режим работы - синхронный или асинхронный (точнее говоря, физическому драйверу это знать не нужно).
adi_dev_Write. Эта функция виртуально работает идентично функции adi_dev_Read, за исключением того, что предназначена для передачи данных в обратном направлении. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Write.
adi_dev_Control. Эта функция используется для обработки команд приложения, относящихся к конфигурации устройства. Как и все API-функции, если разрешена проверка ошибок, на входе в функцию будет проверен хендл устройства. Для подробной информации о параметрах функции и кодах возврата см. её описание во врезке adi_dev_Control.
Обработка внутри функции adi_dev_Control основана на command ID, переданном в качестве параметра. Некоторые команды полностью обрабатываются Менеджером Устройств, а некоторые только физическим драйвером, и другие команды требуют совместной обработки и Менеджером Устройств, и физическим драйвером. Чтобы выполнить все это, блок кода этой функции выполнен как оператор switch языка C. Каждая команда Менеджеру Устройств обрабатывается отдельной веткой оператора switch.
Когда передана команда, которую должен обработать Менеджер Устройств, то он обрабатывает команду и затем устанавливает флаг, сигнализируя этим, нужно ли передать обработку команды ниже, к физическому драйверу. Когда обработка спускается ниже в функции, если нужно передать команду в физический драйвер, то вызывается функция adi_pdd_Control физического драйвера, и код возврата из физического драйвера будет передан обратно в приложение. Это позволяет каждому физическому драйверу расширить список идентификаторов команд (command ID), и позволяет создать свои собственные уникальные command ID, которыми может управлять приложение.
Менеджер Устройств обрабатывает следующие команды:
ADI_DEV_CMD_GET_2D_SUPPORT. Эта команда используется для определение, поддерживает ли устройство двумерное перемещение данных. На процессорах Blackfin, если устройство поддерживается периферийным DMA, то предоставляется двумерное перемещение данных. Если устройство не поддерживается периферийным DMA, то команда передается физическому драйверу, чтобы определить, может ли физический драйвер поддерживать двумерные данные.
ADI_DEV_CMD_SET_SYNCHRONOUS. Эта команда используется для перевода Менеджера Устройств в синхронный режим для указанного устройства. Обработка команды заключается только в установке флага в структуре ADI_DEV_DEVICE. Эта команда никогда не передается физическому драйверу, так как вся синхронная активность управляется Менеджером Устройств. Скрытие синхронности от физического драйвера дает то преимущество, что для физического драйвера не требуется заботиться (не надо иметь для этого специальной обработки) о реализации синхронного или асинхронного режимов. Физический драйвер может работать таким образом, чтобы лучше всего взаимодействовать с устройством.
ADI_DEV_CMD_SET_DATAFLOW_METHOD. Эта команда используется для установки метода потока данных для указанного устройства. Если устройство не поддерживается периферийным DMA, то Менеджер Устройств не предпринимает никаких действий, кроме как обработку метода потока данных и передачи команды в физический драйвер через функцию adi_pdd_Control. Если устройство поддерживается периферийным DMA, то значение по умолчанию, используемое конфигурационным регистром управления DMA, обновляется соответствующими настройками, подходящими для метода потока данных. Кроме того, как только метод потока данных определен приложением, Менеджер Устройств открывает необходимое количество каналов DMA, требуемое для поддержки устройства. С физическим драйвером происходи взаимодействие через функцию adi_pdd_Control, с которой задается, какой контроллер DMA и какой номер канала назначается устройству для входящих и/или исходящих данных. Затем к Менеджеру DMA осуществляется доступ для открытия соответствующих каналов в соответствующих режимах, таких как кольцевые или цепочечные дескрипторы. Если устройство открыто с методом потока данных ADI_DEV_MODE_CHAINED_LOOPBACK, то Менеджер DMA также конфигурируется. Обратите внимание, что структура ADI_DEV_DEVICE поддерживается обновленной соответствующей информацией, какие контроллеры и какие каналы открыты или закрыты, в каких рабочих режимах они находятся и т. д.
ADI_DEV_CMD_SET_DATAFLOW. Эта команда выдается, чтобы разрешить или запретить поток данных через устройство. Логика, используемая для разрешения или запрещения потока довольно сложна, и изолирована в статической функции SetDataflow (см. далее её описание).
ADI_DEV_CMD_SET_STREAMING. Эта команда выдается для разрешения или запрета режима стриминга для Драйвера Устройства. Чтобы полностью понять, что влечет за собой режим стриминга, пользователи должны быть знакомы с возможностью стриминга системной службы Менеджера DMA [3]. Хотя поддержка периферийного DMA не требуется, чтобы устройство поддерживало стриминг, все-таки устройства с поддержкой периферийного DMA автоматически задействуют возможности стриминга Менеджера DMA.
Когда разрешен режим стриминга, устройство конфигурируется так, чтобы обрабатывать входящие и/или исходящие данные как продолжающийся непрерывный поток данных. Обычно это позволяет драйверу устройства передавать и принимать данные через устройство на максимально возможной скорости. Чтобы можно было использовать режим стриминга Менеджера Устройств, приложение должно гарантировать удовлетворение следующих условий:
• У устройства всегда есть буферы для обработки, и оно никогда не попадает в ситуацию, когда буферы закончились. Это означает, что приложение гарантирует устройствам, которые открыты для входящих или двунаправленных потоков данных, что всегда есть буферы для сохранения принимаемых данных, и что для устройств, открытых для исходящих или двунаправленных потоков данных также всегда есть буфер, откуда передаются данные из устройства.
• Диаграммы времени работы системы спроектированы так, что Менеджер Устройств может подтвердить и обработать callback-и для буфера перед тем, как будет сгенерирован вызов callback для другого буфера того же самого устройства и того же самого направления данных (входящего или исходящего). Эти условия можно довольно легко обеспечить в большинстве систем.
Статические функции. Ниже приведено описание статических функций Менеджера Устройств, которые используются в поддержке API-функций.
PDDCallback. Эта функция вызывается в ответ на события из физического драйвера. После проверки на ошибку хендла устройства (если разрешена проверка ошибок) Менеджер Устройств просто передает эти события обратно в приложение.
Обратите внимание, что в этой подпрограмме (и функции DMACallback) Менеджер Устройств напрямую вызовет callback-функцию клиента, независимо от того, немедленный это (live) callback, или нет. Это можно сделать, так как физическому драйверу передан хендл на службу отложенных функций обратного вызова (см. Менеджер DCB [5]) как часть функции adi_pdd_Open.
Таким образом, если используется служба отложенных функций обратного вызова, то запуск функции PDDCallback в Менеджере Устройств откладывается физическим драйвером. Таким методом функция PDDCallback может напрямую вызвать callback-функцию клиента.
DMACallback. Эта функция вызывается в ответ на события DMA из Менеджера DMA для устройств, которые поддерживаются периферийным DMA. Если разрешена проверка ошибок, то сначала будет проверен хендл устройства. Затем эта функция определяет событие, которое произошло, и выполняет обработку, основываясь на типе события.
Если событие показывает, что был обработан дескриптор, то обновляются поля флага обработки и счетчика обработки в буфере. Затем вовлекается callback-функция приложения, чтобы оповестить приложение о событии.
Если событие показывает, что обработка DMA сгенерировала событие ADI_DEV_EVENT_SUBBUFFER_PROCESSED, то функция делает соответствующий вызов callback приложения, объявляя этим о завершении обработки подбуфера. Если событие показывает, что обработка DMA сгенерировала событие ADI_DEV_EVENT_BUFFER_PROCESSED, то функция делает соответствующий вызов callback приложения, объявляя этим о завершении обработки всего буфера.
Менеджер DMA сообщает об асинхронных ошибках DMA через механизм callback. Эти ошибки, в свою очередь, передаются обратно клиенту через его callback-функцию.
Обратите внимание, что в этой подпрограмме (и в функции PDDCallback) Менеджер Устройств вызывает callback-функцию клиента напрямую, не заботясь о том, немедленный (live) ли это callback или нет. Это возможно благодаря тому, что Менеджеру DMA передан хендл на службу отложенных функций обратного вызова (Менеджер DCB [5]) как часть функции adi_dma_Open. Таким образом, если используется служба отложенных обратных вызовов, то запуск функции DMACallback в Менеджере Устройств будет отложен Менеджером DMA. Таким способом функция dmaCallback может напрямую вызвать callback-функцию клиента.
PrepareBufferList. Функция подготавливает список из одного буфера или список из нескольких буферов для использования в Менеджере DMA (в том случае, когда устройство поддерживается периферийным DMA) или использования физическим драйвером (когда устройство не поддерживается периферийным DMA).
Функция начинает работу с определения значения поля направления в регистре управления конфигурацией DMA. Поскольку имеются отдельные структуры для кольцевых буферов, одномерных буферов и двумерных буферов, то каждая должна быть обработана по-своему.
Если передан кольцевой буфер, то функция предполагает, что здесь есть только один буфер в списке буферов. Для устройств, открытых с методом потока данных ADI_DEV_MODE_CIRCULAR, предоставляется только один буфер, так что это предположение справедливо. Функция конфигурирует регистр управления конфигурацией DMA в соответствии с параметрами, находящимися в структуре кольцевого буфера. Регистр управления конфигурацией DMA устанавливается для генерации прерываний по внутренним циклам, если приложение хочет вызывать callback, когда завершена обработка каждого подбуфера, или устанавливается для генерации прерываний по внешним цикла, если приложение хочет вызывать callback при завершении обработки всего буфера, или ничего не настраивается для запуска callback, если приложению оповещение не нужно. Размер слова устанавливается на ширину элемента данных в буфере, и соответствующим образом устанавливается поле направления. Затем функция возвращает управления в вызывающий код.
Если в функцию передан одномерный или двумерный тип буфера, обработка буферов увеличивается с некоторыми исключениями.
Для каждого переданного буфера очищаются поля флага завершения обработки и счетчика обработки в структуре буфера. Если физическое устройство поддерживается периферийным DMA, то зарезервированная область в начале каждой структуры буфера преобразуется в модель большого дескриптора (large model descriptor). Дескриптор конфигурируется в соответствии с параметрами в структуре буфера, включая такие параметры, как размер буфера, ширина элемента, направления данных, одномерный буфер или двумерный, и т. д. Дескриптор для каждого буфера в цепочке обновляется для указания следующего буфера в цепочке. Последний дескриптор в цепочке, соответствующий последнему буферу в цепочке, обновляется значением NULL для следующего дескриптора. После завершения обработки цепочка буферов оказывается установленной. Все буферы инициализированы правильным образом, и зарезервированная область в каждом буфере содержит дескриптор DMA для этого буфера, который в свою очередь указывает на дескриптор DMA для следующего буфера в цепочке.
И наконец, если устройство открыто для синхронного режима и поддерживается периферийным DMA, по последний дескриптор в цепочке настраивается для генерации callback из Менеджера DMA к Менеджеру Устройств. Это позволяет Менеджеру Устройств получить подтверждение, когда обработан последний буфер, так чтобы он смог соответствующим образом обновить поля завершения обработки. Последний дескриптор также действует как триггер, который срабатывает каждый раз, когда функция adi_dev_Read или adi_dev_Write возвращает управление обратно в приложение.
SetDataflow. Эта функция вызывается в ответ на команду ADI_DEV_CMD_SET_DATAFLOW, принятую API-функцией adi_dev_Control. В ответ разрешается или запрещается поток данных в соответствии с флагом.
Функция SetDataflow начинает обработку с проверки, что система не пытается разрешить поток данных, когда он уже разрешен, или запретить поток, когда он уже запрещен. Если эту проверку не выполнять, то DMA и/или физические драйверы могут генерировать ошибки.
Когда поток данных запрещен, функция сначала вызывает функцию adi_pdd_Control физического драйвера, чтобы запретить поток данных. Если устройство использует периферийный DMA, важно сначала запретить поток на устройстве перед выключением DMA. Как только физический драйвер запретил поток данных, закрываются любые каналы DMA, которые были открыты для устройства. Это выполняется вызовом Менеджера DMA.
Когда разрешается поток данных, если устройство поддерживается периферийным DMA, функция сначала разрешает поток данных на каналах DMA вызовом Менеджера DMA, чтобы разрешить поток данных на канале или каналах, которые открываются для устройства. После того, как был разрешен поток данных на каналах DMA, функция вызывает функцию adi_pdd_Control физического драйвера, чтобы разрешить поток данных.
[Дизайн драйвера физического устройства]
Физический драйвер это та часть драйвера, которая управляет аппаратурой устройства. Только у физического драйвера есть знания о регистрах управления и статуса устройства и полях в этих регистрах. В отличие от Менеджера Устройств, который один во всей системе, может быть любое количество физических драйверов в одной системе.
Под управлением приложения только Менеджер Устройств обменивается сообщениями и данными с физическими драйверами устройств. Приложения никогда напрямую не взаимодействуют с физическим драйвером, или наоборот. Однако имеется аналогичная последовательность выполнения - как работает приложение с Менеджером Устройств, примерно таким же образом Менеджер Устройств управляет физическими драйверами. Менеджер Устройств открывает физические драйверы устройств, управляет ими, закрывает их так же, как приложение открывает, закрывает Менеджер Устройств и управляет им.
Каждый физический драйвер в системе управляется независимо от других физических драйверов в системе. Хотя в системе могут одновременно существовать несколько физических драйверов, несколько физических драйверов не могут управлять одним и тем же устройством.
В основном физический драйвер управляет всеми экземплярами устройства в системе. Например, если у процессора (и в системе) имеется 4 физических последовательных порта (SPORT), то один физический драйвер для периферии SPORT может управлять всеми четырьмя последовательными портами, индивидуально для каждого и одновременно.
Физический драйвер отвечает за подцепление любого и всех прерываний, которые нужны для физического устройства. Многие физические устройства генерируют прерывания для событий ошибки. Эти события определяются физическим драйвером и передаются обратно наверх через механизм функций обратного вызова (callback). Менеджер Прерываний [4] предоставляет простой, прямолинейный механизм, используемый для обработки всех прерываний. Это упрощает задачу портирования Драйверов Устройства на различные рабочие окружения (процессоры), системы программирования и операционные системы.
Если устройство поддерживается периферийным DMA, то физический драйвер сильно упрощается, поскольку Менеджер Устройств обычно управляет всеми процессами взаимодействия с DMA, без какого-либо участия физического драйвера. Когда устройство открывается, Менеджер Устройств взаимодействует с физическим драйвером с целью определить, поддерживается ли устройство периферийным DMA. Если физический драйвер сообщает, что да, поддерживается, то Менеджер Устройств управляет всей активностью DMA (такой как инициализация, предоставление буферов данных, механизм callback и т. д.) через API Менеджера DMA [3]. Таким образом, Менеджер Устройств никогда не вызывает функции adi_pdd_Read и adi_pdd_Write физического драйвера, который поддерживается периферийным DMA. Физические драйверы для устройств, которые поддерживаются периферийным DMA, очень просты для реализации.
Для устройств, которые не поддерживаются периферийным DMA, физические драйверы все еще могут задействовать преимущества Менеджера DMA, потому что DMA по памяти (MDMA) может быть эффективной стратегией для чтения/записи устройств, которые используют программируемый ввод/вывод. Если назначено использование отложенных функций обратного вызова (deferred callbacks), то физические драйверы эксклюзивно используют службы Менеджера DCB (deferred callback manager), чтобы выставлять вызовы callback-ов в Менеджер Устройств. Для получения дополнительной информации по Менеджеру DCB см. статью [5].
У физических драйверов есть собственный API, к которому обращается Менеджер Устройств. В последующих секция описывается этот API и функциональность, предоставляемая физическим драйвером.
Описание API физического драйвера. API физического драйвера аналогично API между Менеджером Устройств и приложением, так что можно привести аналогию между функциями API физического драйвера и функциями API Менеджера Устройств, за исключением функции adi_dev_Init. Функции физического драйвера снабжены префиксом adi_pdd и определены в подключаемом файле Менеджера Устройств (adi_dev.h).
Функции физического драйвера устройства инкапсулированы в структуре ADI_DEV_PDD_ENTRY_POINT. Каждый физический драйвер экспортирует точку входа в структуру. Приложение передает адрес этой структуры в Менеджер Устройств как часть вызова функции adi_dev_Open. Менеджер Устройств, в свою очередь, используется эту структуру для вызова отдельных подпрограмм физического драйвера. Этот механизм позволяет нескольким физическим драйверам существовать в одной системе без возникновения проблем с конфликтом пространства имен.
В API физического драйвера имеется 5 функций. Эти функции описаны в последующих врезках.
adi_pdd_Open. Открывает устройство для использования. adi_pdd_Close. Закрывает устройство. adi_pdd_Read. Предоставляет буферы для приема данных от устройства. adi_pdd_Write. Предоставляет буферы, содержащие данные, которые устройство должно передавать. adi_pdd_Control. Конфигурирует устройство.
Подключаемый файл физического драйвера ("xxx.h"). API для физических драйверов определен в подключаемом заголовочном файле adi_dev.h Менеджера Устройств. Однако физические драйверы могут расширить некоторые определения и перечисления, определенные Менеджером Устройств. Дополнительные идентификаторы команд (command ID), идентификаторы событий (event ID) и коды возврата могут быть созданы каждым физическим драйвером. Эти расширяемые определения описаны в последующих секциях. Эти определения обычно определяются в подключаемом файле, предоставляемом физическим драйвером. Например, драйвер PPI, код которого размещен в файле adi_ppi.c, имеет соответствующий подключаемый файл adi_ppi.h. Только содержимое этого подключаемого файла физического драйвера расширяет определения, делая доступным это расширение для приложения.
Приложения должны подключать файл adi_dev.h Менеджера Устройств для каждого физического драйвера, который будет использован. Например приложение, использующее физический драйвер PPI, должно подключить файлы заголовка adi_dev.h и adi_ppi.h. Файл adi_dev.h Менеджера Устройств и подключаемые файлы физических драйверов для всех устройств, предоставленных компанией Analog Devices находятся в директории Blackfin/Include/Drivers системы программирования VisualDSP++.
Расширяемые определения. Физический драйвер может определить собственные расширения для идентификаторов command ID, event ID и кодов возврата в дополнение к тем, которые были определены в подключаемом файле adi_dev.h Менеджера Устройств.
Физические драйверы могут создать любое количество дополнительных идентификаторов команд (command ID). Приложения могут выдавать эти команды (подставляя соответствующие command ID) через API-функцию adi_dev_Control Менеджера Устройств. Когда функция adi_dev_Control видит расширенный command ID, она вызывает функцию adi_pdd_Control физического драйвера, передавая ей те параметры, которые были предоставлены приложением. Это дает для физического драйвера опцию по созданию дополнительных команд (command ID), относящихся к управляемому устройству.
Например, физический драйвер для DAC может определить идентификаторы command ID, которые позволят приложению установить или прочитать уровень регулирования громкости для DAC.
Подобным образом физические драйверы могут создать дополнительные идентификаторы событий (event ID), которые можно будет передать обратно в приложение. Физические драйверы могут создать любое количество дополнительных event ID. Физические драйверы могут послать эти идентификаторы событий в приложение через механизм callback к Менеджеру Устройств. Когда в функцию PDDCallback Менеджера Устройств был передан расширенный event ID, то он передается ся вместе с параметрами, которые были переданы в Менеджер Устройств, в приложение через его функцию callback. Это дает физическому драйверу опцию создать дополнительные event ID, отражающие происходящее в конкретном управляемом устройстве, и приложение может получить эти события. Например, физический драйвер, который управляет устройством, анализирующим уровень сигнала, может создать событие, оповещающее приложение о том, что уровень сигнала достиг установленной величины.
Физические драйверы также могут возвратить расширенные коды ошибки. Физические драйверы могут создать любое количество дополнительных кодов возврата. Эти драйверы могут возвратить эти коды в ответ на любой вызов API-функции физического драйвера из Менеджера Устройств. Менеджер Устройств программно отслеживает код возврата ADI_DEV_RESULT_SUCCESS. Любые другие коды, не равные ADI_DEV_RESULT_SUCCESS, интерпретируются как ошибка.
Когда API-функция физического драйвера возвратила код ошибки, не равный ADI_DEV_RESULT_SUCCESS, Менеджер Устройств передает код ошибки обратно в приложение как код возврата из API-функции Менеджера Устройств, чем сообщает приложению об ошибке, возникшей в физическом драйвере. Это дает физическому драйверу опцию создания дополнительных кодов возврата, отражающих возможные ошибки, связанные с конкретным управляемым устройством.
Например, физический драйвер может возвратить уникальный код ошибки в ответ на команду, влияющую на параметр устройства. Возвращенный физическим драйвером код ошибки будет передан на уровень выше (через Менеджер Устройств к приложению), что сообщит подробную причину возникновения ошибки.
Файл adi_dev.h file содержит стартовые значения перечислений, применяемых для каждого физического драйвера. Используйте эти значения как стартовые для всех command ID, event ID и кодов возврата.
ADI_DEV_PDD_ENTRY_POINT. Это так называемая точка входа в физический драйвер. Функция подключения физического драйвера должна подключить декларацию точки входа в драйвер. Это определяется как глобальная переменная, адрес точки входа в физический драйвер. Приложение передает адрес точки входа в Менеджер Устройств, когда открывается устройство. Например, строка:
extern ADI_DEV_PDD_ENTRY_POINT PPIEntryPoint; // точка входа в драйвер PPI
в подключаемом файле драйвера PPI информирует приложение передать переменную PPIEntryPoint как параметр точки входа в вызов функции adi_dev_Open, когда открывается драйвер устройства PPI.
Файл исходного кода физического драйвера ("xxx.c"). Все функции исходного кода физического драйвера, включая функции API физического драйвера, объявлены статическими, так что они не могут быть предоставлены никакому внешнему программному компоненту. Глобальной частью кода или данными является только адрес точки входа. Точка входа это просто структура, которая содержит адреса API-функций в следующем порядке:
Исходный код для всех предоставленных компанией Analog Devices физических драйверов размещены в директории Blackfin/Lib/Src/Drivers.
Весь код в файле физического драйвера предназначен для поддержки этих пяти API-функций физического драйвера. Эти функции и их логика описаны в последующих секциях и врезках. Все API-функции физического драйвера возвращают код ошибки. Менеджер Устройств проверяет код возврата для каждого вызова API-функции физического драйвера. Если физический драйвер вернет код, отличающийся от ADI_DEV_RESULT_SUCCESS, то подразумевается, что произошла ошибка какого-то типа.
v Наподобие того, как это реализовано в Менеджере Устройств, настоятельно рекомендуется для физического драйвера реализовать проверку ошибок некоторых типов, идеально с использованием макроса ADI_DEV_DEBUG. Как минимум, должен быть проверен хендл физического драйвера (ADI_DEV_PDD_HANDLE) для каждого вызова API.
adi_pdd_Open. Функция adi_pdd_Open вызывается Менеджером Устройств в ответ на вызов приложением функции adi_dev_Open. Назначение этой функции открыть устройство для использования. Для подробной информации по параметрам и кодам возврата см. врезку adi_pdd_Open.
Функция adi_pdd_Open сначала должна проверить, что запрашиваемое устройство доступно для использования и поддерживает запрашиваемое направление данных. Будут возвращены соответствующие коды ошибки, если устройство недоступно или не поддерживает указанное направление.
Управляемое устройство инициализируется и очищается (flush) от оставшихся кусков данных или ожидающих обработки прерываний. Подцепляется обработка любых прерываний, поддержка обработки которых нужна для работы с устройством. Для устройств, которые поддерживаются периферийным DMA, обычно нужно подцепить только прерывание ошибки. Для подцепления всех прерываний используется Менеджер Прерываний [4] в составе Системных Служб. Разрешение/запрет прерываний через системный контроллер прерываний (system interrupt controller, SIC) также осуществляется через вызовы Менеджера Прерываний.
Физический драйвер сохраняет хендл на службу обратных вызовов функций (callback service handle). Если не NULL (что означает использование отложенного обратного вызова), то физический драйвер запускает все callback-и через службу, идентифицируемую через хендл службы обратных вызовов. Если NULL (означает, что callbacks-и запускаются немедленно и их запуск не будет отложен), то физический драйвер вызывает callback-функцию Менеджера Устройств напрямую, когда посылаются события.
Физический драйвер также сохраняет значение ADI_DEV_PDD_HANDLE в ячейке, предоставленной Менеджером Устройств. Менеджер Устройств передает этот хендл обратно физическому драйверу во всех других вызовах API.
Функция adi_pdd_Open вернет ADI_DEV_RESULT_SUCCESS в случае своего успешного завершения.
adi_pdd_Control. Эта функция вызывается Менеджером Устройств в ответ на вызов функции adi_dev_Control из приложения. Назначение adi_pdd_Control - обработать команды, относящиеся к конфигурации устройства, посылаемые Менеджером Устройств и приложением. Наподобие всех API-функций, если разрешена проверка ошибок, то подпрограмма проверяет на входе хендл физического драйвера. Для подробной информации по параметрам и кодам возврата см. врезку adi_pdd_Control.
Обработка в функции adi_pdd_Control базируется на command ID, переданном в качестве параметра. Значения command ID перечислены в файле adi_dev.h Менеджера Устройств. Как минимум, физический драйвер должен обработать следующие команды:
• ADI_DEV_CMD_SET_DATAFLOW – включает и выключает поток данных через устройство.
• ADI_DEV_CMD_GET_PERIPHERAL_DMA_SUPPORT – отвечает значением TRUE или FALSE, в зависимости от того, поддерживается ли устройство периферийным DMA. Если устройство поддерживается периферийным DMA, то функция adi_pdd_Control также подготавливается для ответа на следующие command ID:
В большинстве случаев функция adi_pdd_Control физического драйвера строится наподобие оператора switch языка C. Каждая команда, о которой заботится драйвер, включает требуемые command ID, перечисленные выше, и любые дополнительные command ID, созданные самим физическим драйвером, и она поступает на вход оператора switch. Если физический драйвер получает command ID, который не понимает, то он обычно возвращает код ADI_DEV_RESULT_NOT_SUPPORTED.
adi_pdd_Read. Эта функция вызывается Менеджером Устройств в ответ на вызов приложением функции adi_dev_Read. Назначение adi_pdd_Read - заполнить буферы входящими данными, которые приняты из устройства. Во всех API-функциях, если разрешена проверка ошибок, код функции проверяет хендл физического драйвера на входе функции. Для подробной информации по параметрам и кодам возврата см. врезку adi_pdd_Read.
Для устройств, которые поддерживаются периферийным DMA, Менеджер Устройств обслуживает постановку в очередь всех буферов и прием. Как результат, если устройство поддерживается периферийным DMA, функция adi_pdd_Read никогда не будет вызываться Менеджером Устройств, и от этой функии не требуется никакого функционала. Это значительно упрощает реализацию драйверов для устройств, которые имеют поддержку DMA процессора. Физические драйверы, которые поддерживаются периферийным DMA, все еще требуют наличия этой функции, но в ней нужно просто вернуть код ADI_DEV_RESULT_NOT_SUPPORTED, потому что эта функция никогда не должна вызываться.
Для устройств, у которых нет поддержки периферийным DMA, функция adi_pdd_Read передает один или большее количество буферов, которые приложение предоставляет для приема входящих данных. Физический драйвер может выбрать немедленную обработку этих буферов, или предоставить логику для очереди или какой-то стадии для обработки этих буферов в определенное время. Однако от физического драйвера требуется, чтобы он обработал буферы в том порядке, в каком он их получил.
Для некоторых устройств может быть невозможным или непрактичным полное заполнение буфера данными. Для примера рассмотрим драйвер Ethernet. Драйвер Ethernet обычно принимает пакеты, которые могут быть разными по длине. Приложение может знать максимальный размер пакета Ethernet, и предоставить драйвер с буферами, размером равными максимальному размеру пакета. Затем этот драйвер может принять пакет из сети, который меньше, чем максимальный размер пакета. Было бы непрактичным для физического драйвера ждать, пока поступят дополнительные пакеты и буфер будет заполнен полностью, и только после этого начать обработку буфера. Так что у физического драйвера есть опция самому принять решение, когда обработать буфер. У каждого буфера есть поле флага "обработан", и поле обработанного размера, которые устанавливает физический драйвер, базируясь на том, когда было принято решения об обработке буфера, и чтобы можно было узнать, сколько полезных данных находится в буфере.
Также любой буфер может быть помечен приложением, чтобы получить оповещение для приложения, когда буфер завершил свою обработку. Если буфер не помечен для callback, то физический драйвер не оповестит Менеджер Устройств о моменте завершения обработки буфера. Однако если буфер помечен для вызова callback, то как только завершилась обработка буфера, физический драйвер установит флаг обработки и поле обработанного размера, и оповестит Менеджер Устройства через его callback-функцию, которая была передана физическому драйверу при вызове adi_pdd_Open, что буфер завершил свою обработку.
adi_pdd_Write. Эта функция вызывается Менеджером Устройств в ответ на вызов приложением функции adi_dev_Write. Назначение adi_pdd_Write - передать данные в буфере наружу через устройство. Во всех API-функциях, если разрешена проверка ошибок, код функции проверяет хендл физического драйвера на входе функции. Для подробной информации по параметрам и кодам возврата см. врезку adi_pdd_Write.
Как и в случае с adi_pdd_Read, для тех устройств, которые поддерживаются периферийным DMA, Менеджер Устройств обслуживает постановку в очередь всех буферов и передачу. В результате, если устройство поддерживается периферийным DMA, то функция adi_pdd_Write никогда не вызывается Менеджером Устройств, и от неё не требуется никакого функционала. Это значительно упрощает реализацию драйверов для устройств, которые имеют поддержку DMA процессора. Физические драйверы, которые поддерживаются периферийным DMA, все еще требуют наличия этой функции, но в ней нужно просто вернуть код ADI_DEV_RESULT_NOT_SUPPORTED, потому что эта функция никогда не должна вызываться.
Для устройств, у которых нет поддержки периферийным DMA, функция adi_pdd_Write передает один или большее количество буферов, которые приложение предоставляет для передачи данных наружу через устройство. Физический драйвер может выбрать немедленную обработку этих буферов, или предоставить логику для очереди или какой-то стадии для передачи этих буферов в определенное время. Однако от физического драйвера требуется, чтобы он обработал буферы в том порядке, в каком он их получил.
У каждого буфера есть поле флага "обработано", и поле обработанного размера, которые физический драйвер устанавливает, когда он решает, что буфер обработан и какое количество данных было передано через устройство. В отличие от случая adi_pdd_Read, ожидается что должно быть передано все содержимое буфера.
Также любой буфер может быть помечен приложением, чтобы получить оповещение для приложения, когда буфер завершил свою обработку. Если буфер не помечен для callback, то физический драйвер не оповестит Менеджер Устройств о моменте завершения обработки буфера. Однако если буфер помечен для вызова callback, то как только завершилась обработка буфера, физический драйвер установит флаг обработки и поле обработанного размера, и оповестит Менеджер Устройства через его callback-функцию, которая была передана физическому драйверу при вызове adi_pdd_Open, что буфер завершил свою обработку.
adi_pdd_Close. Эта функция вызывается Менеджером Устройств в ответ на вызов приложением функции adi_dev_Close. Её назначение - корректно выключить устройство и перевести его в режим ожидания. Во всех API-функциях, если разрешена проверка ошибок, код функции проверяет хендл физического драйвера на входе функции. Для подробной информации по параметрам и кодам возврата см. врезку adi_pdd_Close.
После проверки хендла драйвера функция adi_pdd_Close останавливает передачу и прием данных, если они еще не были остановлены, так как может быть, приложение вызвало adi_dev_Close, когда поток данных был разрешен.
Функция переводит устройство в режим остановки (idle) и оставляет устройство в состоянии, из которого оно может быть открыто снова, если когда-нибудь позже приложению понадобится открыть устройство. Освобождаются все ресурсы, которые были выделены для поддержки устройства. Например, если было подцеплено прерывание ошибки при вызове adi_pdd_Open, то оно освобождается в части функционала adi_pdd_Close.
Функция adi_dev_Close() закрывает устройство. Поток данных останавливается, если он не был остановлен, и устройство переводится обратно в состояние ожидания. Чтобы можно было снова получить доступ к устройству после вызова adi_dev_Close, нужно обязательно открыть его вызовом adi_dev_Open.
Функция adi_dev_Init() создает Менеджер Устройств и инициализирует для него память. Обычно эта функция вызывается один раз, во время инициализации системы.
Указатель на область статической памяти для использования Менеджером Устройств.
MemorySize
Размер памяти в байтах, предоставленной для использования Менеджером Устройств.
pMaxDevices
При выходе из функции в эту ячейку будет записано максимальное количество одновременно открытых устройств, которое может поддерживать Менеджер Устройств на предоставленном для него объеме памяти.
pManagerHandle
Указатель на ячейку памяти, куда будет сохранен хендл Менеджера Устройств.
pEnterCriticalParam
Параметр, который будет передан в функцию, защищающую критический регион кода.
Возвращаемые значения:
ADI_DEV_RESULT_SUCCESS
Менеджер Устройств был успешно инициализирован.
ADI_DEV_RESULT_NO_MEMORY
Было предоставлено недостаточное количество памяти для Менеджера Устройств.
Функция adi_dev_Open() открывает устройство для использования. Инициализируются внутренние структуры, устанавливается предварительное управление устройством, устройство сбрасывается и подготавливается для использования.
Хендл на Менеджер Устройств, который управляет устройством.
pEntryPoint
Адрес точки входа в физический драйвер.
DeviceNumber
Число, представляющее индекс в "таблице" доступных устройств такого типа в системе. Оно указывает конкретный экземпляр устройства определенного вида. Также см. также раздел "Виртуальные устройства и индексация устройств" в статье [6].
ClientHandle
Идентификатор, определяемый приложением. Менеджер Устройств передаст это значение обратно клиенту в качестве аргумента callback-функции.
pDeviceHandle
Указатель на предоставленную приложением ячейку памяти, куда Менеджер Устройств сохранит идентификатор, определяемый Менеджером Устройств. Все последующие коммуникации для этого устройства с Менеджером Устройств, инициированные клиентом, включают в себя этот хендл.
Direction
Направление данных устройства: входящее, исходящее или двунаправленное (подробнее см. перечисление ADI_DEV_DIRECTION).
DMAHandle
Хендл службы Менеджера DMA, который используется для этого устройства (здесь может быть указано NULL, если DMA не используется).
DCBHandle
Хендл на службу отложенных функций обратного вызова (Менеджер DCB). Если здесь указано значение NULL, то все вызовы callback-функции будут немедленными (live).
ClientCallback
Адрес клиентской callback-функции.
Возвращаемые значения:
ADI_DEV_RESULT_SUCCESS
Устройство было успешно открыто.
ADI_DEV_RESULT_BAD_MANAGER_HANDLE
Хендл не указывает на Менеджер Устройств.
ADI_DEV_RESULT_NO_MEMORY
Недостаточное количество памяти для открытия устройства.
Функция adi_dev_Terminate() освобождает всю память, используемую Менеджером Устройств, останавливает потоки данных, закрывает все открытые драйверы устройств и завершает Менеджер Устройств.
Идентифицирует тип буфера: одномерный, двумерный или кольцевой (см. перечисление ADI_DEV_BUFFER_TYPE).
pBuffer
Адрес буфера или первого буфера в цепочке (см. определение ADI_DEV_BUFFER).
Возвращаемые значения:
ADI_DEV_RESULT_SUCCESS
Функция завершена успешно.
ADI_DEV_RESULT_BAD_DEVICE_HANDLE
Хендл устройства не идентифицирует действительное устройство.
ADI_DEV_RESULT_DMA_ERROR
При конфигурировании Менеджера DMA было сообщено об ошибке.
ADI_DEV_RESULT_DATAFLOW_UNDEFINED
Пока не был установлен тип потока данных.
xxx
Зависящий от типа устройства код возврата.
[Публичные типы данных и перечисления, используемые Менеджером Устройств]
В этой секции определены публичные структуры данных и перечисления, используемые службой Менеджера Устройств.
ADI_DEV_BUFFER_TYPE. Это перечисление используется для указания типа буфера, предоставленного для драйвера. В таблице 10-1 описаны значения перечисления ADI_DEV_BUFFER_TYPE.
Таблица 10-1. ADI_DEV_BUFFER_TYPE.
Имя
Описание
ADI_DEV_BUFFER_UNDEFINED
Неопределенный тип.
ADI_DEV_1D
Одномерный буфер.
ADI_DEV_2D
Двумерный буфер.
ADI_DEV_CIRC
Кольцевой буфер.
ADI_DEV_SEQ_1D
Последовательный одномерный буфер.
ADI_DEV_SWITCH
Переключаемый буфер.
ADI_DEV_UPDATE_SWITCH
Буфер для цепочки с существующим переключаемым буфером.
ADI_DEV_BUFFER_SKIP
Пропустить этот буфер и канал DMA (для устройств с несколькими DMA).
ADI_DEV_BUFFER_TABLE
Таблица буферов.
ADI_DEV_BUFFER_END
Конец массива буферов.
ADI_DEV_MODE. Это перечисление используется для указания метода потока данных устройства. В таблице 10-2 описаны значения перечисления ADI_DEV_MODE.
Таблица 10-2. ADI_DEV_MODE.
Имя
Описание
ADI_DEV_MODE_UNDEFINED
Неопределенный режим.
ADI_DEV_MODE_CIRCULAR
Кольцевой буфер.
ADI_DEV_MODE_CHAINED
Цепочка буферов.
ADI_DEV_MODE_CHAINED_LOOPBACK
Цепочка буферов с переходом в начала цепочки.
ADI_DEV_MODE_SEQ_CHAINED
Цепочка последовательных буферов.
ADI_DEV_MODE_SEQ_CHAINED_LOOPBACK
Цепочка последовательных буферов с переходом в начало цепочки.
ADI_DEV_DIRECTION. Это перечисление используется для указания направления данных. В таблице 10-3 описаны значения перечисления ADI_DEV_DIRECTION.
Таблица 10-3. ADI_DEV_DIRECTION.
Имя
Описание
ADI_DEV_DIRECTION_UNDEFINED
Неопределенное направление.
ADI_DEV_DIRECTION_INBOUND
Входящие данные (чтение).
ADI_DEV_DIRECTION_OUTBOUND
Исходящие данные (запись).
ADI_DEV_DIRECTION_BIDIRECTIONAL
Оба направления (и чтение, и запись).
События callback. Файл подключаемого заголовка adi_dev.h Менеджера Устройств содержит перечисления для событий обратных вызовов функций (callback events), которые могут быть расширены физическим драйвером устройства (physical device driver, PDD).
Начальное значение для перечислений, относящихся к Менеджеру Устройств, равно 0x40000000. Это значение определено в заголовочном файле services.h под именем ADI_DEV_ENUMERATION_START. В таблице 10-4 перечислены значения перечисления для событий функций обратного вызова.
Коды возврата. Файл подключаемого заголовка adi_dev.h Менеджера Устройств содержит перечисления для кодов возврата из функций, которые могут быть расширены физическим драйвером устройства (physical device driver, PDD).
Начальное значение для перечислений, относящихся к Менеджеру Устройств, равно 0x40000000. Это значение определено в заголовочном файле services.h под именем ADI_DEV_ENUMERATION_START. В таблице 10-5 перечислены значения перечисления для кодов возврата (Result Codes).
Таблица 10-5. Result Codes.
Имя
Описание
ADI_DEV_RESULT_SUCCESS
Стандартный код успешного завершения = 0
ADI_DEV_RESULT_FAILED
Стандартный код неудачного завершения = 1
ADI_DEV_RESULT_START
0x40000000 - начальная точка
ADI_DEV_RESULT_NOT_SUPPORTED
Функциональность не поддерживается
ADI_DEV_RESULT_DEVICE_IN_USE
Устройство уже используется
ADI_DEV_RESULT_NO_MEMORY
Для операции недостаточно памяти
ADI_DEV_RESULT_NOT_USED_1
Больше не используется
ADI_DEV_RESULT_BAD_DEVICE_NUMBER
Ошибочный номер устройства
ADI_DEV_RESULT_DIRECTION_NOT_SUPPORTED
Направление данных не поддерживается
ADI_DEV_RESULT_BAD_DEVICE_HANDLE
Недопустимый хендл устройства
ADI_DEV_RESULT_BAD_MANAGER_HANDLE
Недопустимый хендл Менеджера Устройств
ADI_DEV_RESULT_BAD_PDD_HANDLE
Недопустимый хендл физического драйвера
ADI_DEV_RESULT_INVALID_SEQUENCE
Недопустимая последовательность команд
ADI_DEV_RESULT_ATTEMPTED_READ_ON_OUTBOUND_DEVICE
Сделана попытка чтения устройства с исходящим потоком данных
ADI_DEV_RESULT_ATTEMPTED_WRITE_ON_INBOUND_DEVICE
Сделана попытка записи устройства с входящим потоком данных
ADI_DEV_RESULT_DATAFLOW_UNDEFINED
Не определен метод потока данных
ADI_DEV_RESULT_DATAFLOW_INCOMPATIBLE
Операция несовместима с методом потока данных
ADI_DEV_RESULT_BUFFER_TYPE_INCOMPATIBLE
Устройство не поддерживает такой тип буфера
ADI_DEV_RESULT_NOT_USED_2
Больше не поддерживается
ADI_DEV_RESULT_CANT_HOOK_INTERRUPT
Не получается подцепить прерывание
ADI_DEV_RESULT_CANT_UNHOOK_INTERRUPT
Не получается отцепить прерывание
ADI_DEV_RESULT_NON_TERMINATED_LIST
Список буферов не заканчивается на NULL
ADI_DEV_RESULT_NO_CALLBACK_FUNCTION_SUPPLIED
Для функции Open не предоставлена callback-функция
ADI_DEV_RESULT_REQUIRES_UNIDIRECTIONAL_DEVICE
Требуется однонаправленное устройство
ADI_DEV_RESULT_REQUIRES_BIDIRECTIONAL_DEVICE
Требуется двунаправленное устройство
ADI_DEV_RESULT_TWI_LOCKED
TWI заблокирован другой операцией
ADI_DEV_RESULT_REQUIRES_TWI_CONFIG_TABLE
Для драйвера TWI требуется конфигурационная таблица
ADI_DEV_RESULT_CMD_NOT_SUPPORTED
Команда не поддерживается
ADI_DEV_RESULT_INVALID_REG_ADDRESS
Доступ к недопустимому адресу регистра устройства
ADI_DEV_RESULT_INVALID_REG_FIELD
Доступ к недопустимому месту расположения поля регистра
ADI_DEV_RESULT_INVALID_REG_FIELD_DATA
Предоставлено недопустимое значение для поля регистра
ADI_DEV_RESULT_ATTEMPT_TO_WRITE_READONLY_REG
Попытка записи в регистр, предназначенный только для чтения
ADI_DEV_RESULT_ATTEMPT_TO_ACCESS_RESERVE_AREA
Попытка доступа в зарезервированную область
ADI_DEV_RESULT_ACCESS_TYPE_NOT_SUPPORTED
Тип доступа, предоставленный драйвером, не поддерживается
ADI_DEV_RESULT_DATAFLOW_NOT_ENABLED
В синхронном режиме - разрешение потока данных перед предоставлением буферов.
ADI_DEV_RESULT_BAD_DIRECTION_FIELD
Режим последовательного ввода/вывода (Sequential I/O) - буферы предоставлены в неправильном направлении
ADI_DEV_RESULT_BAD_IVG
Был определен неправильный номер IVG
ADI_DEV_RESULT_SWITCH_BUFFER_PAIR_INVALID
Предоставлена недопустимая пара буферов с переключением/обновлением для переключаемого типа буфера
ADI_DEV_RESULT_DMA_CHANNEL_UNAVAILABLE
Нет доступного канала DMA для обработки команды/буфера
ADI_DEV_RESULT_ATTEMPTED_BUFFER_TABLE_NESTING
Не разрешается вкладывать друг в друга таблицы буферов
Command ID. Файл подключаемого заголовка adi_dev.h Менеджера Устройств содержит перечисления для идентификаторов команд (command ID), которые могут быть расширены физическим драйвером устройства (physical device driver, PDD).
Начальное значение для перечислений, относящихся к Менеджеру Устройств, равно 0x40000000. Это значение определено в заголовочном файле services.h под именем ADI_DEV_ENUMERATION_START. В таблице 10-6 перечислены значения перечисления для идентификаторов команд.
Таблица 10-6. Command ID.
Имя
Описание
Значение
ADI_DEV_CMD_START
ADI_DEV_ENUMERATION_START
0x40000000
ADI_DEV_CMD_UNDEFINED
Не определено
ADI_DEV_CMD_END
Конец таблицы команд
NULL
ADI_DEV_CMD_PAIR
Была передана одна пара команд
ADI_DEV_CMD_VALUE_PAIR
ADI_DEV_CMD_TABLE
Таблица пар команд
ADI_DEV_CMD_VALUE_PAIR
ADI_DEV_CMD_SET_DATAFLOW
Разрешить/запретить поток данных
TRUE/FALSE
ADI_DEV_CMD_SET_DATAFLOW_METHOD
Установить метод потока
TRUE/FALSE
ADI_DEV_CMD_NOT_USED_1
Больше не используется
ADI_DEV_CMD_SET_SYNCHRONOUS
Установить для устройства синхронный ввод/вывод
TRUE/FALSE
ADI_DEV_CMD_NOT_USED_2
Больше не используется
ADI_DEV_CMD_SET_STREAMING
Установить режим стриминга
TRUE/FALSE
ADI_DEV_CMD_GET_MAX_INBOUND_SIZE
Получить размер самого большого входящего пакета
u32*
ADI_DEV_CMD_GET_MAX_OUTBOUND_SIZE
Получить размер самого большого исходящего пакета
u32*
ADI_DEV_CMD_GET_PERIPHERAL_DMA_SUPPORT
Опросить, поддерживает ли устройство DMA процессора
u32*
ADI_DEV_CMD_GET_INBOUND_DMA_PMAP_ID
Получить значение DMA PMAP периферийного устройства для входящих данных
ADI_DMA_PMAP*
ADI_DEV_CMD_GET_OUTBOUND_DMA_PMAP_ID
Получить значение DMA PMAP для исходящих данных
ADI_DMA_PMAP*
ADI_DEV_CMD_SET_INBOUND_DMA_CHANNEL_ID
Установить идентификатор канала DMA для входящего DMA
ADI_DMA_CHANNEL_ID
ADI_DEV_CMD_GET_INBOUND_DMA_CHANNEL_ID
Получить идентификатор канала DMA для входящего DMA
ADI_DMA_CHANNEL_ID*
ADI_DEV_CMD_SET_OUTBOUND_DMA_CHANNEL_ID
Установить идентификатор канала DMA для исходящего DMA
ADI_DMA_CHANNEL_ID
ADI_DEV_CMD_GET_OUTBOUND_DMA_CHANNEL_ID
Получить идентификатор канала DMA для исходящего DMA
ADI_DMA_CHANNEL_ID*
ADI_DEV_CMD_GET_2D_SUPPORT
Опросить, поддерживает ли устройство двумерные (2D) передачи
u32*
ADI_DEV_CMD_SET_ERROR_REPORTING
Разрешить/запретить сообщения об ошибках
TRUE/FALSE
ADI_DEV_CMD_FREQUENCY_CHANGE_PROLOG
Оповещение о предстоящей смене частоты ядра/шины
ADI_DEV_FREQUENCIES*
ADI_DEV_CMD_FREQUENCY_CHANGE_EPILOG
Оповещение о вступивших в силу новых частотах ядра/шины
ADI_DEV_FREQUENCIES*
ADI_DEV_CMD_REGISTER_READ
Чтение одного регистра устройства
ADI_DEV_ACCESS_REGISTER*
ADI_DEV_CMD_REGISTER_FIELD_READ
Чтение определенного поля регистра устройства
ADI_DEV_ACCESS_REGISTER_FIELD*
ADI_DEV_CMD_REGISTER_TABLE_READ
Чтение таблицы выбранных регистров устройства
ADI_DEV_ACCESS_REGISTER*
ADI_DEV_CMD_REGISTER_FIELD_TABLE_READ
Чтение таблицы полей выбранных регистров устройства
ADI_DEV_ACCESS_REGISTER_FIELD*
ADI_DEV_CMD_REGISTER_BLOCK_READ
Чтение блока находящихся друг за другом регистров
ADI_DEV_ACCESS_REGISTER_BLOCK*
ADI_DEV_CMD_REGISTER_WRITE
Запись в один регистр устройства
ADI_DEV_ACCESS_REGISTER*
ADI_DEV_CMD_REGISTER_FIELD_WRITE
Запись в определенное поле регистра устройства
ADI_DEV_ACCESS_REGISTER_FIELD*
ADI_DEV_CMD_REGISTER_TABLE_WRITE
Запись в таблицу выбранных регистров устройства
ADI_DEV_ACCESS_REGISTER
ADI_DEV_CMD_REGISTER_FIELD_TABLE_WRITE
Запись в таблицу полей выбранных регистров устройства
ADI_DEV_ACCESS_REGISTER_FIELD*
ADI_DEV_CMD_REGISTER_BLOCK_WRITE
Запись в блок находящихся друг за другом регистров
ADI_DEV_ACCESS_REGISTER_BLOCK*
ADI_DEV_CMD_UPDATE_1D_DATA_POINTER
Обновить указатель данных в одном 1D буфере
ADI_DEV_1D_BUFFER*
ADI_DEV_CMD_UPDATE_2D_DATA_POINTER
Обновить указатель данных в одном 2D буфере
ADI_DEV_2D_BUFFER*
ADI_DEV_CMD_UPDATE_SEQ_1D_DATA_POINTER
Обновить указатель данных в одном последовательном 1D буфере
ADI_DEV_SEQ_1D_BUFFER*
ADI_DEV_CMD_GET_INBOUND_DMA_CURRENT_ADDRESS
Получить текущее значение регистра адреса входящего DMA
u32*
ADI_DEV_CMD_GET_OUTBOUND_DMA_CURRENT_ADDRESS
Получить текущее значение регистра адреса исходящего DMA
u32*
ADI_DEV_1D_BUFFER. Структура данных ADI_DEV_1D_BUFFER описывает обычный одномерный буфер. В таблице 10-7 описаны поля этой структуры.
Таблица 10-7. ADI_DEV_1D_BUFFER.
Имя
Тип
Описание
Reserved
char[ADI_DEV_RESERVED_SIZE]
Зарезервировано для использования физическим драйвером.
Data
void*
Указатель на данные.
ElementCount
u32
Счетчик элементов данных. Обратите внимание, что общий размер передачи 64 килобайта. Для дополнительной информации см. руководство по аппаратуре процессора.
ElementWidth
u32
Ширина одного элемента данных (в байтах).
CallbackParameter
void*
Значение параметра для callback-функции (флаг / pArg).
ProcessedFlag
volatile u32
Флаг "обработано".
ProcessedElementCount
u32
Количество обработанных байт.
pNext
struct adi_dev_1d_buffer*
Следующий буфер.
pAdditionalInfo
void*
Зависящий от устройства указатель на дополнительную информацию.
ADI_DEV_2D_BUFFER. Структура данных ADI_DEV_2D_BUFFER описывает обычный двумерный буфер. В таблице 10-8 описаны поля этой структуры.
Таблица 10-8. ADI_DEV_2D_BUFFER.
Имя
Тип
Описание
Reserved
char[ADI_DEV_RESERVED_SIZE]
Зарезервировано для использования физическим драйвером.
Data
void*
Указатель на данные.
ElementWidth
u32
Ширина одного элемента данных (в байтах).
XCount
u32
Количество столбцов.
XModify
s32
Значение модификации по X (в байтах).
YCount
u32
Количество строк.
YModify
s32
Значение модификации по Y (в байтах).
CallbackParameter
void*
Значение параметра для callback-функции (флаг / pArg).
ProcessedFlag
volatile u32
Флаг "обработано".
ProcessedElementCount
u32
Количество обработанных байт.
pNext
struct adi_dev_2d_buffer*
Следующий буфер.
pAdditionalInfo
void*
Зависящий от устройства указатель на дополнительную информацию.
ADI_DEV_CIRCULAR_BUFFER. Структура данных ADI_DEV_CIRCULAR_BUFFER описывает обычный кольцевой буфер. В таблице 10-9 описаны поля этой структуры.
Таблица 10-9. ADI_DEV_CIRCULAR_BUFFER.
Имя
Тип
Описание
Reserved
char[ADI_DEV_RESERVED_SIZE]
Зарезервировано для использования физическим драйвером.
Data
void*
Указатель на данные.
SubBufferCount
u32
Количество подбуферов.
SubBufferElementCount
u32
Количество элементов данных в подбуфере.
ElementWidth
u32
Ширина одного элемента данных (в байтах).
CallbackType
ADI_DEV_CIRCULAR_CALLBACK
Свич типа callback кольцевого буфера.
pAdditionalInfo
void*
Зависящий от устройства указатель на дополнительную информацию.
ADI_DEV_SEQ_1D_BUFFER. Структура данных ADI_DEV_SEQ_1D_BUFFER описывает последовательный одномерный буфер. В таблице 10-10 описаны поля этой структуры.
Таблица 10-10. ADI_DEV_SEQ_1D_BUFFER.
Имя
Тип
Описание
BufferType
ADI_DEV_BUFFER_TYPE
Тип буфера.
pDirection
union ADI_DEV_BUFFER
Указатель на буфер вышеуказанного типа.
ADI_DEV_BUFFER_PAIR. Структура данных ADI_DEV_BUFFER_PAIR описывает парный буфер. В таблице 10-11 описаны поля этой структуры.
Таблица 10-11. ADI_DEV_BUFFER_PAIR.
Имя
Тип
Описание
Buffer
ADI_DEV_1D_BUFFER
Буфер.
Direction
ADI_DEV_DIRECTION
Направление.
ADI_DEV_DMA_INFO. Структура данных ADI_DEV_DMA_INFO хранит информацию канала периферийного DMA. В таблице 10-12 описаны поля этой структуры.
Таблица 10-12. ADI_DEV_DMA_INFO.
Имя
Тип
Описание
MappingID
ADI_DMA_PMAP
Идентификатор привязки периферии к DMA.
ChannelHandle
ADI_DMA_CHANNEL_HANDLE
Хендл на этот канал DMA.
SwitchModeFlag
u8
Флаг статуса режима переключения (TRUE в режиме переключения).
pSwitchHead
ADI_DMA_DESCRIPTOR_UNION*
Голова переключаемой цепочки буферов.
pNext
struct ADI_DEV_DMA_INFO*
Указатель на структуру, содержащую информацию следующего канала DMA.
ADI_DEV_DMA_ACCESS. Структура данных ADI_DEV_DMA_ACCESS используется для доступа к данным входящих и исходящих цепочек DMA. В таблице 10-13 описаны поля этой структуры.
Таблица 10-13. ADI_DEV_DMA_ACCESS.
Имя
Тип
Описание
DmaChannelCount
u8
Количество каналов DMA для доступа выбранного устройства.
pData
void*
Начало места для массива связанных с DMA данных.
ADI_DEV_FREQUENCIES. Структура данных ADI_DEV_FREQUENCIES содержит информацию по изменениям тактовой частоты. В таблице 10-14 описаны поля этой структуры.
Таблица 10-14. ADI_DEV_FREQUENCIES.
Имя
Тип
Описание
CoreClock
u32
Тактовая частота ядра процессора (CCLK).
SystemClock
u32
Тактовая частота системной шины (SCLK).
ADI_DEV_ACCESS_REGISTER. Тип данных ADI_DEV_ACCESS_REGISTER используется для доступа к одиночному регистру устройства, находящемуся вне кристалла процессора. В таблице 10-15 описаны поля этой структуры.
Таблица 10-15. ADI_DEV_ACCESS_REGISTER.
Имя
Тип
Описание
Address
u16
Адрес регистра устройства.
Data
u16
Данные для записи в регистр, или прочитанные из регистра данные.
ADI_DEV_ACCESS_REGISTER_BLOCK. Тип данных ADI_DEV_ACCESS_REGISTER_BLOCK используется для доступа к блоку находящихся друг за другом регистров устройства вне кристалла процессора. В таблице 10-16 описаны поля этой структуры.
Таблица 10-16. ADI_DEV_ACCESS_REGISTER_BLOCK.
Имя
Тип
Описание
Count
u32
Количество регистров, к которым осуществляется доступ.
Address
u16
Начальный адрес блока регистров.
pData
u16*
Указатель на массив данных регистров либо для записи, либо для чтения.
ADI_DEV_ACCESS_REGISTER_FIELD. Тип данных ADI_DEV_ACCESS_REGISTER_FIELD используется для доступа к полям регистров устройства вне кристалла процессора. В таблице 10-17 описаны поля этой структуры.
Таблица 10-17. ADI_DEV_ACCESS_REGISTER_FIELD.
Имя
Тип
Описание
Address
u16
Адрес регистра, к которому осуществляется доступ.
Field
u16
Поле регистра, к которому осуществляется доступ (см. заголовочный подключаемый файл драйвера).
Data
u16
Данные, которые либо записываются в поле регистра, либо прочитаны оттуда.
ADI_DEV_BUFFER. Объединение (union) ADI_DEV_BUFFER описывает объединение всех типов буферов. В таблице 10-18 описаны поля этого объединения.
В этом разделе описан интерфейс программирования, используемый между Менеджером Устройств и каждым физическим драйвером. API физического драйвера приведено в файле adi_dev.h.
Функция adi_pdd_Close() закрывает устройство. Поток данных останавливается, если он не был уже остановлен, и устройство переводится обратно в состояние ожидания (idle state).
Функция adi_pdd_Open() открывает физическое устройство для использования. При этом инициализируются внутренние структуры данных, устанавливается предварительное управление устройством, устройство сбрасывается и подготавливается для использования.
Хендл на Менеджер Устройств, который управляет устройством.
DeviceNumber
Число, начинающееся с 0, представляющее индекс в "таблице" доступных устройств такого типа в системе. Оно указывает конкретный экземпляр устройства определенного вида. Например, если в процессоре имеется 4 последовательных порта, то они получают индексы от 0 до 3.
ClientHandle
Идентификатор, определяемый приложением. Менеджер Устройств передаст это значение обратно клиенту в качестве аргумента callback-функции.
DeviceHandle
Параметр, предоставленный Менеджером Устройств, который уникально идентифицирует устройство для Менеджера Устройств.
pPDDHandle
Указатель на место в памяти, куда физический драйвер сохранил хендл, уникально идентифицирующий устройство для физического драйвера.
Direction
Направление данных устройства: входящее, исходящее или двунаправленное (подробнее см. перечисление ADI_DEV_DIRECTION).
pEnterCriticalParam
Параметр, который передается в функцию, защищающую критические области кода.
DMAHandle
Хендл службы Менеджера DMA, который используется для этого устройства (здесь может быть указано NULL, если DMA не используется).
DCBHandle
Хендл на службу отложенных функций обратного вызова (Менеджер DCB). Если здесь указано значение NULL, то все вызовы callback-функции будут немедленными (live).
Функция adi_pdd_Read() предоставляет буферы для устройства, чтобы получить входящие данные. Эта функция никогда не вызывается для тех устройств, у которых есть поддержка периферийного DMA.
Функция adi_pdd_Write() предоставляет буферы для устройства, чтобы передать наружу исходящие данные. Эта функция никогда не вызывается для тех устройств, у которых есть поддержка периферийного DMA.