首页 > 系统相关 >Linux下PCI设备驱动开发详解(八)

Linux下PCI设备驱动开发详解(八)

时间:2024-01-09 14:34:53浏览次数:40  
标签:MOTOR fpga int ioctl PCI fd io Linux 详解

Linux下PCI设备驱动开发详解(八)

RIFFA的Linux驱动文件夹下有6个C源码文件,riffa_driver.c、riffa_driver.h、circ_queue.c、circ_queue.h、riffa.c、riffa.h。

其中riffa.c和riffa.h不属于驱动源码,它们是系统函数调用驱动封装的一层接口,属于用户态应用程序的一部分。

在讲解riffa之前,我们先看一下什么是系统调用。

开源地址:https://github.com/KastnerRG/riffa

一、系统调用

1. 理论基础

Linux下PCI设备驱动开发详解(八)_驱动程序

探究系统调用,以ioctl为例子,通俗来讲,其实就是探究操作系统实现应用层的ioctl对应上特定驱动程序的ioctl的过程。

由于应用程序的ioctl处于用户空间,驱动程序的ioctl处于内核空间,所以这两者之间不属于简单的调用关系;另外,考虑到内核空间操作的安全性,系统调用过程大量的安全性处理,进而使得系统调用看起来十分复杂。

ioctl作用:应用层的ioctl函数传入的cmd和arg参数会直接传入驱动层的ioctl接口,在对应驱动文件会对相应的命令进行操作。对于传递的ioctl命令有一定的规范,具体可以参考:/include/asm/ioctl.h,/Documentation/ioctl-number.txt 这两个文件。

应用层和驱动程序联系如下:

最终ioctl是通过系统调用sys_ioctl软中断陷入到内核,通过系统中断号最终调用到内核态的ioctl函数。

2. 代码实例

构造ioctl命令linux已经给我们提供了宏命令:

_IO    an ioctl with no parameters
_IOW   an ioctl with write parameters (copy_from_user)
_IOR   an ioctl with read parameters  (copy_to_user)
_IOWR  an ioctl with both write and read parameters
相关参数:
/*
    type:    幻数(设备相关的一个字母,避免和内核冲突)
    nr:      命令编号
    datatype:数据类型
*/
_IO(type,nr)           //无参数的命令
_IOR(type,nr,datatype)  //应用从驱动中读数据
_IOW(type,nr,datatype)  //应用从驱动中写数据

下面给出简单的实例用户态应用层代码:

//应用程序
 
#define MOTOR_CMD_BASE'M'  
#define SET_MOTOR_STEP _IOW(MOTOR_CMD_BASE, 1u, int)
#define GET_MOTOR_STEP _IOW(MOTOR_CMD_BASE, 2u, int)
 ...
    int step= 0;
    int value = 0;
    int fd = -1;
 
    fd = open("/dev/motor",O_RDWR);   
    if (fd== -1) {   
        printf("open device error!/n");   
        return -1;   
    }  
   ...  
    printf("Please input the motor step:/n"  
    scanf("%d",&step);    
    if(ioctl(fd, SET_MOTOR_STEP, &step)<0){  
          perror("ioctl error");  
        exit(1);  
    }

驱动程序的代码:

//驱动程序
//假设该驱动程序建立了名为motor的字符设备
 
#define MOTOR_CMD_BASE'M'  
#define SET_MOTOR_STEP _IOW(MOTOR_CMD_BASE, 1u, int)
#define GET_MOTOR_STEP _IOW(MOTOR_CMD_BASE, 2u, int)
 
 
int motor_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)  
{
    int step=0;   
    int value = 0;
    switch (cmd) {  
        case SET_MOTOR_STEP :  
            if(copy_from_user(&step, (int*)arg, sizeof(int)))
                  return fail;
          //处理程序
            break;
        case GET_MOTOR_STEP :
            value = 100;
            if(copy_to_user((int*)arg, &value, sizeof(int)))
                return fail;
            break;
    ...
    }
}

二、RIFFA代码分析

这里我们直接给出源代码:

fpga_t * fpga_open(int id) 
{
	fpga_t * fpga;

	// Allocate space for the fpga_dev
	fpga = (fpga_t *)malloc(sizeof(fpga_t));
	if (fpga == NULL)
		return NULL;
	fpga->id = id;	

	// Open the device file.
	fpga->fd = open("/dev/" DEVICE_NAME, O_RDWR | O_SYNC);
	if (fpga->fd < 0) {
		free(fpga); 
		return NULL;
	}
	
	return fpga;
}

void fpga_close(fpga_t * fpga) 
{
	// Close the device file.
	close(fpga->fd);
	free(fpga);
}

int fpga_send(fpga_t * fpga, int chnl, void * data, int len, int destoff, 
	int last, long long timeout)
{
	fpga_chnl_io io;

	io.id = fpga->id;
	io.chnl = chnl;
	io.len = len;
	io.offset = destoff;
	io.last = last;
	io.timeout = timeout;
	io.data = (char *)data;

	return ioctl(fpga->fd, IOCTL_SEND, &io);
}

int fpga_recv(fpga_t * fpga, int chnl, void * data, int len, long long timeout)
{
	fpga_chnl_io io;

	io.id = fpga->id;
	io.chnl = chnl;
	io.len = len;
	io.timeout = timeout;
	io.data = (char *)data;

	return ioctl(fpga->fd, IOCTL_RECV, &io);
}

void fpga_reset(fpga_t * fpga)
{
	ioctl(fpga->fd, IOCTL_RESET, fpga->id);
}

int fpga_list(fpga_info_list * list) {
	int fd;
	int rc;

	fd = open("/dev/" DEVICE_NAME, O_RDWR | O_SYNC);
	if (fd < 0)
		return fd;
	rc = ioctl(fd, IOCTL_LIST, list);
	close(fd);
	return rc;
}

对应的ioctls

// IOCTLs
#define IOCTL_SEND _IOW(MAJOR_NUM, 1, fpga_chnl_io *)
#define IOCTL_RECV _IOR(MAJOR_NUM, 2, fpga_chnl_io *)
#define IOCTL_LIST _IOR(MAJOR_NUM, 3, fpga_info_list *)
#define IOCTL_RESET _IOW(MAJOR_NUM, 4, int)

riffa.c代码定义了几个用户操作:打开、关闭、读、写、以及复位。其实都是通过ioctl实现的。如果我们后期扩展,想加上自己的函数,调用一个指定编号的ioctl,同时在驱动里面自己写好这个驱动,就可以实现我们想要的功能了。 eg,

#define IOCTL_XXX _IOR(MAJOR_NUM, 5, XXX)

分析代码我们看到其实这些功能实现都是想组装一个IOCTRL结构,之后通过IOCTRL把这些参数传递给下层驱动进行控制。

这些参数应该是在每次读写开始的时候都要写到FPGA里面进行设置的。

另外,riffa.c是编译成静态库文件(.a),供大家调用的,其实使用的时候直接包含进来这个riffa.c的源文件也可以。

当然我们也可以使用动态调用.so文件。

四、参考资料

https://zhuanlan.zhihu.com/p/478247461

标签:MOTOR,fpga,int,ioctl,PCI,fd,io,Linux,详解
From: https://blog.51cto.com/u_16419576/9161221

相关文章

  • 使用Python的requests库在Linux中进行HTTP通信
    在Linux中,使用Python的requests库进行HTTP通信是一种高效且简洁的方法。requests库为发送HTTP请求提供了丰富的功能,包括GET、POST、PUT、DELETE等常见的HTTP方法,以及处理cookies、会话、参数等高级功能。首先,确保您的Linux系统已经安装了Python和requests库。如果尚未安装,可以使用......
  • 使用bash脚本在Linux中发送HTTP GET请求
    在Linux中,使用bash脚本发送HTTPGET请求是一种常见的自动化任务。下面是一个简单的bash脚本示例,用于发送HTTPGET请求并处理响应:bash复制代码#!/bin/bash#定义URLurl="http://example.com" #发送GET请求并获取响应response=$(curl-s"$url")#检查响应状态码if [$?-eq......
  • 在Linux中设置HTTP代理服务器
    在Linux中设置HTTP代理服务器涉及到几个关键步骤。下面是一个简单的指南,帮助你设置一个基本的HTTP代理服务器:1. 选择代理软件:有许多软件可以用来设置HTTP代理服务器,其中一些流行的选择包括Squid、Privoxy和Polipo。在本指南中,我们将使用Squid作为示例。2. 3. 安装Squid:首先,你需......
  • Linux中虚拟网卡是什么?有何作用?
    网卡是一块用来允许计算机在计算机网络上进行通讯的计算机硬件,它使得用户可以通过电缆或无线相互连接。而在Linux操作系统中,网卡驱动中又内含了很多虚拟网卡,那么Linux中虚拟网卡是什么?有何作用?我们来看看具体内容介绍。在Linux中,虚拟网卡是一种虚拟网络设备,它允许将多个......
  • 如何查找Linux死机的原因?
    Linux死机指操作系统在运行过程中突然停止响应的现象,对于使用Linux的用户来说,这无疑是一个令人头疼的问题。那么,我们应该如何快速查找并解决Linux死机的原因呢?一般来说,Linux死机的原因可以归结为硬件故障、软件冲突、系统漏洞等。那么,常见的解决方法都有哪些呢?检......
  • Linux debian安装、配置和使用PuTTY教程
    PuTTY是一个小巧、好用、免费的跨平台的访问Linux服务器的终端工具。PuTTY工具可以使用Telnet、SSH、rlogin、纯TCP以及串行接口连接服务器,使用非常广泛。本文主要介绍Debian系统如何安装PuTTY和如何设置该工具的字体、颜色。从而实现个性化定制自己的PuTTY工具,让PuTTY用起来更舒服......
  • Linux Debian11使用国内源安装 Podman环境
    一、Podman简介Podman是一个开源的容器运行时项目,可在大多数Linux平台上使用。Podman提供与Docker非常相似的功能。正如前面提到的那样,它不需要在你的系统上运行任何守护进程,并且它也可以在没有root权限的情况下运行。Podman可以管理和运行任何符合OCI(OpenContainerI......
  • JavaScript apply、call、bind 函数详解
    apply和callapply和call非常类似,都是用于改变函数中this的指向,只是传入的参数不同,等于间接调用一个函数,也等于将这个函数绑定到一个指定的对象上:letname='window'functiongetName(param1,param2){console.log(this.name)console.log(param1,param2)}letobj=......
  • Linux debian安装、配置和使用PuTTY教程
    PuTTY是一个小巧、好用、免费的跨平台的访问Linux服务器的终端工具。PuTTY工具可以使用Telnet、SSH、rlogin、纯TCP以及串行接口连接服务器,使用非常广泛。本文主要介绍Debian系统如何安装PuTTY和如何设置该工具的字体、颜色。从而实现个性化定制自己的PuTTY工具,让PuTTY用起来更舒......
  • linux-查看文件(夹)数量
    在linux中使用man[命令],可以查看完整的命令帮助文档。ls命令中,我们常用的选项在帮助文档中的解释是:ls-listdirectorycontents.(外出目录内容)-a,--all-l,--usealoglistingformat(使用日志列表格式)-h,--humanreadablewith-l,printsizesinhumanreadable......