驱动认知-驱动代码编写与执行

驱动的认知

在这里插入图片描述
应用层进行open,read,write驱动程序的时候,linux系统调用过程
在这里插入图片描述

基于驱动框架代码编写

寻找一个驱动参考(字符设备驱动)

作为初学者我们可以选择别人写好的驱动作为参考

#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  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件

static struct class *pin4_class;  
static struct device *pin4_class_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");  //内核的打印函数和printf类似
      
    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 pin4_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, &pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin4_class=class_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_destroy(pin4_class, devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}

module_init(pin4_drv_init);  //入口,内核加载该驱动(insmod)的时候,这个宏被使用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

驱动框架设计过程

  1. 确定主设备号
  2. 定义自己的 file_operations 结构体
  3. 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
  4. 实现入口函数:安装驱动程序时,就会去调用这个入口函数,执行工作: ①把 file_operations 结构体告诉内核:注册驱动程序register_chrdev.
    ②创建类class_create.
    ③创建设备device_create.
  5. 实现出口函数:卸载驱动程序时,就会去调用这个出口函数,执行工作: ①把 file_operations 结构体从内核注销:unregister_chrdev.
    ②销毁类class_create.
    ③销毁设备结点class_destroy.
  6. 其他完善:GPL协议、入口加载

1.确定主设备号

static dev_t devno;                //设备号
static int major = 231;  		   //主设备号
static int minor = 0;			   //次设备号
static char *module_name = "hello_drv";   //模块名

2.定义自己的 file_operations 结构体

linux的file_operations 结构体
在这里插入图片描述
自己修改后(多添加了个read):

static const struct file_operations hello_drv = {
	.owner		= THIS_MODULE,
	.open		= hello_drv_open,
	.write		= hello_drv_write,
	.read		= hello_drv_read,	
};

3.实现对应的 drv_open/drv_read/drv_write 等函数(函数应该添加在代码中结构体的上方,参考驱动就是把函数写在结构体上面的)

static ssize_t hello_drv_read (struct file * file, char __user * buf, size_t size, loff_t * offset){
	printk("hello_drv_read\n");//printk打印在内核中的信息	
	return 0;
}

static ssize_t hello_drv_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
	printk("hello_drv_write\n");
	return 0;
}

static int hello_drv_open (struct inode * node, struct file * file){
	printk("hello_drv_open\n");
	return 0;
}

4.实现入口函数

int __init hello_drv_init(void)   //真实驱动入口
{
    int ret;
    devno = MKDEV(major, minor);  //创建设备号
    ret   = register_chrdev(major, module_name, &hello_drv);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    hello_drv_class=class_create(THIS_MODULE, "hello class");		//创建类,用代码在dev自动生成设备
    hello_drv_class_dev=device_create(hello_drv_class, NULL, devno, NULL, module_name);  //创建设备文件
    
    return 0;
}

实现出口函数

void __exit hello_drv_exit(void)
{//和入口函数的顺序相反
    device_destroy(hello_drv_class, devno);
    class_destroy(hello_drv_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}

其他完善:GPL协议 入口加载

module_init(hello_drv_init);  //入口,内核加载该驱动的时候,这个宏被使用
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL v2");

修改结果

#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  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件


static struct class *hello_drv_class;
static struct device *hello_drv_class_dev;

/*1.确定主设备号*/
static dev_t devno;                //设备号
static int major = 231;  		   //主设备号
static int minor = 0;			   //次设备号
static char *module_name = "hello_drv";   //模块名
/*3.实现对应的 drv_open/drv_read/drv_write 等函数*/

static ssize_t hello_drv_read (struct file * file, char __user * buf, size_t size, loff_t * offset){
	printk("hello_drv_read\n");//printk打印在内核中的信息	
	return 0;
}

static ssize_t hello_drv_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
	printk("hello_drv_write\n");
	return 0;
}

static int hello_drv_open (struct inode * node, struct file * file){
	printk("hello_drv_open\n");
	return 0;
}


/*2.定义自己的 file_operations 结构体*/
static const struct file_operations hello_drv = {
	.owner		= THIS_MODULE,
	.open		= hello_drv_open,
	.write		= hello_drv_write,
	.read		= hello_drv_read,
	
};
/*4.实现入口函数,安装驱动程序时,就会去调用这个入口函数*/
int __init hello_drv_init(void)   //真实驱动入口
{
    int ret;
    devno = MKDEV(major, minor);  //创建设备号
    ret   = register_chrdev(major, module_name, &hello_drv);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    hello_drv_class=class_create(THIS_MODULE, "hello class");		//创建类,用代码在dev自动生成设备
    hello_drv_class_dev=device_create(hello_drv_class, NULL, devno, NULL, module_name);  //创建设备文件
    
    return 0;
}

/*5.实现出口函数,卸载驱动程序时,就会去调用这个入口函数*/
void __exit hello_drv_exit(void)
{//和入口函数的顺序相反
    device_destroy(hello_drv_class, devno);
    class_destroy(hello_drv_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}
/*其他完善:GPL协议 入口加载*/
module_init(hello_drv_init);  //入口,内核加载该驱动的时候,这个宏被使用
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL v2");

将驱动代码进行编译然后测试

编译阶段

  1. 进入目录:/home/cyh/SYSTEM/linux-rpi-4.14.y/drivers/char把修改好的代码拷贝到此位置
    在这里插入图片描述
    为了方便阅读,这里把mytext.c改一个名字
    在这里插入图片描述

  2. 修改该文件夹下Makefile(为了让整个过程编译的时候编到它)

在这里插入图片描述
m: 表示块设备
在这里插入图片描述
3. 回到linux-rpi-4.14.y/编译驱动文件
在该目录执行ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules进行编译

在这里插入图片描述

测试阶段

  1. 到linux-rpi-4.14.y/drivers/char下把生成的驱动发送给树莓派
    在这里插入图片描述
    在这里插入图片描述
  2. 编写一个测试的程序,然后发给树莓派(也可以在树莓派上编写)
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
        int fd;

        fd=open("/dev/hello_drv",O_RDWR);
        if(fd==-1){
                printf("open failed\n");
                perror("reason");
        }else{

                printf("open success\n");
        }
         write(fd,"1",1);
        return 0;

		
        return 0;
}

在这里插入图片描述

  1. 驱动传到树莓派后,需要加载驱动(一定要sudo)
    sudo insmod hello_drv.ko

驱动卸载:sudo rmmod hello_drv 不需要写ko
查看驱动模块:lsmod

在这里插入图片描述
成功加载到dev中:(如果没有加载,dev中将没有这个驱动)
在这里插入图片描述
4. 运行测试程序 在这里插入图片描述
运行后看不到任何关于驱动代码相关函数的结果,是因为我们看到的终端是上层界面,而驱动代码的函数打印的结果是在内核中打印的。
5. 查看运行结果
使用dmesg查看
在这里插入图片描述

验证步骤总结
a.装载驱动
b.查看驱动装载后是否生成设备
c.运行测试程序调试驱动
d.内核的printk打印在内核层,通过dmesg查看内核打印信息

调用流程

我们上层空间的open去查找dev下的驱动(文件名),文件名背后包含了驱动的主设备号和次设备号,此时用户open触发一个系统调用,系统调用经过vfs(虚拟文件系统),vfs根据文件名背后的设备号去调用sys_open去判断,找到内核中驱动链表的驱动位置,再去调用驱动里面自己的dev_open函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值