Compiling a Linux Device driver
This article illustrates how to compile the «Hello World» example on page 16 chapter II on the Linux Device Driver 3rd Edition book written by Jonathan Corbet, Alessandro Rubini and Greg Kroah-Hartman for O’Reilly
This book is the bible for who wants to write Linux Device Drivers. You can read it for free from here:
Before proceede you have to properly configure and built the Linux Kernel for your Acme board as explained on these articles:
Hello World example
This is the original source code of the basic Hello World example
#include #include MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) < printk(KERN_ALERT "Hello, world\n"); return 0; >static void hello_exit(void) < printk(KERN_ALERT "Goodbye, cruel world\n"); >module_init(hello_init); module_exit(hello_exit);
Create a directory to store your module in your home page on your Linux Ubuntu PC.
~$ mkdir ldd3 ~$ cd ldd3 ~/ldd3$and save here the example code in hello.c.
Create the Makefile
Create a file called Makefile in the same source directory with a single line:
The fully explanation on how it works is illustrated on page 23 chapter II or in /Documentation/kbuild in the Linux Kernel sources.
Now issue the kernel module compilation by typing:
~/ldd3$ make -C ~/linux-3.16.1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- M=`pwd` modules make: Entering directory `/home/tanzilli/linux-3.13' CC [M] /home/tanzilli/ldd3/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/tanzilli/ldd3/hello.mod.o LD [M] /home/tanzilli/ldd3/hello.ko make: Leaving directory `/home/tanzilli/linux-3.13'Change ~/linux-3.16.1 with the Linux source directory on your PC.
The hello.ko file you will obtain is the kernel module ready to be launched on your board. Copy this file into the board file system and launch it by typing:
~# insmod hello.ko Hello, worldGet the modules list by typing:
~# lsmod Module Size Used by hello 820 0 .Remove the module by typing:
~# rmmod hello Goodbye, cruel worldRelated links
Linux driver make module
Рис.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 .
/* ofd.c – Код нашего первого драйвера */ #include #include #include static int __init ofd_init(void) /* Конструктор */ < printk(KERN_INFO "Namaskar: ofd registered"); return 0; >static void __exit ofd_exit(void) /* Дескруктор */ < printk(KERN_INFO "Alvida: ofd unregistered"); >module_init(ofd_init); module_exit(ofd_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anil Kumar Pugalia "); MODULE_DESCRIPTION("Our First Driver");
С учетом вышесказанного это полный код нашего первого драйвера; назовем его ofd.c. Обратите внимание, что отсутствует заголовок stdio.h (заголовок пользовательского пространства), вместо него мы используем аналог kernel.h (заголовок пространства ядра). Функция printk() эквивалентна функции printf() . Кроме того, для обеспечения совместимости версии модуля с ядром, в которое будет загружен модуль, добавлен заголовок version.h . С помощью макроса MODULE_* заполняется информация, относящаяся к модулю, которая будет использована как "подпись" модуля.
Сборка нашего первого драйвера
Т.к. у нас есть код на языке C, настало время его скомпилировать и создать файл модуля ofd.ko . Для этого мы используем систему сборки ядра. В приведенном ниже файле Makefile происходит обращение к системе сборки ядра из исходных кодов, а файл Makefile ядра, в свою очередь, обращается к файлу Makefile нашего нового драйвера с тем, чтобы собрать драйвер.
Чтобы собрать драйвер для Linux, у вас в системе должен быть исходный код ядра (или, по крайней мере, заголовки ядра). Предполагается, что исходный код ядра будет находиться в директории /usr/src/linux . Если в вашей системе он находится в каком-нибудь другом месте, то укажите это место в переменной KERNEL_SOURCE в файле Makefile .
# Makefile – makefile of our first driver # if KERNELRELEASE is defined, we've been invoked from the # kernel build system and can use its language. ifneq ($,) obj-m := ofd.o # Otherwise we were called directly from the command line. # Invoke the kernel build system. else KERNEL_SOURCE := /usr/src/linux PWD := $(shell pwd) default: $ -C $ SUBDIRS=$ modules clean: $ -C $ SUBDIRS=$ clean endif
Когда есть код на языке C ( ofd.c ) и готов файл Makefile , то все, что нам нужно сделать для сборки нашего первого драйвера ( ofd.ko ), это вызвать команду make .
$ make make -C /usr/src/linux SUBDIRS=. modules make[1]: Entering directory `/usr/src/linux' CC [M] . /ofd.o Building modules, stage 2. MODPOST 1 modules CC . /ofd.mod.o LD [M] . /ofd.ko make[1]: Leaving directory `/usr/src/linux'
Подведем итог
Как только у нас будет файл ofd.ko , мы в роли пользователя root или с помощью команды sudo выполним обычные действия.
# su # insmod ofd.ko # lsmod | head -10
Команда lsmod должна вам сообщить о том, что драйвер ofd загружен.
Пока студенты экспериментировали со своим первым модулем, прозвенел звонок, сообщивший об окончании урока. Профессор Гопи подвел итог: "В настоящий момент мы не увидели ничего, кроме того, что модуль lsmod сообщил о загрузке драйвера. Куда выводит информацию команда printk ? Найдите это самостоятельно на лабораторных занятиях и познакомьте меня с своими выводами. Также учтите, что наш первый драйвер будет шаблоном для любого драйвера, который можно написать для Linux. Написание специализированных драйверов это всего лишь вопрос о том, чем будет заполнен конструктор и деструктор драйвера. Поэтому дальнейшее изучение будет представлять собой расширение данного драйвера с целью получить драйвер с конкретными функциональными возможностями".