驱动的调用

目录

设备文件

​编辑

测试驱动:读写回环测试

步骤:

源文件:详细的讲解看注释即可

应用和驱动之间的数据交换


在应用层调用open来打开这个系统文件,在向这个设备文件使用read、write等即可调用驱动的函数去工作。

设备文件

设备文件连接着应用层与驱动。对于应用:通过打开设备文件与驱动挂钩;对于驱动,驱动把自己打包装成设备文件拿给应用层去操作。

我们的每一个驱动其实就是一个个file_operations结构体(最多255个)。比如,我这个设备有20个驱动,也就有20个file_operations结构体,这20个结构体组成一个数组

使用ls/dev/来查看目前的设备文件,显示的每一个文件都代表1个驱动(硬件设备),如fb0就是显示屏lcd

[主设备号可以理解为一类设备(比如:四个led其实是一个主设备号,由四个次设备号区分)]

使用mknod创建设备文件:

mknod /dev/xxx c 主设备号 次设备号

设备号 = 主设备号 + 次设备号,使用ls -l去查看设备文件,就可以得到这个设备文件对应的主次设备号。

测试驱动:读写回环测试

应用这边调用open函数,则驱动那边就会运行   .open   函数。应用调用write,则驱动运行 .write函数,其余同理。

步骤:

/*如果驱动改了,需要重新卸载安装驱动*/
rmmod xxxtest.ko   //卸载模块
lsmod               //确认一下还在不在
cat /proc/devices   //再次确认

ls /dev/xxx        //xxx是自己命名的设备文件,我们看看有没有,有的话就删掉
rm /dev/xxx        //删掉后也还可以通过重启开发板来保证删掉

/*装驱动*/
insmod xxxtest.ko
lsmod                //看一下是否成功
cat /proc/devices   //再次确认


/*建立设备文件;主设备号250,次设备号0*/
mknod /dev/test c 250 0
ls /dev/test -l            //看一下没问题
./app                    //执行应用程序


源文件:详细的讲解看注释即可

app.c(应用层代码):

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


#define FILE	"/dev/test"			// 刚才mknod创建的设备文件名

char buf[100];

int main(void)
{
	int fd = -1;
	
	fd = open(FILE, O_RDWR);
	if (fd < 0)
	{
		printf("open %s error.\n", FILE);
		return -1;
	}
	printf("open %s success..\n", FILE);
	
	// 读写文件
	write(fd, "helloworld2222", 14);
	read(fd, buf, 100);
	printf("读出来的内容是:%s.\n", buf);
	
	// 关闭文件
	close(fd);
	
	return 0;
}

module_test.c(驱动代码):

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>



#define MYMAJOR		200
#define MYNAME		"testchar"

int mymajor;

char kbuf[100];			// 内核空间的buf


static int test_chrdev_open(struct inode *inode, struct file *file)
{
	// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
	// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
	printk(KERN_INFO "test_chrdev_open\n");
	
	return 0;
}

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

ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_read\n");
	
	ret = copy_to_user(ubuf, kbuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_to_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_to_user success..\n");
	
	
	return 0;
}

// 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,
	size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_write\n");

	// 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中
	//memcpy(kbuf, ubuf);		// 不能用memcpy,因为2个不在一个地址空间中
	ret = copy_from_user(kbuf, ubuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_from_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_from_user success..\n");

	// 真正的驱动中,数据从应用层复制到驱动中后,我们就要根据这个数据
	// 去写硬件完成硬件的操作。所以这下面就应该是操作硬件的代码
	
	
	return 0;
}


// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,				// 惯例,直接写即可
	
	.open		= test_chrdev_open,			// 将来应用open打开这个设备时实际调用的
	.release	= test_chrdev_release,		// 就是这个.open对应的函数
	.write 		= test_chrdev_write,
	.read		= test_chrdev_read,
};


// 模块安装函数
static int __init chrdev_init(void)
{	
	printk(KERN_INFO "chrdev_init helloworld init\n");

	// 在module_init宏调用的函数中去注册字符设备驱动
	// major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号
	// 内核如果成功分配就会返回分配的主设备好;如果分配失败会返回负数
	mymajor = register_chrdev(0, MYNAME, &test_fops);
	if (mymajor < 0)
	{
		printk(KERN_ERR "register_chrdev fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);

	return 0;
}

// 模块下载函数
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");
	
	// 在module_exit宏调用的函数中去注销字符设备驱动
	unregister_chrdev(mymajor, MYNAME);
	
}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

应用和驱动之间的数据交换

(1)copy_from_user,将数据从用户空间复制到内核空间。

和常规有点不同。返回值如果成功复制则返回0,如果 不成功复制则返回尚未成功复制剩下的字节数。
(2)copy_to_user,将数据从内核空间复制到用户空间。

比如:我们在应用层调用write(fd, "helloworld", 10);这个helloworld写进驱动层的char __user *buf,在这个函数的实现里,会去调用copy_from_user将数据从用户空间复制到内核空间。

注意:复制是和map的映射相对应去区分的。mmap是同一个物理内存地址直接去映射的,而上边那两个是从一个地址复制到另外一个地址的。

另外,因为copy_from_user与copy_to_user是复制,不适合大量数据的传输(速度太慢),如果遇到大量数据的传输,用map(以后讲)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
xdma pcie驱动是一种用于处理PCI Express总线设备的驱动程序。PCI Express是一种用于在计算机系统中进行高速数据传输的总线标准。xdma pcie驱动可用于控制和管理与PCI Express总线设备通信的过程。 xdma pcie驱动调用过程一般包括以下步骤: 1. 程序初始化:首先,需要在程序中进行xdma pcie驱动的初始化设置。这通常包括加载驱动程序、建立与设备的通信连接等操作。 2. 配置设备:在驱动初始化后,需要进行设备的配置。设置设备的寄存器值、中断控制等参数,以确保设备在通信过程中的正常工作。 3. 发送数据:当设备配置完成后,可以通过调用驱动程序提供的接口来发送数据。这通常需要指定设备的物理地址和要发送的数据内容。 4. 接收数据:类似地,通过调用驱动程序提供的接口,可以接收来自设备的数据。同样需要指定设备的物理地址和要接收的数据长度。 5. 数据处理:在接收到数据后,可以对其进行处理。例如,可以进行数据的解析、计算、存储等操作,以满足实际应用的需求。 6. 终止:在完成数据处理后,需要终止与设备的通信,并释放相关资源。这通常包括关闭驱动程序、断开与设备的连接等操作。 总之,xdma pcie驱动调用过程涉及到驱动程序的初始化、设备的配置、数据的发送和接收、数据的处理以及终止等步骤。通过调用驱动程序提供的接口,可以实现与PCI Express设备的高速数据传输和交互。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值