Loading drivers in linux

Самые полезные настройки и команды Linux

Автор: Igor Ljubuncic, перевод: С.Русинов

Установка драйверов

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

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

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

Установка

Подобно любому программному обеспечению, драйверы могут быть скомпилированными или нет. Обычно они не скомпилированы. Драйверы обычно распространяются в виде исходных текстов, с целью достижения максимально возможной совместимости аппаратных средств на установленной платформе. Это значит, что вам придется компилировать их из исходных текстов. Элементарно! Мы уже знаем как это сделать.

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

После успешной распаковки из архива и компиляции исходников (./configure, make, make install), вы, скорее всего, столкнетесь с выбором из трех вариантов:

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

При втором варианте, процесс установки, будет выглядеть следующим образом:

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

Вам нужно открыть конфигурационный файл, содержащий список модулей. Также необходимо знать его точное имя и размещение файла в вашем дистрибутиве. В Ubuntu этот файл называется modules.conf и размещается в /etc каталоге ( /etc/modules.conf ). Мы обновим этот файл, но вначале мы сделаем его резервную копию. Пожалуйста помните, что для изменения конфигурационных файлов нужны права суперпользователя.

Так будет выглядеть эта процедура:

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

Вот пример, файла modules.conf для Kubuntu Linux, установленной на виртуальной машине. Добавим новый драйвер. Мы просто запишем его имя ниже существующих записей. Конечно, необходимо знать ТОЧНОЕ имя соответствующего драйвера.

Третий вариант немного более сложный.

Загрузка драйверов

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

Нам нужно установить драйвер в ядро. Это можно сделать с помощью команды insmod .

После загрузки драйвера его можно конфигурировать. Вы можете убедиться в том, что драйвер действительно присутствует в списке всех доступных модулей:

Если вы случайно сделали серьезную ошибку и хотите удалить драйвер, то можете воспользоваться командой rmmod :

Конфигурирование драйверов

Конфигурирование драйвера требует немного знаний о его функциональности. Чаще всего инструкции находятся в текстовых файлах how-to руководства.

Ниже приведенный пример показывает как конфигурировать сетевую карту после загрузки сетевого драйвера. Сетевой карте присвоен идентификатор и IP адрес. В данном случае, eth0 — имя выбранного устройства, но оно может быть другим, например: eth1, eth2 и т. д. . Назначенный IP адрес показывает нам, что машина будет частью локальной сети.

Читайте также:  Что такое иксы linux mint

После перезагрузки вы увидите, что сетевое подключение отсутствует. Это происходит из-за того, что драйвер отсутствует в общем каталоге по умолчанию, и система не знает, где его искать. Вам придется повторить всю процедуру снова:

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

Скрипты

Как и в системах DOS и Windows, скрипты могут быть написаны в текстовом редакторе. Учитывая внутренние различия между текстовыми файлами и скриптами, необходимо различать текстовые файлы и скрипты. В системе Windows достаточно изменить расширение .txt на .bat и файл станет скриптом. В Linux немного по-другому.

Командная строка Linux находится внутри оболочки или, точнее сказать, и есть сама оболочка или Шелл (Shell). Существует несколько оболочек, каждая со своим уникальным набором команд. Самая популярная (устанавливается по умолчанию) оболочка Linux это BASH . Нам необходимо добавить информацию в наш скрипт, если хотим сделать его связанным с нашей оболочкой.

Таким образом, записав в файл приведенные выше команды плюс ссылка на оболочку, получим следующий скрипт:

Можно сделать это короче:

Теперь у нас есть рабочий скрипт. Или точнее текстовый файл, который содержит соответствующие команды. Нам необходимо сделать его исполняемым файлом. Во-первых, нужно сохранить этот файл. Назовем его network_script .

Сделаем скрипт исполняемым.

Теперь у нас есть работающий скрипт. Нам нужно разместить его в каталоге /etc/init.d и он будет запускаться во время начальной загрузки системы.

В завершение нужно обновить систему для активации скрипта.

После перезагрузки вы поймете, что драйвер загружен автоматически и сетевая карта сконфигурирована! Возможен и другой вариант, make install и драйвер будет помещен в каталог по умолчанию:

Или вы могли разместить драйвер в этом каталоге сами. Таким образом, вы могли бы избежать написания скрипта.

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

Источник

Linux, отложенная загрузка драйверов и неработающие прерывания

Сегодня я расскажу о неожиданных проблемах, которые возникли при подключении матричной клавиатуры к ARM-борде под управлением Linux в приборе Беркут-ETN (ETN — новая аппаратная ревизия Беркут-ET). А конкретно о том, почему драйвер adp5589 не захотел получать прерывания и как мы смогли заставить его это делать.

Кому интересно — добро пожаловать под кат.

  • Описание железной части
  • Где у нас проблема?
  • Пару слов о Device Tree
  • Немного о регистрации устройств и драйверов
  • Механизм отложенной загрузки драйверов
  • Как заставить всё работать

Описание железа вокруг клавиатуры:

У самой клавиатуры контроллера нет — она подключена по шине I2C с помощью специального контроллера матричной клавиатуры — микросхемы adp5589. У микросхемы есть линия прерывания, заведённая на один из GPIO пинов ARM SoCа. В итоге схема подключения выглядит примерно так:

portb — это порт, на пин которого заведено прерывание от контроллера клавиатуры;
intc — главный контроллер прерываний;
i2c0 — контроллер шины i2c.

Драйвер adp5589 по каким-то причинам упорно не хочет получать номер прерывания. Что же может быть причиной такого поведения? Возможно, для загрузки драйвера клавиатуры не хватает каких-то ресурсов. Может быть не успели загрузиться устройства, от которых он зависит? Давайте посмотрим, от каких устройств он может зависеть:

Во-первых — от контроллера шины I2C, к которой он подключен.
Во-вторых — от контроллера порта, на пин которого у нас заведена линия прерывания.

Теперь посмотрим в каком порядке загружаются драйвера этих устройств:

gic
designware-i2c
adp5589
dw-apb-gpio-port

Ага! Вот и причина — когда загружается драйвер клавиатуры, его interrupt-parent ещё не загружен. Как результат — драйвер клавиатуры не получает номер прерывания. Стандартное решение этой проблемы — механизм отложенной загрузки драйверов.

Его суть в том, что драйвер может потребовать повторной загрузки, если какой-нибудь нужный ему ресурс ещё не доступен. А потребовать он это может, вернув значение -EPROBE_DEFER из своей функции probe. Тогда этот драйвер будет повторно загружен позже. К тому времени или нужный ресурс уже будет доступен, или загрузка драйвера снова будет отложена.

Читайте также:  Dhcp server linux debian

Добавляем проверку в функцию probe драйвера клавиатуры:

В надежде смотрим на новый порядок загрузки:
gic
adp5589
designware-i2c
dw-apb-gpio-port
(deferred)adp5589
(deferred)adp5589
(deferred)adp5589

Что-то пошло не так — драйвер клавиатуры повторно загрузился после драйвера GPIO, но прерывания так и не получил. Похоже, придётся зарываться в исходный код глубже, чем ожидалось.

Напрашивается три возможных пути решения:

  • Захардкодить номер прерывания прямо в драйвер
  • Каким-либо способом задать порядок загрузки драйверов
  • Разобраться с механизмом отложенной загрузки драйверов, который почему-то не заработал

Первый вариант:

Вариант рабочий, но не желательный. Подойдёт в качестве временного, но если что-нибудь поменять в аппаратной части (например выход прерывания подключить к другому порту GPIO), то изменения придётся вносить не только в Device Tree, но и в исходный код драйвера.

Явно задать порядок загрузки драйверов возможности нет. Так что этот вариант не подходит.

Самый правильный. Его и будем рассматривать.

Здесь, пожалуй, стоит кратко рассказать про такую вещь как Device Tree, так как далее будут отсылки к ней.

Device Tree — это одна из форм описания аппаратной части устройства, на котором мы хотим использовать Linux. Представляется в виде дерева узлов, в которых задаётся нужная информация. DT существует в виде текстовых человекочитаемых файлов (.dts; .dtsi) и собираемого из них бинарного файла (.dtb).

Для примера рассмотрим кусочек .dts файла описывающий структуру подключения нашего контроллера клавиатуры к другим устройствам SoCа.

(Не интересующие нас сейчас узлы и свойства вырезаны для облегчения понимания)

i2c0, keybs, inc и portb — узлы, всё остальное — их свойства. Из кода сразу становится видно что чип контроллера клавиатуры подключен к I2C шине. В свойстве compatible — строка, которая описывает производителя и модель устройства. Именно по этому свойству ОС понимает какой драйвер нужно связать с этим устройством.

interrupt-controller — свойство, указывающее, что это устройство может являться контроллером прерываний, а interrupt-parent указывает к кому подключено прерывание от текущего устройства.

#interrupt-cells — свойство, указывающее на количество параметров, которыми описываются прерывания данного контроллера прерываний, а interrupts — свойство, в котором задаются параметры для данного прерывания.

Например в portb указано: #interrupt-cells = Это значит, что в узлах, для которых portb это interrupt-parent в свойстве interrupts нужно описать два параметра. portb это interrupt-parent для keybs. Смотрим в keybs. Там указано: interrupts = . Что это значит?

Здесь описываются два параметра. Первый — это номер пина в порте portb, на который у нас заведена линия прерывания от контроллера клавиатуры. Второй — тип прерывания (по низкому или высокому уровню). Как узнать сколько для контроллера прерываний нужно описывать параметров, и что каждый из них значат? Обычно про это написано в документации. Так, про portb написано в этом файле: Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt.

&portb — ссылка на узел portb (в нашем случае ссылка на portb будет равна /soc/gpio@ff709000/gpio-controller@0)
Остальные свойства нам пока не понадобятся, про них, и вообще про Device Tree, подробно можно почитать здесь: devicetree.org/Device_Tree_Usage.

Ещё не лишним будет упомянуть про процесс регистрации устройств и драйверов (не беспокойтесь, к основной теме мы вернёмся уже в следующем абзаце). Согласно Linux Device Model:

Устройство — физический или виртуальный объект, который подключен к шине(возможно тоже виртуальной)
Драйвер — программный объект, который может быть связан с устройством и может выполнять какие-либо функции управления.
Шина — устройство, предназначенное быть “точкой крепления” других устройств. Базовая функциональность всех шин, поддерживаемых ядром, определяется структурой bus_type. В этой структуре объявлена вложенная структура subsys_private, в которой объявлены два списка: klist_devices и klist_drivers.
klist_devices — список устройств, которые подключены к шине.
klist_drivers — список драйверов, которые могут управлять устройствами на этой шине.
Устройства и драйвера добавляются в эти списки с помощью функций device_register и driver_register. Кроме того, device_register и driver_register связывают устройство с подходящим драйвером. device_register проходит по списку драйверов и пытается найти драйвер, подходящий для данного устройства. (driver_register проходит по списку устройств и пытается найти устройства, которыми он может управлять) Проверка подходит ли драйвер для устройства производится с помощью функции match(dev, drv), указатель на которую есть в структуре bus_type.

Читайте также:  Не включается компьютер windows не включается что сделать

Теперь можно перейти и к основной теме — реализации механизма отложенной загрузки драйверов. Заглянем в файл drivers/base/dd.c Вот краткое описание того, что мы там увидим:

Для управления повторной загрузкой драйверов имеются два списка — deferred_probe_pending_list и deferred_probe_active_list.

deferred_probe_pending_list — список устройств, для загрузки драйвера которых не хватает каких-то ресурсов.
deferred_probe_active_list — список устройств, драйвер которых можно попробовать запустить повторно.

В функции really_probe вызывается функция probe для шины, на которой расположено устройство. В нашем случае это функция i2c_device_probe и выглядит это так dev->bus->probe(dev). Возвращаемое значение проверяется на ошибки, и, если оно равно -EPROBE_DEFER, то устройство добавляется в deferred_probe_pending_list.

Но самое интересное — как и когда драйвер вызывается заново. Пока драйверы возвращают -EPROBE_DEFER, устройства последовательно добавляются в deferred_probe_pending_list. Но как только для какого-либо драйвера функция probe завершилась успешно, все устройства из deferred_probe_pending_list переносятся в deferred_probe_active_list. Выглядит логично — возможно, того драйвера, который у нас последний был успешно загружен, и не хватало для нормальной загрузки отложенных драйверов. Повторная попытка запуска драйверов из deferred_probe_active_list производится функцией deferred_probe_work_func. В ней вызывается bus_probe_device для каждого устройства из списка.

Вызов bus_probe_device в конечном итоге снова приведёт нас к функции really_probe для пары из нашего устройства и его драйвера (см. выше).

Но подождите! Мы сейчас говорили о вызове функции probe для шины, на которой расположено устройство. То есть о i2c_device_probe. А как же функция probe драйвера клавиатуры? Нет, про неё мы не забыли, она как раз будет вызвана из i2c_device_probe. В этом можно убедиться, взглянув на её код в файле drivers/i2c/i2c-core.с:

Ладно, повторная загрузка вроде бы работает, почему же тогда драйвер клавиатуры не получает номера прерывания?
Попробуем отследить то, как номер прерывания должен попасть в наш драйвер.

В функцию adp5589_probe(struct i2c_client *client, const struct i2c_device_id *id) передаётся структура client, одно из полей которой — irq — номер прерывания, которое будет генерировать наше устройство (контроллер клавиатуры). adp5589_probe вызовется из функции i2c_device_probe(struct device * dev). В неё передаётся структура device, из указателя на которую вычисляется указатель на структуру i2c_client (с помощью магии макроса container_of).

Этот макрос принимает на вход указатель на поле структуры, тип этой структуры и имя поля на которое указывает указатель, а возвращает указатель на саму структуру.

Про его работу хорошо расписано здесь.

Значит нужно найти где заполняется структура i2c_client. Заполняется она в функции i2c_new_device(struct i2c_adapter * adap, struct i2c_board_info const * info); Конкретно поле irq копируется из одноимённого поля структуры i2c_board_info.

Структура i2c_board_info заполняется в функции of_i2c_register_devices(struct i2c_adapter * adap).

irq_of_parse_and_map — это обёртка для цепочки из двух функций — of_irq_parse_one и irq_create_of_mapping; функция of_irq_parse_one пытается найти узел, который заявлен в device tree как interrupt-controller для текущего устройства.
Помните эти несколько строчек в device tree?

Именно portb и ищет of_irq_parse_one, а по результатам своей работы заполняет структуру of_phandle_args, которая передаётся функции irq_create_of_mapping. irq_create_of_mapping уже и возвращает искомый номер прерывания.

В первый раз of_irq_parse_one не находит порт GPIO, на что ругается в лог:

irq: no irq domain found for /soc/gpio@ff709000/gpio-controller@0!

А что происходит при повторной загрузке драйвера? А ничего. Вызываются то только i2c_device_probe и adp5589_probe.
Вот в чём и проблема. Прерывание устанавливается только в первый раз и остаётся таким навечно, сколько бы мы не выполняли повторную загрузку нашего драйвера.

Проблему нашли, но как её исправить?

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

Но лучше давайте заглянем в исходники более свежей версии ядра (у нас установлена версия 3.18) Вот что мы там увидим:
Установку прерывания i2c клиента перенесли в функцию i2c_device_probe.

В структуре i2c_board_info хоть и осталось поле irq но оно не используется. Так что в новых версиях ядра проблема исправлена.

Осталось всего лишь перенести изменения в нашу версию. Все изменения коснутся файла drivers/i2c/i2c-core.c
Добавим в нашу i2c_device_probe установку прерывания клиента i2c, что появилась в свежей версии, а в функции of_i2c_register_devices удаляем установку прерывания.

Проверяем — клавиатура работает. Смотрим в /proc/interrupt:

Источник

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