关注微信公众号:Linux 内核拾遗
文章来源:https://mp.weixin.qq.com/s/aXehGqP3FBN0B9u-3my10g
1 应用程序与硬件设备交互
应用程序与硬件设备通信和交互的完整流程如下:
首先,应用程序需要打开设备文件,该文件由设备驱动创建;
然后,根据主设备号和次设备号找到对应的设备驱动程序;
随后,应用程序便能通过设备驱动程序与硬件设备进行通信。
2 主/次设备号
Linux 内核的一个重要特性是将设备抽象为文件,使得硬件设备可以像普通文件一样进行打开、关闭、读取和写入操作,统一使用标准系统调用。因此,在 Linux 中,一切皆为文件,无论是向硬盘写入数据、从键盘读取数据,还是备份数据到磁带设备,甚至是从内存读取数据,都通过文件操作完成。设备驱动程序会为每个硬件设备创建特殊文件,我们可以通过这些文件与硬件进行通信。
要创建特殊文件,我们需要了解设备驱动程序中的主设备号和次设备号(Major && Minor Number)。
Linux 内核按照"<major>:<minor>"方式来表示一个字符设备或者块设备。
2.1 主设备号
通常主设备号用于标识与硬件设备关联的驱动程序,一个主设备号也可以被多个设备驱动程序共享。可以查看 /proc/devices
来了解系统中的主设备号分配:
2.2 次设备号
多个设备可能使用相同的主设备号,因此需要为使用相同主设备号的每个设备分配编号,即次设备号。换言之,设备驱动程序使用次设备号来区分单个的物理或逻辑设备。
2.3 设备号分配
主/次设备号支持静态分配和动态分配两种方式。
2.3.1 静态分配
用于需要为设备指定特定设备号的场景。
int register_chrdev_region(dev_t first, unsigned int count, char *name);
复制代码
其中:
first:设备号范围的起始编号;
count:请求分配的连续设备号的个数,如果取值过大,分配的设备号可能溢出到下一个主设备号;
name:关联到分配的设备号的设备名称,它会显示到/proc/devices 和 sysfs 中。
dev_t 类型定义在"linux/types.h"头文件中,长度为 32-bit,用于保存设备号(12-bit 主设备号 + 20bit 次设备号)。
可以通过下面的宏来定义一个 dev_t 变量,或者从 dev_t 变量中获取主设备号或者次设备号:
dev_t dev = MKDEV(int major, int minor);
unsigned major = MAJOR(dev_t dev);
unsigned minor = MINOR(dev_t dev);
复制代码
2.3.2 动态分配
如果不需要分配固定的设备号,可以使用动态分配的方式,它能够避免与其他设备驱动产生设备号冲突:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);
复制代码
其中,调用完成后 dev 指向的是分配的设备号范围的第一个设备号。
2.4 设备号注销
设备号不再使用的时候,应当将其释放(通常是在设备驱动程序的退出函数中):
void unregister_chrdev_region(dev_t first, unsigned int count);
复制代码
3 示例
3.1 静态分配
下面代码静态分配了主设备号 168:
statically_allocate_devno.c
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include <linux/fs.h>
dev_t dev = MKDEV(168, 0);
static int __init my_init(void)
{
register_chrdev_region(dev, 1, "my_dev_1");
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
pr_info("Kernel Module Inserted!\n");
return 0;
}
static void __exit my_exit(void)
{
unregister_chrdev_region(dev, 1);
pr_info("Kernel Module Removed!\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("feifei <feifei@gmail.com>");
MODULE_DESCRIPTION("A simple linux device driver");
复制代码
编译运行:
3.2 动态分配
下面代码演示的是设备号动态分配:
dynamically_allocate_devno.c
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
dev_t dev = 0;
static int __init my_init(void)
{
if((alloc_chrdev_region(&dev, 0, 1, "my_dev_2")) <0){
pr_err("Failed to allocate major number for device 1\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
pr_info("Kernel Module Inserted!\n");
return 0;
}
/*
** Module exit function
*/
static void __exit my_exit(void)
{
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("feifei <feifei@gmail.com>");
MODULE_DESCRIPTION("A simple linux device driver");
复制代码
编译运行:
关注微信公众号:Linux 内核拾遗
文章来源:https://mp.weixin.qq.com/s/aXehGqP3FBN0B9u-3my10g
评论