概述
草稿:
上层调用open 导致底层的pin4_open被调用
内核的打印函数用printk
=========================
首先假设树莓派上已经有了一个设备驱动(dev下的pin4)(dev:根目录 )
访问一个上层设备跟访问普通文件没区别
~由认知课知道上层调用open,它会调用sys_call,再调用sys_open,再去内核的驱动链表里找到相关节点,调用节点里的open。
~对引脚的操作放到open和write里完成
驱动框架(最精简的,没有任何功能):
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class device声明
#include <linux/uaccess.h> //copy_from_user的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //iorsemap iorsemap的头文件
static struct class *pin4_class;
static struct device *pin4_dev;
static dev_t devno; //设备号
static int major=231; //主设备号
static int minor = 0; //次设备号
static char *module_name = "pin4"; //模块名
// led_open函数
static int pin4_open(struct inode *inode, struct file *file)
{
printk("pin4_openn");
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
{
printk("pin4_writen");
return 0;
}
static struct file_operations leds_fops ={
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
};
int __init pin4_drv_init(void) //真实驱动入口
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name, &leds_fops); //注册驱动,告诉内核 把这个驱动加到内核的链表中
pin4_class = clas_create( THIS_MODULE,"myfirstdemo"); //让代码在dev自动生成设备
pin4_class_dev = device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件
return 0;
}
void __exit pin4_drv_exit(void)
{
device_destory(pin4_class,devno);
class_destory(pin4_class);
unregister_chrdev(major,module_name); //卸载驱动
}
module_init(pin4_drv_init); // 入口,内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
代码解释和注意事项
1.open和write要被注册到驱动里的结构体里
2.static的作用:限定这个结构体只在这个文件中生效 防止和其他文件中的该结构体撞了(内核中有好多代码,防止命名冲突)
3.
4.上层程序,通过设备名打开某个设备
5.这种写法只适用于linux
6.file_operations里有很多东西
7.每个函数都有入口,对于内核驱动程序框架的入口就是:
但真实的入口(初始化是在这里):
8.
把pin4_fops这个结构体加到内核的驱动链表里,加到哪里?根据设备号的位置来找,还带着设备名字module_name。
9.用代码自动生成设备,不用手动mknod的方式了
手动生成方式:
10.生成时是先生成类然后生成设备,销毁时反着来。
销毁时根据设备号和设备名把那个节点删除
11.驱动加载时,进入到module_init,它 调用pin4_drv_init ,创建设备号,把open/read/write 结构体放到内核的驱动链表里
编译:
进入到driver驱动目录下,io口是字符设备目录,所以进入char目录下
把写好的代码拷贝到乌班图下
如何配置能让整个工程编译时编译到他?
修改Makefile:
vi Makefile
修改后进行模块编译
交叉编译测试程序 pin4test.c
arm-linux-gnueabihf-gcc pin4test.c -o pin4test
驱动测试:
1.远程拷贝(驱动程序:pin4driver2.ko) 和 (测试程序:pin4test) 到树莓派
scp drivers/char/pin4driver2.ko pi@192.168.113.247:/home/pi
2. 加载内核驱动:
sudo insmod xxx.ko 【必须先装载驱动】
3. 查看 dev 下没有生成 pin4 这个设备驱动 :ls /dev/pin4
发现已经生成了,哈哈。
ls /dev/pin4 -l 查看信息,发现主设备号次设备号和我写的驱动内容中的设备号也是对应的
4.这时./运行发现没有任何结果,因为内核和上层是不一样的想看到内核的打印信息就需要 dmesg (查看内核printk 打印的信息)
5. ./pin4test
6. 添加访问权限, sudo chmod 666 /dev/pin4 (666:让所有人都有访问的权限)
7.
8. 然后打开dmesg 发现pin4open和pin4write都被调用了
驱动总结:
上层调用底层整个过程:
open时,通过文件的名字去调用设备驱动,文件名字是有主设备号和次设备号。open会触发系统调用,系统调用会穿透虚拟文件系统VFS,虚拟文件系统会通过设备号在内核的驱动链表里找到对应的驱动,去调用驱动程序里的open函数。(write同理)
最后
以上就是繁荣白昼为你收集整理的驱动的编写的全部内容,希望文章能够帮你解决驱动的编写所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复