Linux驱动实践:你知道【字符设备驱动程序】的两种写法吗?
扫描二维码
随时随地手机看文章
作 者:道哥,10 年嵌入式开发老兵,专注于:C/C 、嵌入式、Linux。目录关注下方公众号,回复【书籍】,获取 Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章( PDF 格式)。
-
混乱的 API 函数
-
旧的 API 函数
-
新的 API 函数
-
代码实操
-
创建驱动程序源文件
-
创建 Makefile 文件
-
编译、加载驱动模块
-
应用程序
-
打开、读取、写入设备
-
卸载驱动模块
-
小结
-
自动在 /dev 目录下创建设备节点
-
代码下载
- 这篇文章的实际操作部分,使用的是的 API 函数;
- 下一篇文章,再来演示新的 API 函数;
混乱的 API 函数
我在刚开始接触Linux驱动的时候,非常的困扰:注册一个字符设备,怎么有这么多的 API 函数啊?
它们的功能都是向系统注册字符设备,但是只从函数名上看,初学者谁能分得清它们的区别?!
- register_chrdev(...);
- register_chrdev_regin(...);
- cdev_add(...);
旧的 API 函数
在Linux内核代码2.4版本和早期的2.6版本中,注册、卸载字符设备驱动程序的经典方式是:
参数1 major:如果为0 - 由操作系统动态分配一个主设备号给这个设备;如果非0 - 驱动程序向系统申请,使用这个主设备号;如果是动态分配,那么这个函数的返回值就是:操作系统动态分配给这个设备的主设备号。参数2 name:设备名称;
参数3 fops:file_operations 类型的指针变量,用于操作设备;
参数1 major:设备的主设备号,也就是 register_chrdev() 函数的返回值(动态),或者驱动程序指定的设备号(静态方式);参数2 name:设备名称;
新的 API 函数
注册设备:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
上面这2个注册设备的函数,其实对应着旧的 API 函数 register_chrdev:把参数 1 表示的动态分配、静态分配,拆分成2个函数而已。
register_chrdev_region(): 静态注册设备;这两个函数的参数含义是:alloc_chrdev_region(): 动态注册设备;
参数1 from: 注册指定的设备号,这是静态指定的,例如:MKDEV(200, 0) 表示起始主设备号 200, 起始次设备号为 0;alloc_chrdev_region参数:参数2 count: 驱动程序指定连续注册的次设备号的个数,例如:起始次设备号是 0,count 为 10,表示驱动程序将会使用 0 ~ 9 这 10 个次设备号;
参数3 name:设备名称;
参数1 dev: 动态注册就是系统来分配设备号,那么驱动程序就要提供一个指针变量来接收系统分配的结果(设备号);补充一下关于设备号的内容:参数2 baseminor: 驱动程序指定此设备号的起始值;
参数3 count: 驱动程序指定连续注册的次设备号的个数,例如:起始次设备号是 0,count 为 10,表示驱动程序将会使用 0 ~ 9 这 10 个次设备号;
参数4 name:设备名称;
MAJOR(dev_t dev): 从 dev_t 类型中获取主设备号;卸载设备:MINOR(dev_t dev): 从 dev_t 类型中获取次设备号;
MKDEV(int major,int minor): 把主设备号和次设备号转换为 dev_t 类型;
参数1 from: 注销的设备号;参数2 count: 注销的连续次设备号的个数;
代码实操
下面,我们就用旧的API函数,一步一步的描述字符设备驱动程序的:编写、加载和卸载过程。
API函数来编写字符设备驱动程序,下一篇文章再详细讨论。
以下所有操作的工作目录,都是与上一篇文章相同的,即:~/tmp/linux-4.15/drivers/。
创建驱动目录和驱动程序
$ cd linux-4.15/drivers/
$ mkdir my_driver1
$ cd my_driver1
$ touch driver1.c
driver1.c文件的内容如下(不需要手敲,文末有代码下载链接):
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static unsigned int major;
int driver1_open(struct inode *inode, struct file *file)
{
printk("driver1_open is called. \n");
return 0;
}
ssize_t driver1_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
printk("driver1_read is called. \n");
return 0;
}
ssize_t driver1_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
printk("driver1_write is called. \n");
return 0;
}
static const struct file_operations driver1_ops={
.owner = THIS_MODULE,
.open = driver1_open,
.read = driver1_read,
.write = driver1_write,
};
static int __init driver1_init(void)
{
printk("driver1_init is called. \n");
major = register_chrdev(0, "driver1",