树莓派4B Linux的底层驱动编写体验

一、编写前的准备工作

       本博客是在虚拟机中编写驱动程序,然后交叉编译到树莓派。所以需要确认几件事:
       1. 确保树莓派的内核版本和虚拟机中的Linux内核版本保持一致,否则无法安装驱动;
       2. 虚拟机中有交叉编译工具;
       3. 对于树莓派4来说,交叉编译驱动模块的时候,KERNEL=kernel7l,树莓派2、3代KERNEL=kernel7;

二、驱动程序编写

       驱动的编写同样会根据上一篇博客(Linux底层驱动的简单认知)的框架来写:

1.构建 file_operations结构体

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>

static int Test_open(struct inode *inode,struct file *file)
{
        printk("pin4_open\n");
        return 0;
}
static ssize_t Test_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= Test_open,
        .write=Test_write,
}

使用static关键字是为了函数名冲突,谁都不能保证拥有一万多C文件的Linux内核中中会不会有名字冲突,所以 static很有必要。这个程序的功能会在内核环境打印相关信息。

2.编写初始化函数

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>

static struct class  *Test_class;
static struct device *Test_dev;
static dev_t devno;     //device numble

static int major=240; //major device numble 
static int minor=0;     //minor device numble
static char *module_name = "test";//device name

static intTest_open(struct inode *inode,struct file *file)
{
        printk("Test_open\n");
        return 0;
}

static ssize_t Test_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
        printk("Test_write\n");
        return 0;
}


static struct file_operations Test_fops ={
        .owner=THIS_MODULE,
        .open=Test_open,
        .write=Test_write,
}
/*  初始化函数   */
int __init Test_dev_init(void)
{
        int ret;

        devno =MKDEV(major,minor);
        ret=register_chrdev(major,module_name,&Test_fops); //在驱动链表中注册驱动为字符设备
        Test_class=class_create(THIS_MODULE,"Test_class");//创建类
       	Test_dev =device_create(pin4_class,NULL,devno,NULL,module_name);//创建驱动文件
    return 0;
}

3.编写剩下内容

void __exit Test_exit(void)
 {
 
        device_destroy(Test_class,devno);
         class_destroy(Test_class);
        unregister_chrdev(major,module_name);

}
 module_init(Test_dev_init);
 module_exit(Test_exit);
 MODULE_LICENSE("GPL v2");

二、驱动模块的编译

       写好驱动之后需要把驱动文件放到内核文件的 /drivers/char/ 目录下,即字符设备文件夹:

cp Test.c  /home/.../drivers/char/

然后在内核文件的 /drivers/char/ 修改 Makefile 文件,添加:

obj-m                           += test.o //obj-m 即编译成模块,

在这里插入图片描述
保存退出之后,回到内核文件根目录,树莓派4使用

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7l make -j4 modules

一次性编译驱动模块:
在这里插入图片描述
然后根据提示修改错误,我这里提示36行附近少了分号 “;” ,结构体没加分号,修改后再次编译:
在这里插入图片描述
编译成功之后会在 /drivers/char/ 目录下生成一个以 .ko 为后缀的驱动模块文件,使用 scp 指令把这个 xxx.ko 驱动模块文件发送给树莓派:

scp drivers/char/xxx.ko pi@/*树莓派的IP地址*//home/xxx//*树莓派的某个路径*/

在这里插入图片描述

三、驱动模块的安装

       在树莓派里使用:

sudo insmod xxx.ko   
sudo chmod 666 /dev/xxx

安装驱动之后,给驱动添加权限,这样我们用户才能去使用这个驱动;
在这里插入图片描述
如果安装完后,使用 ls /dev/xxx 指令没有相关的模块的话,请检查驱动程序中创建类函数中的第二个是否都是小写字母:
在这里插入图片描述
注意:这个参数不能和其他模块有冲突;否则也会安装失败。可以

四、测试驱动

       驱动的测试非常简单,我们只要写一个简单的程序去运行就可以:
测试程序代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
        int fd;
        int cmd=1;
        fd=open("/dev/test",O_RDWR);
        if(fd<0){
                perror("reson");
                return -1;
        }
        fd=write(fd,&cmd,sizeof(int));  
        return 0;
}

在这里插入图片描述
因为驱动里面打印的信息在内核环境,所以上层环境看不到任何信息,可使用:

dmesg

指令来查看内核的打印信息,驱动的安装错误提示也可以用该指令来查看驱动的名字最好用小写字母来命名;
师承(某(dou)音)上官可编程

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值