- Драйверы устройств в Linux
- Часть 11: Драйверы USB в Linux
- Обнаружение устройства USB в Linux
- Разбираемся в секции, описывающей устройство USB
- Регистрация драйвера USB для флеш устройства
- Подведем итог
- Драйверы устройств в Linux
- Часть 12: Драйверы USB в Linux — продолжение
- Источники / приемники данных устройств 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
Часть 12: Драйверы USB в Linux — продолжение
Оригинал: «Device Drivers, Part 12: USB Drivers in Linux Continued»
Автор: Anil Kumar Pugalia
Дата публикации: November 1, 2011
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.
В этой двенадцатой статье из серии статей, посвященных драйверам устройств в Linux, мы продвинемся дальше в написании нашего первого драйвера USB для Linux; эта статья — продолжение предыдущей статьи.
Пагс продолжил: «Давай соберем драйвер устройства USB, который мы закодировали предыдущий раз, и будет использовать то же самое удобное флеш-устройство JetFlash фирмы Transcend, имеющее идентификационный код поставщика 0x058f (vendor ID) и идентификационный код изделия 0×6387 (product ID). Для этого давай подробнее рассмотрим протокол USB, а затем преобразуем наши знания в код».
Источники / приемники данных устройств USB и их типы
В зависимости от типа и параметров информации, подлежащей передаче, устройство USB может иметь один или несколько источников / приемников данных, каждый из которых должен относиться к одной из следующих четырех категорий:
- Control — для передачи управляющей информации. К примерам относятся перезагрузка устройства, запрос информации об устройстве и т. д. Во всех устройствах USB всегда есть источник / приемник данных типа control, имеющий номер ноль и используемый по умолчанию.
- Interrupt — для быстрой передачи данных малого объема, как правило, до 8 байтов. Например, передача данных в последовательные порты, обмен данными с устройствами, с которыми непосредственно работает человек (HIDS — human interface devices), такими как клавиатуры, мыши и т. д.
- Bulk — для большой, но сравнительно медленной передачи данных.Типичным примером будет передача данных в запоминающие устройства.
- Isochronous — для передачи данных большого объема при наличии широкой пропускной способности канала, но целостность данных не гарантируется. На практике типичными примерами будут данные, для которых важно время передачи, например, аудиоданные, видеоданные и т.д.
Кроме того, для всех случаев, кроме источников / приемников данных типа control, должен указываться режим «in» или «out», определяющий направление передачи данных; «in» означает передачу данных с устройства USB на хост-машину, а «out» — наоборот.
Технически, источник / приемник данных идентифицируется с помощью 8-битового числа, в самом старшем бите (MSB) которого указывается направление потока данных: 0 означает «out», а 1 — «in». Источники / приемники данных типа control являются двунаправленными и бит MSB игнорируется.
На рис.1 показан типичный фрагмент спецификаций USB устройств, подключенных к системе.
Рис.1: Фрагмент данных из proc, описывающие устройства USB
Если конкретно, то в строках для устройства E: , изображаемых на рисунке, показаны примеры источника / приемника данных хост контроллера UHCI и двух источников / приемников данных типа bulk для рассматриваемого нами флэш-устройства. Кроме того показано, что номера источников / приемников данных равны (в шестнадцатеричном виде) 0x81 , 0x01 и 0x82 , соответственно; старшие биты (MSB) в первом и третьем случае равны 1, что указывает на источники / приемники типа «in» — на рисунке они указывается с помощью I , а во втором случае — источник / приемник данных вида «out», что указывается с помощью O . В битах MxPS указывается максимальный размер пакета, т. е. размер данных, которые могут быть переданы за один раз. Опять же, как предполагается, что для источника / приемника данных типа interrupt это будет 2 ( ), и 64 — для источников / приемников данных типа bulk. В Ivl определяется интервал в миллисекундах, который должен быть между двумя последовательными передачами пакетов данных для правильной их передачи, и он более важен для источников / приемников данных типа interrupt.
Разбираемся с секцией устройства USB
Как мы только что мы выяснили при обсуждении строки для устройства E:, сейчас потребуется также разобраться с полями в других строках. Если кратко, то в этих строчках в секции устройства USB приведено полное описание устройства, соответствующее спецификациям USB, которые обсуждались в предыдущей статье.
Вернемся обратно к рис.1. Первой буквой в первой строке в секции каждого устройства будет буква T , указывающая положение устройства в дереве USB, однозначно определяемое тройкой ( ). Символ D представляет собой дескриптор устройства, в котором указывается, как минимум, версия устройства, класс/ категория устройства, а также количество конфигураций для этого устройства.
Здесь будет столько строк C, сколько есть конфигураций, хотя, как правило, используется одна конфигурация. В конфигурационном дескрипторе C указывается индекс конфигурации, атрибуты устройства для данной конфигурации, максимальная мощность (на самом деле, ток), потребляемая устройством в данной конфигурации, и количество интерфейсов для данной конфигурации.
В зависимости от этого, будет определяться количество строк I . Их будет больше в случае, если есть альтернативные варианты интерфейсов, т. е. с тем же самым номером интерфейса, но с различными свойствами — типичный сценарий для веб-камер.
I представляет собой дескриптор интерфейса, в котором указывается индекс интерфейса, альтернативный номер, функциональный класс/категория этого интерфейса, драйвер, связанный с этим интерфейсом, и номер источника / приемника данных, используемого с этим интерфейсом.
Класс интерфейса может совпадать с классом устройства, либо может от него отличаться. И, как об этом уже говорилось ранее, строк E , в зависимости от числа источников / приемников данных, конечных точек, может быть много.
Символ * , расположенный после C и I , указывает на используемую в настоящий момент конфигурацию и интерфейс, соответственно. В строке P указываются идентификационный код поставщика (vendor ID), идентификационный код изделия (product ID) и версия изделия. Строки S являются текстовыми дескрипторами, в которых приводится описание устройства, предоставляемое некоторыми конкретными производителями устройств.
«Было бы хорошо заглянуть в директорий /proc/bus/usb/devices с тем, чтобы выяснить, было ли обнаружено устройство или нет, и, возможно, получить первые краткие сведения об устройстве. Но, скорее всего, эти сведения также будут нужны для того, чтобы написать драйвер устройства. Итак, есть ли способ получить к этим сведениям из кода на языке C?» — спросила Светлана.
«Да, конечно; это как раз то, о чем я собираюсь рассказать тебе дальше. Помнишь ли ты, что как только устройство USB подключается к системе, драйвер хост контроллера USB заносит информацию об этом устройстве в общий слой ядра USB? Чтобы быть точным, он помещает эту информацию в набор структур, входящих одна в другую точно также как это определено в спецификациях USB» — ответил Пагс.
Ниже показаны конкретные структуры данных, которые определены в
; для лучшего понимания они приведены здесь в обратном порядке:
Итак, при доступе к дескриптору struct usb_device для конкретного устройства, можно получить всю информация о конкретном устройстве USB, точно такую, какая есть в /proc . Но как получить дескриптор устройства?
Поскольку драйверы USB пишутся для интерфейсов устройств, а не для всего устройства в целом, дескриптор устройства, на самом деле, не находится непосредственно в драйвере; вместо этого для каждого интерфейса есть дескриптор интерфейса (указывающий на структуру struct usb_interface ).
Напомним, что в обратных вызовах probe и disconnect, которые выполняются ядром USB для каждого интерфейса зарегистрированного устройства, в качестве первого параметра указывается дескриптор соответствующего интерфейса. Ниже приведены прототипы:
Итак, если есть указатель на интерфейс, то можно получить доступ ко всей информации о соответствующем интерфейсе, а для получения дескриптора устройства можно воспользоваться следующим макросом:
Добавляем полученные знания в наш драйвер, который раньше мог выполнять только регистрацию устройства, и получаем следующий листинг с кодом ( pen_info.c ):
Затем, вместе со специальными операциями с флеш-устройством, можем повторить обычные операции, которые выполняются для всех драйверов Linux:
- Собираем драйвер (файл .ko ) с помощью запуска команды make .
- Загружаем драйвер с помощью команды insmod pen_info.ko
- Подключаем флейш-устройство (после того, как мы увидели, что драйвер usb-storage еще не загружен)
- Отсоединяем флеш-устройство.
- Проверяем журналы с помощью команды dmesg
- Выгружаем драйвер с помощью команды rmmod pen_info .
На рис.2 показана часть описанных выше шагов, которые были выполнены в системе Пагса. Не забудьте проверить (посмотрите вывод команды cat /proc/bus/usb/devices ), что к интерфейсу флеш-устройства подключен не обычный драйвер usb-storage , а драйвер pen_info .
Рис.2: Вывод данных с помощью dmesg
Подведем итог
Прежде, чем сделать еще один перерыв, Пагс воспользовался двумя из множества способов, с помощью которых драйвер сообщает в ядро USB через таблицу struct usb_device_id о том, какое устройство используется. При использовании первого способа с помощью макроса USB_DEVICE() точно так, как это делалось выше, указывается пара ( ). При использовании второго способа с помощью макроса USB_DEVICE_INFO() указывается класс/категория устройства. На самом деле, в заголовочном файле
есть много других макросов, которые можно использовать в различных сочетаниях. Более того, большое число этих макросов можно указывать в таблице usb_device_id (которая завершается записью null) и указывать критерий выбора, что позволяет написать один драйвер для максимально возможного количества устройств.
«Ранее ты также упоминал о том, что для одного устройства пишутся несколько драйверов. А как, в общем случае, мы при регистрации интерфейсов устройства USB выбираем, какой интерфейс регистрировать, а какой — нет?» — спросила Светлана. «Разумеется, это тема дальнейшего обсуждения нашей последней темы для драйверов любого устройства — механизмов передачи данных» — ответил Пагс.
Источник