EtherCAT主站IGH-- 1 -- IGH之cdev.h/c文件解析
- 0 预览
- 一 该文件功能
- `cdev.c` 文件功能函数预览
- 二 函数功能介绍
- `cdev.c` 中主要函数的作用
- 1. `ec_cdev_init`
- 2. `ec_cdev_clear`
- 3. `eccdev_open`
- 4. `eccdev_release`
- 5. `eccdev_ioctl`
- 6. `eccdev_mmap`
- 7. `eccdev_vma_fault`
- 8. `eccdev_vma_nopage`
- 三 h文件翻译
- 四 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
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
void ec_cdev_clear(ec_cdev_t *cdev)
{cdev_del(&cdev->cdev);
}
- 功能和用途:清理 EtherCAT 主控设备的字符设备。
- 使用场景:在应用程序关闭或设备不再使用时进行清理操作。
3. eccdev_open
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 DEBUGEC_MASTER_DBG(cdev->master, 0, "File opened.\n");
#endifreturn 0;
}
- 功能和用途:打开字符设备文件,初始化私有数据结构。
- 使用场景:在用户空间程序需要访问设备时调用。
4. eccdev_release
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 DEBUGEC_MASTER_DBG(master, 0, "File closed.\n");
#endifkfree(priv);return 0;
}
- 功能和用途:释放字符设备文件,清理私有数据结构。
- 使用场景:在用户空间程序关闭设备文件时调用。
5. eccdev_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 DEBUGEC_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);
#endifreturn ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
}
- 功能和用途:处理来自用户空间的 IO 控制命令。
- 使用场景:当用户空间程序发出 ioctl() 命令时调用。
6. eccdev_mmap
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
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSIONstatic
#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,
#endifstruct vm_fault *vmf)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)struct vm_area_struct *vma = vmf->vma;
#endifunsigned 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);
#endifreturn 0;
}
#else
- 功能和用途:处理虚拟内存区域的缺页错误。
- 使用场景:当内核处理缺页错误时调用(内核版本 >= 2.6.23)。
8. eccdev_vma_nopage
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文件翻译
/******************************************************************************\** $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 的工业产权和类似权利的情况下使用。*****************************************************************************//**\fileEtherCAT 主站字符设备。
*//*****************************************************************************/#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文件翻译
/******************************************************************************\** $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 的工业产权和类似权利的情况下使用。*****************************************************************************//**\fileEtherCAT 主站字符设备。
*//*****************************************************************************/#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 *, /**< 虚拟内存区域。 */
#endifstruct 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 DEBUGEC_MASTER_DBG(cdev->master, 0, "文件已打开。\n");
#endifreturn 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 DEBUGEC_MASTER_DBG(master, 0, "文件已关闭。\n");
#endifkfree(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 DEBUGEC_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);
#endifreturn 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, /**< 虚拟内存区域。 */
#endifstruct vm_fault *vmf /**< 错误数据。 */)
{
#if LINUX版本_CODE >= KERNEL版本 (4, 11, 0)struct vm_area_struct *vma = vmf->vma;
#endifunsigned 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);
#endifreturn 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文件解析的内容。
有不明白的地方欢迎留言;有建议欢迎留言,我后面编写文档好改进。
创作不容,如果文档对您有帮助,记得给个赞。