字符设备在Linux设备驱动中占有重要地位,实际上字符设备驱动程序是Linux驱动程序中最常见的一类设备驱动程序。而字符设备驱动程序的注册是其实现的关键,其中一个重要的函数就是register_chrdev()函数。
一、register_chrdev函数介绍
register_chrdev()函数是Linux字符设备驱动程序实现中最重要的函数之一,通过这个函数可以在Linux中注册一个字符设备驱动。此函数的原型定义如下:
```c
int register_chrdev(unsigned int major,const char *name,const struct file_operations *fops)
```
其中,各个参数意义如下:
- major:表示设备号的主设备号,分配设备号的方式有很多种,其中比较常见的方式就是由开发者在内核源码中定义主设备号,使用时解除定义使用。Linux内核将按照分配的主设备号,紧接着在次设备号中分配一个唯一的设备号。这个唯一的设备号将作为register_chrdev()函数的返回值。主设备号的范围是0到255,其中0是留给系统使用的。
- name:表示注册驱动时对设备的名称进行标识,这个等同于向/dev下添加了一个名为“name”的设备节点。这里提醒一下,在系统中不能出现同名设备,否则会导致无法区分设备节点和设备名。
- fops:表示想要连接到这个设备上的操作函数集,是一个指向文件操作函数集结构体的指针,开发者在实现驱动时需要定义自己的操作函数集。
二、register_chrdev函数的实现方法
在大多数驱动程序中,都是在模块初始化函数中注册字符设备驱动程序,也就是通过module_init()函数注册初始化函数,将register_chrdev函数嵌入模块初始化函数中。一个简单的字符设备驱动程序可以看作是以下方式:
```c
#include #include #include MODULE_LICENSE("GPL"); static int major = 0; static struct file_operations fops = {...}; static int __init init_dr(void) { major = register_chrdev(0, "dev_name", &fops); if(major < 0){ printk("register_chrdev failed!\n"); return -1; } printk("major:%d\n",major); return 0; } static void __exit exit_dr(void) { unregister_chrdev(major,"dev_name"); printk("unregister_chrdev!\n"); } module_init(init_dr); module_exit(exit_dr); ``` 如上代码,注册字符设备的函数可以在初始化模块的函数中进行,同时要记得设定对应的操作函数组。最后将驱动模块从内核中删除时,需要调用unregister_chrdev()函数。 三、register_chrdev函数案例 接下来,我们模拟一个最简单的字符设备驱动,并使用register_chrdev函数进行注册。 1、在开发板中新建mychrdev.c文件,代码如下: ```c #include #include #include int major;//要注册的主设备号 static int my_open(struct inode *inode, struct file *filp) { printk("This is open\n"); return 0; } static int my_release(struct inode *inode, struct file *filp) { printk("This is release\n"); return 0; } static ssize_t my_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { printk("This is read\n"); return 0; } static ssize_t my_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) { printk("This is write\n"); return size; } struct file_operations fops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, .read = my_read, .write = my_write, }; static int __init my_init(void) { int ret; printk(KERN_INFO "mychrdev init\n"); //1、动态分配主设备号 ret = alloc_chrdev_region(&major, 0, 1, "mychrdev"); if(ret < 0){ printk(KERN_INFO "alloc_chrdev_region error\n"); return ret; } printk(KERN_INFO "major:%d\n",major); //2、注册字符设备驱动 ret = register_chrdev(major, "mychrdev", &fops); if(ret < 0){ printk(KERN_INFO "register_chrdev error\n"); return ret; } printk(KERN_INFO "mychrdev register success!\n"); return 0; } static void __exit my_exit(void) { //1、取消字符设备 unregister_chrdev(major, "mychrdev"); //2、释放主设备 unregister_chrdev_region(major, 1); printk(KERN_INFO "mychrdev removed!\n"); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("farsight"); MODULE_DESCRIPTION("A Simple Chrdev Driver Module"); MODULE_LICENSE("GPL"); ``` 其中,我们做了以下几个操作: - 动态分配主设备号 - 注册字符设备驱动 - 释放主设备号 2、makefile文件编写如下: ```makefile obj-m:=mychrdev.o KDIR:=/home/farsight/raspberry/linux-rpi-4.19.y ccflags-y := -Werror all: make -C $(KDIR) M=$(PWD) modules rm -rf *.o *.mod.c *.order *.symvers *.bak clean: make -C $(KDIR) M=$(PWD) clean ``` 其中,KDIR表示内核源码所在的位置,必须在该内核源码中进行模块编译。 3、编译运行。在终端中输入以下指令: ```sh make sudo insmod mychrdev.ko ``` 此时我们已经注册了一个字符设备“mychrdev”,主设备号为254。接下来查看dmesg的输出结果,可以得到如下结果: ```sh farsight@raspberrypi:~/module$ sudo insmod mychrdev.ko farsight@raspberrypi:~/module$ dmesg [91737.699489] mychrdev init [91737.699674] major:254 [91737.700840] mychrdev register success! ``` 表示注册成功。我们还可以在/dev下查看到相应创建的设备节点/dev/mychrdev。 可以在驱动中加入open、read、write以及release等基本操作函数,实现对字符设备的简单读写操作。例如,我们在终端中输入以下指令: ```sh echo 'hello world'>/dev/mychrdev ``` 可以在终端中查看到my_write函数的输出,“This is write”。 四、register_chrdev函数总结 register_chrdev函数是Linux驱动程序中最常用的函数之一,其作用是将字符设备注册到内核中,从而使应用程序可以通过/dev设备节点来访问字符设备驱动的功能接口。 本篇文章介绍了register_chrdev函数的实现方法、案例分析及其参数的意义,希望可以对大家掌握register_chrdev函数的技巧和实现原理提供一些帮助。 壹涵网络我们是一家专注于网站建设、企业营销、网站关键词排名、AI内容生成、新媒体营销和短视频营销等业务的公司。我们拥有一支优秀的团队,专门致力于为客户提供优质的服务。 我们致力于为客户提供一站式的互联网营销服务,帮助客户在激烈的市场竞争中获得更大的优势和发展机会!
发表评论 取消回复