Конечно, полностью от казаться от использования драйверов при работе с USB-устройствами нельзя, организация шины требует подключения целого стека драйверов, однако в данном случае рассматривается использование в стеке USB только стандартных драйверов, имеющихся по умолчанию в операционной системе.
Все сказанное ниже проверялось тестировалось под операционной системой MS Windows XP (2000 и выше). Для UNIX-систем сказать ничего не могу, с ними в этом плане пробел.
Итак, самые распространенные типы стандартных устройств:
Класс MSC малопригоден для организации удобного и простого обмена с приложением. Данный класс можно использовать, если устройство имеет большой объем FLASH-памяти с файловой системой и требуется дать к нему доступ.
Так же можно упомянуть такой класс устройств как CCID, его можно использовать вместе с драйвером usbccid.sys, штатно доступном начиная с Windows XP SP2. Это класс, который используют ридеры смарт-карт, а так же некоторые USB-токены.
Класс USB-CCID довольно интересен в случае, если требуется создать обеспечить быстрый обмен (он позволяет передавать данные быстрее HID и обеспечивает меньшие задержки чем CDC-класс), однако довольно специфичен, т.к. с ним требуется работать в рамках APDU-посылок.
Теперь рассмотрим работу с каждым классом устройсв более подробно:
Данный класс не очень удобен для работы, т.к. требует большое количество обрабатывающего кода и завязывается на работу с файловой системой. Однако, с устройством данного класса. Интересен он становится, если устройство имеет свою файловую систему или просто большой объем внутреней памяти.
Стоит так же заметить, что во встроенном загрузчике микроконтроллера LPC134x, есть функция заливки через MSC-устройство. В данном режиме память микроконтроллера представляется в виде FLASH-диска и запись прошивки производится простым копированием файла через стандартный менеджер.
В принципе, работа с устройствами MSC можно производить и без файловой системы, путем прямого чтения/записи диска. Для этого требуется просто открыть при помощи функции CreateFile непосредственно устройство:
// Открываем носитель F: как физическое устройство: HANDLE hFile = CreateFileA( "//./F:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); // Блокируем устройство для доступа другим программам int iRet = DeviceIoControl(hFile, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwLen, NULL); // Устанавливаем смещение на диске: iRet = SetFilePointer(hFile, dwOffset, &dwOffsetH, 0); // Пишем/читаем буфер: iRet = WriteFile(hFile, pbFileBuff, dwLen, &dwLen, 0);
При использовании класса CDC после подключения устройства в системе создается виртуальный COM-порт, общение с которым со стороны операционной системы совершенно не отличается от работы с физическим COM-портом. На урове WinAPI работа с таким устройсвом сводится к вызовам функций CreateFile, WriteFile, ReadFile и CloseHandle.
Идеальный вариант, если до этого устройство использовало COM-порт для общения с PC. В данном случае не требуется никаких переделок в программном обеспечении на стороне PC, да и вообще решение является шаблонным. Любое устройство с COM-портом через переходник можно повесить на USB. В некоторой серийной продукции (мне попадались такие бесперебойники) переход на шину USB осуществлялся именно путем внедрения конвертора USB-COM.
Так же вариант с использованием CDC-класса хорош тем, что протокол асинхронный и в нем можно отсылать блоки данных любой длины.
Однако, стоит помнить, что класс устройства стандартный, в Windows для обеспечения его работы есть драйвер usbserial.sys, однако тот же конвертор FTDI FT232хх использует собственную реализацию протокола CDC и все-таки требует установку драйвера.
Хоть HID-устройсва и предназначаются для взаимодействия с джойстиками и клавиатурами, они без проблем конфигурируются для передачи произвольных данных пользователя. Довольно серьезный недостаток HID - блоки данных могут быть только фиксированного размера, который устанавливается в HID-дескрипторе.
Основная сложность при работе с HID-устройством - получить его имя в системе. После того, как имя получено работать с устройством можно с помощью функций CreateFile, WriteFile, ReadFile и CloseHandle. Один момент, который стоит упомянуть, первым байтом в передаваемом функциям Read/WriteFile идет идентификатор посылки, обычно это 0. Таким образом, если размер блока данных HID равен 64 байта, в функцию требуется передавать 65 байт. Этот байт используется в драйвере и не уходит в устройство.
Каким же образом получить имя HID-устройства, лучше всего посмотреть в библиотеке по ссылке в конце статьи.
Первая особенность работы по CCID, требуется выбрать ATR-строку и протокол T=0 или T=1. Лучше всего выбирать T=1, т.к. в нем есть APDU Case 4. Наверное, уже все перепугались кучей непонятных слов. Сам протокол описывается в стандарте ISO 7816-12 (имеется соответствующий ГОСТ/ИСО), так же формат APDU посылок можно посмотреть в ISO 7816-3 и ISO 7816-4. К сожалению, стандарты эти платные, но хорошо поискав, всю необходимую информацию из них все же можно найти в открытом доступе.
Общение с устройством производится при помощи функций SCardConnect, SCardTransmit. Функция SCardTransmit занимается непосредственно приемом-передачей данных.
APDU посылка состоит из заголовка и блока данных. На нее обязательно требуется ответить хотя бы 2 байтами: код возврата, 90 00 в случае если все в порядке и другое значение (по стандарту 6х хх). Если есть данные они идут в начале, а код возврата дописывается 2 байтами в конец посылки. Основные коды, которые могут пригодится при работе описаны в таблице (в реальности, кодов ошибок довольно много):
Возврат SW1 SW2 | Описание ошибки |
90 00 | Все в порядке |
67 00 | Неверная длина входных данных |
6D 00 | Неподдерживаемая команда |
68 00 | Неверное значение CLA |
6C xx | Неверная длина выходного буфера, xx - рекомендуемая длина |
6A 80 | Неверные данные |
Теперь непосредственно форматы посылок (T=1):
OUT: CLA INS P1 P2 IN: SW1 SW2
OUT: CLA INS P1 P2 Le IN: DATA[Le] SW1 SW2 IN(при ошибке): SW1 SW2
OUT: CLA INS P1 P2 Lc DATA[Lc] IN: SW1 SW2
OUT: CLA INS P1 P2 Lc DATA[Lc] Le IN: DATA[Le] SW1 SW2 IN(при ошибке): SW1 SW2
Т.е. есть 4 класса APDU команд: без данных, с данным в устройство, с возвратом данных из устройства и с передачей данных сразу в обе стороны. CLA - класс команды, INS - код команды, P1 и P2 - параметры команды. Так же при разрабтке стоит учитывать, что операционная система может сама в отдельных случаях попытаться послать устройству набор стандартных APDU-команд.
Напоследок вопрос о выборе VID и PID. По этим значениям происходит привязка к устройству драйверов в случае если устройство не относится к стандартному классу. Если с одинаковыми PID и VID пытаться подключать устройсва разных классов могут возникнуть проблемы с драйверами, т.к. предполагается что каждое устройство имеет уникальные VID и PID.
Уникальность гарантируется тем, что ассоциация USB.org продет VID-ы (Vendor ID), стоит он около 2000$. После покупки, имеется диапазон PID, в котором можно произвольно выбирать PID значения для работанных устройств. В хоббийной практике, можно выбирать любые значения. Вообще, шанс того, что взятая с потолка комбинация VID/PID окажется задействованной и это где-то всплывет, очень мал. Так же у разработчиков демо-плат, к примеру Keil-а, есть в примерах заданные идентификаторы. Их так же можно использовать в самодельных конструкциях. Но вот при запуске устройства в более-меннее крупную серию все же придется покупать VID (или договариваться с какой-нибудь конторой, чтобы они "отсыпали" пару PID-ов из своего диапазона, хотя договоренность эта получится по большому счету устной).