Linux驱动学习记录-字符设备驱动
2021/9/12 7:07:10
本文主要是介绍Linux驱动学习记录-字符设备驱动,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
字符设备是Linux驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作,读写数据分先后顺序。比如常见的点灯,按键,IIC,LCD等。
加载/卸载函数
加载/卸载函数module_init(XXX_init), module_exit(XXX_exit),其中XXX_init和XXX_exit是需要自己编写的函数,当执行命令”insmod”和”remod”会加载/卸载驱动时,会调用上面两个函数。模板如下:
static int __init XXX_init(void) { /*入口函数的内容*/ return 0; } static void __exit XXX_exit(void) { /*出口函数的内容*/ } module_init(XXX_init); module_exit(XXX_exit);
其中static表示静态函数,与普通函数的区别:在函数的返回类型前加修饰词static,这个函数就变成静态函数,此函数只能在声明他的文件里使用,不能被其他文件调用。而普通函数的定义和声明默认是extern的。好处是在其他文件中可以定义同名函数而不会有冲突。
__init和__exit修饰符:给内核的暗示,表示此函数只是初始化函数,在初始化完毕后可以丢掉,释放内存;标识这个函数只用于模块卸载,只有在卸载时才会调用,其他任何时刻调用都会出错。
字符设备的注册和注销
字符设备的注册和注销,static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)和static inline void unregister_chrdev(unsigned int major, const char *name)。
- major:主设备,Linux下每一个设备都有一个设备号。Linux设备号范围0~4095,有主号就有次号,后面会说。
- name:设备名称,字符串。
- fops:结构体file_operations类型的指针,指向设备操作函数的集合变量。此结构体存放函数包括read(),write(),open()等等。
static struct file_operations test_fops; static int __int XXX_init(void) { /*注册字符设备驱动*/ int temp = 0; temp = register_chrdev(200, "test", &test_fops) if(temp < 0) { printk("register error!"); } return 0; } static void __exit XXX_exit(void) { /*注销字符设备驱动*/ unregister_chrdev(200, "test"); }
上面是采用静态分配设备号的方法,即注册的时候指定一个数字作为设备号。但是你不知道这个设备号是否已经被使用(通过命令 "cat /proc/devices"可以查看系统已经使用的设备号)。下面介绍动态分配设备号的方法:在注册设备之前先申请一个设备号,系统会自动给你一个没有使用的设备号,避免冲突,卸载驱动的时候要释放这个设备号。推荐使用动态分配,虽然可能会复杂一些。
申请和释放设备号函数int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)和void unregister_chrdev_region(dev_t from, unsigned count)。
- dev:保存申请到的设备号。
- baseminor:次设备号起始地址。一般为0.
- count:要申请的设备号数量。
- name:设备名字。
int major; int minor; dev_t devid; //设备号结构体,"猜测是含有俩个unsigned int变量的结构体" if(major) //如果major有效 { devid = MKDEV(major, 0); //定义设备号,次设备号一般是0 /*MKDEV是将主设备和次设备号转换为dev_t类型的内核函数*/ register_chrdev_region(devid, 1, "test"); //申请设备号 } else //无效 { alloc_chrdev_region(&devid, 0, 1, "test"); //申请设备号 major = MAJOR(devid); //获取主号 minor = MINOR(devid); //获取次号 } unregister_chrdev_region(devid, 1); //注销设备号,简单
里面多了一个函数int register_chrdev_region(dev_t from, unsigned count, const char *name),如果有主号和次号就用这个函数申请。(既然有设备号了,为啥不直接调用函数register_chrdev,进行设备注册,不太懂)
设备具体操作函数
设备具体操作函数,file_operations结构体是具体的设备操作函数。假设这里的test设备控制一段缓冲区(内存),则要有read和write函数进行读写。
/*打开设备需要做的*/ /* inode:传递给驱动的inode filp:设备文件,file结构体结构体有个叫做 private_data 的成员变量,一般在 open 的时候将private_data 指向设备结构体。 */ static int test_open (struct inode *inode, struct file *filp) { /**/ return 0; } /*从设备读取*/ /* filp:要打开的设备 buf:返回给用户空间的数据缓冲区 cnt:要读取的数据长度 offt:相对于文件首地址的偏移 */ static ssizet_t test_read (struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { /**/ return 0; } /*写设备*/ static ssize_t test_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { /**/ return 0; } /*释放设备*/ static int test_release(struct inode *inode, struct file *filp) { /**/ return 0; } /*设备操作函数结构体*/ static sturct file_operations test_fops = { .owner = THIS_MODULE, .open = test_open, .read = test_read, .write = test_write, .release = test_release, };
添加信息
添加信息。在最后要添加LICENSE信息和作者信息。其中LICENSE是必须的,否则编译出错。示例如下:
module_init(XXX_init); module_exit(XXX_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("GaoXu");
这篇关于Linux驱动学习记录-字符设备驱动的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-18git仓库有更新,jenkins 自动触发拉代码怎么配置的?-icode9专业技术文章分享
- 2024-12-18Jenkins webhook 方式怎么配置指定的分支?-icode9专业技术文章分享
- 2024-12-13Linux C++项目实战入门教程
- 2024-12-13Linux C++编程项目实战入门教程
- 2024-12-11Linux部署Scrapy教程:新手入门指南
- 2024-12-11怎么将在本地创建的 Maven 仓库迁移到 Linux 服务器上?-icode9专业技术文章分享
- 2024-12-10Linux常用命令
- 2024-12-06谁看谁服! Linux 创始人对于进程和线程的理解是…
- 2024-12-04操作系统教程:新手入门及初级技巧详解
- 2024-12-04操作系统入门:新手必学指南