EtherCAT主站IGH-- 1 -- IGH之cdev.h/c文件解析
- [0 预览](#0 预览)
- [一 该文件功能](#一 该文件功能)
-
- [`cdev.c` 文件功能函数预览](#
cdev.c
文件功能函数预览)
- [`cdev.c` 文件功能函数预览](#
- [二 函数功能介绍](#二 函数功能介绍)
-
- [`cdev.c` 中主要函数的作用](#
cdev.c
中主要函数的作用) -
-
- [1. `ec_cdev_init`](#1.
ec_cdev_init
) - [2. `ec_cdev_clear`](#2.
ec_cdev_clear
) - [3. `eccdev_open`](#3.
eccdev_open
) - [4. `eccdev_release`](#4.
eccdev_release
) - [5. `eccdev_ioctl`](#5.
eccdev_ioctl
) - [6. `eccdev_mmap`](#6.
eccdev_mmap
) - [7. `eccdev_vma_fault`](#7.
eccdev_vma_fault
) - [8. `eccdev_vma_nopage`](#8.
eccdev_vma_nopage
)
- [1. `ec_cdev_init`](#1.
-
- [`cdev.c` 中主要函数的作用](#
- [三 h文件翻译](#三 h文件翻译)
- [四 c文件翻译](#四 c文件翻译)
- 该文档修改记录:
- 总结
0 预览
一 该文件功能
该文件定义了 EtherCAT 主站的字符设备驱动程序。EtherCAT 是一种实时以太网通信标准,广泛用于工业自动化控制系统。字符设备驱动程序允许用户空间程序与内核空间的 EtherCAT 主站进行交互,通过字符设备文件对 EtherCAT 主站进行操作。
该文件实现了 EtherCAT 主控设备的字符设备驱动程序。此驱动程序允许用户空间程序与 EtherCAT 主设备进行交互,主要功能包括打开设备、释放设备、处理 IO 控制命令、内存映射以及处理虚拟内存区域的缺页错误。
cdev.c
文件功能函数预览
函数 | 功能和用途 | 使用场景 |
---|---|---|
ec_cdev_init |
初始化 EtherCAT 主控设备的字符设备。 | 在应用程序启动时进行 EtherCAT 主控设备的初始化。 |
ec_cdev_clear |
清理 EtherCAT 主控设备的字符设备。 | 在应用程序关闭或设备不再使用时进行清理操作。 |
eccdev_open |
打开字符设备文件,初始化私有数据结构。 | 在用户空间程序需要访问设备时调用。 |
eccdev_release |
释放字符设备文件,清理私有数据结构。 | 在用户空间程序关闭设备文件时调用。 |
eccdev_ioctl |
处理来自用户空间的 IO 控制命令。 | 当用户空间程序发出 ioctl() 命令时调用。 |
eccdev_mmap |
处理设备文件的内存映射请求。 | 当用户空间程序请求内存映射时调用。 |
eccdev_vma_fault |
处理虚拟内存区域的缺页错误。 | 当内核处理缺页错误时调用(内核版本 >= 2.6.23)。 |
eccdev_vma_nopage |
处理虚拟内存区域的首次访问缺页错误。 | 当内核处理首次访问缺页错误时调用(内核版本 < 2.6.23)。 |
二 函数功能介绍
cdev.c
中主要函数的作用
1. ec_cdev_init
c
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
{
int ret;
cdev->master = master;
cdev_init(&cdev->cdev, &eccdev_fops);
cdev->cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev->cdev, MKDEV(MAJOR(dev_num), master->index), 1);
if (ret) {
EC_MASTER_ERR(master, "Failed to add character device!\n");
}
return ret;
}
- 功能和用途:初始化 EtherCAT 主控设备的字符设备。
- 使用场景:在应用程序启动时进行 EtherCAT 主控设备的初始化。
2. ec_cdev_clear
c
void ec_cdev_clear(ec_cdev_t *cdev)
{
cdev_del(&cdev->cdev);
}
- 功能和用途:清理 EtherCAT 主控设备的字符设备。
- 使用场景:在应用程序关闭或设备不再使用时进行清理操作。
3. eccdev_open
c
int eccdev_open(struct inode *inode, struct file *filp)
{
ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
ec_cdev_priv_t *priv;
priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
if (!priv) {
EC_MASTER_ERR(cdev->master, "Failed to allocate memory for private data structure.\n");
return -ENOMEM;
}
priv->cdev = cdev;
priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
priv->ctx.requested = 0;
priv->ctx.process_data = NULL;
priv->ctx.process_data_size = 0;
filp->private_data = priv;
#if DEBUG
EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
#endif
return 0;
}
- 功能和用途:打开字符设备文件,初始化私有数据结构。
- 使用场景:在用户空间程序需要访问设备时调用。
4. eccdev_release
c
int eccdev_release(struct inode *inode, struct file *filp)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
ec_master_t *master = priv->cdev->master;
if (priv->ctx.requested) {
ecrt_release_master(master);
}
if (priv->ctx.process_data) {
vfree(priv->ctx.process_data);
}
#if DEBUG
EC_MASTER_DBG(master, 0, "File closed.\n");
#endif
kfree(priv);
return 0;
}
- 功能和用途:释放字符设备文件,清理私有数据结构。
- 使用场景:在用户空间程序关闭设备文件时调用。
5. eccdev_ioctl
c
long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
#if DEBUG
EC_MASTER_DBG(priv->cdev->master, 0,
"ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
filp, cmd, _IOC_NR(cmd), arg);
#endif
return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
}
- 功能和用途:处理来自用户空间的 IO 控制命令。
- 使用场景:当用户空间程序发出 ioctl() 命令时调用。
6. eccdev_mmap
c
int eccdev_mmap(struct file *filp, struct vm_area_struct *vma)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
vma->vm_ops = &eccdev_vm_ops;
vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
vma->vm_private_data = priv;
return 0;
}
- 功能和用途:处理设备文件的内存映射请求。
- 使用场景:当用户空间程序请求内存映射时调用。
7. eccdev_vma_fault
c
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
static
#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 0)
vm_fault_t
#else
int
#endif
eccdev_vma_fault(
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
struct vm_area_struct *vma,
#endif
struct vm_fault *vmf)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
struct vm_area_struct *vma = vmf->vma;
#endif
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
struct page *page;
if (offset >= priv->ctx.process_data_size) {
return VM_FAULT_SIGBUS;
}
page = vmalloc_to_page(priv->ctx.process_data + offset);
if (!page) {
return VM_FAULT_SIGBUS;
}
get_page(page);
vmf->page = page;
EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault, virtual_address = %p,"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
" offset = %lu, page = %p\n", (void*)vmf->address, offset, page);
#else
" offset = %lu, page = %p\n", vmf->virtual_address, offset, page);
#endif
return 0;
}
#else
- 功能和用途:处理虚拟内存区域的缺页错误。
- 使用场景:当内核处理缺页错误时调用(内核版本 >= 2.6.23)。
8. eccdev_vma_nopage
c
struct page *eccdev_vma_nopage(
struct vm_area_struct *vma,
unsigned long address,
int *type)
{
unsigned long offset;
struct page *page = NOPAGE_SIGBUS;
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
ec_master_t *master = priv->cdev->master;
offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
if (offset >= priv->ctx.process_data_size)
return NOPAGE_SIGBUS;
page = vmalloc_to_page(priv->ctx.process_data + offset);
EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
" offset = %#lx, page = %p\n", address, offset, page);
get_page(page);
if (type)
*type = VM_FAULT_MINOR;
return page;
}
#endif
- 功能和用途:处理虚拟内存区域的首次访问缺页错误。
- **使用场景
**:当内核处理首次访问缺页错误时调用(内核版本 < 2.6.23)。
三 h文件翻译
c
/******************************************************************************\
*
* $Id$
*
* 版权所有 (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
*
* 本文件是 IgH EtherCAT 主站的一部分。
*
* IgH EtherCAT 主站是免费软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证第2版的条款重新分发和/或修改它。
*
* IgH EtherCAT 主站的分发目的是希望它有用,但没有任何保证;甚至没有适销性或特定用途适用性的隐含保证。详情请参阅 GNU 通用公共许可证。
*
* 您应该已经收到了与 IgH EtherCAT 主站一起提供的 GNU 通用公共许可证的副本;如果没有,请写信给自由软件基金会,地址是:51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA。
*
* ---
*
* 上述许可证仅适用于源代码。使用 EtherCAT 技术和品牌仅允许在遵守 Beckhoff Automation GmbH 的工业产权和类似权利的情况下使用。
*****************************************************************************/
/**
\file
EtherCAT 主站字符设备。
*/
/*****************************************************************************/
#ifndef __EC_CDEV_H__
#define __EC_CDEV_H__
#include <linux/fs.h>
#include <linux/cdev.h>
#include "globals.h"
/*****************************************************************************/
/** EtherCAT 主站字符设备。
*/
typedef struct {
ec_master_t *master; /**< 拥有该设备的主站。 */
struct cdev cdev; /**< 字符设备。 */
} ec_cdev_t;
/*****************************************************************************/
int ec_cdev_init(ec_cdev_t *, ec_master_t *, dev_t);
void ec_cdev_clear(ec_cdev_t *);
/*****************************************************************************/
#endif
四 c文件翻译
c
/******************************************************************************\
*
* $Id$
*
* 版权所有 (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
*
* 本文件是 IgH EtherCAT 主站的一部分。
*
* IgH EtherCAT 主站是免费软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证第2版的条款重新分发和/或修改它。
*
* IgH EtherCAT 主站的分发目的是希望它有用,但没有任何保证;甚至没有适销性或特定用途适用性的隐含保证。详情请参阅 GNU 通用公共许可证。
*
* 您应该已经收到了与 IgH EtherCAT 主站一起提供的 GNU 通用公共许可证的副本;如果没有,请写信给自由软件基金会,地址是:51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA。
*
* ---
*
* 上述许可证仅适用于源代码。使用 EtherCAT 技术和品牌仅允许在遵守 Beckhoff Automation GmbH 的工业产权和类似权利的情况下使用。
*****************************************************************************/
/**
\file
EtherCAT 主站字符设备。
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include "cdev.h"
#include "master.h"
#include "slave_config.h"
#include "voe_handler.h"
#include "ethernet.h"
#include "ioctl.h"
/** 设置为 1 以启用设备操作调试。
*/
#define DEBUG 0
/*****************************************************************************/
static int eccdev_open(struct inode *, struct file *);
static int eccdev_release(struct inode *, struct file *);
static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
static int eccdev_mmap(struct file *, struct vm_area_struct *);
/** 这是 .fault 成员在 vm_operations_struct 中可用的内核版本。
*/
#define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23)
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
static
#if LINUX_VERSION_CODE > KERNEL版本 (5, 0, 0)
vm_fault_t
#else
int
#endif
eccdev_vma_fault(
#if LINUX_VERSION_CODE < KERNEL版本 (4, 11, 0)
struct vm_area_struct *, /**< 虚拟内存区域。 */
#endif
struct vm_fault *); /**< 错误数据。 */
#else
static struct page *eccdev_vma_nopage(
struct vm_area_struct *, unsigned long, int *);
#endif
/*****************************************************************************/
/** EtherCAT 字符设备的文件操作回调。
*/
static struct file_operations eccdev_fops = {
.owner = THIS_MODULE,
.open = eccdev_open,
.release = eccdev_release,
.unlocked_ioctl = eccdev_ioctl,
.mmap = eccdev_mmap
};
/** 用 ecdevc_mmap() 检索到的虚拟内存区域的回调。
*/
struct vm_operations_struct eccdev_vm_ops = {
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
.fault = eccdev_vma_fault
#else
.nopage = eccdev_vma_nopage
#endif
};
/*****************************************************************************/
/** 文件句柄的私有数据结构。
*/
typedef struct {
ec_cdev_t *cdev; /**< 字符设备。 */
ec_ioctl_context_t ctx; /**< 上下文。 */
} ec_cdev_priv_t;
/*****************************************************************************/
/** 构造函数。
*
* \return 成功时返回0,否则返回<0
*/
int ec_cdev_init(
ec_cdev_t *cdev, /**< EtherCAT 主站字符设备。 */
ec_master_t *master, /**< 父主站。 */
dev_t dev_num /**< 设备号。 */
)
{
int ret;
cdev->master = master;
cdev_init(&cdev->cdev, &eccdev_fops);
cdev->cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev->cdev,
MKDEV(MAJOR(dev_num), master->index), 1);
if (ret) {
EC_MASTER_ERR(master, "添加字符设备失败!\n");
}
return ret;
}
/*****************************************************************************/
/** 析构函数。
*/
void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML 设备 */)
{
cdev_del(&cdev->cdev);
}
/******************************************************************************
* 文件操作
*****************************************************************************/
/** 打开字符设备时调用。
*/
int eccdev_open(struct inode *inode, struct file *filp)
{
ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
ec_cdev_priv_t *priv;
priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
if (!priv) {
EC_MASTER_ERR(cdev->master,
"分配私有数据结构内存失败。\n");
return -ENOMEM;
}
priv->cdev = cdev;
priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
priv->ctx.requested = 0;
priv->ctx.process_data = NULL;
priv->ctx.process_data_size = 0;
filp->private_data = priv;
#if DEBUG
EC_MASTER_DBG(cdev->master, 0, "文件已打开。\n");
#endif
return 0;
}
/*****************************************************************************/
/** 关闭字符设备时调用。
*/
int eccdev_release(struct inode *inode, struct file *filp)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
ec_master_t *master = priv->cdev->master;
if (priv->ctx.requested) {
ecrt_release_master(master);
}
if (priv->ctx.process_data) {
vfree(priv->ctx.process_data);
}
#if DEBUG
EC_MASTER_DBG(master, 0, "文件已关闭。\n");
#endif
kfree(priv);
return 0;
}
/*****************************************************************************/
/** 发出 ioctl() 命令时调用。
*/
long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
#if DEBUG
EC_MASTER_DBG(priv->cdev->master, 0,
"ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
filp, cmd, _IOC_NR(cmd), arg);
#endif
return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
}
/*****************************************************************************/
#ifndef VM_DONTDUMP
/** VM_RESERVED 在 3.7 中消失。
*/
#define VM_DONTDUMP VM_RESERVED
#endif
/** EtherCAT 字符设备的内存映射回调。
*
* 实际映射将在虚拟内存区域的 eccdev_vma_nopage() 回调中完成。
*
* \return 总是返回零(成功)。
*/
int eccdev_mmap(
struct file *filp,
struct vm_area_struct *vma
)
{
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
vma->vm_ops = &eccdev_vm_ops;
vma->vm_flags |= VM_DONTDUMP; /* 页面不会被换出 */
vma->vm_private_data = priv;
return 0;
}
/*****************************************************************************/
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
/** 虚拟内存区域的页面错误回调。
*
* 在首次访问使用 ecdev_mmap() 检索到的虚拟内存区域时调用。
*
* \return 成功时返回零,否则返回负错误代码。
*/
static
#if LINUX_VERSION_CODE > KERNEL版本 (5, 0, 0)
vm_fault_t
#else
int
#endif
eccdev_vma_fault(
#if LINUX_VERSION_CODE < KERNEL版本 (4, 11, 0)
struct vm_area_struct *vma, /**< 虚拟内存区域。 */
#endif
struct vm_fault *vmf /**< 错误数据。 */
)
{
#if LINUX版本_CODE >= KERNEL版本 (4, 11, 0)
struct vm_area_struct *vma = vmf->vma;
#endif
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
struct page *page;
if (offset >= priv->ctx.process_data_size) {
return VM_FAULT_SIGBUS;
}
page = vmalloc_to_page(priv->ctx.process_data + offset);
if (!page) {
return VM_FAULT_SIGBUS;
}
get_page(page);
vmf->page = page;
EC_MASTER_DBG(priv->cdev->master, 1, "虚拟内存区域错误,虚拟地址 = %p,"
#if (LINUX_VERSION_CODE >= KERNEL版本 (4,10,0))
" 偏移量 = %lu, 页面 = %p\n", (void*)vmf->address, offset, page);
#else
" 偏移量 = %lu, 页面 = %p\n", vmf->virtual_address, offset, page);
#endif
return 0;
}
#else
/** 虚拟内存区域的无页回调。
*
* 在首次访问使用 ecdev_mmap() 检索到的虚拟内存区域时调用。
*/
struct page *eccdev_vma_nopage(
struct vm_area_struct *vma, /**< 内核初始化的虚拟内存区域。 */
unsigned long address, /**< 请求的虚拟地址。 */
int *type /**< 类型输出参数。 */
)
{
unsigned long offset;
struct page *page = NOPAGE_SIGBUS;
ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
ec_master_t *master = priv->cdev->master;
offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
if (offset >= priv->ctx.process_data_size)
return NOPAGE_SIGBUS;
page = vmalloc_to_page(priv->ctx.process_data + offset);
EC_MASTER_DBG(master, 1, "无页错误 vma,地址 = %#lx,"
" 偏移量 = %#lx, 页面 = %p\n", address, offset, page);
get_page(page);
if (type)
*type = VM_FAULT_MINOR;
return page;
}
#endif
/*****************************************************************************/
该文档修改记录:
修改时间 | 修改说明 |
---|---|
2024年6月29日 | EtherCAT主站IGH 该 文件解析 |
总结
以上就是EtherCAT主站IGH文件解析的内容。
有不明白的地方欢迎留言;有建议欢迎留言,我后面编写文档好改进。
创作不容,如果文档对您有帮助,记得给个赞。