- Драйверы устройств в Linux
- Часть 11: Драйверы USB в Linux
- Обнаружение устройства USB в Linux
- Разбираемся в секции, описывающей устройство USB
- Регистрация драйвера USB для флеш устройства
- Подведем итог
- Драйверы устройств в Linux
- Часть 13: Передача данных в устройство и из устройства USB
- Передача данных через USB
Драйверы устройств в Linux
Часть 11: Драйверы USB в Linux
Оригинал: «Device Drivers, Part 11: USB Drivers in Linux»
Автор: Anil Kumar Pugalia
Дата публикации: October 1, 2011
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.
Эта статья, которая является частью серии статей о драйверах устройств в Linux, поможет вам начать писать ваш первый драйвер USB в системе Linux.
Флеш устройство Пагса было именно тем устройством, которым Светлана воспользовалась, когда они сели вдвоем за изучение мира драйверов USB в Linux. Самым быстрым способом с ним разобраться был обычный способ Пагса — выбрать устройство USB и написать для него драйвер для того, чтобы с ним поэкспериментировать. Поэтому они выбрали флэш устройство (т.е. USB флешку), которое было под рукой — JetFlash от Transcend, с ID поставщика 0x058f и ID продукта 0x6387 .
Обнаружение устройства USB в Linux
Независимо от того, есть ли драйвер для устройств USB в Linux системе или его нет, допустимое устройство USB всегда будет обнаруживаться в системе Linux в пространстве аппаратных средств и в пространстве ядра, поскольку система создана (и выполняет обнаружение) в соответствии со спецификациями протокола USB. Обнаружение в аппаратном пространстве осуществляется хост контроллером USB — как правило, соответствующем шинным устройством, аналогичным устройству PCI в системах x86. Соответствующий драйвер хост-контроллера обнаруживает устройство и транслирует информацию низкоуровнего физического слоя в конкретную информацию более высокого уровня протокола USB. Затем информация протокола USB, касающаяся устройства и, имеющая специальный формат, заносится в общий слой ядра USB (драйвер usbcore) в пространстве ядра, что позволяет обнаруживать устройства USB в пространстве ядра даже в том случае, когда отсутствует драйвер конкретного устройства.
Дальше — дело различных драйверов, интерфейсов и приложений (которые различны в различных дистрибутивах Linux) отображать обнаруженные устройства в пользовательском пространстве. На рис.1 показана иерархия подсистемы USB в Linux.
Рис.1: Подсистема USB в Linux
Краткий список всех обнаруженных устройств USB можно получить с помощью команды lsusb , которую следует запустить в роли пользователя root. На рис.2 приведен такой список как для случая с флэш устройством, так и без него. Параметр -v в команде lsusb позволяет получить более подробную информацию.
Рис.2: Информация, выдаваемая командой lsusb
Во многих дистрибутивах Linux, таких как Mandriva, Fedora, . , драйвер usbfs сконфигурирован так, что он загружается по умолчанию. В результате можно с помощью команды cat /proc/bus/usb/devices из директория /proc извлечь конкретную информации об обнаруженном USB-устройстве, представленную в удобном виде. На рис.3 показан типичный пример такой информации, которая находится в специальной секции, описывающей флэш-устройство. В списке обычно присутствует по одному такому разделу для каждого допустимого устройства USB, обнаруженного в системе.
Рис.3: Фрагмент информации из proc, касающейся USB
Разбираемся в секции, описывающей устройство USB
Чтобы дальше разбираться с этими секциями, нужно в первую очередь понять, что такое допустимое устройство USB. Для всех допустимых устройств USB есть одна или несколько конфигураций. Конфигурация устройства USB похожа на профиль, причем в качестве конфигурации, используемой по умолчанию, обычно используется первая конфигурация. Таким образом, в Linux для каждого устройства по умолчанию поддерживается только одна конфигурация. Для каждой конфигурации в устройстве может быть один или несколько интерфейсов. Интерфейс соответствует функции, предоставляемой устройством.
Интерфейсов может быть столько, сколько есть функций, предоставляемых устройством. Так, скажем, устройство МФУ USB-принтер (многофункциональное устройство) может выполнять печать, сканирование и отправку факсов, и, скорее всего, для него будет, по крайней мере, три интерфейса, по одному для каждой из функций. Таким образом, в отличие от других драйверов устройств, драйвер USB устройства, как правило, связывается / пишется отдельно для каждого интерфейса, а не для устройства в целом — это значит, что для устройства USB может быть несколько драйверов устройств, причем для интерфейсов различных устройств может использоваться один и тот же драйвер, — хотя, конечно, для одного интерфейса не может быть более одного драйвера.
Вполне нормальной и достаточно обычной является ситуация, когда для всех интерфейсов устройства USB используется один и тот же драйвер USB. В записи Driver=. для директория proc (рис. 3) показано, что в драйвер отсутствует отображение интерфейса ( none ).
Для каждого интерфейса есть один или несколько источников / приемников данных. Источник / приемник данных (endpoint) похож на конвейер (pipe), используемый для передачи информации в зависимости от функции либо в интерфейс, либо из интерфейса устройства. В зависимости от типа информации, источники / приемники данных могут быть четырех типов: Control, Interrupt, Bulk и Isochronous.
Прим.пер.: Подробное описание указанных четырех типов источников / приемников данных будет приведено в следующей статье данной серии статей.
Согласно спецификациям протокола USB во всех допустимых устройствах USB должен быть неявно используемый источник / приемник данных с номером 0 (end-point zero) — единственный двунаправленный источник / приемник данных. На рис.4 приведена полная наглядная схема допустимого устройства USB, соответствующее приведенному выше объяснению.
Рис.4: Общий взгляд на устройство USB
Вернемся обратно к секциям устройств USB (рис. 3) — первая буква в каждой строке соответствует различным частям спецификации устройства USB. Например, D — устройству, C — конфигурации, I — интерфейсу, E — источнику / приемнику данных (endpoint) и т.д. Подробнее об этом и о многом другом смотрите в исходном коде ядра в файле Documentation/usb/proc_usb_info.txt .
Регистрация драйвера USB для флеш устройства
«Похоже, для того, чтобы можно было самостоятельно написать первый драйвер USB, потребуется узнать много всего о протоколе USB, — конфигурацию устройства, интерфейсы, конвейеры передачи данных, четыре типа передачи данных, а также многие другие обозначения, например, T, B, S, …, которые есть в спецификации устройств USB» — вздохнула Светлана.
«Да, но ты не беспокойся — со всем этим можно будет разобраться подробнее позже. Давай со всем этим разбираться последовательно — возьмем интерфейс флеш устройства, связанного с драйвером нашего USB-устройства ( pen_register.ko )» — утешил Пагс.
Как и в любом другом Linux-драйвере, здесь также требуется конструктор и деструктор — используется тот же самый шаблон драйвера, который использовался для всех драйверов. Но содержимое будет другим, поскольку это драйвер слоя аппаратного протокола, т.е. горизонтальный драйвер в отличие от символьного драйвера, который был одним из вертикальных драйверов, рассмотренных ранее. Разница лишь в том, что вместо регистрации и отмены регистрации в VFS, здесь это должно выполняться на уровне соответствующего протокола — в данном случае — в ядре USB; вместо того, чтобы предоставлять интерфейс пользовательского пространства, например, файл устройства, он должен подключиться к реальному устройству в пространстве аппаратных средств.
Интерфейсы API для ядра USB выглядят следующим образом (прототип в
):
В структуре usb_driver в соответствующих полях должны быть указаны имя устройства, идентификационная таблица, используемая для автообнаружения конкретного устройства, и две функции обратного вызова, которые вызываются ядром USB при горячем подключении и отключении устройства, соответственно.
Собираем все вместе в файл pen_register.c , который будет выглядеть следующим образом:
Затем можно повторить обычные шаги, выполняемые для любого Linux драйвера:
- Собираем драйвер (файл .ko ) с помощью запуска команды make .
- Загружаем драйвер с помощью команды insmod .
- Выдаем список загруженных модулей с помощью команды lsmod .
- Выгружаем драйвер с помощью команды rmmod .
Но, что удивительно, результат не будет таким, как ожидалось. Используйте команду dmesg и загляните в директорий proc для просмотра различных журналов и прочих подробностей. Это связано не с тем, что драйвер USB отличается от символьного драйвера, — здесь есть одна проблема. На рис.3 показано, что у флэш-устройства есть один интерфейс (с номером 0), который уже связан с обычным драйвером usb-storage.
Теперь, для того, чтобы связать наш драйвер с этим интерфейсом, нам нужно выгрузить драйвер usb-storage (т. е. выполнить команду rmmod usb-storage ) и переподключить флэш-накопитель. Как только это будет сделано, результаты станут такими, как ожидалось. На рис.5 показан фрагмент информации из журналов и из директория proc . Снова подключите и отключите (в горячем режиме) флеш устройство и пронаблюдайте, как действуют вызовы probe и disconnect.
Рис.5: Флеш устройство в действии
Подведем итог
«Наконец-то! Что-то действует!» — облегченно сказала Светлана. «Но мне кажется, что для того, чтобы собрать полный драйвер устройства USB, здесь есть еще много того, с чем следует разбираться (например, с идентификационной таблицей, обратными вызовами probe и disconnect и т. д.)».
«Да, ты права. Давай разбираться со всем по порядку и с перерывами » — ответил Пагс, прервав самого себя.
Источник
Драйверы устройств в Linux
Часть 13: Передача данных в устройство и из устройства USB
Оригинал: «Device Drivers, Part 13: Data Transfer to and from USB Devices»
Автор: Anil Kumar Pugalia
Дата публикации: December 29, 2011
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.
В этой статье, которая является частью серии статей о драйверах устройств в Linux, продолжаем обсуждать тему предыдущих двух статей. В статье подробно рассматривается последний этап — передача данных в устройство USB и из устройства USB с использованием нашего первого USB драйвера для Linux.
Пагс продолжил: «Чтобы ответить на твой вопрос о том, как драйвер выборочно регистрирует или пропускает определенный интерфейс устройства USB, ты должна разобраться, насколько важно значение, возвращаемое обратным вызовом probe() «. Обратите внимание, что ядро USB должно обращаться к probe для всех интерфейсов обнаруженного устройства, за исключением тех, которые уже зарегистрированы — таким образом, когда это делается в первый раз, это будет происходить для всех интерфейсов. Итак, если probe возвращает 0, то это значит, что драйвер зарегистрирован для этого интерфейса. Возвращение кода ошибки означает, что для него регистрация не выполнена. Вот и все. «Это просто» — прокомментировала Светлана.
«Теперь, давайте поговорим о последней задаче — передаче данных в устройство USB и из этого устройства» — продолжил Пагс.
«Но, прежде всего, скажи, что это за макрос MODULE_DEVICE_TABLE? Он стал интересовать меня еще с того момента, когда ты объяснял мне о макросах идентификационной таблицы устройств USB» — спросила Светлана и это заставило Пагса остановиться.
«Все тривиально. Этот макрос, главным образом, для depmod , работающем в пользовательском пространстве» — сказал он. «Модуль» это еще один термин, обозначающий драйвер, который можно динамически загружать/выгружать. Макрос MODULE_DEVICE_TABLE создает две переменные в секции модуля, доступной только на чтение, которые с помощью depmod выделяются и переносятся в /lib/modules/ в глобальные файлы, используемые для отображения устройств. Двумя такими файлами будут файлы драйверов для USB и PCI — modules.usbmap и modules.pcimap , соответственно. В результате, как мы видели на примере с автозагрузкой драйвера usb-storage, появляется возможность автоматически выполнять загрузку этих драйверов.
Передача данных через USB
«Настало время передавать данные через USB. Давай соберем USB драйвер устройства, который мы кодировали ранее, и воспользуемся тем же самым удобным флеш-устройством JetFlash фирмы Transcend, имеющим идентификационный код поставщика (vendor ID) равный 0x058f и идентификационный код изделия (product ID) равный 0x6387 » — сказал с энтузиазмом Пагс.
USB, будучи аппаратным протоколом, формирует в пространстве ядра обычный горизонтальный слой. И, следовательно, чтобы предоставить интерфейс пользовательского пространства, к нему следует подключаться через один из вертикальных слоев. Поскольку символьная вертикаль (драйвер) уже рассматривалась, то для простоты рассмотрения всего потока передачи данных именно ее предпочтительнее выбрать для соединения с горизонталью USB.
Также нам не нужно искать свободный старший символьный номер, а можно воспользоваться старшим символьным номером 180, зарезервированным для файлов символьных устройств, использующих USB. Кроме того, для объединения воедино всей логики символьного драйвера с горизонталью USB, предоставляются следующие интерфейсы, объявленные в заголовочном файле
:
Как и обычно, мы предполагаем, что эти функции должны вызываться в конструкторе и деструкторе модуля, соответственно. Но чтобы получить возможность горячего переподключения файла (символьного) устройства, соответствующего устройству USB, обращения к этим функция должны осуществляться из соответствующих функций обратного вызова probe и disconnect.
Первый параметр в указанных выше функциях является указателем на интерфейс, получаемый в качестве первого параметра в обеих функциях probe и disconnect. Во второй параметр, struct usb_class_driver , нужно прежде, чем вызывать функцию usb_register_dev , занести имя файла устройства и набор операций, используемых с файлом устройства. Пример использования смотрите в тексте функций pen_probe и pen_disconnect в листинге кода pen_driver.c , приведенного ниже.
Кроме того, поскольку теперь предоставляются операции для работы с файлами (запись, чтение и т. д.), это именно то, что нам нужно при передаче данных в устройство USB и из него. Так что в функциях pen_write и pen_ read , которые показаны ниже, показывается, как можно сделать вызов функции usb_bulk_msg() (прототип в
) для того, чтобы выполнить обмен данными соотвественно с источниками / приемниками данных 0×01 и 0×82 для памяти флеш-устройства. Чтобы определить номера источников / приемников данных для нашего флеш-устройства, взгляните на строки ‘Е’ в средней части рис.1.
Рис.1: Спецификации USB флеш-устройства
Обратитесь к заголовочному файлу
Обратите внимание, что флэш-накопитель относится к классу устройств USB массового хранения данных, для работы с которыми предполагается использование команд, похожих на команды SCSI, которые предназначены для передачи данных в источник / приемник данных массовой памяти (bulk endpoints). Поэтому в случае, если данные не отформатированы должным образом, команды read/write, который показаны ниже в листинге кода, могут, в действительности, не передавать данные так, как это от них ожидается. Но все же, в этом коде собран весь драйвер USB. Чтобы получить представление о реальной передаче данных через USB простым и элегантным способом, может потребоваться воспользоваться некоторым специально настроенным устройством USB, похожим на то, что показано здесь.
В качестве напоминания повторим над кодом, приведенным выше, обычные действия, выполняемые при создании любых Linux-драйверов, а также следующие специальные действия с флэш-накопителем:
- Собираем драйвер (файл .ko) с помощью запуска команды make .
- Запускаем драйвер с помощью команды insmod pen_driver.ko .
- Подключаем флеш-устройство (после того, как мы убедились в том, что драйвер usb-storage еще не загружен)
- Проверяем наличие динамически созданного файла /dev/pen0 (младшим полученным числом будет 0 — с помощью dmesg посмотрите в журналах значение, используемое в вашей системе)
- Возможно, пытаемся что-нибудь записать на устройство / прочитать из устройства /dev/pen0 (из-за того, что используемые команды не соответствуют командам SCSI, вы, скорее всего, получите ошибки таймаута и/или ошибки прерывания передачи данных через конвейер)
- Отключаем флеш-накопитель и смотрим, что произошло с /dev/pen0 .
- Выгружаем драйвер с помощью команды rmmod pen_info .
Тем временем Пагс для живой демонстрации передачи данных через USB подключил к своей системе единственное в своем роде творение — Комплект для создания драйверов в Linux (LDDK- Linux device driver kit).
«Ага! Наконец крутой и полностью работающий драйвер USB», — взволнованно пошутила Светлана. «Нужно что-нибудь еще круче? Мы могли бы сделать над ним блочный драйвер . » — добавил Пагс. «О! В самом деле?» — восторженно спросила Светлана. «Да. Но перед этим мы должны понять механизм разбиения устройства на разделы» — прокомментировал Пагс.
Источник