Как писать драйвер для линукс

Драйверы устройств в Linux

Часть 2: Пишем в классе наш первый драйвер для Linux

Оригинал: «Device Drivers, Part 2: Writing Your First Linux Driver in the Classroom»
Автор: Anil Kumar Pugalia
Дата публикации: December 1, 2010
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.

В этой статье, которая является частью серии статей о драйверах устройств в Linux, речь идет о концепции динамической загрузки драйверов — сначала мы перед тем, как собирать драйвер, напишем драйвер для Linux, а затем, после сборки, загрузим его.

Светлана и Пагс добрались в свой класс с опозданием и увидели, что их профессор уже начал читать лекцию. Светлана робко попросила разрешения войти. Раздраженный профессор Гопи ответил: «Входите! Вы, друзья, опять сегодня опоздали, и по какой причине»?

Пагс поспешно ответил, что они обсуждали именно ту тему, которую сегодня изучают в классе — драйверы устройств в Linux. Пагс был более, чем счастлив, когда профессор сказал: «Хорошо! Тогда что-нибудь скажите о динамической загрузке в Linux. Если вы справитесь, то я прощу вас обоих!». Пагс знал, что один из способов сделать профессора счастливым, это — покритиковать Windows.

Он объяснил: «Как известно, при обычной установке драйверов в Windows для того, чтобы их активировать, необходимо перезагрузить систему. А если это, предположим, действительно неприемлемо в случае, если это нужно делать на сервере? Вот где выигрывает Linux. В Linux можно загружать и выгружать драйверы на лету, и это активно используется сразу после загрузки системы. Кроме того, драйвер мгновенно отключается после его выгрузки. Это называется динамической загрузкой и выгрузкой драйверов в Linux «.

Это впечатлило профессора. «Хорошо! Идите на свои места, но больше не опаздывайте». Профессор продолжил лекцию: «Теперь, когда вы уже знаете, что такое динамическая загрузка и выгрузка драйверов, я, прежде, чем мы перейдем к написанию нашего первого драйверов, покажу вам, как загружать и выгружать драйвера».

Динамическая загрузка драйверов

Эти динамически загружаемые драйвера чаще всего называют модулями, которые собираются в виде отдельных модулей с расширением .ko (объект ядра). В каждой системе Linux в корне файловой системы (/) есть стандартное место для всех предварительно собранных модулей. Они организованы аналогично древовидной структуре исходных кодов ядра и находятся в директории /lib/modules/ /kernel , где результат вывода системной команды uname -r (см.рис.1).

Рис.1: Предварительно собранные модули Linux

Чтобы динамически загружать и выгружать драйверы, воспользуйтесь следующими командами, которые находятся в директории /sbin и должны выполняться с привилегиями пользователя root:

  • lsmod — список модулей, загруженных в текущий момент
  • insmod — добавление / загрузка указанного файла модуля
  • modprobe — добавление / загрузка модуля вместе со всеми его зависимостями
  • rmmod — удаление / выгрузка модуля

Давайте в качестве примера рассмотрим соответствующие драйвера файловой системы FAT. На рис.2 показан весь процесс нашего эксперимента. Файлы с модулями будут fat.ko , vfat.ko и т.д., находящиеся в директории fat (в vfat для старых версий ядра) в /lib/modules/`uname -r`/kernel/fs . Если они представлены в сжатом формате .gz , вам нужно будет распаковать их с помощью команды gunzip , прежде чем вы сможете выполнить операцию insmod .

Рис.2: Операции с модулями Linux

Модуль vfat зависит от модуля fat , так что первым должен быть загружен модуль fat.ko . Чтобы автоматически выполнить распаковку и загрузку зависимостей, воспользуйтесь командой modprobe . Обратите внимание, что когда вы пользуетесь командой modprobe , вы не должны в имени модуля указывать расширение .ko . Команда rmmod используется для выгрузки модулей.

Наш первый драйвер для Linux

Перед тем, как написать наш первый драйвер, давайте рассмотрим некоторые понятия. Драйвер никогда не работает сам по себе. Он похож на библиотеку, загружаемую из-за функций, которые будут вызваны из работающего приложения. Он написан на языке C, но в нем отсутствует функция main() . Кроме того, он будет загружаться / компоноваться с ядром, поэтому он должен компилироваться аналогично тому, как было откомпилировано ядро, и вы можете в качестве заголовочных файлов использовать только те, что есть в исходном коде ядра, а не из стандартного директория /usr/include .

Интересный факт, касающийся ядра, это то, что оно, как мы видим даже на примере нашего первого драйвера, представляет собой объектно-ориентированную реализацию на языке C. В любом драйвере есть конструктор и деструктор. Когда модуль успешно загружается в ядро, то вызывается конструктор модуля, а дескруктор модуля вызывается, когда команде rmmod удается успешно выгрузить модуль. Это в драйвере две обычные функции, разве что они называются init и exit, соответственно, и вызываются с помощью макросов module_init() и module_exit() , которые определены в заголовков ядра module.h .

Читайте также:  Как закрыть кортана windows 10

С учетом вышесказанного это полный код нашего первого драйвера; назовем его ofd.c. Обратите внимание, что отсутствует заголовок stdio.h (заголовок пользовательского пространства), вместо него мы используем аналог kernel.h (заголовок пространства ядра). Функция printk() эквивалентна функции printf() . Кроме того, для обеспечения совместимости версии модуля с ядром, в которое будет загружен модуль, добавлен заголовок version.h . С помощью макроса MODULE_* заполняется информация, относящаяся к модулю, которая будет использована как «подпись» модуля.

Сборка нашего первого драйвера

Т.к. у нас есть код на языке C, настало время его скомпилировать и создать файл модуля ofd.ko . Для этого мы используем систему сборки ядра. В приведенном ниже файле Makefile происходит обращение к системе сборки ядра из исходных кодов, а файл Makefile ядра, в свою очередь, обращается к файлу Makefile нашего нового драйвера с тем, чтобы собрать драйвер.

Чтобы собрать драйвер для Linux, у вас в системе должен быть исходный код ядра (или, по крайней мере, заголовки ядра). Предполагается, что исходный код ядра будет находиться в директории /usr/src/linux . Если в вашей системе он находится в каком-нибудь другом месте, то укажите это место в переменной KERNEL_SOURCE в файле Makefile .

Когда есть код на языке C ( ofd.c ) и готов файл Makefile , то все, что нам нужно сделать для сборки нашего первого драйвера ( ofd.ko ), это вызвать команду make .

Подведем итог

Как только у нас будет файл ofd.ko , мы в роли пользователя root или с помощью команды sudo выполним обычные действия.

Команда lsmod должна вам сообщить о том, что драйвер ofd загружен.

Пока студенты экспериментировали со своим первым модулем, прозвенел звонок, сообщивший об окончании урока. Профессор Гопи подвел итог: «В настоящий момент мы не увидели ничего, кроме того, что модуль lsmod сообщил о загрузке драйвера. Куда выводит информацию команда printk ? Найдите это самостоятельно на лабораторных занятиях и познакомьте меня с своими выводами. Также учтите, что наш первый драйвер будет шаблоном для любого драйвера, который можно написать для Linux. Написание специализированных драйверов это всего лишь вопрос о том, чем будет заполнен конструктор и деструктор драйвера. Поэтому дальнейшее изучение будет представлять собой расширение данного драйвера с целью получить драйвер с конкретными функциональными возможностями».

Источник

[Реквест подсказки]Самостоятельное написание драйвера под linux

Здравствуйте! Некое время назад попал ко мне замечательный модем Г-Link DU-562M, который на офсайте имеет драйвер под виндовс, который на офсайте производителя чипа (conexant) имеет ссылку на сайт с платным закрытым драйвером под линукс (или со «свободными» 28,8 Кб/сек). И хоть lsusb его показывает, как устройство он работать не хочет.

В связи с этим хочу научиться писать драйверы для usb устройств(хотя с адсл этот модем не нужен), чтобы лучше узнать принципы работы компьютера. Прошу знающих людей дать гайд по изучению написания драйверов, то есть пошаговую инструкцию с человеческим подходом. Например:

>1. Прочитать url(). Там описание типов потоков данных в usb и как что робит

2. Вызубрить учебник book() — там нет лишней информации, которая будет занимать некоторое время твоего обучения..

3. Вот учебник по assembler’у — ass(), там в пятой главе как раз обращения к устройствам и выделение памяти.

4. Если что в личку написал свой мыл, можешь задавать вопросы, потому что мне тоже интересно развитие всего что с СПО связано(или иные мотивы, либо без них :)).

Из знаний: php на уровне «могу сайт сбацать». mYsql — «могу в phpMA войти и таблицу создать». bash — «могу скрипт создать, сам все проверит, обновит, логи покажет и удалит хоум, если нужно». В профессиональном программировании — «0.1».

http://lwn.net/Kernel/LDD3/
Сам правда драйвера не писал, но хочется, а времени нет.

Для начала документацию на модем этот найди. Без этого можно особо не напрягаться, тем более с такими стартовыми условиями.

При наличии документации все довольно просто, можно даже LDD особо не читать, достаточно найти пимерно такой-же драйвер и перезаточить его под конкретное железо.

Си не знаете? Тогда стоит прочитать Брайан Керниган, Деннис Ритчи «Язык программирования C». В любом случае не повредит.

И ищите спеки на модем. Если их нет, то придется слушать юсб-трафик и реверс-инженерить. Возможно, что это будет сложновато.

Я хочу написать драйвер для своего принтера canon IP1700. Где мне искать документацию к нему?

При современных ценах на железо, нет профита в написании драйверов для Г-поделий производителей, не дающих спеки на свои Г-поделки.

Хочешь скилл прокачать, напиши что-то полезное, а Г-поделку отдай врагам.

> Вот учебник по assembler’у — ass(), там в пятой главе как раз отлов констант вроде 28800 и вызовов sleep, как все это править или заменять на nop.

Самому бы такую книжку

Два месяца назад начал писать драйвер для USB сканера Canon LiDE 70 (далее csl70). До этого имел нормальный опыт программирования только на Делфях под окнами (собственно писать начал, как только на линукс и перешёл). Дрова пришлось писать на основе логов УСБ сниффера. Сейчас дописываю фрон-энд.

Читайте также:  Astra linux драйвер radeon

Как у меня всё шло:
1. Разобраться как робит УСБ.
2. Несколько разных логов сниффером захватываешь. Пишешь прогу которая преобразует логи в читаемый вид.
Можно пробовать данные из лога передавать на устройство (для сканера — просто, для модема — х3, т.к. ответная реакция может быть иная, в зависимости от ответа какого-нибудь провайдера)
Ищешь закономерности в логе. Данные которые идут через УСБ могут представлять собой команды, данные, либо и то и другое одновременно в одном пакете. Например для csl70 данные передаваемые через УСБ в BULK пакете «01 46 01 00» означают читать(01) значение регистра 46. «00 46 01 00 22» — записать(00) в 46-ой регистр значение 22. Т.е. сначала надо разобраться со смыслом передаваемых пакетов.
Далее написал «детранслятор», который преобразует логи в вид наподобие
..
getreg(46)
setreg(46,22)
..
По сути создаешь свой мини-скриптовый язык.
3. Далее ищешь крупные закономерности. Например серия записей в регистры определяет ширину сканируемой картинки, или адрес в ОЗУ сканера для чтения\записси данных.
Улучшаешь детранслятор, чтоб он определял эти крупные функции.
И так до тех пор, пока всё не будет удобночитаемо.
Можно уже экспериментировать со значениями передаваемых данных, и последовательностью передаваемых команд.

4. Когда всё ясно, и больше узнавать НЕ`ЧЕГО, то можно скрипты перетаскивать на с \ с++ или др. язык.

PS. Использовать лучше libusb1, чтоб с ядром не заморачиваться. Видмо придётся ещё разбираться с созданием всяких интерфесов\устройств — tun и т.п. — в этом не разбираюсь, подсказать не могу. Если доки на модем найдутся — то отлично.

Спасибо! Не ожидал что столько полезной информации без троллинга здесь можно найти.. Гугл однообразные ответы без субъективной практики дает, в нем подобного искать — замучаешься. Еще раз спасибо! Теперь есть стимул.

А насчет Г-поделок: устройство есть, почему оно должно место в кладовке занимать? Менять железо из-за особенностей ПО — виндовс вей с их запросами на ОЗУ и место под солнцем на жестком, не так ли?

> почему оно должно место в кладовке занимать?

Избавляйся от плюшкинизма, подарить врагу — самое оно

Источник

Русские Блоги

Написать простой драйвер под Linux

В этой статье сначала описывается пример драйвера, который можно протестировать и запустить, а затем обсуждаются элементы шаблона драйвера в Linux и процесс выполнения от приложения к драйверу в Linux. Я считаю, что описание от мелкого до глубокого, от конкретных примеров до абстрактной теории, для начинающих легче начать с двери, управляемой Linux.

1. Простой пример драйвера

Файл драйвера hello.c

Файл драйвера в основном включает в себя функции hello_open, hello_write, hello_init, hello_exit. Тестовый пример не дает модулю драйвера практическую функцию, а только печатает журнал, чтобы сообщить консоли некоторую отладочную информацию, чтобы мы могли понять выполнение драйвера ,

При печати с использованием printk добавление «KERN_EMERG» к параметрам может обеспечить вывод информации для печати на консоль. Поскольку printk print разделен на 8 уровней, верхний уровень выводится на консоль, а нижний уровень выводится в файл журнала.

Makefile требуется для компиляции драйвера

Для компиляции файла драйвера необходим подходящий make-файл, потому что при компиляции драйвера нужно знать файл заголовка ядра, правила компиляции и т. Д.

Тестовый верхний код приложения hellotest.c

В верхнем тестовом случае сначала откройте файл устройства, а затем запишите данные на устройство. Таким образом, будут вызваны соответствующие функции xxx_open и xxx_write в драйвере, и информация о печати драйвера может быть использована для определения, действительно ли соответствующая функция выполняется должным образом.

Во-вторых, пример теста диска

Общий метод тестирования состоит в том, чтобы скомпилировать драйвер и тестовое приложение верхнего уровня, загрузить драйвер и вызвать драйвер через приложение верхнего уровня и, наконец, выгрузить драйвер.

1. Скомпилируйте драйвер

Создайте команду, напрямую вызовите Makefile для компиляции hello.c и, наконец, сгенерируйте «hellomodule.ko».

2. Скомпилируйте приложение верхнего уровня

С помощью этой команды вы можете скомпилировать приложение hellotest верхнего уровня.

3. Загрузите драйвер

Когда insmod загружает драйвер, вызывается функция hello_init (), и распечатанная отладочная информация выглядит следующим образом.

Кроме того, вы можете увидеть загруженные модули в «/ proc / devices».

4. Создайте узел

Хотя драйвер hellomodule.ko был загружен, и загруженный модуль HelloModule также отображается в файле / proc / devices, этот модуль не может использоваться, поскольку в каталоге устройств / dev нет соответствующего файла устройства. Поэтому вам необходимо создать узел устройства.

Номер основного устройства модуля HelloModule в / proc / devices равен 231. Когда узел создается, файл устройства / dev / hellodev подключается к номеру основного устройства. Таким образом, когда приложение манипулирует файлом / dev / hellodev, оно обнаружит модуль HelloModule.

Читайте также:  Записать флешку с windows 10 линукс

Разница между / proc / devices и / dev

  • Устройство в / proc / devices генерируется драйвером, оно может генерировать мажор для mknod в качестве параметра. Содержимое этого файла показывает модули, которые в настоящее время смонтированы в системе. Когда загружается драйвер HelloModule, соответствующий файл устройства не генерируется для абстрактной инкапсуляции устройства для доступа к приложениям верхнего уровня.
  • Устройство в / dev добавляется mknod, и пользователь получает доступ к драйверу через это имя устройства. Я думал, что файлы в / dev можно рассматривать как абстрактный пакет аппаратных модулей, и все устройства в Linux были упакованы в виде файлов.

5. Вызов драйвера приложения верхнего уровня

Приложение hellotest сначала открывает файл «/ dev / hellodev», а затем записывает переменную val в этот файл. В течение этого периода будут вызваны функции hello_open и hello_write в базовом драйвере. Ниже приведены результаты работы hellotest.

6. Удалите драйвер

Когда insmod удаляет драйвер, он вызывает функцию hello_exit (), и печатная информация об отладке выглядит следующим образом.

Суммируйте последовательность операций модуля:

(1) Зарегистрируйте модуль с помощью команды insmod

(2) Создайте файл устройства «xxx» в каталоге / dev с помощью команды mknod и установите соединение с модулем через основной номер устройства.

(3) Прикладной уровень управляет базовым модулем через файл устройства / dev / xxx

Три, диск шаблон

Понимание структуры драйвера с точки зрения макроса, а затем доведение до совершенства функции драйвера из деталей — это основной этап разработки драйвера. К основным элементам шаблона драйвера относятся заголовочные файлы, функции инициализации, функции выхода и информация об авторских правах. Обычно используемые элементы расширения предназначены для добавления некоторых функциональных функций для улучшения функций базового драйвера.

1. Заголовочные файлы

init.h определяет функции, связанные с инициализацией и выходом драйвера
kernel.h определяет часто используемые прототипы функций и определения макросов
module.h определяет функции, переменные и макросы, связанные с модулем ядра

2. Функция инициализации

Когда драйвер загружается в ядро, эта функция инициализации будет выполняться автоматически.

Функция инициализации используется для инициализации модуля, как следует из названия. Обычно используемая функция заключается в регистрации функции через register_chrdev. Ядро выделяет часть памяти (массив) для хранения набора функций символьного устройства.Функция register_chrdev заполнит содержимое hello_flops в позиции HELLO_MAJOR этого массива, которое должно зарегистрировать адрес функции функции HelloModule для набора памяти управления устройством.

Метафора изображения, по-видимому, заключается в том, что операционная система предоставляет множество вешалок для одежды, а зарегистрированное устройство похоже на повешение одежды на определенной вешалке для одежды. На одежде много карманов, как будто каждый модуль имеет множество функциональных программных интерфейсов. Очевидно, что если вы хотите использовать определенную функцию устройства, вы можете сначала найти соответствующую вешалку для одежды, затем найти соответствующий карман, вызвать соответствующую функцию и выполнить действие.

3. Выход из функции

Когда драйвер удален, функция выхода будет выполнена автоматически, выполнив некоторые понятные задачи.

При загрузке драйвера мы регистрировали соответствующие функции функции модуля в памяти устройства управления. При удалении драйвера необходимо очистить пространство памяти, занимаемое этим модулем. Таким образом, при регистрации других устройств остается больше места для выбора.

Метафора изображения заключается в том, что при выгрузке диска одежда снимается с вешалки, так что вешалка становится пустой.

4. Информация об авторских правах

Ядро Linux выпущено в соответствии с GPL, и драйвер Linux также должен предоставлять информацию об авторских правах, в противном случае система выдаст предупреждение при загрузке в ядро.

5. Функциональная функция

View Code

В-четвертых, процесс выполнения от приложения верхнего уровня до драйвера нижнего уровня

1. Иерархическая структура системы Linux

Многоуровневая структура системы Linux: прикладной уровень —-> библиотека —-> ядро ​​—-> драйвер —-> аппаратное устройство.

2. Процесс выполнения от приложения верхнего уровня до драйвера нижнего уровня

Возьмем функцию «open (» / dev / hellodev «, O_RDWR)» в качестве примера для иллюстрации.

(1) Приложение использует функцию открытия, предоставляемую библиотекой, чтобы открыть файл устройства, представляющий hellodev.

(2) Библиотека выполняет swi-инструкцию в соответствии с параметрами, переданными функцией open, что приведет к сбоям в работе процессора и попаданию в ядро.

(3) Функция обработки исключений ядра находит соответствующий драйвер в соответствии с этими параметрами.

(4) Выполните соответствующий драйвер.

(5) Верните дескриптор файла в библиотеку, а затем в приложение.

3. Характеристики исполнения водителя

В отличие от прикладных программ, драйвер никогда не запускается активно, он пассивен: он инициализируется в соответствии с требованиями прикладной программы, а чтение и запись выполняется в соответствии с требованиями прикладной программы. Драйвер загружается в ядро, просто говоря ядру: «Я здесь, я могу выполнять эти задания», и когда эти задания запускаются, это зависит от приложения.

Драйвер работает в «пространстве ядра», которое является частью «доверия» системы. Ошибки драйвера могут привести к сбою всей системы.

«Полное руководство по разработке приложений для встроенного Linux»

Источник

Оцените статью