Major and minor linux
Библиотека сайта rus-linux.net
Ранее (вплоть до ядра 2.4) каждый старший номер использовался в качестве указания на отдельный драйвер, а младший номер использовался для указания на конкретное подмножество функциональных возможностей драйвера. В ядре 2.6 такое использование номеров не является обязательным; с одним и тем же старшим номером может быть несколько драйверов, но, очевидно, с различными диапазонами младших номеров.
Однако, такое использование больше характерно для незарезервированных старших номеров, а стандартные старшие номера обычно резервируются для вполне определенных конкретных драйверов. Например, 4 — для последовательных интерфейсов, 13 — для мышей, 14 — для аудио-устройств и так далее. С помощью следующей команды можно будет выдать список файлов различных символьных устройств, имеющихся в вашей системе:
Использование чисел в ядре 2.6
Тип (определен в заголовке ядра linux/types.h ):
Макрос (определен в заголовке ядра linux/kdev_t.h ):
- MAJOR(dev_t dev) — из dev извлекается старший номер
- MINOR(dev_t dev ) — из dev извлекается младший номер
- MKDEV(int major, int minor) — из старшего и младшего номеров создается dev
Подключение файла устройства к драйверу устройства осуществляется за два шага:
- Выполняется регистрация файлов устройств для диапазона
- Подключение операций, выполняемых над файлом устройства, к функциям драйвера устройства.
Первый шаг выполняется с помощью одного из следующих двух API, определенных в заголовке ядра linux/fs.h :
+ int register_chrdev_region(dev_t first, unsigned int cnt, char *name); + int alloc_chrdev_region(dev_t *first, unsigned int firstminor, unsigned int cnt, char *name);
С помощью первого API число cnt регистрируется как среди номеров файлов устройств, которые начинаются с first и именем файла name . С помощью второго API динамически определяется свободный старший номер и регистрируется число cnt среди номеров файлов устройств, начинающиеся с , с заданным именем файла name. В любом случае в директории /proc/devices указывается список имен с зарегистрированным старшим номером. Имея эту информацию, Светлана добавила в код первого драйвера следующее:
#include #include #include static dev_t first; // Global variable for the first device number
В конструктор она добавила:
В деструктор она добавила:
unregister_chrdev_region(first, 3);
И собрала все это вместе следующим образом:
#include #include #include #include #include #include static dev_t first; // Global variable for the first device number static int __init ofcd_init(void) /* Constructor */ < printk(KERN_INFO "Namaskar: ofcd registered"); if (alloc_chrdev_region(&first, 0, 3, "Shweta") < 0) < return -1; >printk(KERN_INFO ": \n", MAJOR(first), MINOR(first)); return 0; > static void __exit ofcd_exit(void) /* Destructor */ < unregister_chrdev_region(first, 3); printk(KERN_INFO "Alvida: ofcd unregistered"); >module_init(ofcd_init); module_exit(ofcd_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anil Kumar Pugalia "); MODULE_DESCRIPTION("Our First Character Driver");
Затем Светлана повторила обычные шаги, которые она узнала при изучении первого драйвера:
- Собрала драйвер (файл .ko ), выполнив команду make .
- Загрузила драйвер с помощью команды insmod .
- Выдала список загруженных модулей с помощью команды lsmod .
- Выгрузила драйвер с помощью команды rmmod .
Подведем итог
Кроме того, перед выгрузкой драйвера она заглянула в директорий /proc/devices для того, чтобы с помощью команды cat /proc/devices найти зарегистрированный старший номер с именем «Shweta». Он там был. Тем не менее, она не смогла в директории /dev найти ни одного файла устройств с таким же старшим номером, т.к. она создала этот номер вручную с помощью команды mknod , а затем пыталась выполнить операции чтения и записи. Все эти действия показаны на рис.2.
Рис.2: Эксперименты с файлом символьного устройства
Обратите внимание, что в зависимости от номеров, уже используемых в системе, старший номер 250 может варьироваться от системы к системе. На рис.2 также показаны результаты, которые получила Светлана при чтении и записи одного из файлов устройств. Это напомнило ей, что все еще не сделан второй шаг подключения файла устройства к драйверу устройства, при котором операции над файлом устройства связываются с функциями драйвера устройства. Она поняла, что ей для того, чтобы завершить этот шаг, нужно поискать дополнительную информацию, а также выяснить причины отсутствия файлов устройств в директории /dev .
Embedded Guru
Special or Device files are located in /dev directory. These are of two types:
1. char device files: Identified by a «c» in the first column of the output of ls -l command
2. Block device files: Identified by a «b» in the first column of the output of ls -l command
Character and block devices are represented in Linux Kernel as pair of :.
Major and Minor Number:
With «ls -l /dev» command, you can find the major and minor number of the particular device.
Major Number: Each character and block driver registered with the kernel has a major number. It’s used to identify the driver. Kernel uses major number at open call to forward execution to the appropriate driver
Minor Number: A single driver can control various hardware, or support multiple instances of the same hardware, minor number is used to distinguish between the various hardware it controls. Minor number is used by only the driver mentioned by the major number.
How are major and minor numbers stored/represented in Linux Kernel
Kernel uses dev_t data type to store both major and minor number.
Definition is present in header file.
dev_t is a 32-bit unsigned integer. Where 12 upper most bits are used to store the major number and the remaining lower most 20 bits are used to store the minor number. Note: Don’t Extract the major and minor number directly.
Macros provided by Linux Kernel for Major and Minor Number
Header File:
Are Major and Minor numbers unique
Yes, Major, Minor combination are unique. Note that character and block devices have distinct numbering spaces, e.g. Major number 1 of block is assigned for RAM disks and Major number 1 of char is assigned to a set of kernel devices such as /dev/zero and /dev/null.
How are major and minor number assigned to drivers
There are two ways of a driver assigning major and minor number.
1. Static Assignment
2. Dynamic Assignment
Please note that some of the major and minor number are already reserved. You can refer Documentation/admin-guide/devices.txt for more information. Major number 230-254, 384-511 are reserved for Dynamic assignment.
Static Assignment:
register_chrdev_region is the function to allocate device number statically.
int register_chrdev_region(dev_t first, unsigned int count,
char *name);
- first -> First device number of the range you would like to allocate
- count -> Maximum number of devices to support.
- name -> Name of the device. It will appear in /proc/devices and sysfs
Dynamic Assignment:
alloc_chrdev_region is the kernel function to allocate device numbers dynamically
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
- dev -> On success, contains the first device number
- firstminor -> Requested first minor number to use, usually 0
- count -> Maximum number of devices to support
- name -> Name of the device. It will appear in /proc/devices and sysfs
Regardless of static or dynamic, use this function to free the device numbers in your cleanup function.
- first -> The first device number allocated
- count -> Number of device numbers requested while allocating.
MODULE_LICENSE(«GPL»);
dev_t device_number;
bool dynamic = false;
#define MAX_DEVICES 5
static int test_static_init(void)
int retval;
printk(KERN_INFO»%s: In init\n», __func__);
if (dynamic) retval = alloc_chrdev_region(&device_number, 0, 5, «embedded»);
>
else device_number = MKDEV(240, 0);
retval = register_chrdev_region(device_number, 5, «embedded»);
>
if (!retval)
printk(KERN_INFO»%s: Major Number:%d\t Minor Number:%d\n»,
__func__, MAJOR(device_number), MINOR(device_number));
else
printk(KERN_ERR»%s: Failed in allocating device number »
«Error:%d\n», __func__, retval);
return 0;
>
static void test_static_exit(void)
unregister_chrdev_region(device_number, 5);
printk(KERN_INFO»%s: In exit\n», __func__);
>
What will happen when we try to statically assign the same device number which is already in use?
We get -EBUSY error