驱动的编写

草稿:

上层调用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_open\n");
        return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
{
    printk("pin4_write\n");
    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同理)

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值