Linux驱动认知简明导论 ② —— 基于驱动框架编写并测试树莓派引脚文件pin4Driver.c

原创首发于优快云,转载请注明出处,谢谢!https://blog.youkuaiyun.com/weixin_46959681/article/details/117962761



为什么要学习驱动编程?

作为一个合格的开发人员,学习驱动编程有以下理由:

  • 以51、STM32等单片机或者树莓派进简单应用的开发,如实现 LED点灯、超声波的测距继电器的电路控制轻而易举,因为生产厂家都已经把所需要的库文件打包封装实现了,但在未来的工作中不一定有相应的库文件;
  • 工作开发中的硬件平台只要能运行Linux系统,则一定有标准的C库文件存在;
  • Linux内核的源码是通用的,具备芯片手册和电路图,就可在标准的C库文件基础进行开发;
  • LInux内核是每一个程序员都应该追逐的圣杯 。

用户态上层的应用测试代码 pin4Test.c

上层测试代码: pin4Test.c

/* pin4Test.c */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
	int fd;
	
	fd = open("/dev/pin4",O_RDWR);
	if(fd < 0){
		perror("Open failed:");
	}else{
		printf("Open success.\n");
	}
	write(fd,'3',1);
	return 0;	
}

基于驱动框架编写引脚驱动文件 pin4Driver.c

底层驱动代码: pin4Driver.c

/* pin4Driver.c */
#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"; //模块名

//函数pin4_open
static int pin4_open(struct inode *inode,struct file *file)
{
   printk("pin4_open\n");  
   return 0;
}

//函数pin4_write
static ssize_t pin4_write(struct file *file1,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;
    
    //1.创建设备号(主设备、次设备号)。
    devno = MKDEV(major,minor);  
    printk("drive init succeed\n");
    //2.注册驱动:通知内核,将该驱动加入到驱动链表。
    ret = register_chrdev(major,module_name,&pin4_fops);  
	//3.加载驱动代码文件时自动操作路径“/root/dev”下生成驱动设备。
    pin4_class = class_create(THIS_MODULE,"myfirstdriver");
    //4.创建设备文件
    pin4_class_dev = device_create(pin4_class,NULL,devno,NULL,module_name);  
    return 0;
}

void __exit pin4_drv_exit(void)
{	
	//1.销毁设备
    device_destroy(pin4_class,devno);
    //2.销毁类文件。
    class_destroy(pin4_class);
    //3.卸载驱动。
    unregister_chrdev(major, module_name);
}

//整体驱动框架文件的初始化入口。
module_init(pin4_drv_init);
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

注意:

  • 内核代码的编写与上层代码的编写有不同之处。内核里的文件代码是一个异常巨大的结构,若你希望在内核里的驱动链表里添加驱动文件,那就必须遵循链表的操作规则,即 驱动框架
  • 上层与底层的代码实际上是一一对应的,上层opne("/dev/pin4",O_RDWR)底层一定有pin4_open,操作函数read、write同理。
  • 对于其他底层的驱动文件也是类似的,open、read、write都是被放在大的结构体之后才加入到驱动链表中。
  • 不同于系统的上层文件,底层的驱动框架文件中对于变量的声明有大量的static。之所以大量充斥着该函数,是因为整个内核文件数量庞大足足有上万个之多,容易与其他文件的函数命名冲突,函数static限定了变量的作用域仅在该文件中。

交叉编译两个文件并将文件传送至树莓派

  1. 引脚驱动文件编译。 将引脚的驱动文件放在系统的字符驱动文件目录下 home/xxx/linux-rpi-4.14.y/drivers/char,直接 vi 修改引导配置文件 Makefile,在文件中直接插入字段 obj-m += pin4Driver.o(前者代表编译的方式,后者代表编译生成的文件。)
  2. 模块化编译。 在第一步的前提下跳转回到系统源码目录下 home/xxx/linux-rpi-4.14.y/ ,输入指令进行交叉编译:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 modules。等待编译完成后再次打开字符设备驱动文件,编译成功则目录底下会生成新的文件 pin4Driver.ko
    【“j4”表示使用4个核进行编译工作,“modules”表示生成的驱动模块。相比于笔者关于树莓派内核编译的那篇文章中,此处并不需要生成配置文件“dtbs”、内核文件“zImage”,故而只需要一个参数。】
  3. 上层应用测试代码编译。上层的文件处理比较简单,直接对其进行交叉编译处理即可,输入指令:arm-linux-gnueabihf-gcc pin4Test.c -o pin4Test
  4. 文件传送至树莓派。使用 scp 命令将新生成的文件传送至树莓派内,指令:
    scp pin4Test pi@树莓派的IP地址:/home/pi
    scp pin4Driver.ko pi@树莓派的IP地址:/home/pi

在树莓派内加载驱动并进行检测

  1. 驱动加载。输入指令:sudo insmod pin4Driver.ko。查看驱动文件是否加载成功有两个方面:
    输入指令:ls /dev/pin4 -l ,查看设备文件下是否有设备文件;
    输入指令:lsmod ,查看内核是否挂载设备驱动文件 pin4Driver.ko。
  2. 设备权限设置。输入指令:sudo chmod 666 /dev/pin4
    【666 ,表示所有人都可以访问该设备。】
  3. 测试。在树莓派主目录 pi@home 下运行文件 ./pin4Test。注意,此时上层界面是没有任何显示的(你也可以往测试文件中 pin4Test.c 加入少许调试信息),真正的运行现场是在内核空间。输入指令dmesg | grep pin4 ,若显示下图中的调试信息则说明在内核中调用函数 printk() 成功,完成了从最上层到最底层的逻辑调用。
    在这里插入图片描述

注意:步骤相当繁琐,笔者不排除读者的每一步操作都出现小毛病,这需要自己去排查。


思绪

在只有模拟电路和数字电路时,远古工程师们是如何进行这些操作的呢?


参考资料


文章更新时间记录

  • “用户态上层的应用测试代码 pin4Test.c”一节完成。 「2021.6.18 11:51」
  • "基于驱动框架编写引脚驱动文件 pin4Driver.c"一节完成。 「2021.6.18 12:07」
  • “为什么学习驱动编程?”一节完成。 「2021.6.19 17:02」
  • “在树莓派内加载驱动文件并进行检测”一节完成。 「2021.6.23 12:44」

P.S.1 后续对树莓派重新进行刷机。 「2021.6.23 12:46」

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值