Здравствуйте, хаброчитатели!
Иногда так случается, что возникает необходимость перейти на более новую версию ядра Linux и, соответственно, выполнить перенос уже существующих драйверов устройств.
![](https://habrastorage.org/webt/5r/nt/bj/5rntbjo583-vvnhho7wszb7pjs0.png)
Процесс переноса может занять от нескольких минут до более продолжительного промежутка времени. Зависит это не только от сложности драйвера, но и от того, с какой и на какую версию ядра вы собираетесь перейти (API имеет свойство меняться — отсюда лезут все проблемы), а также от качества реализации кода, бывает, что проще переписать, чем перенести, но об этом не будем.
К сожалению, я не могу прикрепить исходный код драйвера, но мы рассмотрим все проблемы, с которыми я и вы можете столкнуться в процессе переноса. Далее будет рассмотрен пример переноса простого драйвера c версии ядра 2.6.25 на 4.12.5, который расположен в drivers/serial/name_uart.c. Также нам очень поможет следующий ресурс 2.6.25 и 4.12.5, где можно посмотреть структуру ядра, а также исходные коды.
Постановка задачи крайне примитивна и проста — требуется перенести выше упомянутый драйвер с версии ядра 2.6.25 на 4.12.5.
Первым делом, если вы не знакомы с драйвером который вам предстоит перенести, то я бы порекомендовал хотя бы поверхностно изучить его. Это может значительно упростить задачу. После этого можно приступать к переносу.
Шаг первый
Наш драйвер имеет следующий путь в ядре 2.6.25: drivers/serial/name_uart.c
Теперь нам нужно найти подходящую директорию, куда можно его положить. И тут мы встречаем первую проблему — такой директории drivers/serial/ в ядре 4.12.5 нет.
Решается она очень просто: берем и кладем наш драйвер в drivers/tty/serial/name_uart.c
Шаг второй
После этого, чтобы Linux смог включить наш драйвер в сборку, нам нужно добавить его в два файлика.
Первый файлик: drivers/tty/serial/Makefile
В него мы добавляем следующую строчку:
Второй файлик: drivers/tty/serial/Kconfig
В него мы пишем следующее:
Шаг третий
Как только мы выполнили первые два шага, можно перейти к сборке.
Из корневой директории запускаем make menuconfig и включаем наш драйвер в сборку.
При выполнении команды открывается графический интерфейс и проблемы найти в нем наш драйвер быть не должно (поиск можно сделать следующим путем: жмем / и пишем полное или часть названия, далее enter).
Есть и иной способ — можно просто отредактировать .config файл и включить в сборку ваш драйвер там.
Далее можно попытаться собрать ядро с нашим включенным драйвером.
Шаг четвертый
После того как мы попытались собрать ядро, скорее всего у нас посыпались ошибки, которые нужно решить, чтобы после выполнить успешную сборку ядра и обязательно проверить на работоспособность наш драйвер.
Ошибка, с которой столкнулся я — это устаревший интерфейс работы с proc системой, а также удаленный макрос.
Например, вызов функции irequest_irq в ядре 2.6.25 проходит успешно, НО
в ядре 4.12.5 макрос IRQF_DISABLED был удален, и поэтому драйвер не собирался.
Решение — возьмем и подставим 0 вместо IRQF_DISABLED.
Следующая ошибка заключалась в том, что в ядре версии 2.6.25 взаимодействие с proc системой происходило с помощью create_proc_entry, реализацию которой вы больше не найдете в 4.12.5.
Поэтому необходимо было немного переписать реализацию и в итоге получился следующий вариант:
Если обобщить все выше изложенное, то перенос драйвера разделяется на следующие этапы:
1. скопировать исходные коды драйвера предыдущей версии на новую версию ядра Linux;
2. добавить описание в Kconfig и в Makefile;
3. подцепить с помощью busybox в сборку ядра наш драйвер (или с помощью прямого редактирования .config файла);
4. пытаться устранить все ошибки при сборке, пока ядро не соберется.
Итак, мы рассмотрели основы того, как происходит процесс портирования драйвера устройства на более новую версию ядра Linux, а также проблемы, с которыми можно столкнуться.
В следующей статье мы напишем свой первый полноценный собственный драйвер по заданному техническому заданию для одной из новых версий ядра Linux, а также протестируем его не только с помощью стандартных утилит Linux, но и напишем свой тест для него.
Пожалуйста, если вы нашли неточности, или вам есть что добавить — напишите в ЛС или в комментарии.
Спасибо!
Введение
Иногда так случается, что возникает необходимость перейти на более новую версию ядра Linux и, соответственно, выполнить перенос уже существующих драйверов устройств.
![](https://habrastorage.org/webt/5r/nt/bj/5rntbjo583-vvnhho7wszb7pjs0.png)
Процесс переноса может занять от нескольких минут до более продолжительного промежутка времени. Зависит это не только от сложности драйвера, но и от того, с какой и на какую версию ядра вы собираетесь перейти (API имеет свойство меняться — отсюда лезут все проблемы), а также от качества реализации кода, бывает, что проще переписать, чем перенести, но об этом не будем.
К сожалению, я не могу прикрепить исходный код драйвера, но мы рассмотрим все проблемы, с которыми я и вы можете столкнуться в процессе переноса. Далее будет рассмотрен пример переноса простого драйвера c версии ядра 2.6.25 на 4.12.5, который расположен в drivers/serial/name_uart.c. Также нам очень поможет следующий ресурс 2.6.25 и 4.12.5, где можно посмотреть структуру ядра, а также исходные коды.
Постановка задачи
Постановка задачи крайне примитивна и проста — требуется перенести выше упомянутый драйвер с версии ядра 2.6.25 на 4.12.5.
Реализация
Первым делом, если вы не знакомы с драйвером который вам предстоит перенести, то я бы порекомендовал хотя бы поверхностно изучить его. Это может значительно упростить задачу. После этого можно приступать к переносу.
Шаг первый
Наш драйвер имеет следующий путь в ядре 2.6.25: drivers/serial/name_uart.c
Теперь нам нужно найти подходящую директорию, куда можно его положить. И тут мы встречаем первую проблему — такой директории drivers/serial/ в ядре 4.12.5 нет.
Решается она очень просто: берем и кладем наш драйвер в drivers/tty/serial/name_uart.c
Шаг второй
После этого, чтобы Linux смог включить наш драйвер в сборку, нам нужно добавить его в два файлика.
Первый файлик: drivers/tty/serial/Makefile
В него мы добавляем следующую строчку:
obj-$(CONFIG_SERIAL_NAME) += name_uart.o
Второй файлик: drivers/tty/serial/Kconfig
В него мы пишем следующее:
config SERIAL_NAME
tristate "SERIAL_NAME UART driver"
help
Write description here
Шаг третий
Как только мы выполнили первые два шага, можно перейти к сборке.
Из корневой директории запускаем make menuconfig и включаем наш драйвер в сборку.
При выполнении команды открывается графический интерфейс и проблемы найти в нем наш драйвер быть не должно (поиск можно сделать следующим путем: жмем / и пишем полное или часть названия, далее enter).
Есть и иной способ — можно просто отредактировать .config файл и включить в сборку ваш драйвер там.
Далее можно попытаться собрать ядро с нашим включенным драйвером.
Шаг четвертый
После того как мы попытались собрать ядро, скорее всего у нас посыпались ошибки, которые нужно решить, чтобы после выполнить успешную сборку ядра и обязательно проверить на работоспособность наш драйвер.
Ошибка, с которой столкнулся я — это устаревший интерфейс работы с proc системой, а также удаленный макрос.
Например, вызов функции irequest_irq в ядре 2.6.25 проходит успешно, НО
irequest_irq(64 + ISC_DMA, dma_interrupt_handler, IRQF_DISABLED, "DMA", NULL);
в ядре 4.12.5 макрос IRQF_DISABLED был удален, и поэтому драйвер не собирался.
Решение — возьмем и подставим 0 вместо IRQF_DISABLED.
irequest_irq(64 + ISC_DMA, dma_interrupt_handler, 0, "DMA", NULL);
Следующая ошибка заключалась в том, что в ядре версии 2.6.25 взаимодействие с proc системой происходило с помощью create_proc_entry, реализацию которой вы больше не найдете в 4.12.5.
Поэтому необходимо было немного переписать реализацию и в итоге получился следующий вариант:
/******************************************************************************
* /proc interface
******************************************************************************/
static int xr16_get_status(struct seq_file *m, void *v)
{
struct uart_name_uart *pp;
unsigned long f;
u64 tmp;
int i, xmax = ARRAY_SIZE(pp->in_irq);
seq_printf(m, "UART NAME ports stat:\n");
for (i = 0; i < UART_NAME_MAXPORTS; i++) {
pp = &uart_name_16_ports[i];
if (!pp->used)
continue;
local_irq_save(f);
tmp = ktime_to_us(ktime_sub(ktime_get(), pp->ktm_last));
do_div(tmp, USEC_PER_SEC);
/*
* Необходимая вам реализация.
*/
local_irq_restore(f);
}
return 0;
}
static int xr16_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, uart_name_get_status, NULL);
}
static const struct file_operations uart_name_proc_fops = {
.owner = THIS_MODULE,
.open = uart_name_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
}
Если обобщить все выше изложенное, то перенос драйвера разделяется на следующие этапы:
1. скопировать исходные коды драйвера предыдущей версии на новую версию ядра Linux;
2. добавить описание в Kconfig и в Makefile;
3. подцепить с помощью busybox в сборку ядра наш драйвер (или с помощью прямого редактирования .config файла);
4. пытаться устранить все ошибки при сборке, пока ядро не соберется.
Итак, мы рассмотрели основы того, как происходит процесс портирования драйвера устройства на более новую версию ядра Linux, а также проблемы, с которыми можно столкнуться.
В следующей статье мы напишем свой первый полноценный собственный драйвер по заданному техническому заданию для одной из новых версий ядра Linux, а также протестируем его не только с помощью стандартных утилит Linux, но и напишем свой тест для него.
Пожалуйста, если вы нашли неточности, или вам есть что добавить — напишите в ЛС или в комментарии.
Спасибо!
Комментарии (4)
Alexeynew Автор
11.03.2018 20:14Да, полностью согласен, обязательно лучше изучить уже готовые практики в других драйверах, которые входят в основную ветку ядра, а не изобретать велосипед. Решил опустить эту информацию чтобы не отойти от главной цели статьи:) Спасибо за комментарий!)
ilmarin77
12.03.2018 16:55хорошо-бы описать как собрать свой драйвер без перекомпиляции всего ядра и систему DKMS.
Т.е сокращённую версию github.com/torvalds/linux/blob/master/Documentation/kbuild/modules.txt и github.com/dell/dkms
xomachine
Не уверен, что подстановка
0
вместо именованной константы в общем случае хорошая идея. Думаю, что было бы правильнее изучить аналогичные вызовыirequest_irq
в других драйверах чтобы найти аналогичную по смыслу именованную константу для нового ядра (если она существует).