一、linux驱动开发-1.2-字符设备实验

       本节以chrdevbase虚拟设备为例,它有两个缓冲区写缓冲和读缓冲,大小都为100字节,应用程序可以向写缓冲区写入数据,从读缓冲区读取数据。

       基本思路为:应用程序调用open函数打开chrdevbase设备,用wirte函数向写缓冲区写数据,调用read函数向读缓冲区读取数据,操作完后,应用程序调用close函数关闭chrdevbase设备。

目录

一、实验程序编写

1.1、创建工程

1.2、实验程序编写

1.2.1、编写驱动程序

1.2.2、编写应用测试程序

二、编译驱动程序和测试APP

2.1、编译驱动程序

2.2、编译测试APP

三、运行测试

3.1、加载驱动模块

3.2、创建设备节点

​3.3、测试

3.4、卸载驱动模块


一、实验程序编写

1.1、创建工程

        这里用vscode建立工程,编写linux驱动需要用到linux源码中的函数,因此需要在vscode中添加linux源码的头文件路径。

        打开vscode,按下“ctrl+shift+p”。输入“C/C++:Edit configuration(JSON)”,在includePath栏添加自己虚拟机上linux源码头文件,如:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/home/zk/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/include", 
                "/home/zk/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/include", 
                "/home/zk/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/include/generated/"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

1.2、实验程序编写

1.2.1、编写驱动程序

        这里要用到两个函数copy_to_user 、copy_from_user实现内核空间数据与用户空间数据的相互访问。

/*
@Decription:用户空间-->内核空间
@to        :目标地址(内核空间)
@from      :源地址(用户空间)
@n         :将要拷贝数据的字节数
@return    成功返回0,失败返回没有拷贝成功的数据字节数
*/
unsigned long copy_from_user(void *to, const void *from, unsigned long n);
/*
@Decription :内核空间-->用户空间
@to         :目标地址(用户空间)
@from       :源地址(内核空间)
@n          :将要拷贝数据的字节数
@return     :成功返回0,失败返回没有拷贝成功的数据字节数
*/
unsigned long copy_to_user(void *to, const void *from, unsigned long n)

         具体的驱动程序如下:

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

static char kernel_data[] = "hello linux";
static char write_buf[100];    //写缓冲区
static char read_buf[100];      //读缓冲区

static int chrdevbase_open(struct inode *inode, struct file *filp)
{
	printk("chrdevbase open ok!\n");
	return 0;
}

static ssize_t chrdevbase_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos)
{
	int ret;

	memcpy(read_buf, kernel_data, strlen(kernel_data));
	ret = copy_to_user(buf, read_buf, count);
	if (ret == 0)
	{
		printk("read data ok\n");
	}
	else
	{	
		printk("read data fail\n");
	}
}

static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t count, loff_t *oppos)
{
	int ret;
	ret = copy_from_user(write_buf, buf, count);
	if (ret == 0)
	{
		printk("write_buf is %s\n", write_buf);
	}
	else
	{
		printk("write data fail\n");
	}

	return 0;
}

static int chrdevbase_release(struct inode *inode, struct file *filp)
{
	printk("chrdevbase release\n");
	return 0;
}

static const struct file_operations chrdevbase_fops = {
	.owner = THIS_MODULE,
	.open  = chrdevbase_open,
	.read  = chrdevbase_read,
	.write = chrdevbase_write,
	.release = chrdevbase_release,
};

static int __init chrdevbase_init(void)
{
	int ret;

	ret = register_chrdev(200, "chrdevbase", &chrdevbase_fops);
	if (ret < 0)
	{
		printk("register chrdevbase fail\n");
	}
	else
	{
		printk("register chrdevbase ok\n");
	}
	
	return ret;
}

static void __exit chrdevbase_exit(void)
{
	unregister_chrdev(200, "chrdevbase");
	printk("unregister chrdevbase ok\n");
}

module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZK");

1.2.2、编写应用测试程序

        编写应用需要了解文件IO相关的函数,链接如下:

https://blog.csdn.net/rrring/article/details/120815878https://blog.csdn.net/rrring/article/details/120815878

二、编译驱动程序和测试APP

2.1、编译驱动程序

        创建Makefile文件

KERNELDIR := /home/zk/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

         输入make即可编译生成.ko驱动模块

2.2、编译测试APP

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

static char userdata[] = "user data!";

int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    char readbuf[100], writebuf[100];

    if (argc != 3)
    {
        printf("Usage:\n");
        printf("\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("open file %s fail\n", filename);
    }

    if (!strcmp(argv[2], "read"))
    {
        read(fd, readbuf, sizeof(readbuf));
        printf("read %s from kernel\n", readbuf);
    }
    else if (!strcmp(argv[2], "write"))
    {
        memcpy(writebuf, userdata, sizeof(writebuf));
        write(fd, writebuf, sizeof(writebuf));
        printf("write %s to kernel\n", writebuf);
    }
    else
    {
        printf("UnKnow Command, please inter for help\n");
    }
}

        利用交叉编译器编译,本虚拟机上安装的是arm-linux-gnueabihf-gcc,输入如下指令:

arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp

三、运行测试

        在此之前,设置好开发板的linux系统通过TFTP从网络启动,并使用NFS挂载网络根文件系统。

3.1、加载驱动模块

        这里使用modprobe命令加载驱动模块,首先要看板子根文件系统中有没有/lib/modules/<kernel version>,该目录是存放驱动模块的,kernel version根据所使用的内核版本设置,比如我现在用的是4.1.15的内核,因此创建一个/lib/modules/4.1.15,然后把chrdevbase.ko和chrdevbaseAPP复制到此目录下。

        之前已经通过NFS将Ubuntu中的rootfs目录挂载为根文件系统,所以复制到rootfs/lib/modules/4.1.15目录中。

sudo cp chrdevbase.ko chrdevbaseApp /home/zk/linux/nfs/rootfs/lib/modules/4.1.15/ -f

        在板子上查看文件:

         用modprobe chrdevbase.ko加载驱动:

         提示没找到modules.dep文件,这里直接输入depmod即可自动生成,然后重新加载,显示加载成功:

         输入lsmod查看当前系统中存在的模块:

         再查看下当前系统中有没有chrdevbase这个设备:

3.2、创建设备节点

       因为应用程序时通过操作设备节点来完成对具体设备的操作,因此驱动加载成功后,需要在/dev目录下创建一个对应的设备节点文件。

mknod /dev/chrdevbase c 200 0

mknod:创建节点命令

/dev/chrdevbase:节点文件

c:字符设备

200:主设备号

0:次设备号

        查看下:

3.3、测试

3.4、卸载驱动模块

        输入如下指令卸载设备: 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值