主站应用层代码(example/user/main.c)为入口,好好探究一下主站都做了什么,一点点分析。

 master = ecrt_request_master(0);

在文件ecrt.h中有这个函数的原型以及功能描述
大致意思就是为实时操作申请一个主站接口,在应用层是通过ecrt_open_master()和ecrt_master_reserve()这两个函数来完成,接下来深入函数内部进行学习

/** Requests an EtherCAT master for realtime operation.** Before an application can access an EtherCAT master, it has to reserve one* for exclusive use.** In userspace, this is a convenience function for ecrt_open_master() and* ecrt_master_reserve().** This function has to be the first function an application has to call to* use EtherCAT. The function takes the index of the master as its argument.* The first master has index 0, the n-th master has index n - 1. The number* of masters has to be specified when loading the master module.** \return Pointer to the reserved master, otherwise NULL.*/ec_master_t *ecrt_request_master(unsigned int master_index /**< Index of the master to request. */);

返回值

其中master是一个指针变量,在应用程序上文有定义

static ec_master_t *master = NULL;

ec_master结构体完整定义在lib/master.h文件中

struct ec_master {int fd;uint8_t *process_data;size_t process_data_size;ec_domain_t *first_domain;ec_slave_config_t *first_config;
};

fd用来保存在用户层打开设备文件返回的文件句柄
uint8_t是无符号字符型数据,*process_data表示指向进程数据的指针,以字符为单位进行读写操作
size_t与平台有关,主站用32位系统,size_t占4字节,保存进程数据大小
ec_domain_t和ec_slave_config_t同样是一个指针变量,分别用来描述与数据域和从站配置相关的信息
c语言中虽然没有类或对象的含义,但是c语言中可以使用一个结构体来存放一个“对象”的各种属性和信息,高级一些的c程序员会把这个运用的淋漓尽致。

static ec_domain_t *domain = NULL;

结构体定义在lib/domain.h文件中,完整定义如下

struct ec_domain {ec_domain_t *next;unsigned int index;ec_master_t *master;uint8_t *process_data;
};

这个貌似没什么再说了,他们这个结构体嵌套着实很皮
ec_slave_config_t 定义在lib/slave_config.h文件中,如下

struct ec_slave_config {ec_slave_config_t *next;ec_master_t *master;unsigned int index;uint16_t alias;uint16_t position;ec_sdo_request_t *first_sdo_request;ec_reg_request_t *first_reg_request;ec_voe_handler_t *first_voe_handler;
};

uint16_t无符号短整型
对于没有直接用到的结构体,暂时只看一下结构体功能和有什么成员,成员具体属性等用到再看

ecrt_request_master(0);函数

完整定义在lib/common.c文件中,如下

ec_master_t *ecrt_request_master(unsigned int master_index)
{ec_master_t *master = ecrt_open_master(master_index);if (master) {if (ecrt_master_reserve(master) < 0) {ec_master_clear(master);free(master);master = NULL;}}return master;
}

果然,函数是由ecrt_open_master()和ecrt_master_reserve()函数组成,ec_master_clear()和free()函数是在出错情况下
返回值刚才已经说过,参数为主站索引,分析第一句

ecrt_open_master(master_index);

 ec_master_t *master = ecrt_open_master(master_index);

返回值ec_master_t类型
ec_open_master()函数定义同样在lib/common.c文件中,如下
代码太长了,直接在代码段中写分析记录

ec_master_t *ecrt_open_master(unsigned int master_index)
{// 定义一个char型数组,MAX_PATH_LEN,数组长度,宏定义 #define MAX_PATH_LEN 64
// 用来存放设备驱动文件地址char path[MAX_PATH_LEN];ec_master_t *master = NULL;/**ec_ioctl_module_t结构体定义在lib/ioctl.h文件中* typedef struct {* uint32_t ioctl_version_magic;* uint32_t master_count;* } ec_ioctl_module_t;*/ec_ioctl_module_t module_data;int ret;
// 申请分配空间,master是一个指针变量,指向这段内存,这段内存的读取方式是ec_master类型master = malloc(sizeof(ec_master_t));// 判断是否分配成功if (!master) {fprintf(stderr, "Failed to allocate memory.\n");return 0;}
// 初始化成员变量master->process_data = NULL;master->process_data_size = 0;master->first_domain = NULL;master->first_config = NULL;
/** int snprintf(char *str, size_t size, const char *format, ...) 将可变参数(...)按照 format 格式化成字符串,* 并将字符串复制到 str 中,size 为要写入的字符的最大数目,超过 size 会被截断。* 本次以普通内核进行讨论
*/snprintf(path, MAX_PATH_LEN - 1,// 如果在搭建主站的时候使用RTDM接口,就会运行这个
#ifdef USE_RTDM"EtherCAT%u",
#else"/dev/EtherCAT%u",
#endifmaster_index);
// 这时候path内容为/dev/EtherCAT0
#ifdef USE_RTDMmaster->fd = rt_dev_open(path, O_RDWR);
#else
/**  以可读可写的方式打开/dev/EtherCAT0字符设备文件,这个是在打开主站的时候生成的* 返回值是一个文件描述符,也就是一个int型数字,通过这个操作字符设备,如果没有开启主设备*/master->fd = open(path, O_RDWR);
#endif
// 宏定义#define EC_IOCTL_IS_ERROR(X) ((X) == -1)if (EC_IOCTL_IS_ERROR(master->fd)) {/** 把后面的参数作为内容输出到标准错误流(stderr这个默认输出到屏幕上)* streeor()从内部数组中搜索错误号 ,也就是错误原因* #define EC_IOCTL_ERRNO(X) (errno),定义为错误号*/// 如果没有开启主设备,提示的Failed to open master device /dev/EtherCAT0: No such file or directory错误就是在这里发生的fprintf(stderr, "Failed to open %s: %s\n", path,strerror(EC_IOCTL_ERRNO(master->fd)));goto out_clear;}
/**ioctl()函数在lib/iotcl.h文件中有定义,#define ioctl ioctl* ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。* 第一个参数就是要操作文件的文件描述符* 第二个参数是用户程序对设备的控制命令(cmd)* 用户程序所作的只是通过命令码(cmd)告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。* 再下面和上面的一样,判断是否调用成功,反正这个ioctl()函数就很麻烦* 下面的解释是进入到内核里面,这个函数调用会调用内核里面的ec_ioctl()函数* 追溯到master/ioctl.h,有定义* long ec_ioctl(ec_master_t *, ec_ioctl_context_t *, unsigned int, void __user *);* 本次调用是EC_IOCTL_MODULE命令码,可以在master/ioctl.c文件中找到关于ec_ioctl()函数完整定义,可以找到* case EC_IOCTL_MODULE:*         ret = ec_ioctl_module(arg);*         break;* 再往上有ex_ioctl_module()函数定义,太多了,下面再说吧* #define EC_IOCTL_IS_ERROR(X) ((X) == -1)* 正常情况下,操作之后ret=0*/ret = ioctl(master->fd, EC_IOCTL_MODULE, &module_data);if (EC_IOCTL_IS_ERROR(ret)) {fprintf(stderr, "Failed to get module information from %s: %s\n",path, strerror(EC_IOCTL_ERRNO(ret)));goto out_clear;}
/** 判断版本号* master/ioctl.h文件中#define EC_IOCTL_VERSION_MAGIC 27* 这个就是在判断ioctl()有没有调用错误*/if (module_data.ioctl_version_magic != EC_IOCTL_VERSION_MAGIC) {fprintf(stderr, "ioctl() version magic is differing:"" %s: %u, libethercat: %u.\n",path, module_data.ioctl_version_magic,EC_IOCTL_VERSION_MAGIC);goto out_clear;}
// 如果正常,返回master,这个时候master中只有fd被赋值return master;
// 如果打开设备之后出现错误需要释放master
out_clear:ec_master_clear(master);free(master);return 0;
}

继续探究master/ioctl.c关于ec_ioctl_module()函数都做了什么事情,这些是在内核里面了

// 在这次操作中,函数的参数是module_data
static ATTRIBUTES int ec_ioctl_module(void *arg /**< Userspace address to store the results. */)
{// 这个结构体成员和用户层是一样的ec_ioctl_module_t data;
// data.ioctl_version_magic = 27;data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC;
// ec_master_count()函数仅仅是返回了master_countdata.master_count = ec_master_count();
/** copy_to_user是从内核中拷贝内容,参数为目标地址(用户空间),源地址(内核空间),要拷贝的字节数* 所以做了这样的操作,刚才赋值之后的data结构体信息拷贝到arg(module_data)地址中* 其实我并没有看出master_count在哪进行赋值操作* 很明显后面是出错情况下返回错误号,关于错误代码含义可以看博客了解一下[错误代码](https://blog.csdn.net/a8039974/article/details/25830705)*/if (copy_to_user((void __user *) arg, &data, sizeof(data)))return -EFAULT;return 0;
}

在master/module.c中


unsigned int ec_master_count(void)
{return master_count; // Get the number of masters.
}

关于ecrt_open_master()函数说完了,这个函数是在用户空间打开/dev/EtherCAT0设备驱动文件,通过文件描述符fd进行操作。但是后面的ioctl()函数我并不是很懂,我发现的是只是一个局部结构体从内核复制了ioctl()版本号和master_count,但是并没有把这个数据返回,然后只做了一个版本号对比,看了几遍没看出什么蹊跷,暂时就放在这吧。
在lib/master.c中

ec_master_clear(ec_master_t *master)函数


void ec_master_clear(ec_master_t *master)
{if (master->process_data)  {// 删除内存映射对象munmap(master->process_data, master->process_data_size);}
// 清除一些配置,下面再说ec_master_clear_config(master);if (master->fd != -1) {#if USE_RTDMrt_dev_close(master->fd);
#else
// 关闭文件描述符,对应上面的open()close(master->fd);
#endif}
}

在lib/master.c文件里面可以找到ec_master_clear_config()函数定义原型。

void ec_master_clear_config(ec_master_t *master)
{ec_domain_t *d, *next_d;ec_slave_config_t *c, *next_c;d = master->first_domain;while (d) {next_d = d->next;
// 清除域,但是在lib/domain.c文件中找到函数原型,函数没有做任何操作ec_domain_clear(d);d = next_d;}master->first_domain = NULL;c = master->first_config;while (c) {next_c = c->next;// 清除从站的一些配置ec_slave_config_clear(c);c = next_c;}master->first_config = NULL;
}

在lib/slave_config.c文件中有ec_slave_config_clear()函数的完整定义

void ec_slave_config_clear(ec_slave_config_t *sc)
{ec_sdo_request_t *r, *next_r;ec_reg_request_t *e, *next_e;ec_voe_handler_t *v, *next_v;r = sc->first_sdo_request;while (r) {next_r = r->next;ec_sdo_request_clear(r);r = next_r;}e = sc->first_reg_request;while (e) {next_e = e->next;ec_reg_request_clear(e);e = next_e;}v = sc->first_voe_handler;while (v) {next_v = v->next;ec_voe_handler_clear(v);v = next_v;}
}
// lib/sdo_request.c
void ec_sdo_request_clear(ec_sdo_request_t *req)
{if (req->data) {free(req->data);}
}
// lib/reg_request.c
void ec_reg_request_clear(ec_reg_request_t *reg)
{if (reg->data) {free(reg->data);}
}
// lib/handler.c
void ec_voe_handler_clear(ec_voe_handler_t *voe)
{if (voe->data) {free(voe->data);}
}

ecrt_master_reserve(master)

这个函数在lib/master.c中有定义

int ecrt_master_reserve(ec_master_t *master)
{int ret = ioctl(master->fd, EC_IOCTL_REQUEST, NULL);if (EC_IOCTL_IS_ERROR(ret)) {fprintf(stderr, "Failed to reserve master: %s\n",strerror(EC_IOCTL_ERRNO(ret)));return -EC_IOCTL_ERRNO(ret);}return 0;
}

在master/ioctl.h文件中有

#define EC_IOCTL_REQUEST                EC_IO(0x1e)

和上面的一样,通过ioctl()函数调用的函数就进入内核查看具体做了什么
在master/ioctl.c文件中有

case EC_IOCTL_REQUEST:if (!ctx->writable) {ret = -EPERM;break;}ret = ec_ioctl_request(master, arg, ctx);break;

在master/ioctl.c文件中有

static ATTRIBUTES int ec_ioctl_request(ec_master_t *master, /**< EtherCAT master. */void *arg, /**< ioctl() argument. */ec_ioctl_context_t *ctx /**< Private data structure of file handle. */)
{ec_master_t *m;int ret = 0;m = ecrt_request_master_err(master->index);if (IS_ERR(m)) {ret = PTR_ERR(m);} else {ctx->requested = 1;}return ret;
}

我感觉ecrt_request_master()函数除了增强容错性的判断语句和操作语句,假设无任何错误,此函数做的操作是调用了ecrt_open_master(master_index),ecrt_master_reserve(master)两个函数,打开了/dev/EtherCAT%master_index字符驱动文件,通过调用ioctl()函数发送了命令码EC_IOCTL_MODULE(包含无参数命令编号),EC_IOCTL_REQUEST命令码(包含无参数命令编号);为master申请了空间,返回值结构体master,其中只有成员fd被操作赋值。

EtherCAT源代码分析(1)相关推荐

  1. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  2. 《LINUX3.0内核源代码分析》第一章:内存寻址

    https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page ...

  3. Scrapy源代码分析-经常使用的爬虫类-CrawlSpider(三)

    CrawlSpider classscrapy.contrib.spiders.CrawlSpider 爬取一般站点经常使用的spider.其定义了一些规则(rule)来提供跟进link的方便的机制. ...

  4. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  5. Android应用程序进程启动过程的源代码分析(1)

    Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实 ...

  6. AFNetworking 源代码分析

    关于其他 AFNetworking 源代码分析的其他文章: AFNetworking 概述(一) AFNetworking 的核心 AFURLSessionManager(二) 处理请求和响应 AFU ...

  7. Hadoop源代码分析 - MapReduce(转载)

    1. Hadoop源代码分析(MapReduce概论) http://caibinbupt.javaeye.com/blog/336467

  8. RTMPdump(libRTMP) 源代码分析 3: AMF编码

    2019独角兽企业重金招聘Python工程师标准>>> 注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表: RTMPdump 源代码分析 1: ...

  9. Android系统默认Home应用程序(Launcher)的启动过程源代码分析(3)

    Step 13.  ActivityStack.startActivityLocked 这个函数定义在frameworks/base/services/java/com/android/server/ ...

最新文章

  1. 如何通过动态生成Html灵活实现DataGrid分类统计的界面显示功能
  2. mac搭建appium自动化之testNG
  3. c#启动EXE文件(简单的)
  4. Linux学习参考书
  5. Online DDL
  6. mysql分片建表语句_Mysql元数据如何生成Hive建表语句注释脚本详解
  7. caffe调用的一个例子
  8. es6删除数组某一项_精学手撕系列——数组扁平化
  9. @程序员,计算机重启包治百“病”?
  10. “亚马逊与开源彻底决裂”
  11. Shadow DOM系列1-简介
  12. Android学习系列(4)--App自适应draw9patch不失真背景
  13. ISO9000标准简介
  14. 运动会比赛计分系统c语言课程设计,c语言课程设计运动会比赛计分系统(含任务书).doc...
  15. 百度快照劫持的解决方法
  16. 2022-2027(新版)中国生物素酰三肽-1行业发展动态与投资趋势预测报告
  17. k8s存储之Volumes卷类型
  18. 关于如何在网络上提问!
  19. 做网站用java 还是php_做网站用java还是php
  20. 事件驱动架构引领产业技术升级:事件驱动联盟(中国)成立

热门文章

  1. 印度邦政府计划采用区块链技术减轻政府欺诈行为
  2. Qt-5-and-OpenCV-4-Computer-Vision-Projects 学习笔记 - 检测人脸
  3. substance的使用示例(转)
  4. CPI PPI M2
  5. 十年磨一剑,云原生分布式数据库PolarDB-X的核心技术演化
  6. Signal Tap Logic Analyzer的stp文件制作及添加到编译工程
  7. 数学分析课程笔记(张平):函数
  8. 最优分配问题——拍卖算法
  9. SOUKE组合营销软件v9.1官方版
  10. hdu1869 六度分离(Floyd)