
ПОУК / 09 семестр / Книги и методические указания / Драйвер USB устройства URC220 для OS QNX
.pdf
Рис.5.9 Пакеты SETUP транзакции
второго пакета всегда равен “DATA0”.
случая. Так как процесс передачи данных контролирует хост, то Token Packet в обоих случаях имеет направление OUT, то есть от хоста к устройству.
Успешная SETUP транзакция состоит из трёх пакетов (см. рис. 5.9). Она аналогична OUT транзакции, но размер данных составляет ровно 8 байт, а PID указывает на то, что это первый пакет SETUP транзакции. PID
Данные через Pipe могут пересылаться, используя один из следующих способов: Таблица 5.3
Способ |
Описание |
Control |
Обязательное использование нулевой двунаправленной конечной |
Transfer |
точки для передачи данных. |
|
Состоит из трёх этапов: |
|
1) SETUP этап – SETUP транзакция с передачей 8-ми байт, |
|
которые описывают, какие данные будут отправлены на |
|
следующем этапе; |
|
2) DATA этап (может отсутствовать) – несколько IN или OUT |
|
транзакций для передачи данных; |
|
3) STATUS этап – транзакция, где длина данных во втором |
|
пакете равна 0 (пустой пакет, ZLP – Zero Length Packet). |
|
Этот тип передачи данных используется для начальной |
|
конфигурации устройства хостом. |
Bulk |
Обмен данными на максимально возможной скорости с аппаратным |
Transfer |
контролем ошибок, не гарантируется постоянная пропускная |
|
способность. Состоит из одной IN или OUT транзакции. |
|
Используется для передачи больших объёмов информации |
|
(например, USB диск). Хост выделяет средства на этот способ |
|
передачи данных только после того, как будут переданы данные |
|
другими способами. |
Interrupt |
Возможность посылать данные не реже чем один раз за кадр (1 мс |
Transfer |
для LowSpeed и FullSpeed и 1/8 мс для HighSpeed), аппаратный |
|
контроль ошибок. Состоит из одной IN или OUT транзакции. |
|
Используется для быстрой передачи информации о состоянии |
|
устройства (например, в USB клавиатуре или мышке). |
Isochronous |
Имеет гарантированную пропускную способность, но не защищён от |
Transfer |
ошибок. Основное применение данного способа передачи данных - |
|
передача аудио и видео данных, где необходимо поддерживать |
|
постоянную скорость передачи, но потеря небольшого количества |
|
данных несущественна. |
|
Состоит из одной IN или OUT транзакции, при этом не имеет |
|
Handshake пакета, так как здесь отсутствует коррекция ошибок. |
21
5.6 Протокол обмена данными
5.6.1 Управление устройством
Когда устройство подключается к USB, и резистор замыкает линию питания на D+ или D-, хост определяет факт подключения. После этого посылает устройству сигнал сброса, чтобы после этого устройство стало находиться в известном состоянии. В этом состоянии оно откликается по начальному нулевому адресу. Хост сбрасывает только одно устройство в один момент времени, поэтому не может появиться двух устройств, отвечающих по нулевому адресу.
Далее хост отправляет запрос на endpoint 0 устройства 0, чтобы выяснить максимальный размер пакета, который устройство может принимать и отправлять. Хост выясняет это с помощью команды “Get Descriptor” (Device).
Обычно хост (например, Windows) опять сбрасывает устройство, далее отправляет запрос “Set Address” с уникальным адресом устройства на адрес 0, после чего устройство принимает новый адрес, и с этого момента хост может сбросить другое недавно подключённое устройство.
На данном этапе устройство имеет адрес, но не сконфигурировано и отвечает только на стандартные запросы, которые можно передавать только с помощью Control Transfer, а единственной доступной конечной точкой является двунаправленная нулевая конечная точка.
Как правило, хост теперь начинает опрашивать устройство, чтобы определить всю необходимую информацию об этом устройстве. Среди этих запросов такие как:
-Get Device Descriptor
-Get Configuration Descriptor
-Get String Descriptor
Когда компьютер узнаёт достаточно информации об устройстве, он загружает соответствующий драйвер. Этот драйвер затем выберет нужную конфигурацию,
отправив запрос “Set Configuration”.
После этого устройство становится сконфигурированным и может выполнять те функции, для которых было разработано, отвечая при этом как на специальные запросы драйвера, так и на стандартные запросы.
5.6.2 Дескрипторы (Descriptors)
Устройство содержит множество дескрипторов, которые помогают определить, какое устройство было подключено, а также возможности этого устройства. Как было сказано выше, хост отправляет запросы “Get Device Descriptor”, “Get Configuration Descriptor” и “Get String Descriptor” для определения соответствующих дескрипторов.
Устройство имеет один Device Descriptor, который хранит информацию о производителе - Vendor Identification Number (VID), устройстве - Part Identification
22

Number (PID), максимальном размере пакета, а также о количестве Configuration дескрипторов - bNumConfigurations.
Зная VID и PID, операционная система загружает необходимый драйвер.
Далее происходит считывание первого конфигурационного дескриптора, при этом данные передаются в определённом порядке (см. рис. 5.10): сначала сам Configuration дескриптор, затем,
первый Interface Descriptor, а затем уже все Endpoint дескрипторы (два на рисунке слева). Далее идёт ещё один Interface Descriptor, а затем опять все Endpoint дескрипторы. Т.е. считывание данных происходит так, как показано на рисунке, т.е. сверху вниз. После этого, если bNumConfigurations больше одного,
происходит считывание следующей конфигурации устройства (“Other Configuration Descriptors”).
Драйвер при этом должен выбрать одну из конфигураций, для которой Рис.5.10 Порядок передачи дескрипторов заданы дескрипторы (то есть описание) того, какие конечные
точки использует устройство, какой способ передачи данных и т.д. Как правило, выбирается первая конфигурация.
После этого устройство начинает нормально функционировать, выполняя необходимые функции и используя все конечные точки для обмена информацией между устройством и драйвером.
Ознакомиться со спецификацией USB можно на официальном сайте: www.usb.org
Интересный ресурс об устройстве и использовании USB: www.usbmadesimple.co.uk
23

6. Реализация ПО для управления URC220
Для управления устройством URC220, то есть для обмена данными с ним, была реализована архитектура клиент-сервер. При запуске сервер следит за подключением и отключением устройств, реализует передачу данных между сервером и каждым устройством, а также передачу данных между сервером и клиентами. Таким образом, клиентские программы работают с устройствами через сервер (см. рис. 6.1). Клиент-сервер взаимодействие через общую оперативную память.
Рис.6.1 Cхема работы ПО
6.1 Алгоритм работы сервера
Сервер представляет собой консольное приложение для QNX, разработанное в среде Momentics. Исходный код программы реализован в 4-х файлах. В файлах “Main.cpp” и “Main.h” расположены основные функции для работы сервера, а файлы “urcpipe.cpp” и “urcpipe.h” являются USB драйвером для обеспечения обмена данными между сервером и устройствами.
Общая схема работы сервера показана на рисунке 6.2. Рассмотрим назначение основных функций.
main() – стартовая функция, с которой начинает выполнять работу программа, здесь происходит инициализация программы, ожидание во время работы сервера, а затем завершение работы.
shmem_create() – функция для создания общей памяти, то есть памяти, которую могут использовать несколько процессов, запущенных в системе: сервер и
24

клиентские программы. Функция сначала создаёт объект для работы с памятью, далее выделяет необходимый объём памяти и очищает память.
Рис.6.2 Общая схема работы сервера
shmem_delete() – функция для удаления общей памяти, вызывается в самом конце, перед окончанием выполнения программы.
usb_connect() – выполняет подключение к USB стеку QNX, то есть к средствам работы с USB. Сообщает ОС различные параметры, среди которых PID и VID устройства, с которым планируется работать, а также функции в программе (callback_* - см. далее), которые необходимо вызывать при возникновении различных событий.
usb_disconnect() – отключение от USB стека.
callback_insertion() – вызывается операционной сразу после того, как устройство было подключено к одному из USB портов, причём только для того устройства с указанным ранее PID и VID. Функция выводит о нём информацию, далее производит поиск свободного номера, который сопоставляет с этим устройством, а далее вызывает функцию dev_open() для подключения и настройки устройства.
callback_removal() – вызывается тогда, когда устройство отключили от USB. Функция сначала определяет ранее присвоенный этому устройству номер, а затем вызывает функцию dev_close() для завершения работы с устройством.
Итак, после запуска, основной поток (функция main()) программы производит подключение к USB стеку и инициализирует общую память, после чего зависает, ожидая нажатия на клавишу. В это время, при подключении и отключении
25

устройств, ОС вызывает функции callback_insertion() и callback_removal() для обработки этих событий. Эти функции производят нумерацию устройств и вызывают функции dev_open() и dev_close() для начала и окончания работы с устройством.
После нажатия на клавишу, основной поток закрывает все устройства, завершает работу с USB и удаляет общую память, после чего программа завершается.
Рассмотрим подробнее устройство функций dev_open() и dev_close(). Алгоритм работы этих функций показан на рисунке 6.3.
Рис.6.3 Работа функций dev_open() и dev_close()
Объект dev представляет собой массив, элементами которого являются структуры URC_PARAMETERS, содержащие два поля:
1)pipe – объект класса CURCPIPE (реализован в файле “urcpipe.cpp”) для работы с USB: подключение/отключение от устройства и передача данных;
2)thread – идентификатор потока в QNX.
Объект shmem – это общая память, представляющая собой массив структур URC_SHARED_MEM, содержащие поля для обмена данными, в том числе объект mutex, служащий для синхронизации доступа к памяти между несколькими процессами: сервером и клиентскими программами.
Функция dev_open() сначала вызывает функцию open() соответствующего объекта pipe для подключения к устройству, далее инициализирует объект mutex. После этого создаёт поток, в котором начинает выполняться функция dev_thread().
Функция dev_close() выполняет операции, обратные операциям dev_open().
Функция dev_thread() представляет собой бесконечный цикл, где постоянно идёт проверка: нужно ли передать данные от клиента к устройству или наоборот. При этом обращение к данным как функцией dev_thread(), так и клиентскими
26

программами, осуществляется синхронно, так как используется mutex. Один из потоков первым переводит mutex в состояние locked. Далее, когда другой поток пытается обратиться к данным, он зависает до тех пор, пока первый поток не выполнит все операции с данными и не переведёт mutex в состояние unlocked, чтобы к данным мог получить другой поток, который также установит mutex в состояние locked, и так далее. То есть с данными в один момент времени работает только один поток. Для каждого устройства имеется свой mutex и поток, где выполняется dev_thread().
Таким образом, во время работы сервера работают сразу несколько потоков: главный (функция main()) в состоянии ожидания плюс по одному потоку на каждое подключенное к компьютеру устройство.
Алгоритм работы функции dev_thread() показан на рисунке 6.4.
Рис.6.4 Алгоритм работы функции dev_thread()
Для обмена данными с устройством используются функции write() и read() объекта pipe. Таким образом, класс CURCPIPE, кроме конструктора и деструктора состоит из следующих функций.
27

CURCPIPE::open() - функция открывает устройство, и производит его конфигурацию, в случае ошибки возвращает 0.
CURCPIPE::close() – отключает от устройства.
CURCPIPE::bulk_cbf() – функция вызывается операционной системой для обработки различных событий во время передачи данных.
CURCPIPE::wait_io() – после вызова функция ожидает окончания операции приёма или отправки данных до их завершения или до возникновения ошибки.
CURCPIPE::write() – отправляет данные по USB в устройство, вызывает в своём коде функцию wait_io(), поэтому функция write() является синхронной функцией отправки данных.
CURCPIPE::read() – принимает данные по USB от устройства, вызывает в своём коде функцию wait_io(), поэтому функция read() является синхронной функцией приёма данных.
6.2 Алгоритм работы клиентских программ
Алгоритм работы той части клиентских программ, которая отвечает за взаимодействие с устройствами, во многом похож на алгоритм работы функции dev_thread() и показан на рисунке 6.5
Рис.6.5 Алгоритм работы клиентских программ
28
Здесь сначала идёт подключение к программе внешней памяти, чтобы иметь доступ к полям массива структур. Далее идёт получение доступа к соответствующей ячейке массива через mutex (который также является полем этой структуры). После этого идёт копирование данных – чтение или запись, а затем разблокировка данных, чтобы к ним мог получить доступ сервер и выполнить действия для передачи или приёма данных от устройства.
Этот алгоритм реализован в классе CURC220, который представляет собой оболочку для программ конечного пользователя, а также имеет специальный набор функций, необходимый для удобной работы с устройством. Описание этих функций находится в разделе “3.2 Описание функций класса CURC220”.
29
7. Содержание
1. |
Техническое задание ............................................................................... |
2 |
|
2. |
Драйвер..................................................................................................... |
3 |
|
|
2.1 |
Алгоритм работы ........................................................................................... |
3 |
3. Библиотека для работы с платой на компьютере .................................. |
5 |
||
|
3.1 |
Алгоритм работы с библиотекой.................................................................... |
5 |
|
3.2 |
Описание функций класса CURC220............................................................... |
5 |
4. |
Применение библиотеки.......................................................................... |
9 |
|
|
4.1 |
Создание проекта.......................................................................................... |
9 |
|
4.2 |
Пример использования .................................................................................. |
9 |
|
4.3 |
Универсальный пример................................................................................ |
13 |
5. |
Устройство шины USB ............................................................................ |
16 |
|
|
5.1 |
Общие сведения .......................................................................................... |
16 |
|
5.2 |
Версии спецификаций и скорость передачи данных .................................... |
16 |
|
5.3 |
Архитектура................................................................................................. |
16 |
|
5.4 |
Физическая среда........................................................................................ |
17 |
|
5.5 |
Передача данных......................................................................................... |
18 |
|
5.5.1 Состояние шины USB ................................................................................ |
18 |
|
|
5.5.2 Транзакции............................................................................................... |
19 |
|
|
5.6 |
Протокол обмена данными .......................................................................... |
22 |
|
5.6.1 Управление устройством........................................................................... |
22 |
|
|
5.6.2 Дескрипторы (Descriptors)......................................................................... |
22 |
|
6. |
Реализация ПО для управления URC220............................................... |
24 |
|
|
6.1 |
Алгоритм работы сервера............................................................................ |
24 |
|
6.2 |
Алгоритм работы клиентских программ....................................................... |
28 |
7. |
Содержание............................................................................................ |
30 |
30