前言
时隔很久,我又来开始更新驱动模块的学习了,希望在我学习的过程中,能够把自己遇到的问题和干货分享给大家,如果遇到问题,恳请大家评论区及时订正!
一、字符设备驱动框架基本解析
以下将通过一个具体的例子来分析字符设备驱动的基本框架:
功能:实现打开设备、关闭设备的基本驱动操作
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
int major = 11; //主设备号
int minor = 0; //次设备号
int mychar_num = 1; //设备数量
struct cdev mydev; //每一类设备都有一个cdev结构体
int mychar_open(struct inode *pnode, struct file *pfile)
{
printk("mychar open is called!!!\n");
return 0;
}
int mychar_close(struct inode *pnode, struct file *pfile)
{
printk("mychar clsoe is called!!!\n");
return 0;
}
/* 对字符设备的操作函数 */
struct file_operations myops = {
.owner = THIS_MODULE,
.open = mychar_open,
.release = mychar_close,
};
int __init mychar_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major, minor); //组合设备号
ret = register_chrdev_region(devno, mychar_num, "mychar"); //手动申请设备号
if (ret) //返回值为0表示申请成功
{
ret = alloc_chrdev_region(&devno, 0, mychar_num, "mychar"); //申请失败则系统自动分配
if (ret)
{
printk("get devno failed!\n");
return -1;
}
major = MAJOR(devno); //从系统分配的设备号中取出主设备号
minor = MINOR(devno); //从系统分配的设备号中取出次设备号
devno = MKDEV(major, minor); //组合设备号
}
/* 使得设备具有myops中的函数操作方法 */
cdev_init(&mydev, &myops);
mydev.owner = THIS_MODULE; //表示mydev设备属于此内核模块
/* 将设备号为devno的这个设备(mydev)添加到内核(内核hash链表中) */
cdev_add(&mydev, devno, mychar_num);
printk("hello world!\n");
return 0;
}
void __exit mychar_exit(void)
{
dev_t devno = MKDEV(major, minor); //组合设备号
unregister_chrdev_region(devno, mychar_num); //注销设备号
/* 从内核中删除mydev这个设备 */
cdev_del(&mydev);
printk("bye bye!!!\n");
}
MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);
收获:1.在字符设备驱动程序中,每个设备都有主设备号和次设备号,主设备号表示一类设备,
次设备号可以表示这一类设备中具体的某个设备
2.每一类设备都有一个struct cdev类型的结构体来表示这类设备
3.应用层中通过系统调用来访问驱动函数,其中两个重要的结构体分别是
(1)struct inode *pnode:作用为链接设备号,关联字符设备的操作函数集
(2)struct file *pfile :作用为记录文件标识符的位置,传递本次打开文件的私有数据等等
4.struct file_operations myops:表示驱动所支持的函数操作集合
5.编写字符设备驱动的基本顺序:
(1)必须有主设备号,次设备号和设备数量,可以手动申请注册设备号register_chrdev_region或者系统自动分配设备号alloc_chrdev_region,MKDEV可以将主设备号和次设备号组合成设备号
(2)框架中需要涉及到驱动模块入口函数(对字符设备进行cdev_init初始化,添加字符设备cdev_add)* 和驱动模块出口函数(注销设备号unregister_chrdev_region,从内核中删除设备cdev_del)
(3)module_init(mychar_init); module_exit(mychar_exit);指定入口函数和出口函数
二、 应用层
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, const char *argv[])
{
int fd = -1;
if (argc < 2)
{
printf("the arguement is too few!\n");
return -1;
}
fd = open(argv[1], O_RDONLY);
if(fd < 0)
{
printf("fail to open %s\n", argv[1]);
return -1;
}
close(fd);
fd = -1;
return 0;
}
应用层程序,对字符设备进行打开操作,以此可以调用驱动程序中的mychar_read函数;
总结
本文主要分享的分两个部分,一个是驱动程序,另一个是应用程序,主要介绍驱动程序的基本框架。