Compare commits
9
Commits
8d414ae3fb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a56b702d83 | ||
|
|
79633a2b0f | ||
|
|
979da72181 | ||
|
|
beb33ffe50 | ||
|
|
47ea85fb23 | ||
|
|
3bfa240dfa | ||
|
|
85d326d445 | ||
|
|
f09decc127 | ||
|
|
71c4ee6a14 |
@@ -15,6 +15,14 @@ env/
|
||||
*.pcapng
|
||||
sniff_*.txt
|
||||
|
||||
# EEPROM/SII 导出产物(eeprom read --out 生成, 属运行结果不是源码)
|
||||
sii.json
|
||||
*.sii
|
||||
eeprom_*.bin
|
||||
|
||||
# native 构建产物(.obj/.exe/eeprom_image.h/exports.txt 等), 由 build_native.bat 重建
|
||||
build_native/
|
||||
|
||||
# 一次性日志分析脚本(硬编码临时路径, 无留存价值)
|
||||
_analyze_log.py
|
||||
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
# ecat_server.py → ecat_native(C/SOEM) 迁移计划
|
||||
|
||||
## 背景与硬约束
|
||||
- **动机**:原生 C/SOEM 把 PDO 热循环天花板从 Python 的 ~5kHz 抬到 ~7kHz(见 README 原型 A)。
|
||||
但控制面路由(电机命令/CAN/SDO/eeprom/前端)跑在 UI 速率,迁到 C **没有性能收益**,
|
||||
迁移动机是"单一原生进程、去掉 Python 运行时依赖",不是快。
|
||||
- **硬约束:网卡独占**。Npcap 上两个 EtherCAT 主站抢一条总线会互相打架。所以**不能**
|
||||
"native 管 3 条路由、Python 管其余"长期并存 —— 迁移是 **big-bang 切换**:native 做到
|
||||
功能齐平 → 整体接管 8090 → 才删 Python。中途 Python 始终是生产链路,native 用
|
||||
`ECAT_NATIVE_URL` 转发或独立端口(8091)验证,二者不同时持网卡。
|
||||
- **验证纪律**:每个阶段 native 侧真机验证通过(wkc 达标、行为与 Python 一致)才推进下一阶段;
|
||||
全部阶段完成 + 回归对齐后,才删除 ecat_server.py 及其 web 资产。
|
||||
|
||||
## 现状(2026-09-04:迁移完成)
|
||||
- native(阶段 1~4 全部完成):生命周期 `adapters/open/status/close/op.start/op.stop` +
|
||||
电机控制 `motor.start/stop/spin/state/send/rx/period/dbg` + `can/send` + `can/state`
|
||||
+ 配置维护 `sdo`/`eeprom.read|write|verify`/`flash`/`sync_diag`
|
||||
+ 进程生命周期 `shutdown`/`focus`/`--parent-pid`/清场/端口漂移
|
||||
+ 静态托管(motor.html/ecat.html/css/js) + 健康端点 + `native/enctest` 编码对照。
|
||||
前端调用的 24 条路由全覆盖,native 独占 8090 即生产链路。
|
||||
- Python:已彻底删除(2026-09-04 收官,含备份 zip)。`src/` 只剩 CLI 工具、
|
||||
前端资产与诊断脚本;编码回归改打金标准(见阶段 4)。
|
||||
|
||||
## 分阶段(按依赖排序)
|
||||
### 阶段 1:总线生命周期 + 状态机拆分 + 前端托管(✅ 已完成,2026-09-05 真机验证)
|
||||
把 native 启动的"一把梭"拆成与 Python 同构的可控状态机,并加只读生命周期路由 + 静态托管:
|
||||
- 路由:`/api/adapters`、`/api/open`(ec_init+config_init,停 PREOP)、`/api/status`、
|
||||
`/api/close`、`/api/op/start`(ACK 序列进 OP)、`/api/op/stop`、`/api/motor/start`(起循环)、
|
||||
`/api/motor/stop`(停循环)、`/api/motor/spin`。
|
||||
- 静态:GET `/`、`/motor.html`、`/ecat.html`、`/ecat.css`、`/motor.js`(从 src/web/ 读盘)。
|
||||
- 状态位:g_bus_open / g_op / g_loop_running,迁移到与 Python 一致;`--auto` 可选启动即拉起
|
||||
(给 cadence/rate 压测用),默认关、纯路由驱动。控制路由改总线前先 `loop_stop()`
|
||||
(对应 Python 的 `_BUS_EXCLUSIVE_PATHS` 停循环),避免与 PDO 线程并发操作同一 ecx_port。
|
||||
- **验收结果**:open→op/start→motor/start 经前端实际用的同套路由驱动到 `bus_open/op/loop`
|
||||
全 1、wkc 3/3;cadence_diag 端到端 3990Hz(99.8%)、work p50 139µs、路由设周期生效;
|
||||
motor/stop→loop:0、op/stop→退回 INIT 均正确;GET / 返回 ecat.html(26KB)。
|
||||
- **踩坑**:Npcap 设备名 `\Device\NPF_...` 含反斜杠,原样塞进 JSON 报 Invalid \escape →
|
||||
加 `jesc()` 转义所有嵌入变数字符串(adapters desc/name、从站名、desc)。
|
||||
|
||||
### 阶段 2:PDO 循环功能核心(✅ 已完成,2026-09-05 真机验证)
|
||||
把 Python `_pdo_worker` 的功能搬进 native 循环:命令队列泵入(首帧段1 + 批量区段2 共用
|
||||
seq 沿)、槽窗口自适应(cover_bases + 每 64 周期发现窗 + 首圈全扫)、rx 批量回收、
|
||||
完整快照。**验证分两层**:
|
||||
- **编码逐字节对照**(`native_enc_diff.py`,不碰网卡):4 组用例(DLC32 单帧 / 20×DLC8
|
||||
装满+第21帧越界 / dlc=0 只发批量 / 混合 DLC+win 保持)的 384B 输出与 Python
|
||||
`_fill_can_out` **逐字节一致**,seq 同步。当场抓出并修掉 json_tok 不跳键闭合引号
|
||||
的真 bug(该 bug 会让 motor/send 的 id/data 静默全丢 —— 没有这层对照就会带上线)。
|
||||
- **真机闭环**:open→op/start→motor/start(4ms) 后,电机快照与 Python 基准逐字段一致
|
||||
(slot2/node1、slot5/node2、ch2、online、seq 连续递增);探针帧发往**空载通道 ch0**
|
||||
(电机在 ch2,不碰真实电机总线)→ `sent_seq:1`→队列清空→固件 **ack=1 回显**、
|
||||
**tx_count=1**(帧真的发上了 CAN 总线)、tx_result=OK;motor/rx 取回 5 帧 remain 1630
|
||||
(ch2 电机流量经批量区真实回流)。自适应窗口 `[2]` 与 Python 一致(修了一个移植
|
||||
bug:首圈全扫结束分支读陈旧 g_bases,改为当场重算 cover_bases,对齐 Python 1673 行)。
|
||||
- 与 Python 的两处有意差异(阶段 4 对齐):① motor/send/can/send 在循环未启动时报
|
||||
"先 motor/start"(Python 走同步下发 fallback);② can/state 纯读快照不现场 pump。
|
||||
- 本轮未动真实电机命令:任何能驱动电机的帧应由用户在 UI 知情后下发。
|
||||
|
||||
### 阶段 3:配置/维护面(✅ 已完成,2026-09-04 真机验证)
|
||||
`/api/sdo`、`/api/eeprom/read|write|verify`、`/api/flash`、`/api/sync_diag` —— 全部走
|
||||
独占总线(进 `g_bus_lock` 前先 `loop_stop()`,镜像 Python `_BUS_EXCLUSIVE_PATHS`:
|
||||
邮箱/EEPROM 与周期数据并发是历史上邮箱楔死 + SOEM 分段读崩溃的诱因)。
|
||||
- **默认烧录镜像**:`build_native.bat` 在编译前跑 `gen_eeprom.py > build_native/eeprom_image.h`,
|
||||
ecat_native.c `#include` 它。等价于 Python 的 `from ecat_flash_eeprom import load_image`:
|
||||
同一份生成器、同一套自校验断言(断言失败 → 非零退出 → 构建中止),改 SII 只需重跑脚本。
|
||||
生成的数组与 `gen_eeprom.eeprom` **逐字节一致**(离线校验)。
|
||||
- **真机对拍**(Python 停机 → native 独占网卡 → 逐项比对 → 恢复 Python):
|
||||
- SDO 读 `0x2000:1..4`:与 Python 基准同形同值(sync0/sync1=0 free-run、escirq/uptime 递增)。
|
||||
- SDO 写只读对象:Python 回 SdoError abort 文本,native 回 `SdoError wkc=0` —— 判定
|
||||
(ok=false) 与 JSON 形状一致,仅异常文本粒度不同(pysoem 的 abort code 拿不到,已记差异)。
|
||||
- EEPROM 读整片 2048 字:`raw`/`rows` 与 Python **逐字节一致**(4096B 全等)。
|
||||
- `eeprom/verify` 同值数据 → `diffs:0` + 单行 "verify: OK, 全部一致";注入 1 字差异 →
|
||||
`diffs:1` + 差异行 + 汇总行,与 Python 语义(含 `<=10` 只打前 11 行)一致。
|
||||
- `eeprom/write` 同值写回 641 非-FF 字 → `written:641 failed:0`(每字写后立即读回校验)。
|
||||
- `flash` 默认镜像 → 642 字全成功;回读整片与构建期镜像 **零差异**。
|
||||
- **抓到并修掉的 2 个真 bug**:
|
||||
① `logs_add` 不写 NUL 终止符:日志缓冲跨请求复用,`%s` 会把上一次请求的残留一起吐出
|
||||
(实测 verify 返回 "OK, 全部一致" 后面粘着上一轮的 449 行差异,且是非法 JSON)。
|
||||
② flash 失败分支 `"log":[...]"written":n}` 缺逗号 —— 非法 JSON。按 Python 语义改为
|
||||
失败时不带 `written` 键。
|
||||
- **顺带修掉一个跨实现的历史隐患(Python/native/独立脚本三处同改)**:flash 的字节序探针字
|
||||
用的是 `0x0200`,注释说"在 0xFF 填充区深处、远超类别区",**但 SII 类别区早已长到
|
||||
word 0x0281**(STRINGS/General/FMMU/SyncM/RxPDO 196字/TxPDO 324字,END 在 0x0282),
|
||||
0x0200 落在 **TxPDO 类别内部**:探针后"恢复"成 0xFFFF 就把那一字真数据擦成空洞。
|
||||
实测本机芯片 word 0x0200=0xFFFF 而镜像是 0x401B —— 正是历次探针留下的,只因烧录紧随
|
||||
其后重写全片才一直没暴露(单独跑 eeprom 探针不烧录就会留下坏 SII)。改用 END 之后的
|
||||
`0x0400`;复跑 flash 后芯片与镜像零差异、探针字干净回到 0xFFFF。
|
||||
- **本轮未动真实电机命令**(同阶段 2 纪律)。恢复生产:Python 4ms、wkc 3/3、2 台电机
|
||||
在线 seq 递增、win_bases `[2]`;`test_motor_backend.py` 16/16 全绿。
|
||||
|
||||
### 阶段 4:杂项 + 切换(✅ 已完成,2026-09-04 真机验证,Python 已归档)
|
||||
- **can/send 重写为 Python 同款**(此前误并入 motor/send 队列路径 —— 那是真差异,
|
||||
ecat.html CAN 调试页依赖同步 ack 回显):独占停循环 → 未 OP 自动 bring-up(吃
|
||||
`dc_cycle_us`)→ 单帧 DLC≤32 + `clear_stats` → pump 3 拍 → `acked` 判定 + 警告文案
|
||||
→ 桥字段**拍平**进响应(renderCanState 直接读顶层)。`can/state` 同款拍平,
|
||||
循环活着给快照、没跑 pump 1 拍。`motor/send` 补同步 fallback:循环停时批量 pump
|
||||
(3 帧 DLC1 一周期发完,`sent_sync:3 pdo_cycles:1` 实测)。
|
||||
- **do_op_start 全量化**(镜像 `_require_op_ready`):入口无条件预清理(回 INIT +
|
||||
config_init 重枚举,防楔死邮箱上 config_map 原生崩溃)、free-run 清残留 DC 激活
|
||||
(0x0981 跨重连不消失,残留会让 SAFEOP→OP 永久等 PLL)、可选 DC(config_dc +
|
||||
dcsync0)、多行 log 数组、op/start 失败自动重试一次。`motor/start` 补"现读核实 OP"
|
||||
(看门狗踢出后自动重进,实测触发)。
|
||||
- **生命周期契约**:`/api/shutdown`(回响应→优雅退:停循环→INIT→关适配器)、
|
||||
`/api/focus`(探活应答)、`--parent-pid` 看门狗(父进程消失→同款优雅退)、
|
||||
独立启动清场(8090..8095 逐个优雅 shutdown + 按进程名强杀兜底,不碰 python*)、
|
||||
端口漂移 + `PORT=` 行、默认端口 8090。
|
||||
- **切换**:`esp32_server._ecat_autostart()` 改拉 `build_native\ecat_native.exe`
|
||||
(带 `--parent-pid`,cwd=tools/ecat);`ecat_web.vbs/bat` 改起 native。
|
||||
Python 服务代码先归档 `legacy_py/`、切换验证通过后**已删除**(2026-09-04,
|
||||
曾留备份 zip,同日确认后**连备份一并彻底删除**:`ecat_server.py` 行为基准、
|
||||
`test_motor_backend.py` 假从站回归套件、`ecat_app.vbs` 桌面窗口模式均已不存在,
|
||||
本文件的逐字段对拍记录是唯一留存的契约文档)。
|
||||
编码面回归改打金标准:`native_enc_diff.py` 对照 `native_enc_golden.json`
|
||||
(阶段 2 与 Python 逐字节对拍通过后固化,byte0=seq 状态字节除外 + seq 递增校验)。
|
||||
`ecat_tool.py` CLI 仍用 `src/ecat_flash_eeprom.py`(活依赖,已搬回 src)。
|
||||
**ECAT_NATIVE_URL 转发层随切换作废**(native 就是后端,不再需要代理)。
|
||||
- **真机验收**(全走 UI 同款链路):停 Python → native 独立起(清场路径顺带验证:
|
||||
新实例优雅替换旧实例)→ open/op.start/motor.start(4ms)/send/rx/sdo/eeprom/period
|
||||
全通 → 8080 重启新码 → 代理透传 motor/state ✓ → autostart 拉起(`--parent-pid`
|
||||
正确)✓ → 杀 8080 看 native 自我了断(端口释放、从站回 INIT)✓ → 重启 8080
|
||||
自动拉起 → 生产态 4ms/wkc 3/3/2 电机/win_bases[2] 恢复 ✓。
|
||||
- **切换中抓到并修掉 2 个真 bug**:① can/send 路由里 `int cs`(clear_stats)遮蔽了
|
||||
`handle_conn(SOCKET cs)` 的 socket —— 响应发到句柄 0,总线侧成功、HTTP 侧空响应;
|
||||
② 端口漂移循环把 `port++` 写在 for 增量段,绑定成功那轮也自增 —— 日志报出一个
|
||||
没在听的端口(实听 8090、日志 8091)。
|
||||
- **保留的已知差异**(都已在代码注释记录,影响面为零或已验证等价):
|
||||
SDO 失败文本无 pysoem 的 abort code 粒度;~~主站侧不直写过程数据看门狗
|
||||
(从站固件 ≥1.5.45 自己写 1s,Python 那次直写是"双保险")~~ **此假设已破产,
|
||||
见下方"回归 2026-09-05"**;spin 路由无副作用(native 恒自旋)。
|
||||
|
||||
## 回归 2026-09-05:native 无法进 OP(AL 0x001B),已修复
|
||||
- **症状**:`/api/op/start` 卡 SAFEOP→OP,重试一次仍失败,`AL=0x04 code=0x001b`
|
||||
(Sync manager watchdog);`/api/esc/read` 手发 LRW 得 `wkc=1`(期望 3,输出写
|
||||
不匹配、输入读匹配 —— SM2 已被从站禁用)。
|
||||
- **排除面**(全部实测干净):SM/FMMU/DC/看门狗寄存器与 SII、固件 CoE
|
||||
(0x1C12→0x1600 48×8B=384、0x1C13→0x1A00 80×8B=640)逐项一致;PREOP 下 SDO
|
||||
正常(邮箱未楔死);free-run 同样失败(与 DC 无关);**pysoem CLI 同总线同从站
|
||||
成功进 OP + 1237 帧 PDO 正常** —— 从站健康,问题在主站侧。
|
||||
- **根因**:SSC 的 SAFEOP→OP 转换挂起等待 `bEcatFirstOutputsReceived`,而
|
||||
`CheckIfEcatError` 在过程数据看门狗触发位过期时**每 1ms 清一次该标志**。从站
|
||||
看门狗实测是复位默认 **100ms**(divider 0x400=2498→100µs × time 0x420=1000)——
|
||||
固件 1.5.45 的"启动写 1s"(`EscLan9252::configPdWatchdog`)这次上电没生效
|
||||
(需串口看 WARN 行确认是写回失败还是 ESC 事后被复位)。native `do_op_start`
|
||||
第 5 步的喂帧节奏是 **~200ms/帧 > 100ms WD** → 标志永远立不住 → ~9s 超时写
|
||||
0x001B 并 StopOutputHandler。Python 时代不炸是因为"直写 0x420=1s 双保险",
|
||||
迁移时按"固件自己会写 1s"删掉了 —— 这就是回归点。
|
||||
- **修复**(不依赖从站 WD 长度):`do_op_start` 第 5 步改为先以 ~5ms 节奏喂
|
||||
≥1s(250 帧),再请求 OP 并继续 5ms 喂帧轮询(最多 ~3s,每 ~200ms 补写一次
|
||||
AL Control 兜底)。op/start 从"24s 两次失败"变 **1.2s 进 OP**。
|
||||
- **验证**:open→op/start(DC 4ms)→motor/start(4ms) 全链路:wkc 3/3、双电机在线
|
||||
(slot2/N1、slot5/N2)、seq 递增、win_bases=[2],与 Python 生产基准一致。
|
||||
## 回归 2026-09-05(下午):OP 态数据冻结("切到 ECAT 一瞬间有数据然后没了")
|
||||
- **症状**:进 OP 成功、wkc 3/3、网卡 ~33Mbps 流量正常,但从站输入区停更
|
||||
(slot seq / 桥 rx_count / global_seq 全冻结),ack 不回显;UI 2s 后全节点判离线。
|
||||
换固件 1.5.56→1.5.58 无效;用户实测 1.5.56 + 旧 Python 主站完全正常。
|
||||
- **排除面**(全部实测):`native_enc_diff.py` 金标准 4 用例逐字节通过(输出编码与
|
||||
Python 一致);主站输入解析偏移与固件 `ethercat_can_bridge.h` 逐字节对齐
|
||||
(MOTW 32/33/34/35/36/40、槽 40B、XRX 320..、RXB 364..);PREOP 下 SDO 正常。
|
||||
⇒ 主站编码/解析/链路全干净,wkc=3 证明 FMMU 层写匹配正常。
|
||||
- **根因(固件侧,其源码自述)**:`dc_app.c` `EcatDbg_GuardAleventMask` 的
|
||||
1.5.48/1.5.49 注释原文——"停止回 INIT 后重进 OP **概率性**失败:主站 LRW
|
||||
WKC=3/3 但 bEcatFirstOutputsReceived 永不置位……evt=0x0400 在 0x220 挂起 10s
|
||||
无人处理(PDI_Isr 的 SM2 分支从未执行),escirq 2.6/s 证明 EXTI 只在 AL Control
|
||||
事件时跳动"。实测冻结态 escirq 只随邮箱事件 +3~5/s(4ms LRW 应 ~250/s)、
|
||||
can/send 探针 ack=0,与该记录完全吻合。**楔死点 = PDI_Isr SM2 分支被内部旗标
|
||||
(bEscIntEnabled 门控)挡住 → ECAT_Application→EcatPdo_Process→buildInput 停 →
|
||||
输入停更 + 输出不消费**;固件自愈"仅 SAFEOP 触发"(注释原文),OP 态无人管。
|
||||
触发条件正是"停止回 INIT 后重进 OP"——native 主站每次 motor/start 异常恢复、
|
||||
每次传输切换都走这条路,比 Python 时代更频繁,故表现为"上位机更新后坏"。
|
||||
- **主站侧防护(本轮新增,已真机验证)**:
|
||||
- `pdo_worker` 楔死看门狗:主站每周期轮转输出窗口字节 WIN(自适应窗+每 64 周期
|
||||
发现窗),健康从站输入段 2 的 win_base 回显必然跟着变;回显冻结超
|
||||
128 周期+2s(单基窗时发现窗可能与自适应窗同值,最久 128 周期)即楔死。
|
||||
与电机流量无关,零误报(30s 稳态实测无误报)。
|
||||
- `/api/motor/state` 自动恢复:检测后 0.5s,TryEnter g_bus_lock(防与互斥路由
|
||||
互等死锁)→ 完整重拉 INIT→PREOP→SAFEOP→OP,10s 冷却。快照新增 `wr` 字段
|
||||
(自动恢复次数);WEDGE 现场打 console(含 win_echo/ack/gseq/rx/wkc/period,
|
||||
供与固件串口日志对时)。
|
||||
- **遗留(固件侧,根因)**:① 串口抓一次复现,看 [ECAT][AEM] WARN 行
|
||||
(固件自述"一行日志即可定位楔死路径");② 把 1.5.49 自愈扩展到 OP 态
|
||||
(判据:LRW 在跑而 ack/win 回显不动——主站看门狗的判据可直接搬);③ 排查
|
||||
bEscIntEnabled/bDcSyncActive 旗标卡值;④ 怀疑与 DC 模式相关
|
||||
(`ethercat_can_bridge.cpp` seqlock 注释:DC 下 buildInput 跑在 Sync0_Isr——
|
||||
本轮实测冻结多发生在 DC 会话)。另:250µs 亚毫秒周期从站吃不下
|
||||
(槽刷新率掉 99%,见上节),修复前别用。
|
||||
|
||||
## 迁移结论
|
||||
Python→native 的价值不在控制面(UI 速率下两者无感),在于 PDO 热循环天花板
|
||||
~5kHz→~7kHz 与尾抖动干净;那 0.5% 的 I226 驱动周期维护停顿与语言无关。
|
||||
`legacy_py/` 与备份 zip 均已删除,Python 后端不留副本:以后改路由语义的参照就是
|
||||
本文件的对拍记录 + 真机验收;编码面回归用 `native_enc_diff.py`(金标准模式,无 Python 依赖)。
|
||||
@@ -2,13 +2,21 @@
|
||||
|
||||
**DCU LAN9252 EtherCAT 主站工具集**(工作目录 `DCU/tools/ecat`)
|
||||
|
||||
在 Windows 上用 Python + pysoem(SOEM)直接从网卡驱动 DCU 的 LAN9252 EtherCAT 从站:
|
||||
**生产后端是原生 C/SOEM 守护进程** `build_native/ecat_native.exe`(`ecat_native.c` +
|
||||
`build_native.bat` 编译,见 `MIGRATION.md` 的迁移记录):同一套 REST 契约、同一批
|
||||
前端页面,PDO 热循环天花板从 Python 的 ~5kHz 提到 ~7kHz。Python 版后端已彻底删除
|
||||
(2026-09-04,含备份);编码面回归改打金标准
|
||||
`native_enc_golden.json`(`native_enc_diff.py`)。命令行工具 `ecat_tool.py`
|
||||
仍是 Python + pysoem(低频诊断用,不与时序敏感的热循环抢网卡)。
|
||||
|
||||
在 Windows 上直接从网卡驱动 DCU 的 LAN9252 EtherCAT 从站:
|
||||
扫描总线、读写 SII EEPROM、CoE SDO 读写、PDO 循环收发,以及一个本地网页/桌面控制台。
|
||||
|
||||
## 环境准备
|
||||
|
||||
- Python 3.12(venv 不入库)
|
||||
- [Npcap](https://npcap.com/):pysoem 收发裸 EtherCAT 帧的前提
|
||||
- Python 3.12(venv 不入库;命令行工具与 legacy 后端用)
|
||||
- [Npcap](https://npcap.com/):收发裸 EtherCAT 帧的前提(native 与 pysoem 共用)
|
||||
- VS2022(Community 即可):编译 `build_native.bat` 产出原生守护进程
|
||||
- 一块直连从站的物理网卡(示例中为 Intel I226)
|
||||
|
||||
```bat
|
||||
@@ -16,35 +24,67 @@ py -3.12 -m venv venv
|
||||
venv\Scripts\python.exe -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 目录内容
|
||||
## 目录结构
|
||||
|
||||
入口在最外层,双击即用;代码全部收进 `src/`。
|
||||
|
||||
```
|
||||
ecat/
|
||||
├─ ecat_web.vbs ← 浏览器标签(启动 native 守护进程, 隐藏控制台)
|
||||
├─ ecat_web.bat ← 控制台模式(看日志)
|
||||
├─ ecat.bat ← 命令行工具
|
||||
├─ run_scan.bat ← bring-up 扫描
|
||||
├─ ecat_native.c ← 原生守护进程(生产后端, 全路由)
|
||||
├─ build_native.bat ← 编译 native(先跑 gen_eeprom.py 生成 SII 默认镜像头)
|
||||
├─ soem-src/ ← vendored SOEM(从 pysoem sdist 解出, 有一处补丁)
|
||||
├─ MIGRATION.md ← Python→native 迁移记录(契约/验收/已知差异)
|
||||
├─ native_enc_diff.py ← 编码回归: native enctest vs 金标准(不碰网卡)
|
||||
├─ native_enc_golden.json ← 金标准(阶段 2 与 Python 逐字节对拍通过后固化)
|
||||
├─ README.md / requirements.txt
|
||||
├─ build_native/ ← ecat_native.exe + eeprom_image.h 等构建产物
|
||||
├─ src/
|
||||
│ ├─ ecat_tool.py 一体化命令行主站
|
||||
│ ├─ ecat_flash_eeprom.py 经网线烧写 SII 镜像(被 ecat_tool flash 复用)
|
||||
│ ├─ web/ 前端资源(URL 路径不变, 仍是 / 与 /motor.html; native 也托管这里)
|
||||
│ └─ diag/ 独立诊断脚本(互不依赖, 直接单跑)
|
||||
└─ venv/ 本地虚拟环境(不入库; CLI 与构建期 gen_eeprom 用)
|
||||
```
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `ecat_tool.py` | 一体化命令行主站:`adapters` / `status` / `flash` / `eeprom` / `sdo` / `send` 等子命令 |
|
||||
| `ecat_server.py` | 网页控制台后端(REST API + 静态文件),默认端口 **8090**(8080 已被 ESP32 tools 占用) |
|
||||
| `ecat.html` / `ecat.css` | 网页控制台前端 |
|
||||
| `ecat_scan.py` | 最小化 bring-up 扫描:从站身份 + SII dump,`--op` 追加 PDO 测试 |
|
||||
| `ecat_esc_regs.py` | 从**网络侧**直读 LAN9252 ESC 寄存器,绕开 GD32/PDI,用于判断 EEPROM 加载是否真的失败;支持 `--reload` |
|
||||
| `ecat_flash_eeprom.py` | 通过网线烧写 `gen_eeprom.py` 生成的 SII 镜像,逐字回读校验(烧完需断电重启让 ESC 重新加载) |
|
||||
| `ecat_sniff.py` | 旁路抓包,hexdump SOEM 自己发出的帧,用于对照裸帧格式 |
|
||||
| `_probe_wedge.py` | 邮箱楔死现场逐环节定位:A 写 SM0 的 WKC → B `0x220` bit0 是否置位 → C SSC 是否清事件并置 SM1 就绪 → D SM1 是否滞留旧响应,用于区分 ESC 硬件楔死与 SSC 软件死循环 |
|
||||
| `_probe_wedge2.py` | 按真实邮箱协议探测:查写邮箱空 → 写满 256B → 验事件/响应 |
|
||||
| `_probe_stuck.py` | 验证「SM1 响应滞留 → 邮箱死锁」假说:复现失败 → 手动读走 SM1 清 bit3 → 再读是否恢复 |
|
||||
| `_probe_scan.py` | 扫描定位响应帧头在 SM1 RAM 中的确切偏移,同步 dump SM0 请求侧 |
|
||||
| `src/ecat_tool.py` | 一体化命令行主站:`adapters` / `status` / `flash` / `eeprom` / `sdo` / `send` 等子命令 |
|
||||
| `ecat_native.c` + `build_native.bat` | **生产后端**:原生 C/SOEM 守护进程(全路由 + 生命周期契约),默认端口 **8090**(8080 已被 ESP32 tools 占用)。迁移记录见 `MIGRATION.md` |
|
||||
| `src/ecat_flash_eeprom.py` | 通过网线烧写 `gen_eeprom.py` 生成的 SII 镜像,逐字回读校验(烧完需断电重启让 ESC 重新加载)。被 `ecat_tool.py flash` 复用 |
|
||||
| `native_enc_diff.py` + `native_enc_golden.json` | 编码回归:native `enctest` 路由输出 vs 金标准(阶段 2 与 Python `_fill_can_out` 逐字节对拍通过后固化)。改 `fill_can_out`/`pack_tx_batch` 后必跑 |
|
||||
| ~~`legacy_py/`~~ | Python 后端已彻底删除(2026-09-04 切换收官,含备份 zip)。行为基准只存在于 `MIGRATION.md` 的逐字段对拍记录里;`ecat_app.vbs` 桌面窗口模式随之退役 |
|
||||
| `src/web/ecat.html` + `ecat.css` | 网页控制台前端 |
|
||||
| `src/web/motor.html` + `motor.js` | 电机控制台:N1–N9 九节点,走 EtherCAT 过程数据(不经 WiFi/HTTP)。节点总览、8 种 Control_Mode、STATUS 实时解码、示波器曲线 |
|
||||
| `src/diag/ecat_scan.py` | 最小化 bring-up 扫描:从站身份 + SII dump,`--op` 追加 PDO 测试 |
|
||||
| `src/diag/ecat_esc_regs.py` | 从**网络侧**直读 LAN9252 ESC 寄存器,绕开 GD32/PDI,用于判断 EEPROM 加载是否真的失败;支持 `--reload` |
|
||||
| `src/diag/ecat_sniff.py` | 旁路抓包,hexdump SOEM 自己发出的帧,用于对照裸帧格式 |
|
||||
|
||||
## 启动方式
|
||||
|
||||
双击根目录下的入口即可(脚本自己定位路径,不必在特定目录下运行):
|
||||
|
||||
| 入口 | 效果 |
|
||||
|------|------|
|
||||
| `ecat_app.vbs` | pywebview 桌面窗口,无控制台无浏览器标签(已针对部分 GPU 驱动白屏加 `--disable-gpu-compositing`) |
|
||||
| `ecat_web.vbs` | 后台起服务并打开浏览器标签,无控制台窗口 |
|
||||
| `ecat_web.vbs` | 后台起 native 守护进程并打开浏览器标签,无控制台窗口 |
|
||||
| `ecat_web.bat` | 控制台模式,可见日志,调试用 |
|
||||
| `ecat.bat` | 命令行工具入口,如 `ecat.bat status`、`ecat.bat flash` |
|
||||
| `run_scan.bat` | 直接跑 bring-up 扫描,无参数则列出 Npcap 适配器 |
|
||||
|
||||
电机控制台在服务起来后从主页右上「电机控制台」进入,或直接访问 `/motor.html`。
|
||||
它只读写 EtherCAT 过程数据,需先在主页点「连接」并让从站进入 OP。
|
||||
|
||||
## 注意
|
||||
|
||||
- ESC 的 SyncManager 寄存器是 `0x0800 + 8n`:邮箱是 SM0=`0x0800`、SM1=`0x0808`,
|
||||
`0x0810`/`0x0818` 是 SM2/SM3(过程数据)。写诊断代码时别混用,否则抓到的现场会误导判断。
|
||||
- **邮箱楔死判据**:SM0/SM1 寄存器正常(start=0x1000/0x1200、act=01)但 SDO 持续失败,
|
||||
就是 SSC 的 `u16EscAddrSendMbx` 错位——响应帧写到 SM1 start+4/+8 且逐会话累积,
|
||||
断电才清零。固件 <1.5.52 无自愈需断电重启,1.5.52+ 有守护自动写回。
|
||||
`config_map` 失败时后端会自动转储 4 个 SM 现场,可直接对照。
|
||||
- 前端 CSS/JS 改动后记得 bump `?v=` 缓存版本,否则浏览器会加载旧代码。
|
||||
`src/web/ecat.css` 被 `ecat.html` 与 `motor.html` 共用,两处版本号要一起改。
|
||||
- EEPROM 烧写后必须断电冷启动,ESC 才会重新加载配置。
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
"""_probe_scan.py — 精确定位响应帧头在 SM1 RAM 的偏移 + SM0 请求侧同步 dump。"""
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
import ecat_server as es
|
||||
import pysoem
|
||||
|
||||
r = es.api_open({"substr": "I226"})
|
||||
if not r.get("ok"):
|
||||
sys.exit(1)
|
||||
m = es.g_master
|
||||
s = m.slaves[0]
|
||||
|
||||
|
||||
def fprd(addr, size):
|
||||
try:
|
||||
return bytes(s._fprd(addr, size, 20000))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def sm_reg(addr):
|
||||
d = fprd(addr, 8)
|
||||
if d is None:
|
||||
return None
|
||||
start, length = struct.unpack("<HH", d[:4])
|
||||
return {"start": start, "len": length, "ctl": d[4], "st": d[5], "act": d[6]}
|
||||
|
||||
|
||||
m.state = pysoem.INIT_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
m.state_check(pysoem.INIT_STATE, 1_000_000)
|
||||
m.config_init()
|
||||
m.state = pysoem.PREOP_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
print("PREOP:", "OK" if m.state_check(pysoem.PREOP_STATE, 1_000_000) == pysoem.PREOP_STATE else "FAIL", flush=True)
|
||||
|
||||
sm0, sm1 = sm_reg(0x0800), sm_reg(0x0808)
|
||||
print(f"SM0: start=0x{sm0['start']:04X} len={sm0['len']} | SM1: start=0x{sm1['start']:04X} len={sm1['len']}", flush=True)
|
||||
|
||||
# 注入请求(SOEM 标准格式 SDO 读 0x1018:01)
|
||||
req = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x10, 0x40, 0x01, 0x18, 0x10, 0x01, 0x00, 0x00, 0x00])
|
||||
mbx = (req + bytes(sm0["len"]))[:sm0["len"]]
|
||||
s._fpwr(sm0["start"], mbx)
|
||||
print("请求已写入 SM0", flush=True)
|
||||
time.sleep(0.2)
|
||||
|
||||
# 扫描 SM1 start-8 .. start+72
|
||||
base = sm1["start"]
|
||||
d = fprd(base - 8, 80)
|
||||
if d:
|
||||
print(f"\nSM1 RAM 扫描 [0x{base - 8:04X} .. 0x{base + 72:04X}]:", flush=True)
|
||||
for i in range(0, 80, 8):
|
||||
chunk = d[i:i + 8]
|
||||
off = i - 8
|
||||
mark = " <-- SM1.start" if off == 0 else ""
|
||||
print(f" {off:+4d} (0x{base + off:04X}): {chunk.hex().upper()}{mark}", flush=True)
|
||||
|
||||
st1 = sm_reg(0x0808)
|
||||
print(f"\nSM1 st=0x{st1['st']:02X}(bit3={st1['st'] >> 3 & 1})", flush=True)
|
||||
|
||||
# 同步看 SM0 侧(请求是否被读走)
|
||||
st0 = sm_reg(0x0800)
|
||||
print(f"SM0 st=0x{st0['st']:02X}(bit3={st0['st'] >> 3 & 1})", flush=True)
|
||||
|
||||
es.api_close({})
|
||||
print("DONE", flush=True)
|
||||
@@ -1,77 +0,0 @@
|
||||
"""_probe_stuck.py — 验证"SM1 响应滞留 → 邮箱死锁"假说。
|
||||
板上现状: SM1 挂着一个未读走的响应(上一探针注入)。实验:
|
||||
1. SOEM SDO 读 → 预期失败(复现楔死现象)
|
||||
2. 手动读走 SM1 全长(清 bit3) → 模拟"主站把滞留响应读掉"
|
||||
3. SOEM SDO 再读 → 是否恢复?
|
||||
若恢复 = 根治方向明确:邮箱死锁的物理本质是响应滞留,主站侧可自愈,从站侧可加超时复位。
|
||||
"""
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import ecat_server as es
|
||||
import pysoem
|
||||
|
||||
r = es.api_open({"substr": "I226"})
|
||||
if not r.get("ok"):
|
||||
sys.exit(1)
|
||||
m = es.g_master
|
||||
s = m.slaves[0]
|
||||
|
||||
|
||||
def fprd(addr, size):
|
||||
try:
|
||||
return bytes(s._fprd(addr, size, 20000))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def sm1_state(tag):
|
||||
d = fprd(0x0808, 8)
|
||||
if d is None:
|
||||
print(f"[{tag}] SM1 读失败", flush=True)
|
||||
return None
|
||||
start, length = struct.unpack("<HH", d[:4])
|
||||
st = d[5]
|
||||
print(f"[{tag}] SM1 st=0x{st:02X}(bit3={st >> 3 & 1} 读邮箱满)", flush=True)
|
||||
return {"start": start, "len": length, "st": st}
|
||||
|
||||
|
||||
m.state = pysoem.INIT_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
m.state_check(pysoem.INIT_STATE, 1_000_000)
|
||||
m.config_init()
|
||||
m.state = pysoem.PREOP_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
print("PREOP:", "OK" if m.state_check(pysoem.PREOP_STATE, 1_000_000) == pysoem.PREOP_STATE else "FAIL", flush=True)
|
||||
|
||||
# --- 1. 现状: SM1 挂着滞留响应 ---
|
||||
sm1_state("实验前")
|
||||
|
||||
print("\n--- SOEM SDO #1(预期失败) ---", flush=True)
|
||||
try:
|
||||
d = bytes(s.sdo_read(0x1000, 0, 8))
|
||||
print(f" 成功?! = {d.hex().upper()}", flush=True)
|
||||
except Exception as e:
|
||||
print(f" 失败: {type(e).__name__}: {e}", flush=True)
|
||||
|
||||
sm1_state("SDO#1 后")
|
||||
|
||||
# --- 2. 读走 SM1 滞留响应(全长度读取,读到末字节才清 bit3) ---
|
||||
sm1 = sm1_state("读滞留前")
|
||||
if sm1 and (sm1["st"] & 0x08):
|
||||
d = fprd(sm1["start"], sm1["len"])
|
||||
print(f" 读走 SM1 {sm1['len']}B: {d[:16].hex().upper()}...", flush=True)
|
||||
sm1_state("读走后")
|
||||
|
||||
# --- 3. SOEM SDO 再试 ---
|
||||
print("\n--- SOEM SDO #2(读走滞留后) ---", flush=True)
|
||||
try:
|
||||
d = bytes(s.sdo_read(0x1000, 0, 8))
|
||||
print(f" 成功! = {d.hex().upper()}", flush=True)
|
||||
print(">>> 假说成立:响应滞留是死锁根源,读走即恢复!", flush=True)
|
||||
except Exception as e:
|
||||
print(f" 仍失败: {type(e).__name__}: {e}", flush=True)
|
||||
sm1_state("SDO#2 后")
|
||||
|
||||
es.api_close({})
|
||||
print("DONE", flush=True)
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
"""_probe_wedge.py — 楔死现场逐环节定位(纠正之前 0x0810/0x0818=SM2/SM3 的标号错误,真邮箱 SM 是 0x0800/0x0808)。
|
||||
|
||||
环节定位:
|
||||
A. FPWR 写 SM0 RAM 的 WKC —— 写都进不去 = SM0 硬件层被拒(disabled/错址)
|
||||
B. 写入后 0x220 bit0(SM0 写事件)是否置位 —— ESC 没置事件 = ESC 硬件楔死
|
||||
C. 200ms 后 bit0 是否被 SSC 清掉 + SM1 status bit3(读邮箱满)是否置位 —— 没处理 = SSC 软件死了
|
||||
D. SM1 RAM 里有没有滞留的旧响应(bSendMbxIsFull 死锁证据)
|
||||
"""
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
import ecat_server as es
|
||||
import pysoem
|
||||
|
||||
r = es.api_open({"substr": "I226"})
|
||||
if not r.get("ok"):
|
||||
print("open 失败,板子可能已断电", flush=True)
|
||||
sys.exit(1)
|
||||
m = es.g_master
|
||||
s = m.slaves[0]
|
||||
|
||||
|
||||
def fprd(addr, size):
|
||||
try:
|
||||
return bytes(s._fprd(addr, size, 20000))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def dump(tag):
|
||||
al = fprd(0x0130, 8)
|
||||
ev = fprd(0x0220, 2)
|
||||
am = fprd(0x0204, 2)
|
||||
sm0 = fprd(0x0800, 8)
|
||||
sm1 = fprd(0x0808, 8)
|
||||
def fmt(d):
|
||||
if d is None:
|
||||
return "ERR"
|
||||
start, length = struct.unpack("<HH", d[:4])
|
||||
return f"start=0x{start:04X} len={length} ctl=0x{d[4]:02X} st=0x{d[5]:02X} act=0x{d[6]:02X} pdi=0x{d[7]:02X}"
|
||||
al_s = f"AL=0x{struct.unpack('<H', al[:2])[0]:04X} code=0x{struct.unpack('<H', al[4:6])[0]:04X}" if al else "AL=ERR"
|
||||
ev_v = struct.unpack("<H", ev)[0] if ev else -1
|
||||
am_v = struct.unpack("<H", am)[0] if am else -1
|
||||
print(f"[{tag}] {al_s} 0x220=0x{ev_v:04X}(bit0={ev_v & 1}bit1={(ev_v >> 1) & 1}) 0x204=0x{am_v:04X}", flush=True)
|
||||
print(f" SM0(0x0800): {fmt(sm0)}", flush=True)
|
||||
print(f" SM1(0x0808): {fmt(sm1)}", flush=True)
|
||||
return sm0, sm1, ev_v
|
||||
|
||||
|
||||
# --- 阶段 1: PREOP 下探(邮箱 SM 应处于使能态) ---
|
||||
m.state = pysoem.INIT_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
m.state_check(pysoem.INIT_STATE, 1_000_000)
|
||||
m.config_init()
|
||||
m.state = pysoem.PREOP_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
st = m.state_check(pysoem.PREOP_STATE, 1_000_000)
|
||||
print(f"PREOP state_check = 0x{st & 0x0F:02X}", flush=True)
|
||||
sm0, sm1, ev = dump("PREOP 稳态")
|
||||
|
||||
if sm0 is None or sm1 is None:
|
||||
print("SM0/SM1 读取失败", flush=True)
|
||||
es.api_close({})
|
||||
sys.exit(1)
|
||||
|
||||
# SM0/SM1 RAM 内容(滞留响应证据)
|
||||
mbx0_addr, mbx0_len = struct.unpack("<HH", sm0[:4])
|
||||
mbx1_addr, mbx1_len = struct.unpack("<HH", sm1[:4])
|
||||
print(f"邮箱 RAM: SM0@0x{mbx0_addr:04X}({mbx0_len}B) SM1@0x{mbx1_addr:04X}({mbx1_len}B)", flush=True)
|
||||
for name, addr, ln in (("SM0", mbx0_addr, mbx0_len), ("SM1", mbx1_addr, mbx1_len)):
|
||||
d = fprd(addr, 16)
|
||||
if d:
|
||||
print(f" {name} RAM[0:16] = {d.hex().upper()}", flush=True)
|
||||
|
||||
# --- 阶段 2: 手动注入一个合法 CoE SDO 读请求(0x1000:00 expedited upload) ---
|
||||
# 邮箱帧: len=0x000A addr=0x0000 pri=0 type=0x03(CoE, counter=0) | CANOpen=0x1000+8(UP_REQ) idx=0x1000 sub=0
|
||||
req = struct.pack("<HHBB", 10, 0, 0, 0x03) + struct.pack("<HBBB", 0x1000, 0x100 & 0xFF, 0x00, 0x00)[:5]
|
||||
req = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||||
print(f"\n--- 阶段A: FPWR 写 SM0 RAM(0x{mbx0_addr:04X}) {req.hex().upper()} ---", flush=True)
|
||||
try:
|
||||
s._fpwr(mbx0_addr, req)
|
||||
print(" 写入 OK (WKC=1) —— SM0 硬件接受写入", flush=True)
|
||||
except Exception as e:
|
||||
print(f" 写入失败: {type(e).__name__}: {e} —— SM0 硬件拒绝(环节A死点)", flush=True)
|
||||
|
||||
ev2 = fprd(0x0220, 2)
|
||||
if ev2:
|
||||
v = struct.unpack("<H", ev2)[0]
|
||||
print(f" 阶段B: 0x220=0x{v:04X} bit0(SM0写事件)={v & 1} —— {'已置位' if v & 1 else '未置位!ESC 硬件楔死'}", flush=True)
|
||||
|
||||
# --- 阶段 3: 给 SSC 200ms 处理时间 ---
|
||||
time.sleep(0.2)
|
||||
sm0b, sm1b, ev3 = dump("注入请求 200ms 后")
|
||||
sm1_st = sm1b[5] if sm1b else 0
|
||||
print(f" 阶段C: 0x220 bit0={ev3 & 1}(应=0,SSC 已读走) SM1 st=0x{sm1_st:02X}(bit3={sm1_st >> 3 & 1},应=1,响应就绪)", flush=True)
|
||||
if ev3 is not None and (ev3 & 1):
|
||||
print(" >>> bit0 仍挂起:SSC 轮询没处理邮箱(环节C死点:MainLoop/ECAT_Main 邮箱分支死了)", flush=True)
|
||||
elif sm1_st & 0x08:
|
||||
d = fprd(mbx1_addr, 16)
|
||||
print(f" 阶段D: SM1 响应 RAM = {d.hex().upper() if d else 'ERR'}", flush=True)
|
||||
print(" >>> SSC 活着!邮箱处理正常 —— 楔死只在特定条件下", flush=True)
|
||||
else:
|
||||
print(" >>> bit0 被清了但 SM1 无响应:SSC 收到了但没回(处理链中途死)", flush=True)
|
||||
|
||||
es.api_close({})
|
||||
print("DONE", flush=True)
|
||||
@@ -1,93 +0,0 @@
|
||||
"""_probe_wedge2.py — 按真实邮箱协议探针: 检查写邮箱空 → 写满 256B → 查事件/响应。"""
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
import ecat_server as es
|
||||
import pysoem
|
||||
|
||||
r = es.api_open({"substr": "I226"})
|
||||
if not r.get("ok"):
|
||||
sys.exit(1)
|
||||
m = es.g_master
|
||||
s = m.slaves[0]
|
||||
|
||||
|
||||
def fprd(addr, size):
|
||||
try:
|
||||
return bytes(s._fprd(addr, size, 20000))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def sm_reg(addr):
|
||||
d = fprd(addr, 8)
|
||||
if d is None:
|
||||
return None
|
||||
start, length = struct.unpack("<HH", d[:4])
|
||||
return {"start": start, "len": length, "ctl": d[4], "st": d[5], "act": d[6],
|
||||
"raw": d.hex().upper()}
|
||||
|
||||
|
||||
def show(tag):
|
||||
sm0, sm1 = sm_reg(0x0800), sm_reg(0x0808)
|
||||
ev = fprd(0x0220, 2)
|
||||
al = fprd(0x0130, 8)
|
||||
ev_v = struct.unpack("<H", ev)[0] if ev else -1
|
||||
al_v = struct.unpack("<H", al[:2])[0] if al else -1
|
||||
print(f"[{tag}] AL=0x{al_v:02X} 0x220=0x{ev_v:04X} SM0[st=0x{sm0['st']:02X} act=0x{sm0['act']:02X}] "
|
||||
f"SM1[st=0x{sm1['st']:02X} act=0x{sm1['act']:02X}] (SM0.bit3={sm0['st'] >> 3 & 1}写满 SM1.bit3={sm1['st'] >> 3 & 1}读满)",
|
||||
flush=True)
|
||||
return sm0, sm1, ev_v
|
||||
|
||||
|
||||
m.state = pysoem.INIT_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
m.state_check(pysoem.INIT_STATE, 1_000_000)
|
||||
m.config_init()
|
||||
m.state = pysoem.PREOP_STATE | pysoem.STATE_ACK
|
||||
m.write_state()
|
||||
print("PREOP:", "OK" if m.state_check(pysoem.PREOP_STATE, 1_000_000) == pysoem.PREOP_STATE else "FAIL", flush=True)
|
||||
|
||||
sm0, sm1, ev = show("稳态")
|
||||
|
||||
# 合法 CoE SDO 读 0x1000:00(expedited), padded 到整个邮箱长度 256B
|
||||
req_hdr = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, # len=10, addr=0, pri=0, type=CoE(cnt=0)
|
||||
0x10, 0x00, # CANOpen = 0x0010 (SDO req<<12 | 0x1000? 不对, 服务号 0x10?) —— 用标准 SOEM 格式
|
||||
0x00, 0x10, # Command = 0x40(up req, exp)
|
||||
0x00, 0x10, # Index = 0x1000
|
||||
0x00, 0x00]) # sub=0, +pad
|
||||
# SOEM ecx_SDOread 的帧: CANOpen = (ECT_COES_SDOREQ<<12)=0x1000 | number; Command=0x40; Index; SubIndex
|
||||
req = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x10, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||||
mbx = (req + bytes(sm0["len"]))[:sm0["len"]]
|
||||
print(f"\n写满 SM0 RAM {sm0['len']}B(合法 CoE 读 0x1000:00)...", flush=True)
|
||||
try:
|
||||
s._fpwr(sm0["start"], mbx)
|
||||
print(" 写入 OK (WKC=1)", flush=True)
|
||||
except Exception as e:
|
||||
print(f" 写入失败: {e}", flush=True)
|
||||
|
||||
sm0b, sm1b, ev2 = show("写入后立即")
|
||||
time.sleep(0.2)
|
||||
sm0c, sm1c, ev3 = show("写入后 200ms")
|
||||
|
||||
if sm1c and (sm1c["st"] & 0x08):
|
||||
d = fprd(sm1c["start"], 32)
|
||||
print(f"*** SSC 响应了! SM1 RAM = {d.hex().upper() if d else 'ERR'}", flush=True)
|
||||
print(">>> ESC+SSC 完全正常,楔死另有其因", flush=True)
|
||||
else:
|
||||
print("\nSSC 无响应。检查 0x220 bit0 事件是否置位过...", flush=True)
|
||||
if sm0c and (sm0c["st"] & 0x08):
|
||||
print(f"SM0.bit3(写满)={sm0c['st'] >> 3 & 1} —— 写满标志挂着(主站侧协议已触发)", flush=True)
|
||||
# 模拟 SSC 解锁: 读 SM0 首 word + 末 word
|
||||
fprd(sm0c["start"], 2)
|
||||
fprd(sm0c["start"] + sm0c["len"] - 2, 2)
|
||||
sm0d, sm1d, ev4 = show("模拟 SSC 解锁后")
|
||||
if not (sm0d["st"] & 0x08):
|
||||
print("解锁有效(SM0.bit3 清零) —— SM 状态机活着", flush=True)
|
||||
else:
|
||||
print(f"SM0.bit3(写满)={sm0c['st'] >> 3 & 1 if sm0c else '?'} —— 写满标志都没置!写到末字节也没触发 mailbox full", flush=True)
|
||||
|
||||
es.api_close({})
|
||||
print("DONE", flush=True)
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
setlocal
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul
|
||||
cd /d G:\Robot_v2\DCU\tools\ecat
|
||||
if not exist build_native mkdir build_native
|
||||
rem SII default image (ecat_native.c's #include "eeprom_image.h"): gen_eeprom.py stdout is
|
||||
rem already a clean C header (self-check line goes to stderr); assert failure -> non-zero
|
||||
rem exit -> build aborts. To change SII just rerun this script.
|
||||
venv\Scripts\python.exe "G:\Robot_v2\DCU\DCU_APP\GD32E508_DCU_APP\comm\ssc\port\gen_eeprom.py" > build_native\eeprom_image.h
|
||||
if errorlevel 1 (echo [FAIL] gen_eeprom.py image generation failed & exit /b 1)
|
||||
if not exist build_native\wpcap.lib (
|
||||
dumpbin /exports C:\Windows\System32\Npcap\wpcap.dll > build_native\exports.txt
|
||||
venv\Scripts\python.exe exports2def.py build_native\exports.txt build_native\wpcap.def wpcap
|
||||
lib /def:build_native\wpcap.def /machine:X64 /out:build_native\wpcap.lib
|
||||
if errorlevel 1 exit /b 1
|
||||
)
|
||||
cl /nologo /O2 /W3 /utf-8 /D_CRT_SECURE_NO_WARNINGS /DWIN32 /I build_native /I soem-src\soem /I soem-src\osal /I soem-src\osal\win32 /I soem-src\oshw\win32 /I soem-src\oshw\win32\wpcap\Include /Fo:build_native\ ecat_native.c soem-src\soem\ethercatbase.c soem-src\soem\ethercatcoe.c soem-src\soem\ethercatconfig.c soem-src\soem\ethercatdc.c soem-src\soem\ethercateoe.c soem-src\soem\ethercatfoe.c soem-src\soem\ethercatmain.c soem-src\soem\ethercatprint.c soem-src\soem\ethercatsoe.c soem-src\osal\win32\osal.c soem-src\oshw\win32\nicdrv.c soem-src\oshw\win32\oshw.c /link /OUT:build_native\ecat_native.exe build_native\wpcap.lib ws2_32.lib winmm.lib
|
||||
if errorlevel 1 exit /b 1
|
||||
echo [OK] build_native\ecat_native.exe
|
||||
@@ -0,0 +1,148 @@
|
||||
Microsoft (R) COFF/PE Dumper Version 14.43.34809.0
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
|
||||
Dump of file C:\Windows\System32\Npcap\wpcap.dll
|
||||
|
||||
File Type: DLL
|
||||
|
||||
Section contains the following exports for wpcap.dll
|
||||
|
||||
00000000 characteristics
|
||||
FFFFFFFF time date stamp
|
||||
0.00 version
|
||||
1 ordinal base
|
||||
119 number of functions
|
||||
119 number of names
|
||||
|
||||
ordinal hint RVA name
|
||||
|
||||
1 0 00001010 bpf_dump
|
||||
2 1 00001170 bpf_filter
|
||||
3 2 00001C40 bpf_image
|
||||
4 3 00001190 bpf_validate
|
||||
5 4 00073000 eproto_db
|
||||
6 5 00018850 pcap_activate
|
||||
7 6 000189C0 pcap_breakloop
|
||||
8 7 000189E0 pcap_bufsize
|
||||
9 8 00018A00 pcap_can_set_rfmon
|
||||
10 9 00018A40 pcap_close
|
||||
11 A 00012DD0 pcap_compile
|
||||
12 B 000131B0 pcap_compile_nopcap
|
||||
13 C 00018A70 pcap_create
|
||||
14 D 00018BC0 pcap_createsrcstr
|
||||
15 E 00018BF0 pcap_datalink
|
||||
16 F 00018C10 pcap_datalink_ext
|
||||
17 10 00018C30 pcap_datalink_name_to_val
|
||||
18 11 00018D10 pcap_datalink_val_to_description
|
||||
19 12 00018D60 pcap_datalink_val_to_description_or_dlt
|
||||
20 13 00018E00 pcap_datalink_val_to_name
|
||||
21 14 00018E50 pcap_dispatch
|
||||
22 15 0001DED0 pcap_dump
|
||||
23 16 0001DF60 pcap_dump_close
|
||||
24 17 0001DF90 pcap_dump_file
|
||||
25 18 0001DFA0 pcap_dump_flush
|
||||
26 19 0001DFC0 pcap_dump_ftell
|
||||
27 1A 0001DFD0 pcap_dump_ftell64
|
||||
28 1B 0001DFE0 pcap_dump_hopen
|
||||
29 1C 0001E0F0 pcap_dump_open
|
||||
30 1D 0001E240 pcap_dump_open_append
|
||||
31 1E 000136B0 pcap_ether_aton
|
||||
32 1F 00013720 pcap_ether_hostton
|
||||
33 20 00018E70 pcap_file
|
||||
34 21 00018E80 pcap_fileno
|
||||
35 22 00018EA0 pcap_findalldevs
|
||||
36 23 000220E0 pcap_findalldevs_ex
|
||||
37 24 00019070 pcap_free_datalinks
|
||||
38 25 00019070 pcap_free_tstamp_types
|
||||
39 26 00019080 pcap_freealldevs
|
||||
40 27 00013230 pcap_freecode
|
||||
41 28 00019140 pcap_get_airpcap_handle
|
||||
42 29 000191D0 pcap_get_tstamp_precision
|
||||
43 2A 000191E0 pcap_geterr
|
||||
44 2B 000191F0 pcap_getevent
|
||||
45 2C 00019270 pcap_getnonblock
|
||||
46 2D 0001C130 pcap_hopen_offline
|
||||
47 2E 0001C140 pcap_hopen_offline_with_tstamp_precision
|
||||
48 2F 00019340 pcap_init
|
||||
49 30 000193D0 pcap_inject
|
||||
50 31 00019490 pcap_is_swapped
|
||||
51 32 0001FF90 pcap_lib_version
|
||||
52 33 000194B0 pcap_list_datalinks
|
||||
53 34 00019580 pcap_list_tstamp_types
|
||||
54 35 00019640 pcap_live_dump
|
||||
55 36 00019690 pcap_live_dump_ended
|
||||
56 37 00020050 pcap_lookupdev
|
||||
57 38 000202B0 pcap_lookupnet
|
||||
58 39 000196A0 pcap_loop
|
||||
59 3A 00019720 pcap_major_version
|
||||
60 3B 00019740 pcap_minor_version
|
||||
61 3C 00013800 pcap_nametoaddr
|
||||
62 3D 00013870 pcap_nametoaddrinfo
|
||||
63 3E 000138C0 pcap_nametoeproto
|
||||
64 3F 00013920 pcap_nametollc
|
||||
65 40 000136A0 pcap_nametonetaddr
|
||||
66 41 00013980 pcap_nametoport
|
||||
67 42 00013B40 pcap_nametoportrange
|
||||
68 43 00013BF0 pcap_nametoproto
|
||||
69 44 00019760 pcap_next
|
||||
70 45 00002360 pcap_next_etherent
|
||||
71 46 000197B0 pcap_next_ex
|
||||
72 47 00019810 pcap_offline_filter
|
||||
73 48 00019830 pcap_oid_get_request
|
||||
74 49 00019880 pcap_oid_set_request
|
||||
75 4A 000225D0 pcap_open
|
||||
76 4B 000198D0 pcap_open_dead
|
||||
77 4C 000198E0 pcap_open_dead_with_tstamp_precision
|
||||
78 4D 00019B10 pcap_open_live
|
||||
79 4E 0001C200 pcap_open_offline
|
||||
80 4F 0001C210 pcap_open_offline_with_tstamp_precision
|
||||
81 50 0001A3D0 pcap_parsesrcstr
|
||||
82 51 0001A6D0 pcap_perror
|
||||
83 52 00023F70 pcap_remoteact_accept
|
||||
84 53 00023FA0 pcap_remoteact_accept_ex
|
||||
85 54 00024350 pcap_remoteact_cleanup
|
||||
86 55 00024380 pcap_remoteact_close
|
||||
87 56 00024510 pcap_remoteact_list
|
||||
88 57 0001A740 pcap_sendpacket
|
||||
89 58 0001A7A0 pcap_sendqueue_alloc
|
||||
90 59 0001A800 pcap_sendqueue_destroy
|
||||
91 5A 0001A820 pcap_sendqueue_queue
|
||||
92 5B 0001A890 pcap_sendqueue_transmit
|
||||
93 5C 0001A8D0 pcap_set_buffer_size
|
||||
94 5D 0001A910 pcap_set_datalink
|
||||
95 5E 0001AA80 pcap_set_immediate_mode
|
||||
96 5F 0001AAC0 pcap_set_promisc
|
||||
97 60 0001AB00 pcap_set_rfmon
|
||||
98 61 0001AB40 pcap_set_snaplen
|
||||
99 62 0001AB80 pcap_set_timeout
|
||||
100 63 0001ABC0 pcap_set_tstamp_precision
|
||||
101 64 0001AC50 pcap_set_tstamp_type
|
||||
102 65 0001ACE0 pcap_setbuff
|
||||
103 66 0001AD20 pcap_setdirection
|
||||
104 67 0001ADD0 pcap_setfilter
|
||||
105 68 0001AE10 pcap_setmintocopy
|
||||
106 69 0001AE50 pcap_setmode
|
||||
107 6A 0001AE90 pcap_setnonblock
|
||||
108 6B 00022850 pcap_setsampling
|
||||
109 6C 0001AF00 pcap_setuserbuffer
|
||||
110 6D 0001AF40 pcap_snapshot
|
||||
111 6E 0001AF60 pcap_stats
|
||||
112 6F 0001AFA0 pcap_stats_ex
|
||||
113 70 0001AFE0 pcap_statustostr
|
||||
114 71 0001B150 pcap_strerror
|
||||
115 72 0001B1B0 pcap_tstamp_type_name_to_val
|
||||
116 73 0001B290 pcap_tstamp_type_val_to_description
|
||||
117 74 0001B2E0 pcap_tstamp_type_val_to_name
|
||||
118 75 00073120 pcap_version
|
||||
119 76 000136A0 pcap_wsockinit
|
||||
|
||||
Summary
|
||||
|
||||
A000 .data
|
||||
1000 .fptable
|
||||
4000 .pdata
|
||||
26000 .rdata
|
||||
1000 .reloc
|
||||
1000 .rsrc
|
||||
4C000 .text
|
||||
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul
|
||||
cd /d G:\Robot_v2\DCU\tools\ecat
|
||||
cl /nologo /c /O2 /W3 /utf-8 /D_CRT_SECURE_NO_WARNINGS /I soem-src\soem /I soem-src\osal /I soem-src\osal\win32 /I soem-src\oshw\win32 /I soem-src\oshw\win32\wpcap\Include /Fobuild_native\ soem-src\oshw\win32\nicdrv.c 2>&1
|
||||
@@ -0,0 +1,121 @@
|
||||
LIBRARY wpcap
|
||||
EXPORTS
|
||||
bpf_dump
|
||||
bpf_filter
|
||||
bpf_image
|
||||
bpf_validate
|
||||
eproto_db
|
||||
pcap_activate
|
||||
pcap_breakloop
|
||||
pcap_bufsize
|
||||
pcap_can_set_rfmon
|
||||
pcap_close
|
||||
pcap_compile
|
||||
pcap_compile_nopcap
|
||||
pcap_create
|
||||
pcap_createsrcstr
|
||||
pcap_datalink
|
||||
pcap_datalink_ext
|
||||
pcap_datalink_name_to_val
|
||||
pcap_datalink_val_to_description
|
||||
pcap_datalink_val_to_description_or_dlt
|
||||
pcap_datalink_val_to_name
|
||||
pcap_dispatch
|
||||
pcap_dump
|
||||
pcap_dump_close
|
||||
pcap_dump_file
|
||||
pcap_dump_flush
|
||||
pcap_dump_ftell
|
||||
pcap_dump_ftell64
|
||||
pcap_dump_hopen
|
||||
pcap_dump_open
|
||||
pcap_dump_open_append
|
||||
pcap_ether_aton
|
||||
pcap_ether_hostton
|
||||
pcap_file
|
||||
pcap_fileno
|
||||
pcap_findalldevs
|
||||
pcap_findalldevs_ex
|
||||
pcap_free_datalinks
|
||||
pcap_free_tstamp_types
|
||||
pcap_freealldevs
|
||||
pcap_freecode
|
||||
pcap_get_airpcap_handle
|
||||
pcap_get_tstamp_precision
|
||||
pcap_geterr
|
||||
pcap_getevent
|
||||
pcap_getnonblock
|
||||
pcap_hopen_offline
|
||||
pcap_hopen_offline_with_tstamp_precision
|
||||
pcap_init
|
||||
pcap_inject
|
||||
pcap_is_swapped
|
||||
pcap_lib_version
|
||||
pcap_list_datalinks
|
||||
pcap_list_tstamp_types
|
||||
pcap_live_dump
|
||||
pcap_live_dump_ended
|
||||
pcap_lookupdev
|
||||
pcap_lookupnet
|
||||
pcap_loop
|
||||
pcap_major_version
|
||||
pcap_minor_version
|
||||
pcap_nametoaddr
|
||||
pcap_nametoaddrinfo
|
||||
pcap_nametoeproto
|
||||
pcap_nametollc
|
||||
pcap_nametonetaddr
|
||||
pcap_nametoport
|
||||
pcap_nametoportrange
|
||||
pcap_nametoproto
|
||||
pcap_next
|
||||
pcap_next_etherent
|
||||
pcap_next_ex
|
||||
pcap_offline_filter
|
||||
pcap_oid_get_request
|
||||
pcap_oid_set_request
|
||||
pcap_open
|
||||
pcap_open_dead
|
||||
pcap_open_dead_with_tstamp_precision
|
||||
pcap_open_live
|
||||
pcap_open_offline
|
||||
pcap_open_offline_with_tstamp_precision
|
||||
pcap_parsesrcstr
|
||||
pcap_perror
|
||||
pcap_remoteact_accept
|
||||
pcap_remoteact_accept_ex
|
||||
pcap_remoteact_cleanup
|
||||
pcap_remoteact_close
|
||||
pcap_remoteact_list
|
||||
pcap_sendpacket
|
||||
pcap_sendqueue_alloc
|
||||
pcap_sendqueue_destroy
|
||||
pcap_sendqueue_queue
|
||||
pcap_sendqueue_transmit
|
||||
pcap_set_buffer_size
|
||||
pcap_set_datalink
|
||||
pcap_set_immediate_mode
|
||||
pcap_set_promisc
|
||||
pcap_set_rfmon
|
||||
pcap_set_snaplen
|
||||
pcap_set_timeout
|
||||
pcap_set_tstamp_precision
|
||||
pcap_set_tstamp_type
|
||||
pcap_setbuff
|
||||
pcap_setdirection
|
||||
pcap_setfilter
|
||||
pcap_setmintocopy
|
||||
pcap_setmode
|
||||
pcap_setnonblock
|
||||
pcap_setsampling
|
||||
pcap_setuserbuffer
|
||||
pcap_snapshot
|
||||
pcap_stats
|
||||
pcap_stats_ex
|
||||
pcap_statustostr
|
||||
pcap_strerror
|
||||
pcap_tstamp_type_name_to_val
|
||||
pcap_tstamp_type_val_to_description
|
||||
pcap_tstamp_type_val_to_name
|
||||
pcap_version
|
||||
pcap_wsockinit
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""归因"某一拍为什么超时":把 PDO 时间线与 GC / HTTP 事件时间线做交集。
|
||||
|
||||
背景:250us 和 200us 档的达成率都是 96~97%,几乎不随档位变化 —— 丢拍不像
|
||||
"总线差几十 us"这种比例性原因,更像固定频率的事件(GC 停止世界、HTTP 线程
|
||||
持 GIL 序列化快照、系统杂活)一撞就是一整个时隙。本脚本从 /api/motor/dbg
|
||||
拉三条原始时间线,对每一个超时拍给出责任归属。
|
||||
|
||||
用法(目标服务必须已在跑,本脚本自己不会拉服务):
|
||||
python cadence_diag.py --period 0.25 --sample 6
|
||||
python cadence_diag.py --period 0.2 --gc off # A/B:关掉 GC 看超时拍是否消失
|
||||
python cadence_diag.py --period 0.2 --gc on --hammer 2 # 再叠 2 路 /motor/state 轮询
|
||||
|
||||
判读:
|
||||
超时拍 = loop_end-t0(work) 超过设定周期的拍;它们正是网格跳掉的拍。
|
||||
归因窗口 = 上一拍 loop_end → 本拍 loop_end;GC/HTTP 事件落在窗口内即认领。
|
||||
"裸超时"占大头才说明是内核/中断层面的抢占,而不是进程内流程问题。
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import threading
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
BASE_DEFAULT = "http://127.0.0.1:8090"
|
||||
|
||||
|
||||
def api(base, path, req):
|
||||
r = urllib.request.Request(base + "/api/" + path,
|
||||
data=json.dumps(req).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(r, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def hammer_worker(base, stop, path="motor/state"):
|
||||
while not stop.is_set():
|
||||
try:
|
||||
api(base, path, {})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def classify(beats, gc_ev, http_ev, period_us):
|
||||
"""给每个超时拍贴标签。返回 (late列表, 计数统计)。beats: 按 t0 排序的样本。"""
|
||||
counts = {"GC": 0, "HTTP": 0, "GC+HTTP": 0, "裸超时": 0}
|
||||
late = []
|
||||
for i in range(1, len(beats)):
|
||||
t0, ts, tm, tr, te, k = beats[i]
|
||||
work = (te - t0) * 1e6
|
||||
if work <= period_us:
|
||||
continue
|
||||
w0, w1 = beats[i - 1][4], te # 上一拍结束 → 本拍结束
|
||||
g = [e for e in gc_ev if w0 <= e[0] <= w1 and e[1] > 20.0]
|
||||
h = [x for x in http_ev if x[0] < w1 and x[1] > w0 and (x[1] - x[0]) * 1e6 > 20.0]
|
||||
if g and h:
|
||||
tag = "GC+HTTP"
|
||||
elif g:
|
||||
tag = "GC(gen%d %.0fus)" % (g[-1][2], g[-1][1])
|
||||
counts["GC"] += 1
|
||||
elif h:
|
||||
tag = "HTTP(%s %.0fus)" % (h[-1][2].replace("/api/", ""), (h[-1][1] - h[-1][0]) * 1e6)
|
||||
counts["HTTP"] += 1
|
||||
else:
|
||||
tag = "裸超时"
|
||||
counts["裸超时"] += 1
|
||||
if g and h:
|
||||
counts["GC+HTTP"] += 1
|
||||
late.append((work, k, tag, (t0 - beats[i - 1][4]) * 1e6))
|
||||
return late, counts
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--base", default=BASE_DEFAULT)
|
||||
ap.add_argument("--period", type=float, required=True, help="周期 ms(0.25=250us)")
|
||||
ap.add_argument("--sample", type=float, default=6.0, help="设周期后预热秒数,之后才取窗口")
|
||||
ap.add_argument("--gc", choices=["on", "off", "keep"], default="keep")
|
||||
ap.add_argument("--hammer", type=int, default=0, help="叠加 N 路 /motor/state 轮询线程")
|
||||
args = ap.parse_args()
|
||||
per_us = args.period * 1000.0
|
||||
|
||||
if args.gc != "keep":
|
||||
r = api(args.base, "motor/dbg", {"action": "gc", "enable": args.gc == "on"})
|
||||
print(f"GC → {r.get('gc_enabled')}")
|
||||
|
||||
r = api(args.base, "motor/period", {"period_ms": args.period})
|
||||
print("period:", r.get("log"))
|
||||
if r.get("spin") is False and args.period < 1:
|
||||
print("!! 自旋没生效,这组数据没有意义")
|
||||
return 1
|
||||
|
||||
stop = threading.Event()
|
||||
threads = [threading.Thread(target=hammer_worker, args=(args.base, stop), daemon=True)
|
||||
for _ in range(args.hammer)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
try:
|
||||
time.sleep(args.sample)
|
||||
d = api(args.base, "motor/dbg", {})
|
||||
finally:
|
||||
stop.set()
|
||||
for t in threads:
|
||||
t.join(timeout=1)
|
||||
|
||||
beats = sorted(d["cyc"])[:-50] # 尾部几十拍被 dump 自身的 GIL 占用污染,丢弃
|
||||
if len(beats) < 100:
|
||||
print("样本不足", len(beats))
|
||||
return 1
|
||||
iv = [(beats[i][0] - beats[i - 1][0]) * 1e6 for i in range(1, len(beats))]
|
||||
hz = 1e6 / statistics.fmean(iv)
|
||||
work = sorted((e - t) * 1e6 for t, _, _, _, e, _ in beats)
|
||||
print(f"\n窗口 {len(beats)} 拍 ≈ {sum(iv) / 1e6:.2f}s | 周期 {per_us:.0f}us | "
|
||||
f"实测 {hz:.0f}Hz (达成 {hz / (1e6 / per_us) * 100:.1f}%) | "
|
||||
f"跳拍合计 {sum(k for *_, k in beats)}")
|
||||
gc_in_win = [e[2] for e in d["gc_events"] if beats[0][0] <= e[0] <= beats[-1][4]]
|
||||
print(f"work p50/p90/max = {work[len(work) // 2]:.0f}/{work[int(len(work) * .9)]:.0f}/"
|
||||
f"{work[-1]:.0f}us | 窗口内 GC g0/g1/g2 = "
|
||||
f"{gc_in_win.count(0)}/{gc_in_win.count(1)}/{gc_in_win.count(2)}")
|
||||
|
||||
# 事件按窗口时间过滤,再交集
|
||||
w0, w1 = beats[0][0], beats[-1][4]
|
||||
gc_ev = [e for e in d["gc_events"] if w0 <= e[0] <= w1]
|
||||
http_ev = [x for x in d["http"] if x[1] >= w0 and x[0] <= w1]
|
||||
late, counts = classify(beats, gc_ev, http_ev, per_us)
|
||||
n_late = len(late)
|
||||
print(f"超时拍 {n_late}/{len(beats)} = {n_late / len(beats) * 100:.2f}% | "
|
||||
+ " | ".join(f"{k}: {v}" for k, v in counts.items()))
|
||||
if late:
|
||||
lateness = sorted(x[0] for x in late)
|
||||
print(f"超时拍的 work: 中位 {statistics.median(lateness):.0f}us, "
|
||||
f"p90 {lateness[int(len(lateness) * .9)]:.0f}us, max {lateness[-1]:.0f}us")
|
||||
print("最狠的 8 拍:")
|
||||
for work_us, k, tag, gap in sorted(late, reverse=True)[:8]:
|
||||
print(f" work={work_us:7.0f}us 跳{k}拍 上游空闲{gap:6.0f}us {tag}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,8 +1,9 @@
|
||||
@echo off
|
||||
rem EtherCAT master tool for DCU LAN9252 (scan/flash/send/monitor)
|
||||
rem Examples: ecat.bat status | ecat.bat flash | ecat.bat send --u64 0x1,0x2,0x3,0x4 --duration 5
|
||||
rem 入口在项目根, 代码在 src\; cd 进 src 是为了让 py 之间的 import 正常解析。
|
||||
chcp 65001 >nul
|
||||
set PYTHONUTF8=1
|
||||
cd /d "%~dp0"
|
||||
cd /d "%~dp0src"
|
||||
"%~dp0venv\Scripts\python.exe" ecat_tool.py %*
|
||||
pause
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
' DCU EtherCAT desktop window (pywebview/WebView2). No console, no browser tab.
|
||||
' Browser-tab launcher: ecat_web.vbs Console-mode debug launcher: ecat_web.bat
|
||||
Set sh = CreateObject("WScript.Shell")
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
base = fso.GetParentFolderName(WScript.ScriptFullName)
|
||||
sh.CurrentDirectory = base
|
||||
sh.Environment("PROCESS")("PYTHONUTF8") = "1"
|
||||
' WebView2: keep GPU rasterization but disable GPU compositing.
|
||||
' Full hardware acceleration white-screens this window on some GPU drivers;
|
||||
' --disable-gpu-compositing fixes it while still using the GPU for raster.
|
||||
sh.Environment("PROCESS")("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS") = "--disable-gpu-compositing"
|
||||
sh.Run """" & base & "\venv\Scripts\pythonw.exe"" """ & base & "\ecat_server.py"" --window", 0, False
|
||||
+2229
File diff suppressed because it is too large
Load Diff
-1222
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,7 +1,7 @@
|
||||
@echo off
|
||||
rem DCU EtherCAT master web console (serves ecat.html + pysoem API)
|
||||
rem DCU EtherCAT master web console (native C/SOEM daemon; serves src\web + /api/*)
|
||||
rem Build first: build_native.bat (VS2022 + Npcap). Debug launcher with visible logs.
|
||||
chcp 65001 >nul
|
||||
set PYTHONUTF8=1
|
||||
cd /d "%~dp0"
|
||||
"%~dp0venv\Scripts\python.exe" ecat_server.py
|
||||
"%~dp0build_native\ecat_native.exe" --substr I226 --web src\web
|
||||
pause
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
' DCU EtherCAT web console - hidden launcher (no console window).
|
||||
' Double-click me. Console-mode debug launcher (visible logs): ecat_web.bat
|
||||
' Phase 4: backend is the native daemon (build_native\ecat_native.exe), no Python.
|
||||
' Double-click me. Console-mode debug launcher: ecat_web.bat
|
||||
Set sh = CreateObject("WScript.Shell")
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
base = fso.GetParentFolderName(WScript.ScriptFullName)
|
||||
sh.CurrentDirectory = base
|
||||
sh.Environment("PROCESS")("PYTHONUTF8") = "1"
|
||||
sh.Run """" & base & "\venv\Scripts\python.exe"" """ & base & "\ecat_server.py""", 0, False
|
||||
sh.Run """" & base & "\build_native\ecat_native.exe"" --substr I226 --web src\web", 0, False
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"""exports2def.py — 把 dumpbin /exports 的输出转成 .def(给 lib.exe 生成导入库用)。
|
||||
|
||||
用法: python exports2def.py <exports.txt> <out.def> <LIBRARY名>
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
def main():
|
||||
src, dst, libname = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
names = set()
|
||||
pat = re.compile(r"^\s+\d+\s+[0-9A-Fa-f]+\s+[0-9A-Fa-f]+\s+(\S+)")
|
||||
with open(src, encoding="utf-8", errors="ignore") as f:
|
||||
for line in f:
|
||||
m = pat.match(line)
|
||||
if m:
|
||||
names.add(m.group(1))
|
||||
if not names:
|
||||
print("exports2def: 没解析出任何导出名", file=sys.stderr)
|
||||
return 1
|
||||
with open(dst, "w", encoding="ascii", newline="\r\n") as f:
|
||||
f.write(f"LIBRARY {libname}\nEXPORTS\n")
|
||||
for n in sorted(names):
|
||||
f.write(n + "\n")
|
||||
print(f"exports2def: {len(names)} 个导出 → {dst}")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,72 @@
|
||||
"""native_enc_diff.py — CAN 输出编码回归(不碰网卡,无 Python 后端依赖)。
|
||||
|
||||
阶段 4 切换后 Python 基准已删除;本脚本改为对照 native_enc_golden.json
|
||||
(阶段 2 与 Python _fill_can_out 逐字节对拍通过后固化的 4 组用例)。
|
||||
byte0 是 seq(守护进程状态相关,每次 enctest 都 +1),不参与比对;
|
||||
其余 383 字节必须逐字节一致,另校验 seq 两两 +1(跳 0) 的递增语义。
|
||||
|
||||
用法:native 在 8090 跑着即可(不需要 open/OP):python native_enc_diff.py
|
||||
重新生成金标准(需 legacy 备份里的 ecat_server.py,一般不需要):见 git 历史/zip。
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
GOLDEN = "native_enc_golden.json"
|
||||
URL = "http://127.0.0.1:8090/api/native/enctest"
|
||||
|
||||
|
||||
def main():
|
||||
g = json.load(open(GOLDEN, encoding="utf-8"))
|
||||
cases = g["cases"]
|
||||
body = {"cases": CASES}
|
||||
req = urllib.request.Request(URL, data=json.dumps(body).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req) as r:
|
||||
d = json.loads(r.read())
|
||||
got = d["hexes"]
|
||||
ok = True
|
||||
if len(got) != len(cases):
|
||||
print(f"用例数不符: golden={len(cases)} native={len(got)}")
|
||||
return 1
|
||||
for i, (a, b) in enumerate(zip(cases, got)):
|
||||
if a[2:] != b[2:]:
|
||||
ok = False
|
||||
diff = [j for j in range(2, len(a), 2) if a[j:j+2] != b[j:j+2]]
|
||||
print(f"用例 {i}: 不一致!差异字节偏移 {diff[:12]}")
|
||||
j = diff[0]
|
||||
print(f" golden[{j}:{j+24}] = {a[j:j+24]}")
|
||||
print(f" native[{j}:{j+24}] = {b[j:j+24]}")
|
||||
else:
|
||||
print(f"用例 {i}: ✅ 383B 逐字节一致(byte0=seq 除外)")
|
||||
seqs = [int(h[:2], 16) for h in got]
|
||||
|
||||
def nxt(s):
|
||||
s = (s + 1) & 0xFF
|
||||
return 1 if s == 0 else s
|
||||
inc = all(seqs[i + 1] == nxt(seqs[i]) for i in range(len(seqs) - 1))
|
||||
print(f"seq 递增: {seqs} {'✅' if inc else '❌'}")
|
||||
return 0 if ok and inc else 1
|
||||
|
||||
|
||||
# 与阶段 2 对拍时同一组用例(纯数据)。改动这里 = 改动金标准语义,
|
||||
# 必须连同 native_enc_golden.json 一起重新生成。
|
||||
CASES = [
|
||||
# 基础单帧:DLC32 电机 CMD
|
||||
{"win": 0, "ch": 1, "id": 0x30200, "dlc": 32, "data": bytes(range(32)).hex(), "batch": []},
|
||||
# 批量边界:20×DLC8(332B 区,每项 16B → 20 项 320B,第 21 项 336>332 越界)
|
||||
{"win": 5, "ch": 0, "id": 0x123, "dlc": 8, "data": "AABBCCDDEEFF0011",
|
||||
"batch": [{"ch": 1, "id": 0x300 + i, "dlc": 8, "data": "0102030405060708"} for i in range(21)]},
|
||||
# dlc=0 只发批量;6×DLC64(每项 72B → 4 项 288B,第 5 项 360 越界)
|
||||
{"win": 20, "ch": 2, "id": 0, "dlc": 0, "data": "",
|
||||
"batch": [{"ch": 2, "id": 0xFFFFF, "dlc": 64, "data": "EE" * 64} for _ in range(6)]},
|
||||
# 混合 DLC + win 保持
|
||||
{"win": 255, "ch": 1, "id": 7, "dlc": 32, "data": "11" * 32,
|
||||
"batch": [{"ch": 0, "id": 9, "dlc": 32, "data": "22" * 32},
|
||||
{"ch": 1, "id": 10, "dlc": 4, "data": "33445566"},
|
||||
{"ch": 2, "id": 11, "dlc": 12, "data": "55" * 12}]},
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"note": "阶段2 与 Python _fill_can_out 逐字节对拍通过后固化(byte0=seq 状态相关不参与比对)。改 fill_can_out/pack_tx_batch 后跑 native_enc_diff.py 对照。",
|
||||
"cases": [
|
||||
"01012000000203000000000000000000000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"02000800230100000500000000000000AABBCCDDEEFF00110000000000000000000000000000000000000000000000001400000001080000000300000102030405060708010800000103000001020304050607080108000002030000010203040506070801080000030300000102030405060708010800000403000001020304050607080108000005030000010203040506070801080000060300000102030405060708010800000703000001020304050607080108000008030000010203040506070801080000090300000102030405060708010800000A0300000102030405060708010800000B0300000102030405060708010800000C0300000102030405060708010800000D0300000102030405060708010800000E0300000102030405060708010800000F030000010203040506070801080000100300000102030405060708010800001103000001020304050607080108000012030000010203040506070801080000130300000102030405060708000000000000000000000000",
|
||||
"0302000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000400000002400000FFFF0F00EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE02400000FFFF0F00EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE02400000FFFF0F00EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE02400000FFFF0F00EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0401200007000000FF0000000000000011111111111111111111111111111111111111111111111111111111111111110300000000200000090000002222222222222222222222222222222222222222222222222222222222222222010400000A00000033445566020C00000B0000005555555555555555555555550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
],
|
||||
"seqs": [
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12
|
||||
]
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""PDO 周期极限测速探针(真机复用工具)。
|
||||
|
||||
对**正在运行**的 ecat_server 逐级下发热调周期 /api/motor/period,量每一档的
|
||||
实测周期率、分段耗时(cyc_us)、wkc、自旋状态和 HTTP 读快照延迟。热调不停循环、
|
||||
喂狗不断流,所以跑这个脚本不会把从站弄掉 OP(区别于反复打 motor/start)。
|
||||
|
||||
前提:ecat_server 已启动(端口 8090 在听)。**循环没跑时脚本会自动 open+start**,
|
||||
所以断电重启板子后,直接敲本脚本一条命令即可,不必先开电机页或手敲 start。
|
||||
若自动 start 失败(典型=SSC 邮箱楔死),脚本会把原因如实打出来并退出。
|
||||
|
||||
用法(在本目录的 venv 下跑,系统 python 也行——只用标准库):
|
||||
venv\\Scripts\\python.exe rate_probe.py
|
||||
venv\\Scripts\\python.exe rate_probe.py --periods 0.5,0.25,0.125 --sample 2
|
||||
venv\\Scripts\\python.exe rate_probe.py --restore 0.25 # 测完留在 4kHz 档
|
||||
venv\\Scripts\\python.exe rate_probe.py --no-autostart # 循环没跑时不自动拉起
|
||||
默认从快照读当前周期,测完恢复到那个值。--substr 指定自动 open 的网卡子串(默认 I226)。
|
||||
|
||||
读数怎么解读:
|
||||
* 真机一送一回(send+receive)实测 ≈165us,这就是物理墙:比 165us 更快的
|
||||
档位"实测率"必然低于目标,不是脚本或设置的错,cyc_us.total.p50 会贴住
|
||||
周期值甚至跑超。保底 4kHz(250us) 在物理预算内。
|
||||
* measured 明显低于 target 且 cyc_us 远小于周期 → 是节拍问题(自旋没生效?
|
||||
看 spin 列),不是总线问题。
|
||||
* 自旋占满一个逻辑核是预期行为(脚本进程不占,占的是 ecat_server 的
|
||||
pdo-cycle 线程,正常应钉在 P 核)。
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
DEFAULT_URL = "http://127.0.0.1:8090"
|
||||
|
||||
|
||||
def post(url, path, body, timeout=5.0):
|
||||
req = urllib.request.Request(
|
||||
url + path, data=json.dumps(body).encode("utf-8"),
|
||||
headers={"Content-Type": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=timeout) as r:
|
||||
return json.loads(r.read().decode("utf-8"))
|
||||
|
||||
|
||||
def sample(url, seconds):
|
||||
"""在 seconds 窗口里轮询快照:返回(实测Hz, HTTP延迟p50/p90(ms), 最后快照)。
|
||||
|
||||
实测时刻必须和 cycles 读数**同取同舍**:旧版分子取最后一次轮询的 cycles、
|
||||
分母却在循环退出后才算 —— 退出前还有一次 sleep(0.1),dt 比 cycles 窗口多出
|
||||
0.1s,Hz 被系统性低估 3~5% 且与周期无关(2026-09-04 实踩:把 99.2% 报成 96%,
|
||||
一度被误读成"HTTP 负载吃掉 3 个点")。
|
||||
"""
|
||||
lat = []
|
||||
first = last = None # (读数时刻, cycles) —— 同一时刻成对取
|
||||
t_end = time.perf_counter() + seconds
|
||||
last_snap = None
|
||||
while time.perf_counter() < t_end:
|
||||
t0 = time.perf_counter()
|
||||
snap = post(url, "/api/motor/state", {})
|
||||
t1 = time.perf_counter()
|
||||
lat.append((t1 - t0) * 1000.0)
|
||||
if not snap.get("ok"):
|
||||
raise RuntimeError("motor/state 报错(可能掉出 OP): %s" % snap.get("log"))
|
||||
c = snap.get("cycles", 0)
|
||||
if first is None:
|
||||
first = (t1, c)
|
||||
last = (t1, c)
|
||||
last_snap = snap
|
||||
time.sleep(0.1)
|
||||
dt = last[0] - first[0]
|
||||
hz = (last[1] - first[1]) / dt if dt > 0 else 0.0
|
||||
lat.sort()
|
||||
return hz, (lat[len(lat) // 2], lat[int(len(lat) * 0.9)]), last_snap
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="EtherCAT PDO 周期极限测速")
|
||||
ap.add_argument("--url", default=DEFAULT_URL)
|
||||
ap.add_argument("--periods", default="1,0.5,0.25,0.125,0.0625",
|
||||
help="逗号分隔的周期(ms)档位,允许亚毫秒")
|
||||
ap.add_argument("--sample", type=float, default=2.0, help="每档采样秒数")
|
||||
ap.add_argument("--restore", type=float, default=None,
|
||||
help="测完恢复到的周期(ms);缺省=脚本启动前的原值")
|
||||
ap.add_argument("--substr", default="I226",
|
||||
help="网卡匹配子串(自动 open 时用),默认 I226")
|
||||
ap.add_argument("--no-autostart", action="store_true",
|
||||
help="循环没跑时不要自动 open+start,直接报错退出")
|
||||
a = ap.parse_args()
|
||||
|
||||
st = post(a.url, "/api/motor/state", {})
|
||||
if not st.get("ok"):
|
||||
# 循环没跑:断电重启后这是常态(服务活着但没人 open/start)。自动补上,
|
||||
# 让"断电后只敲 rate_probe 一条命令"成立 —— 不必先开电机页或手敲 start。
|
||||
if a.no_autostart:
|
||||
print("后台 PDO 循环没在跑(--no-autostart):", st.get("log"))
|
||||
return 1
|
||||
print("循环没跑,尝试自动 open + motor/start …")
|
||||
try:
|
||||
op = post(a.url, "/api/open", {"substr": a.substr}, timeout=25.0)
|
||||
if not op.get("ok"):
|
||||
print(" open 失败:", op.get("log"))
|
||||
return 1
|
||||
ms0 = post(a.url, "/api/motor/start", {"period_ms": 4}, timeout=40.0)
|
||||
if not ms0.get("ok"):
|
||||
# 这里最典型的失败就是 SSC 邮箱楔死(config_map 报 MailboxError),
|
||||
# 如实把原因打出来,别让人以为脚本坏了。
|
||||
print(" motor/start 失败(多半是邮箱楔死,需断电重启板子):")
|
||||
for line in (ms0.get("log") or []):
|
||||
print(" ", line[:120])
|
||||
return 1
|
||||
except OSError as e:
|
||||
print(" 连不上服务(没起? 端口对吗?):", e)
|
||||
return 1
|
||||
st = post(a.url, "/api/motor/state", {})
|
||||
if not st.get("ok"):
|
||||
print(" start 后仍读不到快照:", st.get("log"))
|
||||
return 1
|
||||
print(" 已自动进 OP 并起循环。")
|
||||
orig = st.get("period_ms", 4)
|
||||
|
||||
print(f"服务 {a.url} · 当前周期 {orig}ms · 从站 wkc={st.get('wkc')}/{st.get('expected_wkc')}")
|
||||
print(f"{'周期':>9} {'目标Hz':>7} {'实测Hz':>8} {'达成率':>6} "
|
||||
f"{'cyc_us p50/p90':>15} {'HTTP p50':>8} spin 电机")
|
||||
for ms in [float(x) for x in a.periods.split(",")]:
|
||||
r = post(a.url, "/api/motor/period", {"period_ms": ms})
|
||||
if not r.get("ok"):
|
||||
print(" 档位 %s 被拒绝: %s" % (ms, r.get("log")))
|
||||
continue
|
||||
time.sleep(0.3) # 换周期后先让统计攒几个样本
|
||||
try:
|
||||
hz, (p50, p90), snap = sample(a.url, a.sample)
|
||||
except (RuntimeError, OSError) as e:
|
||||
print(f" {ms}ms 档采样失败: {e}")
|
||||
break
|
||||
cyc = (snap.get("cyc_us") or {}).get("total") or {}
|
||||
rate = hz / (1000.0 / ms) * 100.0
|
||||
label = f"{ms*1000:g}us" if ms < 1 else f"{ms:g}ms"
|
||||
print(f"{label:>9} {1000.0/ms:>7.0f} {hz:>8.0f} {rate:>5.0f}% "
|
||||
f"{str(cyc.get('p50'))+' / '+str(cyc.get('p90')):>15} "
|
||||
f"{p50:>6.2f}ms {str(snap.get('spin'))[0]} {len(snap.get('motors') or [])}")
|
||||
if snap.get("wkc") != snap.get("expected_wkc"):
|
||||
print(" !! wkc 不达预期,从站可能在丢帧,停止继续加压")
|
||||
break
|
||||
|
||||
restore = a.restore if a.restore is not None else orig
|
||||
r = post(a.url, "/api/motor/period", {"period_ms": restore})
|
||||
print("恢复:", r.get("log"))
|
||||
print("提示: 真机总线一送一回 ≈165us —— 比它快的档位达成率上不去是物理墙,"
|
||||
"不是配置错;保底 4kHz(250us) 在预算内。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+3
-2
@@ -2,8 +2,9 @@
|
||||
rem EtherCAT master bring-up scan (LAN9252)
|
||||
rem Usage: run_scan.bat -> list Npcap adapters
|
||||
rem run_scan.bat I226 -> scan EtherCAT slaves (add --op for PDO test)
|
||||
rem 入口在项目根, 扫描脚本在 src\diag\。
|
||||
chcp 65001 >nul
|
||||
set PYTHONUTF8=1
|
||||
cd /d "%~dp0"
|
||||
"%~dp0venv\Scripts\python.exe" ecat_scan.py %*
|
||||
cd /d "%~dp0src"
|
||||
"%~dp0venv\Scripts\python.exe" diag\ecat_scan.py %*
|
||||
pause
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#include "ee_x86_64_tsc.h"
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
#define NSECS_PER_SEC 1000000000
|
||||
|
||||
uint64_t osEE_x86_64_tsc_read(void);
|
||||
|
||||
void ee_usleep(uint32 usec);
|
||||
|
||||
inline int osal_usleep (uint32 usec)
|
||||
{
|
||||
ee_usleep(usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
uint64_t time = osEE_x86_64_tsc_read();
|
||||
tv->tv_sec = time/NSECS_PER_SEC;
|
||||
tv->tv_sec += 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start */
|
||||
tv->tv_usec = (time%NSECS_PER_SEC)/1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet ret;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
ret.sec = current_time.tv_sec;
|
||||
ret.usec = current_time.tv_usec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + USECS_PER_SEC - start->usec;
|
||||
} else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert *self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert *self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
/* OSEE_PRINT("current: %d:%d -- expire: %d:%d -- result: %d\n", */
|
||||
/* current_time.tv_sec, */
|
||||
/* current_time.tv_usec, */
|
||||
/* stop_time.tv_sec, */
|
||||
/* stop_time.tv_usec, */
|
||||
/* is_not_yet_expired); */
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdlib.h>
|
||||
#include <ee.h>
|
||||
|
||||
// define if debug print is needed
|
||||
#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT OSEE_PRINT
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||
void *osal_malloc(size_t size);
|
||||
void osal_free(void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <rt.h>
|
||||
#include <sys/time.h>
|
||||
#include <osal.h>
|
||||
|
||||
static int64_t sysfrequency;
|
||||
static double qpc2usec;
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_gettimeofday (struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
return gettimeofday (tv, tz);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
int osal_usleep(uint32 usec)
|
||||
{
|
||||
RtSleepEx (usec / 1000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mutex is not needed when running single threaded */
|
||||
|
||||
void osal_mtx_lock(osal_mutex_t * mtx)
|
||||
{
|
||||
/* RtWaitForSingleObject((HANDLE)mtx, INFINITE); */
|
||||
}
|
||||
|
||||
void osal_mtx_unlock(osal_mutex_t * mtx)
|
||||
{
|
||||
/* RtReleaseMutex((HANDLE)mtx); */
|
||||
}
|
||||
|
||||
int osal_mtx_lock_timeout(osal_mutex_t * mtx, uint32_t time_ms)
|
||||
{
|
||||
/* return RtWaitForSingleObject((HANDLE)mtx, time_ms); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
osal_mutex_t * osal_mtx_create(void)
|
||||
{
|
||||
/* return (void*)RtCreateMutex(NULL, FALSE, NULL); */
|
||||
return (void *)0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#ifdef _MSC_VER
|
||||
#define PACKED_BEGIN __pragma(pack(push, 1))
|
||||
#define PACKED
|
||||
#define PACKED_END __pragma(pack(pop))
|
||||
#elif defined(__GNUC__)
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE RTHANDLE
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timespec current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_nsec / 1000;
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns time from some unspecified moment in past,
|
||||
* strictly increasing, used for time intervals measurement. */
|
||||
static void osal_getrelativetime(struct timeval *tv)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_getrelativetime(&start_time);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_getrelativetime(¤t_time);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_
|
||||
#define _osal_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "osal_defs.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* General types */
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
typedef uint8_t boolean;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef int64_t int64;
|
||||
typedef uint64_t uint64;
|
||||
typedef float float32;
|
||||
typedef double float64;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 sec; /*< Seconds elapsed since the Epoch (Jan 1, 1970) */
|
||||
uint32 usec; /*< Microseconds elapsed since last second boundary */
|
||||
} ec_timet;
|
||||
|
||||
typedef struct osal_timer
|
||||
{
|
||||
ec_timet stop_time;
|
||||
} osal_timert;
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_us);
|
||||
boolean osal_timer_is_expired(osal_timert * self);
|
||||
int osal_usleep(uint32 usec);
|
||||
ec_timet osal_current_time(void);
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff);
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param);
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <osal.h>
|
||||
#include <kern.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <config.h>
|
||||
|
||||
#define timercmp(a, b, CMP) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? \
|
||||
((a)->tv_usec CMP (b)->tv_usec) : \
|
||||
((a)->tv_sec CMP (b)->tv_sec))
|
||||
#define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
#define USECS_PER_TICK (USECS_PER_SEC / CFG_TICKS_PER_SECOND)
|
||||
|
||||
|
||||
/* Workaround for rt-labs defect 776.
|
||||
* Default implementation of udelay() didn't work correctly when tick was
|
||||
* shorter than one millisecond.
|
||||
*/
|
||||
void udelay (uint32_t us)
|
||||
{
|
||||
tick_t ticks = (us / USECS_PER_TICK) + 1;
|
||||
task_delay (ticks);
|
||||
}
|
||||
|
||||
int gettimeofday(struct timeval *tp, void *tzp)
|
||||
{
|
||||
tick_t tick = tick_get();
|
||||
tick_t ticks_left;
|
||||
|
||||
ASSERT (tp != NULL);
|
||||
|
||||
tp->tv_sec = tick / CFG_TICKS_PER_SECOND;
|
||||
|
||||
ticks_left = tick % CFG_TICKS_PER_SECOND;
|
||||
tp->tv_usec = ticks_left * USECS_PER_TICK;
|
||||
ASSERT (tp->tv_usec < USECS_PER_SEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
udelay(usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
return gettimeofday(tv, tz);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
gettimeofday (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == false;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
thandle = task_spawn ("worker", func, 6,stacksize, param);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
thandle = task_spawn ("worker_rt", func, 15 ,stacksize, param);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE task_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <osal.h>
|
||||
#include <vxWorks.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
|
||||
#define timercmp(a, b, CMP) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? \
|
||||
((a)->tv_usec CMP (b)->tv_usec) : \
|
||||
((a)->tv_sec CMP (b)->tv_sec))
|
||||
#define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
/* OBS! config worker threads must have higher prio that task running ec_configuration */
|
||||
#define ECAT_TASK_PRIO_HIGH 20 /* Priority for high performance network task */
|
||||
#define ECAT_TASK_PRIO_LOW 80 /* Priority for high performance network task */
|
||||
#define ECAT_STACK_SIZE 10000 /* Stack size for high performance task */
|
||||
static int ecatTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
|
||||
static int ecatTaskIndex = 0;
|
||||
|
||||
#ifndef use_task_delay
|
||||
#define use_task_delay 1
|
||||
#endif
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
#if (use_task_delay == 1)
|
||||
/* Task delay 0 only yields */
|
||||
taskDelay(usec / 1000);
|
||||
return 0;
|
||||
#else
|
||||
/* The suspension may be longer than requested due to the rounding up of
|
||||
* the request to the timer's resolution or to other scheduling activities
|
||||
* (e.g., a higher priority task intervenes).
|
||||
*/
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
return nanosleep(&ts, NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime CLOCK_MONOTONIC to a avoid NTP time adjustments */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_LOW,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_rt_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_HIGH,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE TASK_ID
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <osal.h>
|
||||
#include "osal_win32.h"
|
||||
|
||||
static int64_t sysfrequency;
|
||||
static double qpc2usec;
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
static int osal_getrelativetime(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
int64_t wintime, usecs;
|
||||
(void)tz;
|
||||
if(!sysfrequency)
|
||||
{
|
||||
timeBeginPeriod(1);
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&sysfrequency);
|
||||
qpc2usec = 1000000.0 / sysfrequency;
|
||||
}
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&wintime);
|
||||
usecs = (int64_t)((double)wintime * qpc2usec);
|
||||
tv->tv_sec = (long)(usecs / 1000000);
|
||||
tv->tv_usec = (long)(usecs - (tv->tv_sec * 1000000));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
FILETIME system_time;
|
||||
int64 system_time64, usecs;
|
||||
(void)tz;
|
||||
/* The offset variable is required to switch from Windows epoch (January 1, 1601) to
|
||||
* Unix epoch (January 1, 1970). Number of days between both epochs: 134.774
|
||||
*
|
||||
* The time returned by GetSystemTimeAsFileTime() changes in 100 ns steps, so the
|
||||
* following factors are required for the conversion from days to 100 ns steps:
|
||||
*
|
||||
* 86.400 seconds per day; 1.000.000 microseconds per second; 10 * 100 ns per microsecond
|
||||
*/
|
||||
int64 offset = -134774LL * 86400LL * 1000000LL * 10LL;
|
||||
|
||||
GetSystemTimeAsFileTime(&system_time);
|
||||
|
||||
system_time64 = ((int64)(system_time.dwHighDateTime) << 32) + (int64)system_time.dwLowDateTime;
|
||||
system_time64 += offset;
|
||||
usecs = system_time64 / 10;
|
||||
|
||||
tv->tv_sec = (long)(usecs / 1000000);
|
||||
tv->tv_usec = (long)(usecs - (tv->tv_sec * 1000000));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert *self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_getrelativetime (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert *self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_getrelativetime (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
int osal_usleep(uint32 usec)
|
||||
{
|
||||
osal_timert qtime;
|
||||
osal_timer_start(&qtime, usec);
|
||||
if(usec >= 1000)
|
||||
{
|
||||
SleepEx(usec / 1000, FALSE);
|
||||
}
|
||||
while(!osal_timer_is_expired(&qtime));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
*(OSAL_THREAD_HANDLE*)thandle = CreateThread(NULL, stacksize, func, param, 0, NULL);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
ret = osal_thread_create(thandle, stacksize, func, param);
|
||||
if (ret)
|
||||
{
|
||||
ret = SetThreadPriority(thandle, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude some conflicting definitions in windows header
|
||||
#include <windows.h>
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED
|
||||
#ifdef __GNUC__
|
||||
#define PACKED_BEGIN _Pragma("pack(push,1)")
|
||||
#define PACKED_END _Pragma("pack(pop)")
|
||||
#else
|
||||
#define PACKED_BEGIN __pragma(pack(push, 1))
|
||||
#define PACKED_END __pragma(pack(pop))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE HANDLE
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_win32_
|
||||
#define _osal_win32_
|
||||
|
||||
/* Convenience macros for operations on timevals.
|
||||
NOTE: `timercmp' does not work for >= or <=. */
|
||||
# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
|
||||
# define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
# define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct timezone;
|
||||
// currently the tz parameter is ignored in the win32 implmentation.
|
||||
int osal_gettimeofday (struct timeval *tv, struct timezone *tz);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ee.h"
|
||||
#include "intel_i210.h"
|
||||
#include "ee_x86_64_tsc.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
void ee_port_lock(void);
|
||||
void ee_port_unlock(void);
|
||||
|
||||
static inline void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int d;
|
||||
struct eth_device *dev;
|
||||
OSEE_PRINT("ecx_setupnic() searching %s...\n", ifname);
|
||||
|
||||
for (d = 0;; ++d) {
|
||||
dev = eth_get_device(d);
|
||||
|
||||
if (dev == NULL)
|
||||
break; // ERROR: device not found
|
||||
|
||||
if (!strncmp(dev->name, ifname, MAX_DEVICE_NAME)){
|
||||
// Device found
|
||||
int i;
|
||||
|
||||
eth_setup_device(d, 1, 100);
|
||||
port->dev_id = d;
|
||||
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
break; // device found
|
||||
}
|
||||
}
|
||||
|
||||
return (dev != NULL);
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
inline int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
// Nothing to do
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt = 0;
|
||||
|
||||
ee_port_lock();
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
idx = 0;
|
||||
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF)) {
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
idx = 0;
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
ee_port_unlock();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
|
||||
eth_send_packet(port->dev_id, (*stack->txbuf)[idx], lp, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
ee_port_lock();
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
eth_send_packet(port->dev_id, &(port->txbuf2), port->txbuflength2, 1);
|
||||
ee_port_unlock();
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = eth_receive_packet(port->dev_id, (*stack->tempbuf), lp, 1, 0);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD)) {
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
} else {
|
||||
ee_port_lock();
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
|
||||
|
||||
while (1) {
|
||||
if (ecx_recvpkt(port, stacknumber)) {
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT)) {
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx) {
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
break;
|
||||
} else if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX) {
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
break;
|
||||
} else {
|
||||
OSEE_PRINT("ecx_inframe(): WARNING: strange things happened\n");
|
||||
/* strange things happened */
|
||||
}
|
||||
} else {
|
||||
OSEE_PRINT("ecx_inframe(): WARNING it is NOT an EtherCAT frame!\n");
|
||||
}
|
||||
} else {
|
||||
// WARNING: no messages received.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ee_port_unlock();
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do {
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) ) {
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) ) {
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) ) {
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do {
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME) {
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do {
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET) {
|
||||
osal_timer_start (&timer2, timeout);
|
||||
} else {
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly
|
||||
from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
int ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
|
||||
/** Device id in the device pool */
|
||||
int dev_id;
|
||||
|
||||
// TODO: add mutex support
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
int ec_inframe(uint8 idx, int stacknumber);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "intel_i210.h"
|
||||
#include "ethercat.h"
|
||||
|
||||
#if !defined(__gnu_linux__)
|
||||
#include <machine/endian.h>
|
||||
#else
|
||||
#include <endian.h>
|
||||
#define __htons(x) htobe16(x)
|
||||
#define __ntohs(x) be16toh(x)
|
||||
#endif
|
||||
|
||||
ec_adaptert adapters [DEVS_MAX_NB];
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
inline uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
// __htons() is provided by the bare-metal x86 compiler
|
||||
return __htons(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
inline uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
// __ntohs() is provided by the bare-metal x86 compiler
|
||||
return __ntohs(network);
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert* oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert *ret = NULL;
|
||||
if (eth_discover_devices() >= 0) {
|
||||
for (int i = 0;; ++i) {
|
||||
struct eth_device *dev = eth_get_device(i);
|
||||
if (dev == NULL) {
|
||||
adapters[i-1].next = NULL;
|
||||
break;
|
||||
}
|
||||
strncpy(adapters[i].name, dev->name, MAX_DEVICE_NAME);
|
||||
adapters[i].next = &adapters[i+1];
|
||||
}
|
||||
ret = &(adapters[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert *adapter)
|
||||
{
|
||||
}
|
||||
|
||||
extern int ec_slavecount;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert* oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only sent by the master,
|
||||
* and the sent packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original sent packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <rt.h>
|
||||
#include <traceapi.h>
|
||||
#include "hpeif2.h"
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "oshw.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
ecx_portt ecx_port;
|
||||
ecx_redportt ecx_redport;
|
||||
|
||||
#endif
|
||||
|
||||
#define ECAT_PRINT_INFO printf
|
||||
#define ECAT_PRINT_WARN printf
|
||||
#define ECAT_PRINT_ERROR printf
|
||||
|
||||
/* HPE port settings */
|
||||
const unsigned long interrupt_mode = NO_INTERRUPT;
|
||||
const unsigned long phy_settings = SPEED_100 | DUPLEX_FULL;
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
HPESTATUS status;
|
||||
HPEMEDIASTATUS mstat;
|
||||
time_t now, t;
|
||||
unsigned char mac[6];
|
||||
int i;
|
||||
int dontWaitForLink = 0;
|
||||
int result = 1;
|
||||
HPE_CONFIG_OPTIONS conf = { 0, 0, NULL};
|
||||
|
||||
status = hpeOpen(ifname, phy_settings, interrupt_mode, &(port->handle));
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeOpen failed with status %04x ", status);
|
||||
if(status == E_EXIST) ECAT_PRINT_ERROR("E_EXIST\n");
|
||||
else if(status == E_STATE) ECAT_PRINT_ERROR("E_STATE\n");
|
||||
else if(status == E_PARAM) ECAT_PRINT_ERROR("E_PARAM\n");
|
||||
else if(status == E_INVALID_ADDR) ECAT_PRINT_ERROR("E_INVALID_ADDR\n");
|
||||
else if(status == E_IO) ECAT_PRINT_ERROR("E_IO\n");
|
||||
else if(status == E_TIME) ECAT_PRINT_ERROR("E_TIME\n");
|
||||
else ECAT_PRINT_ERROR("UNKNOWN\n");
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
conf.option_flags |= OPT_PROMISC;
|
||||
|
||||
status = hpeConfigOptions(port->handle, &conf, sizeof(conf));
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeConfigOptions failed with status %04x\n", status);
|
||||
// NOTE: HPE driver for Intel 10/100 Mbps device currently doesn't support multicast.
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
do
|
||||
{
|
||||
status = hpeGetMediaStatus(port->handle, &mstat);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeGetMediaStatus failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
if (mstat.media_speed == SPEED_NONE)
|
||||
{
|
||||
RtSleepEx(1000);
|
||||
time(&t);
|
||||
}
|
||||
} while (mstat.media_speed == SPEED_NONE && t < (now+10));
|
||||
|
||||
if (((mstat.media_speed & phy_settings) == 0) || ((mstat.media_duplex & phy_settings) == 0)) {
|
||||
ECAT_PRINT_ERROR("Media not connected as requested: speed=%u, duplex=%u\n",
|
||||
mstat.media_speed, mstat.media_duplex);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status= hpeGetMacAddress(port->handle, mac);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeGetMacAddress failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* allocate 2 receive buffers and attach them */
|
||||
status = hpeAllocateReceiveBufferSet(port->handle, &(port->rx_buffers), EC_MAXBUF, EC_BUFSIZE);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeAllocateReceiveBufferSet failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = hpeAttachReceiveBufferSet(port->handle, port->rx_buffers);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeAttachReceiveBufferSet failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->redstate = ECT_RED_NONE;
|
||||
/* Init regions */
|
||||
port->getindex_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->rx_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->tx_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->lastidx = 0;
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
// allocate 1 transmit buffer of two fragments and 500 bytes and attach it
|
||||
port->tx_buffers[i] = (HPETXBUFFERSET *)AllocateRtMemory(sizeof(HPETXBUFFER) + sizeof(HPETXBUFFERSET));
|
||||
port->tx_buffers[i]->buffer_count = 1;
|
||||
port->tx_buffers[i]->buffers[0].fragment_count = 1;
|
||||
// first fragment is the fixed header, second is dynamic data
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].ptr = port->txbuf[i];
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].size = EC_BUFSIZE;
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].paddr = 0;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
HPESTATUS status;
|
||||
int i;
|
||||
|
||||
if (port->tx_buffers)
|
||||
{
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
FreeRtMemory(port->tx_buffers[i]);
|
||||
port->tx_buffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (port->rx_buffers)
|
||||
{
|
||||
hpeFreeReceiveBufferSet(port->handle, &(port->rx_buffers));
|
||||
}
|
||||
|
||||
status= hpeClose(port->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeClose failed with status %04x\n", status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with Ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = (ec_etherheadert *)p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
WaitForRtControl(port->getindex_region);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if ( port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
}
|
||||
|
||||
port->lastidx = idx;
|
||||
ReleaseRtControl();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
HPESTATUS status;
|
||||
DWORD txstate;
|
||||
int lp;
|
||||
ec_stackT *stack;
|
||||
int result = 0;
|
||||
int retries = 1024;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
port->tx_buffers[idx]->buffer_count = 1;
|
||||
port->tx_buffers[idx]->buffers[0].fragments[0].size = lp;
|
||||
|
||||
// wait for transmit to complete
|
||||
do
|
||||
{
|
||||
result = hpeGetTransmitterState(port->handle, &txstate) == E_OK && txstate == HPE_TXBUSY;
|
||||
} while (result && retries-- > 0);
|
||||
|
||||
if (result)
|
||||
{
|
||||
result = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_RT_event('S',(WORD)2);
|
||||
status = hpeAttachTransmitBufferSet(port->handle, port->tx_buffers[idx]);
|
||||
if (status != E_OK)
|
||||
{
|
||||
result = -2;
|
||||
goto end;
|
||||
}
|
||||
log_RT_event('S',(WORD)3);
|
||||
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
status = hpeStartTransmitter(port->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
result = -3;
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_RT_event('S',(WORD)4);
|
||||
result = lp;
|
||||
|
||||
end:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
HPESTATUS status;
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2)[ETH_HEADERSIZE];
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
|
||||
// OBS! redundant not ACTIVE for BFIN, just added to compile
|
||||
//ASSERT (0);
|
||||
hpeAttachTransmitBufferSet(port->redport->handle, port->tx_buffers[idx]);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
status = hpeStartTransmitter(port->redport->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
HPEBUFFER *rxbuffer;
|
||||
HPESTATUS status;
|
||||
ec_stackT *stack;
|
||||
int bytesrx = 0;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
|
||||
log_RT_event('R',(WORD)2);
|
||||
|
||||
status = hpeGetReceiveBuffer(port->handle, &rxbuffer);
|
||||
if (status == E_OK)
|
||||
{
|
||||
memcpy(stack->tempbuf,rxbuffer->ptr, rxbuffer->used);
|
||||
bytesrx = rxbuffer->used;
|
||||
port->tempinbufs = bytesrx;
|
||||
// TODO case no interrupt
|
||||
}
|
||||
log_RT_event('R',(WORD)3);
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForRtControl(port->rx_region);
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp = (ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseRtControl();
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
{
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port,idx,1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
if (timeout == 0 && (port->redstate == ECT_RED_NONE))
|
||||
{
|
||||
int loop_cnt = 0;
|
||||
/* Allow us to consume MAX buffer number of frames */
|
||||
do
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
loop_cnt++;
|
||||
} while (wkc <= EC_NOFRAME && (loop_cnt <= EC_MAXBUF));
|
||||
}
|
||||
else
|
||||
{
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
}
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer1);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#define HAVE_REMOTE
|
||||
|
||||
#include <rt.h>
|
||||
#include "hpeif2.h"
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/* Intime */
|
||||
HPEHANDLE handle;
|
||||
HPERXBUFFERSET *rx_buffers;
|
||||
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
|
||||
} ecx_redportt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
RTHANDLE getindex_region;
|
||||
RTHANDLE tx_region;
|
||||
RTHANDLE rx_region;
|
||||
/* Intime */
|
||||
HPEHANDLE handle;
|
||||
HPERXBUFFERSET *rx_buffers;
|
||||
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
//extern ecx_portt ecx_port;
|
||||
//extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setupheader(void *p);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/endian.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons (uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs (uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters (ec_adaptert * adapter)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "ethercatmain.h"
|
||||
#include "nicdrv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint16 oshw_htons (uint16 hostshort);
|
||||
uint16 oshw_ntohs (uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters (void);
|
||||
void oshw_free_adapters (ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int r, rval, ifindex;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_ll sll;
|
||||
int *psock;
|
||||
pthread_mutexattr_t mutexattr;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_init(&mutexattr);
|
||||
pthread_mutexattr_setprotocol(&mutexattr , PTHREAD_PRIO_INHERIT);
|
||||
pthread_mutex_init(&(port->getindex_mutex), &mutexattr);
|
||||
pthread_mutex_init(&(port->tx_mutex) , &mutexattr);
|
||||
pthread_mutex_init(&(port->rx_mutex) , &mutexattr);
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
/* we use RAW packet socket, with packet type ETH_P_ECAT */
|
||||
*psock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ECAT));
|
||||
if(*psock < 0)
|
||||
return 0;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1;
|
||||
r = 0;
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
i = 1;
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i));
|
||||
/* connect socket to NIC by name */
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
r |= ioctl(*psock, SIOCGIFINDEX, &ifr);
|
||||
ifindex = ifr.ifr_ifindex;
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
ifr.ifr_flags = 0;
|
||||
/* reset flags of NIC interface */
|
||||
r |= ioctl(*psock, SIOCGIFFLAGS, &ifr);
|
||||
/* set flags of NIC interface, here promiscuous and broadcast */
|
||||
ifr.ifr_flags = ifr.ifr_flags | IFF_PROMISC | IFF_BROADCAST;
|
||||
r |= ioctl(*psock, SIOCSIFFLAGS, &ifr);
|
||||
/* bind socket to protocol, in this case RAW EtherCAT */
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifindex;
|
||||
sll.sll_protocol = htons(ETH_P_ECAT);
|
||||
r |= bind(*psock, (struct sockaddr *)&sll, sizeof(sll));
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
if (r == 0) rval = 1;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
close(port->sockhandle);
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
close(port->redport->sockhandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock( &(port->getindex_mutex) );
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock( &(port->getindex_mutex) );
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
|
||||
if (rval == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0) == -1)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter = NULL;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (prev_adapter)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in Linux we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to fund out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i, rval;
|
||||
pcap_t **psock;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = NULL;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_init(&(port->getindex_mutex), NULL);
|
||||
pthread_mutex_init(&(port->tx_mutex), NULL);
|
||||
pthread_mutex_init(&(port->rx_mutex), NULL);
|
||||
port->sockhandle = NULL;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
*psock = pcap_open_live(ifname, 65536, 1, 0, errbuf);
|
||||
if (NULL == *psock)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rval = pcap_setdirection(*psock, PCAP_D_IN);
|
||||
if (rval != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rval = pcap_setnonblock(*psock, 1, errbuf);
|
||||
if (rval != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle != NULL)
|
||||
{
|
||||
pthread_mutex_destroy(&(port->getindex_mutex));
|
||||
pthread_mutex_destroy(&(port->tx_mutex));
|
||||
pthread_mutex_destroy(&(port->rx_mutex));
|
||||
pcap_close(port->sockhandle);
|
||||
port->sockhandle = NULL;
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle != NULL))
|
||||
{
|
||||
pcap_close(port->redport->sockhandle);
|
||||
port->redport->sockhandle = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock(&(port->getindex_mutex));
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock(&(port->getindex_mutex));
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
|
||||
if (rval == PCAP_ERROR)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
struct pcap_pkthdr * header;
|
||||
unsigned char const * pkt_data;
|
||||
int res;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
|
||||
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
|
||||
if (res <=0 )
|
||||
{
|
||||
port->tempinbufs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytesrx = header->len;
|
||||
if (bytesrx > lp)
|
||||
{
|
||||
bytesrx = lp;
|
||||
}
|
||||
memcpy(*stack->tempbuf, pkt_data, bytesrx);
|
||||
port->tempinbufs = bytesrx;
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] tvs = timeout
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pcap/pcap.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
pcap_t **sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lenghts */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in macosx we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int r, rval;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
int *bpf;
|
||||
// const uint8_t bpffnamelen = 12;
|
||||
char fname[13] = {0};
|
||||
const int maxbpffile = 1000;
|
||||
uint true_val = 1;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
bpf = &(port->redport->sockhandle);
|
||||
*bpf = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_init(&(port->getindex_mutex), NULL);
|
||||
pthread_mutex_init(&(port->tx_mutex) , NULL);
|
||||
pthread_mutex_init(&(port->rx_mutex) , NULL);
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
bpf = &(port->sockhandle);
|
||||
}
|
||||
|
||||
/* Open a bpf file descriptor */
|
||||
*bpf = -1;
|
||||
for(i = 0; *bpf == -1 && i < maxbpffile; ++i)
|
||||
{
|
||||
sprintf(fname, "/dev/bpf%i", i);
|
||||
*bpf = open(fname, O_RDWR);
|
||||
}
|
||||
|
||||
if(*bpf == -1)
|
||||
{
|
||||
/* Failed to open bpf */
|
||||
perror("Could not open bpf\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Need to hand the same buffer size as bpf expects,
|
||||
force bpf to use our buffer size! */
|
||||
uint buffer_len = sizeof(ec_bufT);
|
||||
if (ioctl(*bpf, BIOCSBLEN, &buffer_len) == -1) {
|
||||
perror("BIOCGBLEN");
|
||||
}
|
||||
|
||||
if (buffer_len < 1518) {
|
||||
printf("buffer_len %d < 1518\n", buffer_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* connect bpf to NIC by name */
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
if(ioctl(*bpf, BIOCSETIF, &ifr) == -1) {
|
||||
perror("BIOCSETIF");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set required bpf options */
|
||||
|
||||
/* Activate immediate mode */
|
||||
if (ioctl(*bpf, BIOCIMMEDIATE, &true_val) == -1) {
|
||||
perror("BIOCIMMEDIATE");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set interface in promiscuous mode */
|
||||
if (ioctl(*bpf, BIOCPROMISC, &true_val) == -1) {
|
||||
perror("BIOCPROMISC");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow to have custom source address */
|
||||
if (ioctl(*bpf, BIOCSHDRCMPLT, &true_val) == -1) {
|
||||
perror("BIOCSHDRCMPLT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Listen only to incomming messages */
|
||||
uint direction = BPF_D_IN;
|
||||
if (ioctl(*bpf, BIOCSDIRECTION, &direction) == -1) {
|
||||
perror("BIOCSDIRECTION");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set read timeout */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 11000;
|
||||
if (ioctl(*bpf, BIOCSRTIMEOUT, &timeout) == -1) {
|
||||
perror("BIOCSRTIMEOUT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
close(port->sockhandle);
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
close(port->redport->sockhandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock( &(port->getindex_mutex) );
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock( &(port->getindex_mutex) );
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
//rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = write (*stack->sock,(*stack->txbuf)[idx], lp);
|
||||
if (rval == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (write(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2) == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
//bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
|
||||
bytesrx = read(*stack->sock, (*stack->tempbuf), lp);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
/* The data read from /dev/bpf includes an extra header, skip it. */
|
||||
struct bpf_hdr *bpfh = (struct bpf_hdr *)(stack->tempbuf);
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(*stack->tempbuf + bpfh->bh_hdrlen);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
/* The EtherCAT header follows the ethernet frame header. */
|
||||
ecp =(ec_comt*)(&ehp[1]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip headers) */
|
||||
memcpy(rxbuf, &(ehp[1]), port->tempinbufs - ((uint32_t)ecp - (uint32_t)*stack->tempbuf));
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in rtems we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,802 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: fec_ecat.c 138 2014-04-11 09:11:48Z rtlfrm $
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "fec_ecat.h"
|
||||
|
||||
#include <dev.h>
|
||||
#include <kern.h>
|
||||
#include <string.h>
|
||||
#include <uassert.h>
|
||||
#include <eth/fec_buffer.h>
|
||||
#include <eth/phy/mii.h>
|
||||
#include <bsp.h>
|
||||
#include <config.h>
|
||||
|
||||
#undef RTK_DEBUG /* Print debugging info? */
|
||||
#undef RTK_DEBUG_DATA /* Print packets? */
|
||||
|
||||
#ifdef RTK_DEBUG
|
||||
#define DPRINT(...) rprintp ("FEC: "__VA_ARGS__)
|
||||
#else
|
||||
#define DPRINT(...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef RTK_DEBUG_DATA
|
||||
#define DUMP_PACKET(p, l) fec_ecat_dump_packet (p, l)
|
||||
#else
|
||||
#define DUMP_PACKET(p, l)
|
||||
#endif /* RTK_DEBUG_DATA */
|
||||
|
||||
#define ETHERNET_FCS_SIZE_IN_BYTES 4 /* Frame Check Sequence (32-bit CRC) */
|
||||
|
||||
#define DELAY(ms) task_delay (tick_from_ms (ms) + 1)
|
||||
|
||||
#define ETH_PHY_ADDRESS 0x1 /* Default jumper setting on TWR-K60F */
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
#define FEC_ALLIGNED __attribute__((section(".dma"),aligned(16)))
|
||||
|
||||
/*
|
||||
* "The device's system clock is connected to the module clock,
|
||||
* as named in the Ethernet chapter. The minimum system clock frequency
|
||||
* for 100 Mbps operation is 25 MHz."
|
||||
*
|
||||
* - Kinetis K60 manual ch. 3.9.1.1 "Ethernet Clocking Options"
|
||||
*/
|
||||
#define FEC_MIN_MODULE_CLOCK_Hz (25 * 1000 * 1000)
|
||||
|
||||
/* Note that the core clock and the system clock has the same clock frequency */
|
||||
#define FEC_MODULE_CLOCK_Hz CFG_IPG_CLOCK
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
typedef struct fec_mac_t
|
||||
{
|
||||
uint32_t lower; /* octets 0, 1, 2, 3 */
|
||||
uint32_t upper; /* octets 4, 5 */
|
||||
} fec_mac_t;
|
||||
|
||||
/* Message Information Block (MIB) Counters */
|
||||
typedef struct fec_mib
|
||||
{
|
||||
uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */
|
||||
uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */
|
||||
uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */
|
||||
uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */
|
||||
uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */
|
||||
uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */
|
||||
uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */
|
||||
uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */
|
||||
uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */
|
||||
uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */
|
||||
uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */
|
||||
uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */
|
||||
uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */
|
||||
uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */
|
||||
uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */
|
||||
uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */
|
||||
uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */
|
||||
uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */
|
||||
uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */
|
||||
uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */
|
||||
uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */
|
||||
uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */
|
||||
uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */
|
||||
uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */
|
||||
uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */
|
||||
uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */
|
||||
uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */
|
||||
uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */
|
||||
uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */
|
||||
uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */
|
||||
uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */
|
||||
uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */
|
||||
uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */
|
||||
uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */
|
||||
uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */
|
||||
uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */
|
||||
uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */
|
||||
uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */
|
||||
uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */
|
||||
uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */
|
||||
uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */
|
||||
uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */
|
||||
uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */
|
||||
uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */
|
||||
uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */
|
||||
uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */
|
||||
uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */
|
||||
uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */
|
||||
uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */
|
||||
uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */
|
||||
uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */
|
||||
uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */
|
||||
uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */
|
||||
uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */
|
||||
uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */
|
||||
uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */
|
||||
uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */
|
||||
} fec_mib_t;
|
||||
|
||||
COMPILETIME_ASSERT (sizeof (fec_mib_t) == 0x100);
|
||||
|
||||
/* Register read/write struct */
|
||||
typedef struct reg_fec
|
||||
{
|
||||
uint32_t resv0;
|
||||
uint32_t eir;
|
||||
uint32_t eimr;
|
||||
uint32_t resv1;
|
||||
uint32_t rdar;
|
||||
uint32_t tdar;
|
||||
uint32_t resv2[3];
|
||||
uint32_t ecr;
|
||||
uint32_t resv3[6];
|
||||
uint32_t mmfr;
|
||||
uint32_t mscr;
|
||||
uint32_t resv4[7];
|
||||
uint32_t mibc;
|
||||
uint32_t resv5[7];
|
||||
uint32_t rcr;
|
||||
uint32_t resv6[15];
|
||||
uint32_t tcr;
|
||||
uint32_t resv7[7];
|
||||
fec_mac_t pa; /* Physical address. PALR/PAUR */
|
||||
uint32_t opd;
|
||||
uint32_t resv8[10];
|
||||
uint32_t iaur;
|
||||
uint32_t ialr;
|
||||
uint32_t gaur;
|
||||
uint32_t galr;
|
||||
uint32_t resv9[7];
|
||||
uint32_t tfwr;
|
||||
uint32_t resv10;
|
||||
uint32_t frbr;
|
||||
uint32_t frsr;
|
||||
uint32_t resv11[11];
|
||||
uint32_t erdsr;
|
||||
uint32_t etdsr;
|
||||
uint32_t emrbr;
|
||||
uint32_t resv14;
|
||||
uint32_t rx_section_full; /* Not present on all controllers */
|
||||
uint32_t rx_section_empty; /* Not present on all controllers */
|
||||
uint32_t rx_almost_empty; /* Not present on all controllers */
|
||||
uint32_t rx_almost_full; /* Not present on all controllers */
|
||||
uint32_t tx_section_empty; /* Not present on all controllers */
|
||||
uint32_t tx_almost_empty; /* Not present on all controllers */
|
||||
uint32_t tx_almost_full; /* Not present on all controllers */
|
||||
uint32_t resv12[21];
|
||||
const fec_mib_t mib;
|
||||
uint32_t fec_miigsk_cfgr; /* Not present on all controllers */
|
||||
uint32_t resv13;
|
||||
uint32_t fec_miigsk_enr; /* Not present on all controllers */
|
||||
uint32_t resv15[0x7d];
|
||||
fec_mac_t smac[4]; /* Not present on all controllers */
|
||||
} reg_fec_t;
|
||||
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, emrbr) == 0x188);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, tx_almost_full) == 0x1A8);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, fec_miigsk_cfgr) == 0x300);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac) == 0x500);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac[3]) == 0x518);
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* Bit definitions and macros for FEC_EIR */
|
||||
#define FEC_EIR_CLEAR_ALL (0xFFF80000)
|
||||
#define FEC_EIR_HBERR (0x80000000)
|
||||
#define FEC_EIR_BABR (0x40000000)
|
||||
#define FEC_EIR_BABT (0x20000000)
|
||||
#define FEC_EIR_GRA (0x10000000)
|
||||
#define FEC_EIR_TXF (0x08000000)
|
||||
#define FEC_EIR_TXB (0x04000000)
|
||||
#define FEC_EIR_RXF (0x02000000)
|
||||
#define FEC_EIR_RXB (0x01000000)
|
||||
#define FEC_EIR_MII (0x00800000)
|
||||
#define FEC_EIR_EBERR (0x00400000)
|
||||
#define FEC_EIR_LC (0x00200000)
|
||||
#define FEC_EIR_RL (0x00100000)
|
||||
#define FEC_EIR_UN (0x00080000)
|
||||
|
||||
/* Bit definitions and macros for FEC_RDAR */
|
||||
#define FEC_RDAR_R_DES_ACTIVE (0x01000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_TDAR */
|
||||
#define FEC_TDAR_X_DES_ACTIVE (0x01000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_ECR */
|
||||
#define FEC_ECR_DBSWP (0x00000100)
|
||||
#define FEC_ECR_ETHER_EN (0x00000002)
|
||||
#define FEC_ECR_RESET (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_MMFR */
|
||||
#define FEC_MMFR_DATA(x) (((x)&0xFFFF))
|
||||
#define FEC_MMFR_ST(x) (((x)&0x03)<<30)
|
||||
#define FEC_MMFR_ST_01 (0x40000000)
|
||||
#define FEC_MMFR_OP_RD (0x20000000)
|
||||
#define FEC_MMFR_OP_WR (0x10000000)
|
||||
#define FEC_MMFR_PA(x) (((x)&0x1F)<<23)
|
||||
#define FEC_MMFR_RA(x) (((x)&0x1F)<<18)
|
||||
#define FEC_MMFR_TA(x) (((x)&0x03)<<16)
|
||||
#define FEC_MMFR_TA_10 (0x00020000)
|
||||
|
||||
/* Bit definitions and macros for FEC_MSCR */
|
||||
#define FEC_MSCR_DIS_PREAMBLE (0x00000080)
|
||||
#define FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<1)
|
||||
|
||||
/* Bit definitions and macros for FEC_MIBC */
|
||||
#define FEC_MIBC_MIB_DISABLE (0x80000000)
|
||||
#define FEC_MIBC_MIB_IDLE (0x40000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_RCR */
|
||||
#define FEC_RCR_GRS (0x80000000)
|
||||
#define FEC_RCR_NO_LGTH_CHECK (0x40000000)
|
||||
#define FEC_RCR_MAX_FL(x) (((x)&0x7FF)<<16)
|
||||
#define FEC_RCR_CNTL_FRM_ENA (0x00008000)
|
||||
#define FEC_RCR_CRC_FWD (0x00004000)
|
||||
#define FEC_RCR_PAUSE_FWD (0x00002000)
|
||||
#define FEC_RCR_PAD_EN (0x00001000)
|
||||
#define FEC_RCR_RMII_ECHO (0x00000800)
|
||||
#define FEC_RCR_RMII_LOOP (0x00000400)
|
||||
#define FEC_RCR_RMII_10T (0x00000200)
|
||||
#define FEC_RCR_RMII_MODE (0x00000100)
|
||||
#define FEC_RCR_SGMII_ENA (0x00000080)
|
||||
#define FEC_RCR_RGMII_ENA (0x00000040)
|
||||
#define FEC_RCR_FCE (0x00000020)
|
||||
#define FEC_RCR_BC_REJ (0x00000010)
|
||||
#define FEC_RCR_PROM (0x00000008)
|
||||
#define FEC_RCR_MII_MODE (0x00000004)
|
||||
#define FEC_RCR_DRT (0x00000002)
|
||||
#define FEC_RCR_LOOP (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_TCR */
|
||||
#define FEC_TCR_RFC_PAUSE (0x00000010)
|
||||
#define FEC_TCR_TFC_PAUSE (0x00000008)
|
||||
#define FEC_TCR_FDEN (0x00000004)
|
||||
#define FEC_TCR_HBC (0x00000002)
|
||||
#define FEC_TCR_GTS (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_PAUR */
|
||||
#define FEC_PAUR_PADDR2(x) (((x)&0xFFFF)<<16)
|
||||
#define FEC_PAUR_TYPE(x) ((x)&0xFFFF)
|
||||
|
||||
/* Bit definitions and macros for FEC_OPD */
|
||||
#define FEC_OPD_PAUSE_DUR(x) (((x)&0x0000FFFF)<<0)
|
||||
#define FEC_OPD_OPCODE(x) (((x)&0x0000FFFF)<<16)
|
||||
|
||||
/* Bit definitions and macros for FEC_TFWR */
|
||||
#define FEC_TFWR_STR_FWD BIT (8) /* Present on ENET but not on FEC */
|
||||
#define FEC_TFWR_X_WMRK(x) ((x)&0x03)
|
||||
#define FEC_TFWR_X_WMRK_64 (0x01)
|
||||
#define FEC_TFWR_X_WMRK_128 (0x02)
|
||||
#define FEC_TFWR_X_WMRK_192 (0x03)
|
||||
|
||||
/* Bit definitions and macros for FEC_FRBR */
|
||||
#define FEC_FRBR_R_BOUND(x) (((x)&0xFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_FRSR */
|
||||
#define FEC_FRSR_R_FSTART(x) (((x)&0xFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_ERDSR */
|
||||
#define FEC_ERDSR_R_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_ETDSR */
|
||||
#define FEC_ETDSR_X_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_EMRBR */
|
||||
#define FEC_EMRBR_R_BUF_SIZE(x) (((x)&0x7F)<<4)
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* the defines of MII operation */
|
||||
#define FEC_MII_ST 0x40000000
|
||||
#define FEC_MII_OP_OFF 28
|
||||
#define FEC_MII_OP_MASK 0x03
|
||||
#define FEC_MII_OP_RD 0x02
|
||||
#define FEC_MII_OP_WR 0x01
|
||||
#define FEC_MII_PA_OFF 23
|
||||
#define FEC_MII_PA_MASK 0xFF
|
||||
#define FEC_MII_RA_OFF 18
|
||||
#define FEC_MII_RA_MASK 0xFF
|
||||
#define FEC_MII_TA 0x00020000
|
||||
#define FEC_MII_DATA_OFF 0
|
||||
#define FEC_MII_DATA_MASK 0x0000FFFF
|
||||
|
||||
#define FEC_MII_FRAME (FEC_MII_ST | FEC_MII_TA)
|
||||
#define FEC_MII_OP(x) (((x) & FEC_MII_OP_MASK) << FEC_MII_OP_OFF)
|
||||
#define FEC_MII_PA(pa) (((pa) & FEC_MII_PA_MASK) << FEC_MII_PA_OFF)
|
||||
#define FEC_MII_RA(ra) (((ra) & FEC_MII_RA_MASK) << FEC_MII_RA_OFF)
|
||||
#define FEC_MII_SET_DATA(v) (((v) & FEC_MII_DATA_MASK) << FEC_MII_DATA_OFF)
|
||||
#define FEC_MII_GET_DATA(v) (((v) >> FEC_MII_DATA_OFF) & FEC_MII_DATA_MASK)
|
||||
#define FEC_MII_READ(pa, ra) ((FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_RD)) |\
|
||||
FEC_MII_PA(pa) | FEC_MII_RA(ra))
|
||||
#define FEC_MII_WRITE(pa, ra, v) (FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_WR)|\
|
||||
FEC_MII_PA(pa) | FEC_MII_RA(ra) | FEC_MII_SET_DATA(v))
|
||||
|
||||
#define FEC_MII_TIMEOUT 10 // 1 * 10 ms
|
||||
#define FEC_MII_TICK 1 // 1 ms
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* Ethernet Transmit and Receive Buffers */
|
||||
#define PKT_MAXBUF_SIZE 1518
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
typedef enum fec_phy_inteface
|
||||
{
|
||||
/**
|
||||
* MII, Media Independent Interface
|
||||
* 4-bit operating at 25 Mhz
|
||||
*/
|
||||
FEC_PHY_MII,
|
||||
/**
|
||||
* RMII, Reduced Media Independent Interface
|
||||
* 2-bit operating at 50 Mhz
|
||||
*/
|
||||
FEC_PHY_RMII,
|
||||
FEC_PHY_RGMII
|
||||
} fec_phy_inteface_t;
|
||||
|
||||
typedef struct fec_cfg
|
||||
{
|
||||
uint32_t base;
|
||||
uint32_t clock;
|
||||
fec_buffer_bd_t * tx_bd_base;
|
||||
fec_buffer_bd_t * rx_bd_base;
|
||||
fec_phy_inteface_t phy_interface;
|
||||
} fec_cfg_t;
|
||||
|
||||
typedef struct fec
|
||||
{
|
||||
uint32_t clock;
|
||||
fec_buffer_bd_t * tx_bd_base;
|
||||
fec_buffer_bd_t * rx_bd_base;
|
||||
volatile reg_fec_t *base;
|
||||
phy_t * phy;
|
||||
fec_phy_inteface_t phy_interface;
|
||||
} fec_t;
|
||||
|
||||
/* Buffer descriptors. */
|
||||
static fec_buffer_bd_t fec_tx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
||||
static fec_buffer_bd_t fec_rx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
||||
|
||||
/* Data buffers. */
|
||||
static uint8_t fec_tx_data[NUM_BUFFERS * TX_BUFFER_SIZE] FEC_ALLIGNED;
|
||||
static uint8_t fec_rx_data[NUM_BUFFERS * RX_BUFFER_SIZE] FEC_ALLIGNED;
|
||||
|
||||
static fec_t *fec;
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#ifdef RTK_DEBUG
|
||||
const char * fec_ecat_link_duplex_name (uint8_t link_state)
|
||||
{
|
||||
if (link_state & PHY_LINK_OK)
|
||||
{
|
||||
if (link_state & PHY_LINK_FULL_DUPLEX)
|
||||
{
|
||||
return "full duplex";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "half duplex";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "no duplex (link down)";
|
||||
}
|
||||
}
|
||||
|
||||
const char * fec_ecat_link_speed_name (uint8_t link_state)
|
||||
{
|
||||
if (link_state & PHY_LINK_OK)
|
||||
{
|
||||
if (link_state & PHY_LINK_10MBIT)
|
||||
{
|
||||
return "10 Mbps";
|
||||
}
|
||||
else if (link_state & PHY_LINK_100MBIT)
|
||||
{
|
||||
return "100 Mbps";
|
||||
}
|
||||
else if (link_state & PHY_LINK_1000MBIT)
|
||||
{
|
||||
return "1 Gbps";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "unknown speed";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "0 Mbps (link down)";
|
||||
}
|
||||
}
|
||||
#endif /* RTK_DEBUG */
|
||||
|
||||
#ifdef RTK_DEBUG_DATA
|
||||
#include <ctype.h>
|
||||
static void fec_ecat_dump_packet (const uint8_t * payload, size_t len)
|
||||
{
|
||||
size_t i, j, n;
|
||||
char s[80];
|
||||
|
||||
ASSERT (payload != NULL);
|
||||
|
||||
for (i = 0; i < len; i += 16)
|
||||
{
|
||||
n = 0;
|
||||
for (j = 0; j < 16 && (i + j) < len; j++)
|
||||
{
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "%02x ", payload[i + j]);
|
||||
}
|
||||
for (; j < 16; j++)
|
||||
{
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, " ");
|
||||
}
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "|");
|
||||
for (j = 0; j < 16 && (i + j) < len; j++)
|
||||
{
|
||||
uint8_t c = payload[i + j];
|
||||
c = (isprint (c)) ? c : '.';
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "%c", c);
|
||||
}
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "|\n");
|
||||
ASSERT (n <= sizeof(s));
|
||||
DPRINT ("%s", s);
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_DATA */
|
||||
|
||||
static uint16_t fec_ecat_read_phy (void * arg, uint8_t address, uint8_t reg)
|
||||
{
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
fec->base->mmfr = FEC_MII_READ(address, reg); // Write read command.
|
||||
while (!(fec->base->eir & FEC_EIR_MII))
|
||||
{
|
||||
; // Wait for interrupt.
|
||||
}
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
uint16_t data = FEC_MII_GET_DATA(fec->base->mmfr);
|
||||
return data; // Return read data.
|
||||
}
|
||||
|
||||
static void fec_ecat_write_phy (void * arg, uint8_t address, uint8_t reg,
|
||||
uint16_t value)
|
||||
{
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
fec->base->mmfr = FEC_MII_WRITE(address, reg, value); // Write data.
|
||||
while (!(fec->base->eir & FEC_EIR_MII))
|
||||
{
|
||||
; // Wait for interrupt.
|
||||
}
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
}
|
||||
|
||||
static void fec_ecat_init_hw (const fec_mac_address_t * mac_address)
|
||||
{
|
||||
uint32_t mii_speed;
|
||||
|
||||
/* Hard reset */
|
||||
fec->base->ecr = FEC_ECR_RESET;
|
||||
while (fec->base->ecr & FEC_ECR_RESET)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Configure MDC clock.
|
||||
mii_speed = (fec->clock + 499999) / 5000000;
|
||||
fec->base->mscr = (fec->base->mscr & (~0x7E)) | (mii_speed << 1);
|
||||
|
||||
// Receive control register
|
||||
fec->base->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE;
|
||||
|
||||
// set RMII mode in RCR register.
|
||||
if (fec->phy_interface == FEC_PHY_RMII)
|
||||
{
|
||||
fec->base->rcr |= FEC_RCR_RMII_MODE;
|
||||
}
|
||||
else if(fec->phy_interface == FEC_PHY_RGMII)
|
||||
{
|
||||
fec->base->rcr &= ~(FEC_RCR_RMII_MODE | FEC_RCR_MII_MODE );
|
||||
fec->base->rcr |= (FEC_RCR_RGMII_ENA | FEC_RCR_MII_MODE);
|
||||
}
|
||||
|
||||
// Reset phy
|
||||
if (fec->phy->ops->reset)
|
||||
{
|
||||
fec->phy->ops->reset (fec->phy);
|
||||
}
|
||||
|
||||
/* Don't receive any unicast frames except those whose destination address
|
||||
* equals our physical address.
|
||||
*/
|
||||
fec->base->iaur = 0;
|
||||
fec->base->ialr = 0;
|
||||
|
||||
/* Receive all multicast frames. */
|
||||
fec->base->gaur = UINT32_MAX;
|
||||
fec->base->galr = UINT32_MAX;
|
||||
|
||||
/* Set our physical address. */
|
||||
fec->base->pa.lower = (mac_address->octet[0] << 24) +
|
||||
(mac_address->octet[1] << 16) +
|
||||
(mac_address->octet[2] << 8) +
|
||||
(mac_address->octet[3] << 0);
|
||||
fec->base->pa.upper = (mac_address->octet[4] << 24) +
|
||||
(mac_address->octet[5] << 16) +
|
||||
0x8808;
|
||||
|
||||
/* Start link autonegotiation */
|
||||
fec->phy->ops->start (fec->phy);
|
||||
}
|
||||
|
||||
int fec_ecat_send (const void *payload, size_t tot_len)
|
||||
{
|
||||
fec_buffer_bd_t * bd;
|
||||
|
||||
/* Frames larger than the maximum Ethernet frame size are not allowed. */
|
||||
ASSERT (tot_len <= PKT_MAXBUF_SIZE);
|
||||
ASSERT (tot_len <= TX_BUFFER_SIZE);
|
||||
|
||||
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
||||
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
||||
*/
|
||||
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
||||
|
||||
/* Allocate a transmit buffer. We wait here until the MAC has released at
|
||||
* least one buffer. This could take a couple of microseconds.
|
||||
*/
|
||||
bd = NULL;
|
||||
while (bd == NULL)
|
||||
{
|
||||
bd = fec_buffer_get_tx ();
|
||||
}
|
||||
|
||||
DPRINT ("out (%u):\n", tot_len);
|
||||
DUMP_PACKET (payload, tot_len);
|
||||
|
||||
/* Copy frame to allocated buffer */
|
||||
memcpy (bd->data, payload, tot_len);
|
||||
bd->length = tot_len;
|
||||
|
||||
fec_buffer_produce_tx (bd);
|
||||
|
||||
/* Wait for previous transmissions to complete.
|
||||
*
|
||||
* This is a workaround for Freescale Kinetis errata e6358.
|
||||
* See "Mask Set Errata for Mask 3N96B".
|
||||
*/
|
||||
while (fec->base->tdar)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
/* Transmit frame */
|
||||
fec->base->tdar = 1;
|
||||
|
||||
return tot_len;
|
||||
}
|
||||
|
||||
int fec_ecat_recv (void * buffer, size_t buffer_length)
|
||||
{
|
||||
fec_buffer_bd_t * bd;
|
||||
int return_value;
|
||||
size_t frame_length_without_fcs;
|
||||
|
||||
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
||||
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
||||
*/
|
||||
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
||||
|
||||
bd = fec_buffer_get_rx ();
|
||||
if (bd == NULL)
|
||||
{
|
||||
/* No frame received. Not an error. */
|
||||
return_value = 0;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/* The FCS CRC should not be returned to user, so subtract that. */
|
||||
frame_length_without_fcs = bd->length - ETHERNET_FCS_SIZE_IN_BYTES;
|
||||
|
||||
/* A frame was received. Handle it and then return its buffer to hardware */
|
||||
if ((bd->status & BD_RX_L)== 0){
|
||||
/* Buffer is not last buffer in frame. This really should never
|
||||
* happen as our buffers are large enough to contain any
|
||||
* Ethernet frame. Nevertheless, is has been observed to happen.
|
||||
* Drop the packet and hope for the best.
|
||||
*/
|
||||
DPRINT ("recv(): End of frame not found. Status: 0x%x\n", bd->status);
|
||||
return_value = -1;
|
||||
}
|
||||
else if (bd->status & (BD_RX_LG | BD_RX_NO | BD_RX_CR | BD_RX_OV))
|
||||
{
|
||||
DPRINT ("recv(): Frame is damaged. Status: 0x%x\n", bd->status);
|
||||
return_value = -1;
|
||||
}
|
||||
else if (buffer_length >= frame_length_without_fcs)
|
||||
{
|
||||
/* No errors detected, so frame should be valid. */
|
||||
ASSERT (frame_length_without_fcs > 0);
|
||||
|
||||
memcpy (buffer, bd->data, frame_length_without_fcs);
|
||||
DPRINT ("in (%u):\n", frame_length_without_fcs);
|
||||
DUMP_PACKET (buffer, frame_length_without_fcs);
|
||||
return_value = frame_length_without_fcs;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT ("received_frame: User buffer is too small.\n");
|
||||
return_value = -1;
|
||||
}
|
||||
|
||||
/* Tell the HW that there are new free RX buffers. */
|
||||
fec_buffer_produce_rx (bd);
|
||||
fec->base->rdar = 1;
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static void fec_ecat_hotplug (void)
|
||||
{
|
||||
uint8_t link_state = 0;
|
||||
|
||||
/* Disable frame reception/transmission */
|
||||
fec->base->ecr &= ~FEC_ECR_ETHER_EN;
|
||||
|
||||
/* Set duplex mode according to link state */
|
||||
|
||||
link_state = fec->phy->ops->get_link_state (fec->phy);
|
||||
|
||||
/* Set duplex */
|
||||
if (link_state & PHY_LINK_FULL_DUPLEX)
|
||||
{
|
||||
fec->base->tcr |= FEC_TCR_FDEN;
|
||||
fec->base->rcr &= ~FEC_RCR_DRT;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec->base->tcr &= ~FEC_TCR_FDEN;
|
||||
fec->base->rcr |= FEC_RCR_DRT;
|
||||
}
|
||||
|
||||
/* Set RMII 10-Mbps mode. */
|
||||
if (link_state & PHY_LINK_10MBIT)
|
||||
{
|
||||
fec->base->rcr |= FEC_RCR_RMII_10T;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec->base->rcr &= ~FEC_RCR_RMII_10T;
|
||||
}
|
||||
|
||||
/* Clear any pending interrupt */
|
||||
fec->base->eir = 0xffffffff;
|
||||
|
||||
// Disable all interrupts.
|
||||
fec->base->eimr = 0x0;
|
||||
|
||||
/* Configure the amount of data required in the
|
||||
* transmit FIFO before transmission of a frame can begin.
|
||||
*/
|
||||
/* All data in frame (i.e. store-and-forward mode). */
|
||||
fec->base->tfwr = FEC_TFWR_STR_FWD;
|
||||
|
||||
// Reset buffers.
|
||||
fec_buffer_reset ();
|
||||
|
||||
// FEC_ERDSR - Receive buffer descriptor ring start register
|
||||
fec->base->erdsr = (uint32_t)fec->rx_bd_base;
|
||||
|
||||
// FEC_ETDSR - Transmit buffer descriptor ring start register
|
||||
fec->base->etdsr = (uint32_t)fec->tx_bd_base;
|
||||
|
||||
// FEC_EMRBR - Maximum receive buffer size register
|
||||
fec->base->emrbr = RX_BUFFER_SIZE - 1;
|
||||
|
||||
/* Let controller count frames. Useful for debugging */
|
||||
fec->base->mibc = 0x0;
|
||||
|
||||
/* Now enable the transmit and receive processing.
|
||||
*
|
||||
* Also let MAC hardware convert DMA descriptor fields between little endian
|
||||
* (as accessed by ARM core) and big endian (as used internally by the
|
||||
* MAC hardware. Software would otherwise need to do the conversions.
|
||||
*/
|
||||
fec->base->ecr = FEC_ECR_ETHER_EN | FEC_ECR_DBSWP;
|
||||
|
||||
/* Indicate that there have been empty receive buffers produced */
|
||||
// FEC_RDAR - Receive Descriptor ring - Receive descriptor active register
|
||||
fec->base->rdar = 1;
|
||||
|
||||
DPRINT ("Link up. Speed: %s. Mode: %s.\n", fec_ecat_link_speed_name (link_state),
|
||||
fec_ecat_link_duplex_name (link_state));
|
||||
}
|
||||
|
||||
static dev_state_t fec_ecat_probe (void)
|
||||
{
|
||||
uint8_t link_state;
|
||||
|
||||
link_state = fec->phy->ops->get_link_state (fec->phy);
|
||||
|
||||
return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
|
||||
}
|
||||
|
||||
COMPILETIME_ASSERT (FEC_MODULE_CLOCK_Hz >= FEC_MIN_MODULE_CLOCK_Hz);
|
||||
|
||||
int fec_ecat_init (const fec_mac_address_t * mac_address,
|
||||
bool phy_loopback_mode)
|
||||
{
|
||||
dev_state_t state;
|
||||
static const fec_cfg_t eth_cfg =
|
||||
{
|
||||
.base = ENET_BASE,
|
||||
.clock = FEC_MODULE_CLOCK_Hz,
|
||||
.tx_bd_base = fec_tx_bd,
|
||||
.rx_bd_base = fec_rx_bd,
|
||||
.phy_interface = FEC_PHY_RMII,
|
||||
};
|
||||
static phy_cfg_t phy_cfg =
|
||||
{
|
||||
.address = ETH_PHY_ADDRESS,
|
||||
.read = NULL, /* Set by MAC driver */
|
||||
.write = NULL, /* Set by MAC driver */
|
||||
};
|
||||
|
||||
phy_cfg.loopback_mode = phy_loopback_mode;
|
||||
|
||||
/* Initialise buffers and buffer descriptors.*/
|
||||
fec_buffer_init_tx (fec_tx_bd, fec_tx_data, NUM_BUFFERS);
|
||||
fec_buffer_init_rx (fec_rx_bd, fec_rx_data, NUM_BUFFERS);
|
||||
|
||||
fec = malloc (sizeof (fec_t));
|
||||
UASSERT (fec != NULL, EMEM);
|
||||
|
||||
/* Initialise driver state */
|
||||
fec->rx_bd_base = eth_cfg.rx_bd_base;
|
||||
fec->tx_bd_base = eth_cfg.tx_bd_base;
|
||||
fec->clock = eth_cfg.clock;
|
||||
fec->base = (reg_fec_t *)eth_cfg.base;
|
||||
fec->phy_interface = eth_cfg.phy_interface;
|
||||
fec->phy = mii_init (&phy_cfg);
|
||||
fec->phy->arg = fec;
|
||||
fec->phy->read = fec_ecat_read_phy;
|
||||
fec->phy->write = fec_ecat_write_phy;
|
||||
|
||||
/* Initialize hardware */
|
||||
fec_ecat_init_hw (mac_address);
|
||||
state = StateDetached;
|
||||
while (state == StateDetached)
|
||||
{
|
||||
state = fec_ecat_probe ();
|
||||
}
|
||||
fec_ecat_hotplug ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: fec_ecat.h 91 2014-04-02 13:32:29Z rtlfrm $
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup fec EtherCat Ethernet MAC driver for Frescale K60 SoCs.
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef FEC_H
|
||||
#define FEC_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct fec_mac_address
|
||||
{
|
||||
uint8_t octet[6];
|
||||
} fec_mac_address_t;
|
||||
|
||||
int fec_ecat_init (const fec_mac_address_t * mac_address, bool phy_loopback_mode);
|
||||
|
||||
int fec_ecat_send (const void *payload, size_t tot_len);
|
||||
|
||||
int fec_ecat_recv (void * buffer, size_t buffer_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FEC_H */
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
@@ -0,0 +1,423 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2006. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: lw_emac.c 348 2012-10-18 16:41:14Z rtlfrm $
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <kern.h>
|
||||
#include <config.h>
|
||||
#include <bfin_dma.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lw_emac.h"
|
||||
|
||||
/* MII standard registers.
|
||||
See IEEE Std 802.3-2005 clause 22:
|
||||
"Reconciliation Sublayer (RS) and Media Independent Interface (MII)",
|
||||
section 2.4.1 to 3.
|
||||
http://standards.ieee.org/getieee802/download/802.3-2005_section2.pdf */
|
||||
#define MII_BMCR 0x00 /* Basic Mode Control Register */
|
||||
#define MII_BMSR 0x01 /* Basic Mode Status Register */
|
||||
#define MII_PHYIDR1 0x02 /* PHY Identifier Register 1 */
|
||||
#define MII_PHYIDR2 0x03 /* PHY Identifier Register 2 */
|
||||
#define MII_LPAR 0x05 /* Auto-Negotiation Link Partner Ability Register */
|
||||
|
||||
/* MII basic mode control register */
|
||||
#define MII_BMCR_RST BIT (15)
|
||||
#define MII_BMCR_ANEG_EN BIT (12)
|
||||
#define MII_BMCR_ANEG_RST BIT (9)
|
||||
|
||||
/* MII basic mode status register */
|
||||
#define MII_BMSR_ANEGACK BIT (5)
|
||||
#define MII_BMSR_LINK BIT (2)
|
||||
|
||||
/* MII auto-negotiation advertisement register */
|
||||
#define MII_ANAR_100_FD BIT (8) /* Can do 100BASE-TX full duplex */
|
||||
#define MII_ANAR_10_FD BIT (6) /* Can do 10BASE-T full duplex */
|
||||
|
||||
/* Management Data Clock (MDC) frequency.
|
||||
* 2.5Mz corresponds to the minimum period for MDC as defined by the standard.
|
||||
* This is independent of whether the PHY is in 10Mbit/s or 100Mbit/s mode.
|
||||
* See Std. IEEE 802.3 (Ethernet) Clause 22.2.2.11 ("management data clock"). */
|
||||
#define PHY_MDC_Hz 2500000
|
||||
#define PHY_RETRIES 3000
|
||||
/* TODO: This might need changing for different boards. Fix? */
|
||||
#define PHY_ADDR 0
|
||||
|
||||
#define ETH_RX_BUF_SIZE 2
|
||||
#define ETH_TX_BUF_SIZE 1
|
||||
#define ETH_FRAME_SIZE 0x614
|
||||
|
||||
/* Ethernet MAC register structure */
|
||||
typedef struct bfin_emac_regs
|
||||
{
|
||||
/* Control-Status Register Group */
|
||||
uint32_t opmode; /* operating mode */
|
||||
uint32_t addrlo; /* address low */
|
||||
uint32_t addrhi; /* address high */
|
||||
uint32_t hashlo; /* multicast hash table low */
|
||||
uint32_t hashhi; /* multicast hash table high */
|
||||
uint32_t staadd; /* station management address */
|
||||
uint32_t stadat; /* station management data */
|
||||
uint32_t flc; /* flow control */
|
||||
uint32_t vlan1; /* VLAN1 tag */
|
||||
uint32_t vlan2; /* VLAN2 tag */
|
||||
uint32_t _reserved1;
|
||||
uint32_t wkup_ctl; /* wakeup frame control and status */
|
||||
uint32_t wkup_ffmsk[4]; /* wakeup frame n byte mask (n == 0,..,3) */
|
||||
uint32_t wkup_ffcmd; /* wakeup frame filter commands */
|
||||
uint32_t wkup_ffoff; /* wakeup frame filter offsets */
|
||||
uint32_t wkup_ffcrc0; /* wakeup frame filter CRC0/1 */
|
||||
uint32_t wkup_ffcrc1; /* wakeup frame filter CRC2/3 */
|
||||
uint32_t _reserved2[4];
|
||||
/* System Interface Register Group */
|
||||
uint32_t sysctl; /* system control */
|
||||
uint32_t systat; /* system status */
|
||||
/* Ethernet Frame Status Register Group */
|
||||
uint32_t rx_stat; /* RX Current Frame Status */
|
||||
uint32_t rx_stky; /* RX Sticky Frame Status */
|
||||
uint32_t rx_irqe; /* RX Frame Status Interrupt Enable */
|
||||
uint32_t tx_stat; /* TX Current Frame Status Register */
|
||||
uint32_t tx_stky; /* TT Sticky Frame Status */
|
||||
uint32_t tx_irqe; /* TX Frame Status Interrupt Enable */
|
||||
/* MAC Management Counter Register Group */
|
||||
uint32_t mmc_ctl; /* Management Counters Control */
|
||||
uint32_t mmc_rirqs; /* MMC RX Interrupt Status */
|
||||
uint32_t mmc_rirqe; /* MMC RX Interrupt Enable */
|
||||
uint32_t mmc_tirqs; /* MMC TX Interrupt Status */
|
||||
uint32_t mmc_tirqe; /* MMC TX Interrupt Enable */
|
||||
uint32_t _reserved3[27];
|
||||
uint32_t mmc_rxc_ok; /* FramesReceivedOK */
|
||||
uint32_t mmc_rxc_fcs; /* FrameCheckSequenceErrors */
|
||||
uint32_t mmc_rxc_align; /* AlignmentErrors */
|
||||
uint32_t mmc_rxc_octet; /* OctetsReceivedOK */
|
||||
uint32_t mmc_rxc_dmaovf; /* FramesLostDueToIntMACRcvError */
|
||||
uint32_t mmc_rxc_unicst; /* UnicastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_multi; /* MulticastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_broad; /* BroadcastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_lnerri; /* InRangeLengthErrors */
|
||||
uint32_t mmc_rxc_lnerro; /* OutOfRangeLengthField */
|
||||
uint32_t mmc_rxc_long; /* FrameTooLongErrors */
|
||||
uint32_t mmc_rxc_macctl; /* MACControlFramesReceived */
|
||||
uint32_t mmc_rxc_opcode; /* UnsupportedOpcodesReceived */
|
||||
uint32_t mmc_rxc_pause; /* PAUSEMACCtrlFramesReceived */
|
||||
uint32_t mmc_rxc_allfrm; /* FramesReceivedAll */
|
||||
uint32_t mmc_rxc_alloct; /* OctetsReceivedAll */
|
||||
uint32_t mmc_rxc_typed; /* TypedFramesReceived */
|
||||
uint32_t mmc_rxc_short; /* FramesLenLt64Received */
|
||||
uint32_t mmc_rxc_eq64; /* FramesLenEq64Received */
|
||||
uint32_t mmc_rxc_lt128; /* FramesLen65_127Received */
|
||||
uint32_t mmc_rxc_lt256; /* FramesLen128_255Received */
|
||||
uint32_t mmc_rxc_lt512; /* FramesLen256_511Received */
|
||||
uint32_t mmc_rxc_lt1024; /* FramesLen512_1023Received */
|
||||
uint32_t mmc_rxc_ge1024; /* FramesLen1024_MaxReceived */
|
||||
/* TODO: add all registers */
|
||||
} bfin_emac_regs_t;
|
||||
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, opmode) == 0x0);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, staadd) == 0x14);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ctl) == 0x2c);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ffcrc1) == 0x4c);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, sysctl) == 0x60);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, rx_stat) == 0x68);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_ctl) == 0x80);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_tirqe) == 0x90);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ok) == 0x100);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_dmaovf) == 0x110);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ge1024) == 0x15c);
|
||||
|
||||
/* 32-bit aligned struct for ethernet data */
|
||||
typedef struct ethernet_data {
|
||||
uint16_t length; /* When TX = frame data length in bytes, not including 'length'
|
||||
* When RX = 0x0000 padding when using RXDWA
|
||||
*/
|
||||
uint8_t data[ETH_FRAME_SIZE]; /* Hold the ethernet frame */
|
||||
uint16_t padding; /* To make the struct 32-bit aligned */
|
||||
} ethernet_data_t;
|
||||
|
||||
COMPILETIME_ASSERT (sizeof (ethernet_data_t) == 0x618) ;
|
||||
|
||||
static volatile bfin_emac_regs_t * pEth = (bfin_emac_regs_t *) EMAC_OPMODE;
|
||||
|
||||
/* Buffers for rx and tx DMA operations */
|
||||
static ethernet_data_t rxBuffer[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static ethernet_data_t txBuffer[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static volatile uint32_t rxStatusWord[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static volatile uint32_t txStatusWord[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static bfin_dma_descriptor_t rxDMADesc[ETH_RX_BUF_SIZE * 2] __attribute__((section(".dma")));
|
||||
static bfin_dma_descriptor_t txDMADesc[ETH_TX_BUF_SIZE * 2] __attribute__((section(".dma")));
|
||||
|
||||
/* Hold the current index of rx and tx buffers */
|
||||
static uint8_t rxIdx, txIdx;
|
||||
|
||||
|
||||
/* Internal function that writes to a PHY register */
|
||||
static void lw_emac_write_phy_reg(uint8_t phy_addr, uint8_t reg_addr, uint32_t data) {
|
||||
/* Set the flags that should be set reg_addr on PHY */
|
||||
pEth->stadat = data;
|
||||
|
||||
/* Wait for our turn then initiate writing of stadat to PHY register */
|
||||
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STAOP | STABUSY;
|
||||
while (pEth->staadd & STABUSY) ;
|
||||
}
|
||||
|
||||
/* Internal function that reads from; and returns a PHY register */
|
||||
static uint32_t lw_emac_read_phy_reg(uint8_t phy_addr, uint8_t reg_addr) {
|
||||
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STABUSY;
|
||||
while (pEth->staadd & STABUSY) ;
|
||||
|
||||
return pEth->stadat;
|
||||
}
|
||||
|
||||
/* Internal function that sets the MAC address */
|
||||
static void lw_emac_set_mac_addr(uint8_t * ethAddr)
|
||||
{
|
||||
pEth->addrlo =
|
||||
ethAddr[0] |
|
||||
ethAddr[1] << 8 |
|
||||
ethAddr[2] << 16 |
|
||||
ethAddr[3] << 24;
|
||||
pEth->addrhi =
|
||||
ethAddr[4] |
|
||||
ethAddr[5] << 8;
|
||||
}
|
||||
|
||||
static uint8_t lw_emac_init_registers(uint8_t * ethAddr) {
|
||||
uint32_t clock_divisor, sysctl_mdcdiv, phy_stadat, counter;
|
||||
|
||||
/* CONFIGURE MAC MII PINS */
|
||||
|
||||
/* Enable PHY Clock Output */
|
||||
*(volatile uint16_t *) VR_CTL |= PHYCLKOE;
|
||||
|
||||
/* Set all bits to 1 to use MII mode */
|
||||
*(uint16_t *) PORTH_FER = 0xFFFF;
|
||||
|
||||
/* CONFIGURE MAC REGISTERS */
|
||||
|
||||
/*
|
||||
* Set the MMC (MAC Management Counter) control register
|
||||
* RTSC = Clear all counters
|
||||
*
|
||||
* Note that counters are not enabled at this time
|
||||
*/
|
||||
pEth->mmc_ctl = RSTC;
|
||||
|
||||
/* Set MAC address */
|
||||
lw_emac_set_mac_addr (ethAddr);
|
||||
|
||||
clock_divisor = SCLK / PHY_MDC_Hz;
|
||||
sysctl_mdcdiv = clock_divisor / 2 - 1;
|
||||
ASSERT (sysctl_mdcdiv <= 0x3f);
|
||||
|
||||
rprintp ("PHY ID: %04x %04x\n",
|
||||
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR1),
|
||||
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR2)
|
||||
);
|
||||
|
||||
/*
|
||||
* Set the system control register
|
||||
* SET_MDCDIV(x) = Set MDC to 2.5 MHz
|
||||
* RXDWA = Pad incoming frame with 0x0000 as to make the data-part 32-bit aligned
|
||||
* RXCKS = Enable Receive Frame TCP/UDP Checksum Computation
|
||||
*/
|
||||
pEth->sysctl = SET_MDCDIV(sysctl_mdcdiv) | RXDWA ;
|
||||
|
||||
/* CONFIGURE PHY */
|
||||
|
||||
/*
|
||||
* Set the PHY basic control register
|
||||
* MII_BMCR_ANEG_EN = Auto negotiation on
|
||||
* MII_BMCR_ANEG_RST = Restart the auto-neg process by setting
|
||||
* Speed handled by auto negotiation
|
||||
*/
|
||||
phy_stadat = MII_BMCR_ANEG_EN | MII_BMCR_ANEG_RST;
|
||||
lw_emac_write_phy_reg(PHY_ADDR, MII_BMCR, phy_stadat);
|
||||
|
||||
/* Loop until link is up or time out after PHY_RETRIES */
|
||||
counter = 0;
|
||||
do {
|
||||
if (counter > PHY_RETRIES) {
|
||||
rprintp("Ethernet link is down\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
task_delay (tick_from_ms (10));
|
||||
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_BMSR);
|
||||
|
||||
++counter;
|
||||
} while (!(phy_stadat & MII_BMSR_LINK)) ;
|
||||
|
||||
/* Check whether link partner can do full duplex or not */
|
||||
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_LPAR);
|
||||
|
||||
if (phy_stadat & (MII_ANAR_100_FD | MII_ANAR_10_FD) ) {
|
||||
pEth->opmode = FDMODE;
|
||||
}
|
||||
else {
|
||||
pEth->opmode = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup DMA MAC receive/transfer channels with XCOUNT 0 (which we use
|
||||
* together with description based DMA) and XMODIFY 4 (bytes per transfer)
|
||||
*/
|
||||
bfin_dma_channel_init(DMA_CHANNEL_EMAC_RX, 0, 4);
|
||||
bfin_dma_channel_init(DMA_CHANNEL_EMAC_TX, 0, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bfin_EMAC_init (uint8_t *ethAddr)
|
||||
{
|
||||
rxIdx = txIdx = 0;
|
||||
|
||||
if (lw_emac_init_registers(ethAddr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reset status words
|
||||
memset ((uint8_t *)rxStatusWord, 0, sizeof(rxStatusWord));
|
||||
memset ((uint8_t *)txStatusWord, 0, sizeof(txStatusWord));
|
||||
|
||||
txDMADesc[0].next = &txDMADesc[1];
|
||||
txDMADesc[0].start_addr = &txBuffer[0];
|
||||
txDMADesc[0].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
txDMADesc[1].next = &txDMADesc[0];
|
||||
txDMADesc[1].start_addr = &txStatusWord[0];
|
||||
txDMADesc[1].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(0) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_STOP);
|
||||
|
||||
rxDMADesc[0].next = &rxDMADesc[1];
|
||||
rxDMADesc[0].start_addr = &rxBuffer[0];
|
||||
rxDMADesc[0].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[1].next = &rxDMADesc[2];
|
||||
rxDMADesc[1].start_addr = &rxStatusWord[0];
|
||||
rxDMADesc[1].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[2].next = &rxDMADesc[3];
|
||||
rxDMADesc[2].start_addr = &rxBuffer[1];
|
||||
rxDMADesc[2].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[3].next = &rxDMADesc[0];
|
||||
rxDMADesc[3].start_addr = &rxStatusWord[1];
|
||||
rxDMADesc[3].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_RX, &rxDMADesc[0]);
|
||||
|
||||
/* Enable Receiving, Automatic Pad Stripping and receiving of frames with length < 64 butes */
|
||||
pEth->opmode |= ASTP | PSF | RE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int bfin_EMAC_send (void *packet, int length)
|
||||
{
|
||||
UASSERT(length > 0, EARG);
|
||||
UASSERT(length < ETH_FRAME_SIZE, EARG);
|
||||
|
||||
/* TODO: Check DMA Error in IRQ status */
|
||||
|
||||
while (bfin_dma_channel_interrupt_is_active (DMA_CHANNEL_EMAC_TX));
|
||||
|
||||
txBuffer[txIdx].length = length;
|
||||
memcpy(txBuffer[txIdx].data, packet, length);
|
||||
|
||||
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_TX, txDMADesc);
|
||||
pEth->opmode |= TE;
|
||||
|
||||
while ((txStatusWord[txIdx] & TX_COMP) == 0);
|
||||
|
||||
ASSERT(txStatusWord[txIdx] & TX_OK);
|
||||
|
||||
txStatusWord[txIdx] = 0;
|
||||
|
||||
++txIdx;
|
||||
|
||||
if (txIdx == ETH_TX_BUF_SIZE) {
|
||||
txIdx = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bfin_EMAC_recv (uint8_t * packet, size_t size)
|
||||
{
|
||||
uint32_t length;
|
||||
uint32_t status = rxStatusWord[rxIdx];
|
||||
|
||||
/* Check if rx frame is completed */
|
||||
if ((status & RX_COMP) == 0) {
|
||||
return -1;
|
||||
}
|
||||
else if ((status & RX_OK) == 0) {
|
||||
ASSERT(0);
|
||||
/* TODO: Handle error */
|
||||
return -1;
|
||||
}
|
||||
else if (status & RX_DMAO) {
|
||||
ASSERT(0);
|
||||
/* TODO: Handle overrun */
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = status & RX_FRLEN;
|
||||
|
||||
if (size < length) {
|
||||
length = size;
|
||||
}
|
||||
|
||||
memcpy(packet, rxBuffer[rxIdx].data, length);
|
||||
|
||||
bfin_dma_channel_interrupt_clear (DMA_CHANNEL_EMAC_RX);
|
||||
rxStatusWord[rxIdx] = 0;
|
||||
|
||||
++rxIdx;
|
||||
|
||||
if (rxIdx == ETH_RX_BUF_SIZE) {
|
||||
rxIdx = 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* author: Tomas Vestelind
|
||||
*/
|
||||
|
||||
#ifndef LWIP_MAC_H
|
||||
#define LWIP_MAC_H
|
||||
|
||||
int bfin_EMAC_init(uint8_t *enetaddr);
|
||||
int bfin_EMAC_send(void *packet, int length);
|
||||
int bfin_EMAC_recv(uint8_t * packet, size_t size);
|
||||
|
||||
#endif /* LWIP_MAC_H */
|
||||
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <kern.h>
|
||||
#include <ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "osal.h"
|
||||
#include "oshw.h"
|
||||
|
||||
|
||||
#include "lw_mac/lw_emac.h"
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int rVal;
|
||||
int *psock;
|
||||
|
||||
port->getindex_mutex = mtx_create();
|
||||
port->tx_mutex = mtx_create();
|
||||
port->rx_mutex = mtx_create();
|
||||
|
||||
rVal = bfin_EMAC_init((uint8_t *)priMAC);
|
||||
if (rVal != 0)
|
||||
return 0;
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->getindex_mutex = mtx_create();
|
||||
port->tx_mutex = mtx_create();
|
||||
port->rx_mutex = mtx_create();
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
{
|
||||
close(port->sockhandle);
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
{
|
||||
close(port->redport->sockhandle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
mtx_lock (port->getindex_mutex);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
}
|
||||
port->lastidx = idx;
|
||||
|
||||
mtx_unlock (port->getindex_mutex);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = bfin_EMAC_send((*stack->txbuf)[idx], lp);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
mtx_lock (port->tx_mutex);
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
|
||||
// OBS! redundant not ACTIVE for BFIN, just added to compile
|
||||
ASSERT (0);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
bfin_EMAC_send(&(port->txbuf2), port->txbuflength2);
|
||||
mtx_unlock (port->tx_mutex);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = bfin_EMAC_recv((*stack->tempbuf), lp);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtx_lock (port->rx_mutex);
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mtx_unlock (port->rx_mutex);
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert timer)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
}
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME)
|
||||
{
|
||||
primrx = port->rxsa[idx];
|
||||
}
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
secrx = port->redport->rxsa[idx];
|
||||
}
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
osal_timert read_timer;
|
||||
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start(&read_timer, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start(&timer, timeout);
|
||||
do
|
||||
{
|
||||
osal_timert read_timer;
|
||||
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET));
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, read_timer);
|
||||
/* wait for answer with WKC>0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
mtx_t * getindex_mutex;
|
||||
mtx_t * tx_mutex;
|
||||
mtx_t * rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int stacknumber);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include "oshw.h"
|
||||
#include <stdlib.h>
|
||||
#include <lwip/inet.h>
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(const uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(const uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
/* TODO if needed */
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
/* TODO if needed */
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <kern.h>
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
|
||||
uint16 oshw_htons(uint16 host);
|
||||
uint16 oshw_ntohs(uint16 network);
|
||||
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT VxWorks SNARF Mux driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* If EtherCAT is run in parallel with normal IP traffic and EtherCAT have a
|
||||
* dedicated NIC, instantiate an extra tNetX task and redirect the NIC workQ
|
||||
* to be handle by the extra tNetX task, if needed raise the tNetX task prio.
|
||||
* This prevents from having tNet0 becoming a bottleneck.
|
||||
*
|
||||
* The "redundant" option will configure two Mux drivers and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resend through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <muxLib.h>
|
||||
#include <ipProto.h>
|
||||
#include <wvLib.h>
|
||||
#include <sysLib.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
#include "nicdrv.h"
|
||||
|
||||
#define NIC_DEBUG /* Print debugging info? */
|
||||
|
||||
// wvEvent flags
|
||||
#define ECAT_RECV_FAILED 0x664
|
||||
#define ECAT_RECV_OK 0x665
|
||||
#define ECAT_RECV_RETRY_OK 0x666
|
||||
#define ECAT_STACK_RECV 0x667
|
||||
#define ECAT_SEND_START 0x675
|
||||
#define ECAT_SEND_COMPLETE 0x676
|
||||
#define ECAT_SEND_FAILED 0x677
|
||||
|
||||
#ifdef NIC_DEBUG
|
||||
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f) \
|
||||
do { \
|
||||
logMsg (x,a,b,c,d,e,f); \
|
||||
} while (0)
|
||||
#define NIC_WVEVENT(a,b,c) \
|
||||
do { \
|
||||
wvEvent(a, b, c); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#else
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f)
|
||||
#define NIC_WVEVENT(a,b,c)
|
||||
#endif /* NIC_DEBUG */
|
||||
|
||||
#define IF_NAME_SIZE 8
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
/* usec per tick for timeconversion, default to 1kHz */
|
||||
#define USECS_PER_SEC 1000000
|
||||
static unsigned int usec_per_tick = 1000;
|
||||
|
||||
/** Receive hook called by Mux driver. */
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg);
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
void print_nicversion(void)
|
||||
{
|
||||
printf("Generic is used\n");
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "gei0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
char ifn[IF_NAME_SIZE];
|
||||
int unit_no = -1;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
/* Get systick info, sysClkRateGet return ticks per second */
|
||||
usec_per_tick = USECS_PER_SEC / sysClkRateGet();
|
||||
/* Don't allow 0 since it is used in DIV */
|
||||
if(usec_per_tick == 0)
|
||||
usec_per_tick = 1;
|
||||
/* Make reference to packet device struct, keep track if the packet
|
||||
* device is the redundant or not.
|
||||
*/
|
||||
if (secondary)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
pPktDev->redundant = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPktDev = &(port->pktDev);
|
||||
pPktDev->redundant = 0;
|
||||
}
|
||||
|
||||
/* Clear frame counters*/
|
||||
pPktDev->tx_count = 0;
|
||||
pPktDev->rx_count = 0;
|
||||
pPktDev->overrun_count = 0;
|
||||
|
||||
/* Create multi-thread support semaphores */
|
||||
port->sem_get_index = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
|
||||
|
||||
/* Get the dev name and unit from ifname
|
||||
* We assume form gei1, fei0...
|
||||
*/
|
||||
memset(ifn,0x0,sizeof(ifn));
|
||||
|
||||
for(i=0; i < strlen(ifname);i++)
|
||||
{
|
||||
if(isdigit(ifname[i]))
|
||||
{
|
||||
strncpy(ifn, ifname, i);
|
||||
unit_no = atoi(&ifname[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach IP stack */
|
||||
//ipDetach(pktDev.unit,pktDev.name);
|
||||
|
||||
pPktDev->port = port;
|
||||
|
||||
/* Bind to mux driver for given interface, include ethercat driver pointer
|
||||
* as user reference
|
||||
*/
|
||||
/* Bind to mux */
|
||||
pPktDev->pCookie = muxBind(ifn,
|
||||
unit_no,
|
||||
mux_rx_callback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
MUX_PROTO_SNARF,
|
||||
"ECAT SNARF",
|
||||
pPktDev);
|
||||
|
||||
if (pPktDev->pCookie == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Get reference tp END obje */
|
||||
pPktDev->endObj = endFindByName(ifn, unit_no);
|
||||
|
||||
if (port->pktDev.endObj == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->redport->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: Redundant port not allocated",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
|
||||
exit:
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
int i;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
M_BLK_ID trash_can;
|
||||
|
||||
pPktDev = &(port->pktDev);
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
if (msgQReceive(port->msgQId[i],
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
|
||||
2, 3, 4, 5, 6);
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
msgQDelete(port->msgQId[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
|
||||
/* Clean redundant resources if available*/
|
||||
if (port->redport)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->redport->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
if (msgQReceive(port->redport->msgQId[i],
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
|
||||
2, 3, 4, 5, 6);
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
msgQDelete(port->redport->msgQId[i]);
|
||||
}
|
||||
}
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
semTake(port->sem_get_index, WAIT_FOREVER);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
semGive(port->sem_get_index);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Low level transmit buffer over mux layer 2 driver
|
||||
*
|
||||
* @param[in] pPktDev = packet device to send buffer over
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] buf = buff to send
|
||||
* @param[in] len = bytes to send
|
||||
* @return driver send result
|
||||
*/
|
||||
static int ec_outfram_send(ETHERCAT_PKT_DEV * pPktDev, uint8 idx, void * buf, int len)
|
||||
{
|
||||
STATUS status = OK;
|
||||
M_BLK_ID pPacket = NULL;
|
||||
int rval = 0;
|
||||
END_OBJ *endObj = (END_OBJ *)pPktDev->endObj;
|
||||
MSG_Q_ID msgQId;
|
||||
|
||||
/* Clean up any abandoned frames and re-use the allocated buffer*/
|
||||
msgQId = pPktDev->port->msgQId[idx];
|
||||
if(msgQNumMsgs(msgQId) > 0)
|
||||
{
|
||||
pPktDev->abandoned_count++;
|
||||
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ abandoned\n", idx,
|
||||
2, 3, 4, 5, 6);
|
||||
if (msgQReceive(msgQId,
|
||||
(char *)&pPacket,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) == ERROR)
|
||||
{
|
||||
pPacket = NULL;
|
||||
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ mBlk handled by receiver\n", idx,
|
||||
2, 3, 4, 5, 6);
|
||||
}
|
||||
}
|
||||
|
||||
if (pPacket == NULL)
|
||||
{
|
||||
/* Allocate m_blk to send */
|
||||
if ((pPacket = netTupleGet(endObj->pNetPool,
|
||||
len,
|
||||
M_DONTWAIT,
|
||||
MT_DATA,
|
||||
TRUE)) == NULL)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: Could not allocate MBLK memory!\n", 1, 2, 3, 4, 5, 6);
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pPacket->mBlkHdr.mLen = len;
|
||||
pPacket->mBlkHdr.mFlags |= M_PKTHDR;
|
||||
pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf;
|
||||
pPacket->mBlkPktHdr.len = pPacket->m_len;
|
||||
|
||||
netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL);
|
||||
status = muxSend(endObj, pPacket);
|
||||
|
||||
if (status == OK)
|
||||
{
|
||||
rval = len;
|
||||
NIC_WVEVENT(ECAT_SEND_COMPLETE, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
else
|
||||
{
|
||||
netMblkClChainFree(pPacket);
|
||||
/* fail */
|
||||
static int print_once;
|
||||
if (print_once == 0)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: failed\n",
|
||||
1, 2, 3, 4, 5, 6);
|
||||
print_once = 1;
|
||||
}
|
||||
NIC_WVEVENT(ECAT_SEND_FAILED, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit buffer over mux layer 2 driver, passing buffer
|
||||
* and packet device to send on as arguments
|
||||
* @param[in] port = port context holding reference to packet device
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int rval = 0;
|
||||
ec_stackT *stack;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
pPktDev = &(port->pktDev);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
}
|
||||
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = ec_outfram_send(pPktDev, idx, (char*)(*stack->txbuf)[idx],
|
||||
(*stack->txbuflength)[idx]);
|
||||
if (rval > 0)
|
||||
{
|
||||
port->pktDev.tx_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit frame to send as index.
|
||||
* @param[in] port = port context
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary interface */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
rval = ec_outfram_send(&(port->redport->pktDev), idx, &(port->txbuf2), port->txbuflength2);
|
||||
if (rval <= 0)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
/** Call back routine registered as hook with mux layer 2 driver
|
||||
* @param[in] pCookie = Mux cookie
|
||||
* @param[in] type = received type
|
||||
* @param[in] pMblk = the received packet reference
|
||||
* @param[in] llHdrInfo = header info
|
||||
* @param[in] muxUserArg = assigned reference to packet device when init called
|
||||
* @return TRUE if frame was successfully read and passed to MsgQ
|
||||
*/
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
uint8 idxf;
|
||||
ec_comt *ecp;
|
||||
ec_bufT * tempbuf;
|
||||
ecx_portt * port;
|
||||
MSG_Q_ID msgQId;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
int length;
|
||||
int bufstat;
|
||||
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (type == ETH_P_ECAT)
|
||||
{
|
||||
length = pMblk->mBlkHdr.mLen;
|
||||
tempbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
pPktDev = (ETHERCAT_PKT_DEV *)muxUserArg;
|
||||
port = pPktDev->port;
|
||||
|
||||
/* Get ethercat frame header */
|
||||
ecp = (ec_comt*)&(*tempbuf)[ETH_HEADERSIZE];
|
||||
idxf = ecp->index;
|
||||
if (idxf >= EC_MAXBUF)
|
||||
{
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d out of bounds\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if it is the redundant port or not */
|
||||
if (pPktDev->redundant == 1)
|
||||
{
|
||||
bufstat = port->redport->rxbufstat[idxf];
|
||||
msgQId = port->redport->msgQId[idxf];
|
||||
}
|
||||
else
|
||||
{
|
||||
bufstat = port->rxbufstat[idxf];
|
||||
msgQId = port->msgQId[idxf];
|
||||
}
|
||||
|
||||
/* Check length and if someone expects the frame */
|
||||
if (length > 0 && bufstat == EC_BUF_TX)
|
||||
{
|
||||
/* Post the frame to the receive Q for the EtherCAT stack */
|
||||
STATUS status;
|
||||
status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID),
|
||||
NO_WAIT, MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
else if ((status == ERROR) && (errno == S_objLib_OBJ_UNAVAILABLE))
|
||||
{
|
||||
/* Try to empty the MSGQ since we for some strange reason
|
||||
* already have a frame in the MsqQ,
|
||||
* is it due to timeout when receiving?
|
||||
* We want the latest received frame in the buffer
|
||||
*/
|
||||
port->pktDev.overrun_count++;
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d MsgQ overrun\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
M_BLK_ID trash_can;
|
||||
if (msgQReceive(msgQId,
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
status = msgQSend(msgQId,
|
||||
(char *)&pMblk,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT,
|
||||
MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Non blocking or blocking read, if we use timeout we pass minimum 1 tick.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] pMblk = mBlk for received frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, uint8 idx, int stacknumber, M_BLK_ID * pMblk, int timeout)
|
||||
{
|
||||
int bytesrx = 0;
|
||||
MSG_Q_ID msgQId;
|
||||
int tick_timeout = max((timeout / usec_per_tick), 1);
|
||||
|
||||
if (stacknumber == 1)
|
||||
{
|
||||
msgQId = port->redport->msgQId[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
msgQId = port->msgQId[idx];
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), NO_WAIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), tick_timeout);
|
||||
}
|
||||
|
||||
if (bytesrx > 0)
|
||||
{
|
||||
bytesrx = (*pMblk)->mBlkHdr.mLen;
|
||||
NIC_WVEVENT(ECAT_STACK_RECV, (char *)&bytesrx, sizeof(bytesrx));
|
||||
}
|
||||
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. Frames are received by separate receiver
|
||||
* task tNet0 (default), tNet0 fetch what frame index and store a reference to the
|
||||
* received frame in matching MsgQ. The stack user tasks fetch the frame
|
||||
* reference and copies the frame the the RX buffer, when done it free
|
||||
* the frame buffer allocated by the Mux.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber, int timeout)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
ec_bufT *tempinbuf;
|
||||
M_BLK_ID pMblk;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* (non-) blocking call to retrieve frame from Muxlayer */
|
||||
if (ecx_recvpkt(port, idx, stacknumber, &pMblk, timeout))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
|
||||
/* Get pointer to the frame */
|
||||
tempinbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
/* Get pointer to the Ethernet header */
|
||||
ehp = (ec_etherheadert*)tempinbuf;
|
||||
/* Get pointer to the EtherCAT frame as ethernet payload */
|
||||
ecp = (ec_comt*)&(*tempinbuf)[ETH_HEADERSIZE];
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
netMblkOffsetToBufCopy(pMblk, ETH_HEADERSIZE,(void *) rxbuf,
|
||||
(*stack->txbuflength)[idx] - ETH_HEADERSIZE, NULL);
|
||||
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
netMblkClChainFree(pMblk);
|
||||
port->pktDev.rx_count++;
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer, int timeout)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0, timeout);
|
||||
}
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
{
|
||||
wkc2 = ecx_inframe(port, idx, 1, timeout);
|
||||
}
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1, timeout);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer, timeout);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2, timeout);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber, int timeout)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber, timeout);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <vxWorks.h>
|
||||
|
||||
/** structure to connect EtherCAT stack and VxWorks device */
|
||||
typedef struct ETHERCAT_PKT_DEV
|
||||
{
|
||||
struct ecx_port *port;
|
||||
void *pCookie;
|
||||
void *endObj;
|
||||
UINT32 redundant;
|
||||
UINT32 tx_count;
|
||||
UINT32 rx_count;
|
||||
UINT32 overrun_count;
|
||||
UINT32 abandoned_count;
|
||||
}ETHERCAT_PKT_DEV;
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct ecx_redport
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct ecx_port
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
/** Semaphore to protect single resources */
|
||||
SEM_ID sem_get_index;
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
#include "ethercattype.h"
|
||||
#include <Mmsystem.h>
|
||||
#include "nicdrv.h"
|
||||
#include "osal_win32.h"
|
||||
|
||||
#endif
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to fund out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
pcap_t **psock;
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = NULL;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeCriticalSection(&(port->getindex_mutex));
|
||||
InitializeCriticalSection(&(port->tx_mutex));
|
||||
InitializeCriticalSection(&(port->rx_mutex));
|
||||
port->sockhandle = NULL;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
/* we use pcap socket to send RAW packets in windows user mode*/
|
||||
*psock = pcap_open(ifname, 65536, PCAP_OPENFLAG_PROMISCUOUS |
|
||||
PCAP_OPENFLAG_MAX_RESPONSIVENESS |
|
||||
PCAP_OPENFLAG_NOCAPTURE_LOCAL, -1, NULL , errbuf);
|
||||
if (NULL == *psock)
|
||||
{
|
||||
printf("interface %s could not open with pcap\n", ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
timeEndPeriod(1);
|
||||
|
||||
if (port->sockhandle != NULL)
|
||||
{
|
||||
DeleteCriticalSection(&(port->getindex_mutex));
|
||||
DeleteCriticalSection(&(port->tx_mutex));
|
||||
DeleteCriticalSection(&(port->rx_mutex));
|
||||
pcap_close(port->sockhandle);
|
||||
port->sockhandle = NULL;
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle != NULL))
|
||||
{
|
||||
pcap_close(port->redport->sockhandle);
|
||||
port->redport->sockhandle = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
EnterCriticalSection(&(port->getindex_mutex));
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
LeaveCriticalSection(&(port->getindex_mutex));
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
|
||||
if (rval == PCAP_ERROR)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
EnterCriticalSection( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
LeaveCriticalSection( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
struct pcap_pkthdr * header;
|
||||
unsigned char const * pkt_data;
|
||||
int res;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
|
||||
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
|
||||
if (res <=0 )
|
||||
{
|
||||
port->tempinbufs = 0;
|
||||
return 0;
|
||||
}
|
||||
bytesrx = header->len;
|
||||
if (bytesrx > lp)
|
||||
{
|
||||
bytesrx = lp;
|
||||
}
|
||||
memcpy(*stack->tempbuf, pkt_data, bytesrx);
|
||||
port->tempinbufs = bytesrx;
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] tvs = timeout
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define HAVE_REMOTE
|
||||
|
||||
#include <pcap.h>
|
||||
#include <Packet32.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
pcap_t **sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
CRITICAL_SECTION getindex_mutex;
|
||||
CRITICAL_SECTION tx_mutex;
|
||||
CRITICAL_SECTION rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include "oshw.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons (uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs (uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters (void)
|
||||
{
|
||||
int i = 0;
|
||||
pcap_if_t *alldevs;
|
||||
pcap_if_t *d;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
/* find all devices */
|
||||
if (pcap_findalldevs(&alldevs, errbuf) == -1)
|
||||
{
|
||||
fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf);
|
||||
return (NULL);
|
||||
}
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
for(d= alldevs; d != NULL; d= d->next)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name of the device from libpcap */
|
||||
adapter->next = NULL;
|
||||
if (d->name)
|
||||
{
|
||||
strncpy(adapter->name, d->name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
}
|
||||
if (d->description)
|
||||
{
|
||||
strncpy(adapter->desc, d->description, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
prev_adapter = adapter;
|
||||
i++;
|
||||
}
|
||||
/* free all devices allocated */
|
||||
pcap_freealldevs(alldevs);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters (ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons (uint16 hostshort);
|
||||
uint16 oshw_ntohs (uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters (void);
|
||||
void oshw_free_adapters (ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2007 CACE Technologies, Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @ingroup packetapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup packet32h Packet.dll definitions and data structures
|
||||
* Packet32.h contains the data structures and the definitions used by packet.dll.
|
||||
* The file is used both by the Win9x and the WinNTx versions of packet.dll, and can be included
|
||||
* by the applications that use the functions of this library
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __PACKET32
|
||||
#define __PACKET32
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#ifdef HAVE_AIRPCAP_API
|
||||
#include <airpcap.h>
|
||||
#else
|
||||
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
|
||||
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
|
||||
typedef struct _AirpcapHandle *PAirpcapHandle;
|
||||
#endif /* AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ */
|
||||
#endif /* HAVE_AIRPCAP_API */
|
||||
|
||||
#ifdef HAVE_DAG_API
|
||||
#include <dagc.h>
|
||||
#endif /* HAVE_DAG_API */
|
||||
|
||||
// Working modes
|
||||
#define PACKET_MODE_CAPT 0x0 ///< Capture mode
|
||||
#define PACKET_MODE_STAT 0x1 ///< Statistical mode
|
||||
#define PACKET_MODE_MON 0x2 ///< Monitoring mode
|
||||
#define PACKET_MODE_DUMP 0x10 ///< Dump mode
|
||||
#define PACKET_MODE_STAT_DUMP MODE_DUMP | MODE_STAT ///< Statistical dump Mode
|
||||
|
||||
|
||||
/// Alignment macro. Defines the alignment size.
|
||||
#define Packet_ALIGNMENT sizeof(int)
|
||||
/// Alignment macro. Rounds up to the next even multiple of Packet_ALIGNMENT.
|
||||
#define Packet_WORDALIGN(x) (((x)+(Packet_ALIGNMENT-1))&~(Packet_ALIGNMENT-1))
|
||||
|
||||
#define NdisMediumNull -1 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumCHDLC -2 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumPPPSerial -3 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumBare80211 -4 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumRadio80211 -5 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumPpi -6 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
|
||||
// Loopback behaviour definitions
|
||||
#define NPF_DISABLE_LOOPBACK 1 ///< Drop the packets sent by the NPF driver
|
||||
#define NPF_ENABLE_LOOPBACK 2 ///< Capture the packets sent by the NPF driver
|
||||
|
||||
/*!
|
||||
\brief Network type structure.
|
||||
|
||||
This structure is used by the PacketGetNetType() function to return information on the current adapter's type and speed.
|
||||
*/
|
||||
typedef struct NetType
|
||||
{
|
||||
UINT LinkType; ///< The MAC of the current network adapter (see function PacketGetNetType() for more information)
|
||||
ULONGLONG LinkSpeed; ///< The speed of the network in bits per second
|
||||
}NetType;
|
||||
|
||||
|
||||
//some definitions stolen from libpcap
|
||||
|
||||
#ifndef BPF_MAJOR_VERSION
|
||||
|
||||
/*!
|
||||
\brief A BPF pseudo-assembly program.
|
||||
|
||||
The program will be injected in the kernel by the PacketSetBPF() function and applied to every incoming packet.
|
||||
*/
|
||||
struct bpf_program
|
||||
{
|
||||
UINT bf_len; ///< Indicates the number of instructions of the program, i.e. the number of struct bpf_insn that will follow.
|
||||
struct bpf_insn *bf_insns; ///< A pointer to the first instruction of the program.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief A single BPF pseudo-instruction.
|
||||
|
||||
bpf_insn contains a single instruction for the BPF register-machine. It is used to send a filter program to the driver.
|
||||
*/
|
||||
struct bpf_insn
|
||||
{
|
||||
USHORT code; ///< Instruction type and addressing mode.
|
||||
UCHAR jt; ///< Jump if true
|
||||
UCHAR jf; ///< Jump if false
|
||||
int k; ///< Generic field used for various purposes.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Structure that contains a couple of statistics values on the current capture.
|
||||
|
||||
It is used by packet.dll to return statistics about a capture session.
|
||||
*/
|
||||
struct bpf_stat
|
||||
{
|
||||
UINT bs_recv; ///< Number of packets that the driver received from the network adapter
|
||||
///< from the beginning of the current capture. This value includes the packets
|
||||
///< lost by the driver.
|
||||
UINT bs_drop; ///< number of packets that the driver lost from the beginning of a capture.
|
||||
///< Basically, a packet is lost when the the buffer of the driver is full.
|
||||
///< In this situation the packet cannot be stored and the driver rejects it.
|
||||
UINT ps_ifdrop; ///< drops by interface. XXX not yet supported
|
||||
UINT bs_capt; ///< number of packets that pass the filter, find place in the kernel buffer and
|
||||
///< thus reach the application.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Packet header.
|
||||
|
||||
This structure defines the header associated with every packet delivered to the application.
|
||||
*/
|
||||
struct bpf_hdr
|
||||
{
|
||||
struct timeval bh_tstamp; ///< The timestamp associated with the captured packet.
|
||||
///< It is stored in a TimeVal structure.
|
||||
UINT bh_caplen; ///< Length of captured portion. The captured portion <b>can be different</b>
|
||||
///< from the original packet, because it is possible (with a proper filter)
|
||||
///< to instruct the driver to capture only a portion of the packets.
|
||||
UINT bh_datalen; ///< Original length of packet
|
||||
USHORT bh_hdrlen; ///< Length of bpf header (this struct plus alignment padding). In some cases,
|
||||
///< a padding could be added between the end of this structure and the packet
|
||||
///< data for performance reasons. This filed can be used to retrieve the actual data
|
||||
///< of the packet.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Dump packet header.
|
||||
|
||||
This structure defines the header associated with the packets in a buffer to be used with PacketSendPackets().
|
||||
It is simpler than the bpf_hdr, because it corresponds to the header associated by WinPcap and libpcap to a
|
||||
packet in a dump file. This makes straightforward sending WinPcap dump files to the network.
|
||||
*/
|
||||
struct dump_bpf_hdr{
|
||||
struct timeval ts; ///< Time stamp of the packet
|
||||
UINT caplen; ///< Length of captured portion. The captured portion can smaller than the
|
||||
///< the original packet, because it is possible (with a proper filter) to
|
||||
///< instruct the driver to capture only a portion of the packets.
|
||||
UINT len; ///< Length of the original packet (off wire).
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
struct bpf_stat;
|
||||
|
||||
#define DOSNAMEPREFIX TEXT("Packet_") ///< Prefix added to the adapters device names to create the WinPcap devices
|
||||
#define MAX_LINK_NAME_LENGTH 64 //< Maximum length of the devices symbolic links
|
||||
#define NMAX_PACKET 65535
|
||||
|
||||
/*!
|
||||
\brief Addresses of a network adapter.
|
||||
|
||||
This structure is used by the PacketGetNetInfoEx() function to return the IP addresses associated with
|
||||
an adapter.
|
||||
*/
|
||||
typedef struct npf_if_addr {
|
||||
struct sockaddr_storage IPAddress; ///< IP address.
|
||||
struct sockaddr_storage SubnetMask; ///< Netmask for that address.
|
||||
struct sockaddr_storage Broadcast; ///< Broadcast address.
|
||||
}npf_if_addr;
|
||||
|
||||
|
||||
#define ADAPTER_NAME_LENGTH 256 + 12 ///< Maximum length for the name of an adapter. The value is the same used by the IP Helper API.
|
||||
#define ADAPTER_DESC_LENGTH 128 ///< Maximum length for the description of an adapter. The value is the same used by the IP Helper API.
|
||||
#define MAX_MAC_ADDR_LENGTH 8 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
|
||||
#define MAX_NETWORK_ADDRESSES 16 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
|
||||
|
||||
|
||||
typedef struct WAN_ADAPTER_INT WAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
|
||||
typedef WAN_ADAPTER *PWAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
|
||||
|
||||
#define INFO_FLAG_NDIS_ADAPTER 0 ///< Flag for ADAPTER_INFO: this is a traditional ndis adapter
|
||||
#define INFO_FLAG_NDISWAN_ADAPTER 1 ///< Flag for ADAPTER_INFO: this is a NdisWan adapter, and it's managed by WANPACKET
|
||||
#define INFO_FLAG_DAG_CARD 2 ///< Flag for ADAPTER_INFO: this is a DAG card
|
||||
#define INFO_FLAG_DAG_FILE 6 ///< Flag for ADAPTER_INFO: this is a DAG file
|
||||
#define INFO_FLAG_DONT_EXPORT 8 ///< Flag for ADAPTER_INFO: when this flag is set, the adapter will not be listed or openend by winpcap. This allows to prevent exporting broken network adapters, like for example FireWire ones.
|
||||
#define INFO_FLAG_AIRPCAP_CARD 16 ///< Flag for ADAPTER_INFO: this is an airpcap card
|
||||
#define INFO_FLAG_NPFIM_DEVICE 32
|
||||
|
||||
/*!
|
||||
\brief Describes an opened network adapter.
|
||||
|
||||
This structure is the most important for the functioning of packet.dll, but the great part of its fields
|
||||
should be ignored by the user, since the library offers functions that avoid to cope with low-level parameters
|
||||
*/
|
||||
typedef struct _ADAPTER {
|
||||
HANDLE hFile; ///< \internal Handle to an open instance of the NPF driver.
|
||||
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; ///< \internal A string containing the name of the network adapter currently opened.
|
||||
int NumWrites; ///< \internal Number of times a packets written on this adapter will be repeated
|
||||
///< on the wire.
|
||||
HANDLE ReadEvent; ///< A notification event associated with the read calls on the adapter.
|
||||
///< It can be passed to standard Win32 functions (like WaitForSingleObject
|
||||
///< or WaitForMultipleObjects) to wait until the driver's buffer contains some
|
||||
///< data. It is particularly useful in GUI applications that need to wait
|
||||
///< concurrently on several events. In Windows NT/2000 the PacketSetMinToCopy()
|
||||
///< function can be used to define the minimum amount of data in the kernel buffer
|
||||
///< that will cause the event to be signalled.
|
||||
|
||||
UINT ReadTimeOut; ///< \internal The amount of time after which a read on the driver will be released and
|
||||
///< ReadEvent will be signaled, also if no packets were captured
|
||||
CHAR Name[ADAPTER_NAME_LENGTH];
|
||||
PWAN_ADAPTER pWanAdapter;
|
||||
UINT Flags; ///< Adapter's flags. Tell if this adapter must be treated in a different way, using the Netmon API or the dagc API.
|
||||
|
||||
#ifdef HAVE_AIRPCAP_API
|
||||
PAirpcapHandle AirpcapAd;
|
||||
#endif // HAVE_AIRPCAP_API
|
||||
|
||||
#ifdef HAVE_NPFIM_API
|
||||
void* NpfImHandle;
|
||||
#endif // HAVE_NPFIM_API
|
||||
|
||||
#ifdef HAVE_DAG_API
|
||||
dagc_t *pDagCard; ///< Pointer to the dagc API adapter descriptor for this adapter
|
||||
PCHAR DagBuffer; ///< Pointer to the buffer with the packets that is received from the DAG card
|
||||
struct timeval DagReadTimeout; ///< Read timeout. The dagc API requires a timeval structure
|
||||
unsigned DagFcsLen; ///< Length of the frame check sequence attached to any packet by the card. Obtained from the registry
|
||||
DWORD DagFastProcess; ///< True if the user requests fast capture processing on this card. Higher level applications can use this value to provide a faster but possibly unprecise capture (for example, libpcap doesn't convert the timestamps).
|
||||
#endif // HAVE_DAG_API
|
||||
} ADAPTER, *LPADAPTER;
|
||||
|
||||
/*!
|
||||
\brief Structure that contains a group of packets coming from the driver.
|
||||
|
||||
This structure defines the header associated with every packet delivered to the application.
|
||||
*/
|
||||
typedef struct _PACKET {
|
||||
HANDLE hEvent; ///< \deprecated Still present for compatibility with old applications.
|
||||
OVERLAPPED OverLapped; ///< \deprecated Still present for compatibility with old applications.
|
||||
PVOID Buffer; ///< Buffer with containing the packets. See the PacketReceivePacket() for
|
||||
///< details about the organization of the data in this buffer
|
||||
UINT Length; ///< Length of the buffer
|
||||
DWORD ulBytesReceived; ///< Number of valid bytes present in the buffer, i.e. amount of data
|
||||
///< received by the last call to PacketReceivePacket()
|
||||
BOOLEAN bIoComplete; ///< \deprecated Still present for compatibility with old applications.
|
||||
} PACKET, *LPPACKET;
|
||||
|
||||
/*!
|
||||
\brief Structure containing an OID request.
|
||||
|
||||
It is used by the PacketRequest() function to send an OID to the interface card driver.
|
||||
It can be used, for example, to retrieve the status of the error counters on the adapter, its MAC address,
|
||||
the list of the multicast groups defined on it, and so on.
|
||||
*/
|
||||
struct _PACKET_OID_DATA {
|
||||
ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
|
||||
///< for a complete list of valid codes.
|
||||
ULONG Length; ///< Length of the data field
|
||||
UCHAR Data[1]; ///< variable-length field that contains the information passed to or received
|
||||
///< from the adapter.
|
||||
};
|
||||
typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*
|
||||
BOOLEAN QueryWinPcapRegistryStringA(CHAR *SubKeyName,
|
||||
CHAR *Value,
|
||||
UINT *pValueLen,
|
||||
CHAR *DefaultVal);
|
||||
|
||||
BOOLEAN QueryWinPcapRegistryStringW(WCHAR *SubKeyName,
|
||||
WCHAR *Value,
|
||||
UINT *pValueLen,
|
||||
WCHAR *DefaultVal);
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EXPORTED FUNCTIONS
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
PCHAR PacketGetVersion();
|
||||
PCHAR PacketGetDriverVersion();
|
||||
BOOLEAN PacketSetMinToCopy(LPADAPTER AdapterObject,int nbytes);
|
||||
BOOLEAN PacketSetNumWrites(LPADAPTER AdapterObject,int nwrites);
|
||||
BOOLEAN PacketSetMode(LPADAPTER AdapterObject,int mode);
|
||||
BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout);
|
||||
BOOLEAN PacketSetBpf(LPADAPTER AdapterObject,struct bpf_program *fp);
|
||||
BOOLEAN PacketSetLoopbackBehavior(LPADAPTER AdapterObject, UINT LoopbackBehavior);
|
||||
INT PacketSetSnapLen(LPADAPTER AdapterObject,int snaplen);
|
||||
BOOLEAN PacketGetStats(LPADAPTER AdapterObject,struct bpf_stat *s);
|
||||
BOOLEAN PacketGetStatsEx(LPADAPTER AdapterObject,struct bpf_stat *s);
|
||||
BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim);
|
||||
BOOLEAN PacketGetNetType (LPADAPTER AdapterObject,NetType *type);
|
||||
LPADAPTER PacketOpenAdapter(PCHAR AdapterName);
|
||||
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET pPacket,BOOLEAN Sync);
|
||||
INT PacketSendPackets(LPADAPTER AdapterObject,PVOID PacketBuff,ULONG Size, BOOLEAN Sync);
|
||||
LPPACKET PacketAllocatePacket(void);
|
||||
VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length);
|
||||
VOID PacketFreePacket(LPPACKET lpPacket);
|
||||
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
|
||||
BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter);
|
||||
BOOLEAN PacketGetAdapterNames(PTSTR pStr,PULONG BufferSize);
|
||||
BOOLEAN PacketGetNetInfoEx(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries);
|
||||
BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);
|
||||
HANDLE PacketGetReadEvent(LPADAPTER AdapterObject);
|
||||
BOOLEAN PacketSetDumpName(LPADAPTER AdapterObject, void *name, int len);
|
||||
BOOLEAN PacketSetDumpLimits(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks);
|
||||
BOOLEAN PacketIsDumpEnded(LPADAPTER AdapterObject, BOOLEAN sync);
|
||||
BOOL PacketStopDriver();
|
||||
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
|
||||
BOOLEAN PacketStartOem(PCHAR errorString, UINT errorStringLength);
|
||||
BOOLEAN PacketStartOemEx(PCHAR errorString, UINT errorStringLength, ULONG flags);
|
||||
PAirpcapHandle PacketGetAirPcapHandle(LPADAPTER AdapterObject);
|
||||
|
||||
//
|
||||
// Used by PacketStartOemEx
|
||||
//
|
||||
#define PACKET_START_OEM_NO_NETMON 0x00000001
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__PACKET32
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __WIN32_EXTENSIONS_H__
|
||||
#define __WIN32_EXTENSIONS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/*!
|
||||
\brief A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit().
|
||||
*/
|
||||
struct pcap_send_queue
|
||||
{
|
||||
u_int maxlen; ///< Maximum size of the the queue, in bytes. This variable contains the size of the buffer field.
|
||||
u_int len; ///< Current size of the queue, in bytes.
|
||||
char *buffer; ///< Buffer containing the packets to be sent.
|
||||
};
|
||||
|
||||
typedef struct pcap_send_queue pcap_send_queue;
|
||||
|
||||
/*!
|
||||
\brief This typedef is a support for the pcap_get_airpcap_handle() function
|
||||
*/
|
||||
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
|
||||
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
|
||||
typedef struct _AirpcapHandle *PAirpcapHandle;
|
||||
#endif
|
||||
|
||||
#define BPF_MEM_EX_IMM 0xc0
|
||||
#define BPF_MEM_EX_IND 0xe0
|
||||
|
||||
/*used for ST*/
|
||||
#define BPF_MEM_EX 0xc0
|
||||
#define BPF_TME 0x08
|
||||
|
||||
#define BPF_LOOKUP 0x90
|
||||
#define BPF_EXECUTE 0xa0
|
||||
#define BPF_INIT 0xb0
|
||||
#define BPF_VALIDATE 0xc0
|
||||
#define BPF_SET_ACTIVE 0xd0
|
||||
#define BPF_RESET 0xe0
|
||||
#define BPF_SET_MEMORY 0x80
|
||||
#define BPF_GET_REGISTER_VALUE 0x70
|
||||
#define BPF_SET_REGISTER_VALUE 0x60
|
||||
#define BPF_SET_WORKING 0x50
|
||||
#define BPF_SET_ACTIVE_READ 0x40
|
||||
#define BPF_SET_AUTODELETION 0x30
|
||||
#define BPF_SEPARATION 0xff
|
||||
|
||||
/* Prototypes */
|
||||
pcap_send_queue* pcap_sendqueue_alloc(u_int memsize);
|
||||
|
||||
void pcap_sendqueue_destroy(pcap_send_queue* queue);
|
||||
|
||||
int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
|
||||
|
||||
u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync);
|
||||
|
||||
HANDLE pcap_getevent(pcap_t *p);
|
||||
|
||||
struct pcap_stat *pcap_stats_ex(pcap_t *p, int *pcap_stat_size);
|
||||
|
||||
int pcap_setuserbuffer(pcap_t *p, int size);
|
||||
|
||||
int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks);
|
||||
|
||||
int pcap_live_dump_ended(pcap_t *p, int sync);
|
||||
|
||||
int pcap_offline_filter(struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data);
|
||||
|
||||
int pcap_start_oem(char* err_str, int flags);
|
||||
|
||||
PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__WIN32_EXTENSIONS_H__
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 1999 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BITTYPES_H
|
||||
#define _BITTYPES_H
|
||||
|
||||
#ifndef HAVE_U_INT8_T
|
||||
|
||||
#if SIZEOF_CHAR == 1
|
||||
typedef unsigned char u_int8_t;
|
||||
typedef signed char int8_t;
|
||||
#elif SIZEOF_INT == 1
|
||||
typedef unsigned int u_int8_t;
|
||||
typedef signed int int8_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int8_t"
|
||||
#endif
|
||||
#define HAVE_U_INT8_T 1
|
||||
#define HAVE_INT8_T 1
|
||||
|
||||
#endif /* HAVE_U_INT8_T */
|
||||
|
||||
#ifndef HAVE_U_INT16_T
|
||||
|
||||
#if SIZEOF_SHORT == 2
|
||||
typedef unsigned short u_int16_t;
|
||||
typedef signed short int16_t;
|
||||
#elif SIZEOF_INT == 2
|
||||
typedef unsigned int u_int16_t;
|
||||
typedef signed int int16_t;
|
||||
#elif SIZEOF_CHAR == 2
|
||||
typedef unsigned char u_int16_t;
|
||||
typedef signed char int16_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int16_t"
|
||||
#endif
|
||||
#define HAVE_U_INT16_T 1
|
||||
#define HAVE_INT16_T 1
|
||||
|
||||
#endif /* HAVE_U_INT16_T */
|
||||
|
||||
#ifndef HAVE_U_INT32_T
|
||||
|
||||
#if SIZEOF_INT == 4
|
||||
typedef unsigned int u_int32_t;
|
||||
typedef signed int int32_t;
|
||||
#elif SIZEOF_LONG == 4
|
||||
typedef unsigned long u_int32_t;
|
||||
typedef signed long int32_t;
|
||||
#elif SIZEOF_SHORT == 4
|
||||
typedef unsigned short u_int32_t;
|
||||
typedef signed short int32_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int32_t"
|
||||
#endif
|
||||
#define HAVE_U_INT32_T 1
|
||||
#define HAVE_INT32_T 1
|
||||
|
||||
#endif /* HAVE_U_INT32_T */
|
||||
|
||||
#ifndef HAVE_U_INT64_T
|
||||
#if SIZEOF_LONG_LONG == 8
|
||||
typedef unsigned long long u_int64_t;
|
||||
typedef long long int64_t;
|
||||
#elif defined(_MSC_EXTENSIONS)
|
||||
typedef unsigned _int64 u_int64_t;
|
||||
typedef _int64 int64_t;
|
||||
#elif SIZEOF_INT == 8
|
||||
typedef unsigned int u_int64_t;
|
||||
#elif SIZEOF_LONG == 8
|
||||
typedef unsigned long u_int64_t;
|
||||
#elif SIZEOF_SHORT == 8
|
||||
typedef unsigned short u_int64_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int64_t"
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_U_INT64_T */
|
||||
|
||||
#ifndef PRId64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRId64 "I64d"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRId64 "lld"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRId64 */
|
||||
|
||||
#ifndef PRIo64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIo64 "I64o"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIo64 "llo"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIo64 */
|
||||
|
||||
#ifndef PRIx64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIx64 "I64x"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIx64 "llx"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIx64 */
|
||||
|
||||
#ifndef PRIu64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIu64 "I64u"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIu64 "llu"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIu64 */
|
||||
|
||||
#endif /* _BITTYPES_H */
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that: (1) source code distributions
|
||||
* retain the above copyright notice and this paragraph in its entirety, (2)
|
||||
* distributions including binary code include the above copyright notice and
|
||||
* this paragraph in its entirety in the documentation or other materials
|
||||
* provided with the distribution, and (3) all advertising materials mentioning
|
||||
* features or use of this software display the following acknowledgement:
|
||||
* ``This product includes software developed by the University of California,
|
||||
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
|
||||
* the University nor the names of its contributors may be used to endorse
|
||||
* or promote products derived from this software without specific prior
|
||||
* written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/Win32/Include/ip6_misc.h,v 1.5 2006-01-22 18:02:18 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#define IN_MULTICAST(a) IN_CLASSD(a)
|
||||
#endif
|
||||
|
||||
#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xf0000000) == 0xf0000000)
|
||||
|
||||
#define IN_LOOPBACKNET 127
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
/* IPv6 address */
|
||||
struct in6_addr
|
||||
{
|
||||
union
|
||||
{
|
||||
u_int8_t u6_addr8[16];
|
||||
u_int16_t u6_addr16[8];
|
||||
u_int32_t u6_addr32[4];
|
||||
} in6_u;
|
||||
#define s6_addr in6_u.u6_addr8
|
||||
#define s6_addr16 in6_u.u6_addr16
|
||||
#define s6_addr32 in6_u.u6_addr32
|
||||
#define s6_addr64 in6_u.u6_addr64
|
||||
};
|
||||
|
||||
#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
|
||||
#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
|
||||
#if (defined _MSC_VER) || (defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF))
|
||||
typedef unsigned short sa_family_t;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
|
||||
#define __SOCKADDR_COMMON(sa_prefix) \
|
||||
sa_family_t sa_prefix##family
|
||||
|
||||
/* Ditto, for IPv6. */
|
||||
struct sockaddr_in6
|
||||
{
|
||||
__SOCKADDR_COMMON (sin6_);
|
||||
u_int16_t sin6_port; /* Transport layer port # */
|
||||
u_int32_t sin6_flowinfo; /* IPv6 flow information */
|
||||
struct in6_addr sin6_addr; /* IPv6 address */
|
||||
};
|
||||
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) \
|
||||
((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \
|
||||
(((u_int32_t *) (a))[2] == htonl (0xffff)))
|
||||
|
||||
#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff)
|
||||
|
||||
#define IN6_IS_ADDR_LINKLOCAL(a) \
|
||||
((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000))
|
||||
|
||||
#define IN6_IS_ADDR_LOOPBACK(a) \
|
||||
(((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \
|
||||
((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1))
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
|
||||
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
|
||||
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
|
||||
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
|
||||
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
|
||||
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
|
||||
|
||||
#define nd_rd_type nd_rd_hdr.icmp6_type
|
||||
#define nd_rd_code nd_rd_hdr.icmp6_code
|
||||
#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
|
||||
#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
|
||||
|
||||
/*
|
||||
* IPV6 extension headers
|
||||
*/
|
||||
#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */
|
||||
#define IPPROTO_IPV6 41 /* IPv6 header. */
|
||||
#define IPPROTO_ROUTING 43 /* IPv6 routing header */
|
||||
#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
|
||||
#define IPPROTO_ESP 50 /* encapsulating security payload */
|
||||
#define IPPROTO_AH 51 /* authentication header */
|
||||
#define IPPROTO_ICMPV6 58 /* ICMPv6 */
|
||||
#define IPPROTO_NONE 59 /* IPv6 no next header */
|
||||
#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
|
||||
#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */
|
||||
|
||||
#define IPV6_RTHDR_TYPE_0 0
|
||||
|
||||
/* Option types and related macros */
|
||||
#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
|
||||
#define IP6OPT_PADN 0x01 /* 00 0 00001 */
|
||||
#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
|
||||
#define IP6OPT_JUMBO_LEN 6
|
||||
#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */
|
||||
|
||||
#define IP6OPT_RTALERT_LEN 4
|
||||
#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
|
||||
#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
|
||||
#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
|
||||
#define IP6OPT_MINLEN 2
|
||||
|
||||
#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */
|
||||
#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */
|
||||
#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */
|
||||
#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
|
||||
#define IP6OPT_EID 0x8a /* 10 0 01010 */
|
||||
|
||||
#define IP6OPT_TYPE(o) ((o) & 0xC0)
|
||||
#define IP6OPT_TYPE_SKIP 0x00
|
||||
#define IP6OPT_TYPE_DISCARD 0x40
|
||||
#define IP6OPT_TYPE_FORCEICMP 0x80
|
||||
#define IP6OPT_TYPE_ICMP 0xC0
|
||||
|
||||
#define IP6OPT_MUTABLE 0x20
|
||||
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
#ifndef EAI_ADDRFAMILY
|
||||
struct addrinfo {
|
||||
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
|
||||
int ai_family; /* PF_xxx */
|
||||
int ai_socktype; /* SOCK_xxx */
|
||||
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
|
||||
size_t ai_addrlen; /* length of ai_addr */
|
||||
char *ai_canonname; /* canonical name for hostname */
|
||||
struct sockaddr *ai_addr; /* binary address */
|
||||
struct addrinfo *ai_next; /* next structure in linked list */
|
||||
};
|
||||
#endif
|
||||
#endif /* __MINGW32__ */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-bpf.h,v 1.50 2007/04/01 21:43:55 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Some applications
|
||||
* might expect to be able to include <pcap-bpf.h>.
|
||||
*/
|
||||
#include <pcap/bpf.h>
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-namedb.h,v 1.13 2006/10/04 18:13:32 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Some applications
|
||||
* might expect to be able to include <pcap-namedb.h>.
|
||||
*/
|
||||
#include <pcap/namedb.h>
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2009 CACE Technologies, Inc. Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-stdinc.h,v 1.10.2.1 2008-10-06 15:38:39 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#define SIZEOF_CHAR 1
|
||||
#define SIZEOF_SHORT 2
|
||||
#define SIZEOF_INT 4
|
||||
#ifndef _MSC_EXTENSIONS
|
||||
#define SIZEOF_LONG_LONG 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Avoids a compiler warning in case this was already defined
|
||||
* (someone defined _WINSOCKAPI_ when including 'windows.h', in order
|
||||
* to prevent it from including 'winsock.h')
|
||||
*/
|
||||
#ifdef _WINSOCKAPI_
|
||||
#undef _WINSOCKAPI_
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "bittypes.h"
|
||||
#include <time.h>
|
||||
#include <io.h>
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#include "IP6_misc.h"
|
||||
#endif
|
||||
|
||||
#define caddr_t char*
|
||||
|
||||
#if _MSC_VER < 1500
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
#define inline __inline
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <stdint.h>
|
||||
#else /*__MINGW32__*/
|
||||
/* MSVC compiler */
|
||||
#ifndef _UINTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif
|
||||
#define _UINTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef _INTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef __int64 intptr_t;
|
||||
#else
|
||||
typedef _W64 int intptr_t;
|
||||
#endif
|
||||
#define _INTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
#endif /*__MINGW32__*/
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.59 2006/10/04 18:09:22 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Many applications
|
||||
* expect to be able to include <pcap.h>, and at least some of them
|
||||
* go through contortions in their configure scripts to try to detect
|
||||
* OSes that have "helpfully" moved pcap.h to <pcap/pcap.h> without
|
||||
* leaving behind a <pcap.h> file.
|
||||
*/
|
||||
#include <pcap/pcap.h>
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Paolo Abeni (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* bluetooth data struct
|
||||
* By Paolo Abeni <paolo.abeni@email.it>
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/bluetooth.h,v 1.1 2007/09/22 02:10:17 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PCAP_BLUETOOTH_STRUCTS_H__
|
||||
#define _PCAP_BLUETOOTH_STRUCTS_H__
|
||||
|
||||
/*
|
||||
* Header prepended libpcap to each bluetooth h:4 frame.
|
||||
* fields are in network byte order
|
||||
*/
|
||||
typedef struct _pcap_bluetooth_h4_header {
|
||||
u_int32_t direction; /* if first bit is set direction is incoming */
|
||||
} pcap_bluetooth_h4_header;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,934 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)bpf.h 7.1 (Berkeley) 5/7/91
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.19.2.8 2008-09-22 20:16:01 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is libpcap's cut-down version of bpf.h; it includes only
|
||||
* the stuff needed for the code generator and the userland BPF
|
||||
* interpreter, and the libpcap APIs for setting filters, etc..
|
||||
*
|
||||
* "pcap-bpf.c" will include the native OS version, as it deals with
|
||||
* the OS's BPF implementation.
|
||||
*
|
||||
* XXX - should this all just be moved to "pcap.h"?
|
||||
*/
|
||||
|
||||
#ifndef BPF_MAJOR_VERSION
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* BSD style release date */
|
||||
#define BPF_RELEASE 199606
|
||||
|
||||
#ifdef MSDOS /* must be 32-bit */
|
||||
typedef long bpf_int32;
|
||||
typedef unsigned long bpf_u_int32;
|
||||
#else
|
||||
typedef int bpf_int32;
|
||||
typedef u_int bpf_u_int32;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Alignment macros. BPF_WORDALIGN rounds up to the next
|
||||
* even multiple of BPF_ALIGNMENT.
|
||||
*/
|
||||
#ifndef __NetBSD__
|
||||
#define BPF_ALIGNMENT sizeof(bpf_int32)
|
||||
#else
|
||||
#define BPF_ALIGNMENT sizeof(long)
|
||||
#endif
|
||||
#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
|
||||
|
||||
#define BPF_MAXBUFSIZE 0x8000
|
||||
#define BPF_MINBUFSIZE 32
|
||||
|
||||
/*
|
||||
* Structure for "pcap_compile()", "pcap_setfilter()", etc..
|
||||
*/
|
||||
struct bpf_program {
|
||||
u_int bf_len;
|
||||
struct bpf_insn *bf_insns;
|
||||
};
|
||||
|
||||
/*
|
||||
* Struct return by BIOCVERSION. This represents the version number of
|
||||
* the filter language described by the instruction encodings below.
|
||||
* bpf understands a program iff kernel_major == filter_major &&
|
||||
* kernel_minor >= filter_minor, that is, if the value returned by the
|
||||
* running kernel has the same major number and a minor number equal
|
||||
* equal to or less than the filter being downloaded. Otherwise, the
|
||||
* results are undefined, meaning an error may be returned or packets
|
||||
* may be accepted haphazardly.
|
||||
* It has nothing to do with the source code version.
|
||||
*/
|
||||
struct bpf_version {
|
||||
u_short bv_major;
|
||||
u_short bv_minor;
|
||||
};
|
||||
/* Current version number of filter architecture. */
|
||||
#define BPF_MAJOR_VERSION 1
|
||||
#define BPF_MINOR_VERSION 1
|
||||
|
||||
/*
|
||||
* Data-link level type codes.
|
||||
*
|
||||
* Do *NOT* add new values to this list without asking
|
||||
* "tcpdump-workers@lists.tcpdump.org" for a value. Otherwise, you run
|
||||
* the risk of using a value that's already being used for some other
|
||||
* purpose, and of having tools that read libpcap-format captures not
|
||||
* being able to handle captures with your new DLT_ value, with no hope
|
||||
* that they will ever be changed to do so (as that would destroy their
|
||||
* ability to read captures using that value for that other purpose).
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are the types that are the same on all platforms, and that
|
||||
* have been defined by <net/bpf.h> for ages.
|
||||
*/
|
||||
#define DLT_NULL 0 /* BSD loopback encapsulation */
|
||||
#define DLT_EN10MB 1 /* Ethernet (10Mb) */
|
||||
#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */
|
||||
#define DLT_AX25 3 /* Amateur Radio AX.25 */
|
||||
#define DLT_PRONET 4 /* Proteon ProNET Token Ring */
|
||||
#define DLT_CHAOS 5 /* Chaos */
|
||||
#define DLT_IEEE802 6 /* 802.5 Token Ring */
|
||||
#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */
|
||||
#define DLT_SLIP 8 /* Serial Line IP */
|
||||
#define DLT_PPP 9 /* Point-to-point Protocol */
|
||||
#define DLT_FDDI 10 /* FDDI */
|
||||
|
||||
/*
|
||||
* These are types that are different on some platforms, and that
|
||||
* have been defined by <net/bpf.h> for ages. We use #ifdefs to
|
||||
* detect the BSDs that define them differently from the traditional
|
||||
* libpcap <net/bpf.h>
|
||||
*
|
||||
* XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS,
|
||||
* but I don't know what the right #define is for BSD/OS.
|
||||
*/
|
||||
#define DLT_ATM_RFC1483 11 /* LLC-encapsulated ATM */
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_RAW 14 /* raw IP */
|
||||
#else
|
||||
#define DLT_RAW 12 /* raw IP */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Given that the only OS that currently generates BSD/OS SLIP or PPP
|
||||
* is, well, BSD/OS, arguably everybody should have chosen its values
|
||||
* for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they
|
||||
* didn't. So it goes.
|
||||
*/
|
||||
#if defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
#ifndef DLT_SLIP_BSDOS
|
||||
#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */
|
||||
#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */
|
||||
#endif
|
||||
#else
|
||||
#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */
|
||||
#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 17 is used for DLT_OLD_PFLOG in OpenBSD;
|
||||
* OBSOLETE: DLT_PFLOG is 117 in OpenBSD now as well. See below.
|
||||
* 18 is used for DLT_PFSYNC in OpenBSD; don't use it for anything else.
|
||||
*/
|
||||
|
||||
#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */
|
||||
|
||||
/*
|
||||
* Apparently Redback uses this for its SmartEdge 400/800. I hope
|
||||
* nobody else decided to use it, too.
|
||||
*/
|
||||
#define DLT_REDBACK_SMARTEDGE 32
|
||||
|
||||
/*
|
||||
* These values are defined by NetBSD; other platforms should refrain from
|
||||
* using them for other purposes, so that NetBSD savefiles with link
|
||||
* types of 50 or 51 can be read as this type on all platforms.
|
||||
*/
|
||||
#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */
|
||||
#define DLT_PPP_ETHER 51 /* PPP over Ethernet */
|
||||
|
||||
/*
|
||||
* The Axent Raptor firewall - now the Symantec Enterprise Firewall - uses
|
||||
* a link-layer type of 99 for the tcpdump it supplies. The link-layer
|
||||
* header has 6 bytes of unknown data, something that appears to be an
|
||||
* Ethernet type, and 36 bytes that appear to be 0 in at least one capture
|
||||
* I've seen.
|
||||
*/
|
||||
#define DLT_SYMANTEC_FIREWALL 99
|
||||
|
||||
/*
|
||||
* Values between 100 and 103 are used in capture file headers as
|
||||
* link-layer types corresponding to DLT_ types that differ
|
||||
* between platforms; don't use those values for new DLT_ new types.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This value was defined by libpcap 0.5; platforms that have defined
|
||||
* it with a different value should define it here with that value -
|
||||
* a link type of 104 in a save file will be mapped to DLT_C_HDLC,
|
||||
* whatever value that happens to be, so programs will correctly
|
||||
* handle files with that link type regardless of the value of
|
||||
* DLT_C_HDLC.
|
||||
*
|
||||
* The name DLT_C_HDLC was used by BSD/OS; we use that name for source
|
||||
* compatibility with programs written for BSD/OS.
|
||||
*
|
||||
* libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well,
|
||||
* for source compatibility with programs written for libpcap 0.5.
|
||||
*/
|
||||
#define DLT_C_HDLC 104 /* Cisco HDLC */
|
||||
#define DLT_CHDLC DLT_C_HDLC
|
||||
|
||||
#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */
|
||||
|
||||
/*
|
||||
* 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW,
|
||||
* except when it isn't. (I.e., sometimes it's just raw IP, and
|
||||
* sometimes it isn't.) We currently handle it as DLT_LINUX_SLL,
|
||||
* so that we don't have to worry about the link-layer header.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides
|
||||
* with other values.
|
||||
* DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header
|
||||
* (DLCI, etc.).
|
||||
*/
|
||||
#define DLT_FRELAY 107
|
||||
|
||||
/*
|
||||
* OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except
|
||||
* that the AF_ type in the link-layer header is in network byte order.
|
||||
*
|
||||
* DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so
|
||||
* we don't use 12 for it in OSes other than OpenBSD.
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_LOOP 12
|
||||
#else
|
||||
#define DLT_LOOP 108
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's
|
||||
* DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other
|
||||
* than OpenBSD.
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_ENC 13
|
||||
#else
|
||||
#define DLT_ENC 109
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Values between 110 and 112 are reserved for use in capture file headers
|
||||
* as link-layer types corresponding to DLT_ types that might differ
|
||||
* between platforms; don't use those values for new DLT_ types
|
||||
* other than the corresponding DLT_ types.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is for Linux cooked sockets.
|
||||
*/
|
||||
#define DLT_LINUX_SLL 113
|
||||
|
||||
/*
|
||||
* Apple LocalTalk hardware.
|
||||
*/
|
||||
#define DLT_LTALK 114
|
||||
|
||||
/*
|
||||
* Acorn Econet.
|
||||
*/
|
||||
#define DLT_ECONET 115
|
||||
|
||||
/*
|
||||
* Reserved for use with OpenBSD ipfilter.
|
||||
*/
|
||||
#define DLT_IPFILTER 116
|
||||
|
||||
/*
|
||||
* OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, but that's DLT_LANE8023
|
||||
* in SuSE 6.3, so we can't use 17 for it in capture-file headers.
|
||||
*
|
||||
* XXX: is there a conflict with DLT_PFSYNC 18 as well?
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_OLD_PFLOG 17
|
||||
#define DLT_PFSYNC 18
|
||||
#endif
|
||||
#define DLT_PFLOG 117
|
||||
|
||||
/*
|
||||
* Registered for Cisco-internal use.
|
||||
*/
|
||||
#define DLT_CISCO_IOS 118
|
||||
|
||||
/*
|
||||
* For 802.11 cards using the Prism II chips, with a link-layer
|
||||
* header including Prism monitor mode information plus an 802.11
|
||||
* header.
|
||||
*/
|
||||
#define DLT_PRISM_HEADER 119
|
||||
|
||||
/*
|
||||
* Reserved for Aironet 802.11 cards, with an Aironet link-layer header
|
||||
* (see Doug Ambrisko's FreeBSD patches).
|
||||
*/
|
||||
#define DLT_AIRONET_HEADER 120
|
||||
|
||||
/*
|
||||
* Reserved for Siemens HiPath HDLC.
|
||||
*/
|
||||
#define DLT_HHDLC 121
|
||||
|
||||
/*
|
||||
* This is for RFC 2625 IP-over-Fibre Channel.
|
||||
*
|
||||
* This is not for use with raw Fibre Channel, where the link-layer
|
||||
* header starts with a Fibre Channel frame header; it's for IP-over-FC,
|
||||
* where the link-layer header starts with an RFC 2625 Network_Header
|
||||
* field.
|
||||
*/
|
||||
#define DLT_IP_OVER_FC 122
|
||||
|
||||
/*
|
||||
* This is for Full Frontal ATM on Solaris with SunATM, with a
|
||||
* pseudo-header followed by an AALn PDU.
|
||||
*
|
||||
* There may be other forms of Full Frontal ATM on other OSes,
|
||||
* with different pseudo-headers.
|
||||
*
|
||||
* If ATM software returns a pseudo-header with VPI/VCI information
|
||||
* (and, ideally, packet type information, e.g. signalling, ILMI,
|
||||
* LANE, LLC-multiplexed traffic, etc.), it should not use
|
||||
* DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump
|
||||
* and the like don't have to infer the presence or absence of a
|
||||
* pseudo-header and the form of the pseudo-header.
|
||||
*/
|
||||
#define DLT_SUNATM 123 /* Solaris+SunATM */
|
||||
|
||||
/*
|
||||
* Reserved as per request from Kent Dahlgren <kent@praesum.com>
|
||||
* for private use.
|
||||
*/
|
||||
#define DLT_RIO 124 /* RapidIO */
|
||||
#define DLT_PCI_EXP 125 /* PCI Express */
|
||||
#define DLT_AURORA 126 /* Xilinx Aurora link layer */
|
||||
|
||||
/*
|
||||
* Header for 802.11 plus a number of bits of link-layer information
|
||||
* including radio information, used by some recent BSD drivers as
|
||||
* well as the madwifi Atheros driver for Linux.
|
||||
*/
|
||||
#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus radiotap radio header */
|
||||
|
||||
/*
|
||||
* Reserved for the TZSP encapsulation, as per request from
|
||||
* Chris Waters <chris.waters@networkchemistry.com>
|
||||
* TZSP is a generic encapsulation for any other link type,
|
||||
* which includes a means to include meta-information
|
||||
* with the packet, e.g. signal strength and channel
|
||||
* for 802.11 packets.
|
||||
*/
|
||||
#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */
|
||||
|
||||
/*
|
||||
* BSD's ARCNET headers have the source host, destination host,
|
||||
* and type at the beginning of the packet; that's what's handed
|
||||
* up to userland via BPF.
|
||||
*
|
||||
* Linux's ARCNET headers, however, have a 2-byte offset field
|
||||
* between the host IDs and the type; that's what's handed up
|
||||
* to userland via PF_PACKET sockets.
|
||||
*
|
||||
* We therefore have to have separate DLT_ values for them.
|
||||
*/
|
||||
#define DLT_ARCNET_LINUX 129 /* ARCNET */
|
||||
|
||||
/*
|
||||
* Juniper-private data link types, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_MLPPP 130
|
||||
#define DLT_JUNIPER_MLFR 131
|
||||
#define DLT_JUNIPER_ES 132
|
||||
#define DLT_JUNIPER_GGSN 133
|
||||
#define DLT_JUNIPER_MFR 134
|
||||
#define DLT_JUNIPER_ATM2 135
|
||||
#define DLT_JUNIPER_SERVICES 136
|
||||
#define DLT_JUNIPER_ATM1 137
|
||||
|
||||
/*
|
||||
* Apple IP-over-IEEE 1394, as per a request from Dieter Siegmund
|
||||
* <dieter@apple.com>. The header that's presented is an Ethernet-like
|
||||
* header:
|
||||
*
|
||||
* #define FIREWIRE_EUI64_LEN 8
|
||||
* struct firewire_header {
|
||||
* u_char firewire_dhost[FIREWIRE_EUI64_LEN];
|
||||
* u_char firewire_shost[FIREWIRE_EUI64_LEN];
|
||||
* u_short firewire_type;
|
||||
* };
|
||||
*
|
||||
* with "firewire_type" being an Ethernet type value, rather than,
|
||||
* for example, raw GASP frames being handed up.
|
||||
*/
|
||||
#define DLT_APPLE_IP_OVER_IEEE1394 138
|
||||
|
||||
/*
|
||||
* Various SS7 encapsulations, as per a request from Jeff Morriss
|
||||
* <jeff.morriss[AT]ulticom.com> and subsequent discussions.
|
||||
*/
|
||||
#define DLT_MTP2_WITH_PHDR 139 /* pseudo-header with various info, followed by MTP2 */
|
||||
#define DLT_MTP2 140 /* MTP2, without pseudo-header */
|
||||
#define DLT_MTP3 141 /* MTP3, without pseudo-header or MTP2 */
|
||||
#define DLT_SCCP 142 /* SCCP, without pseudo-header or MTP2 or MTP3 */
|
||||
|
||||
/*
|
||||
* DOCSIS MAC frames.
|
||||
*/
|
||||
#define DLT_DOCSIS 143
|
||||
|
||||
/*
|
||||
* Linux-IrDA packets. Protocol defined at http://www.irda.org.
|
||||
* Those packets include IrLAP headers and above (IrLMP...), but
|
||||
* don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy
|
||||
* framing can be handled by the hardware and depend on the bitrate.
|
||||
* This is exactly the format you would get capturing on a Linux-IrDA
|
||||
* interface (irdaX), but not on a raw serial port.
|
||||
* Note the capture is done in "Linux-cooked" mode, so each packet include
|
||||
* a fake packet header (struct sll_header). This is because IrDA packet
|
||||
* decoding is dependant on the direction of the packet (incomming or
|
||||
* outgoing).
|
||||
* When/if other platform implement IrDA capture, we may revisit the
|
||||
* issue and define a real DLT_IRDA...
|
||||
* Jean II
|
||||
*/
|
||||
#define DLT_LINUX_IRDA 144
|
||||
|
||||
/*
|
||||
* Reserved for IBM SP switch and IBM Next Federation switch.
|
||||
*/
|
||||
#define DLT_IBM_SP 145
|
||||
#define DLT_IBM_SN 146
|
||||
|
||||
/*
|
||||
* Reserved for private use. If you have some link-layer header type
|
||||
* that you want to use within your organization, with the capture files
|
||||
* using that link-layer header type not ever be sent outside your
|
||||
* organization, you can use these values.
|
||||
*
|
||||
* No libpcap release will use these for any purpose, nor will any
|
||||
* tcpdump release use them, either.
|
||||
*
|
||||
* Do *NOT* use these in capture files that you expect anybody not using
|
||||
* your private versions of capture-file-reading tools to read; in
|
||||
* particular, do *NOT* use them in products, otherwise you may find that
|
||||
* people won't be able to use tcpdump, or snort, or Ethereal, or... to
|
||||
* read capture files from your firewall/intrusion detection/traffic
|
||||
* monitoring/etc. appliance, or whatever product uses that DLT_ value,
|
||||
* and you may also find that the developers of those applications will
|
||||
* not accept patches to let them read those files.
|
||||
*
|
||||
* Also, do not use them if somebody might send you a capture using them
|
||||
* for *their* private type and tools using them for *your* private type
|
||||
* would have to read them.
|
||||
*
|
||||
* Instead, ask "tcpdump-workers@lists.tcpdump.org" for a new DLT_ value,
|
||||
* as per the comment above, and use the type you're given.
|
||||
*/
|
||||
#define DLT_USER0 147
|
||||
#define DLT_USER1 148
|
||||
#define DLT_USER2 149
|
||||
#define DLT_USER3 150
|
||||
#define DLT_USER4 151
|
||||
#define DLT_USER5 152
|
||||
#define DLT_USER6 153
|
||||
#define DLT_USER7 154
|
||||
#define DLT_USER8 155
|
||||
#define DLT_USER9 156
|
||||
#define DLT_USER10 157
|
||||
#define DLT_USER11 158
|
||||
#define DLT_USER12 159
|
||||
#define DLT_USER13 160
|
||||
#define DLT_USER14 161
|
||||
#define DLT_USER15 162
|
||||
|
||||
/*
|
||||
* For future use with 802.11 captures - defined by AbsoluteValue
|
||||
* Systems to store a number of bits of link-layer information
|
||||
* including radio information:
|
||||
*
|
||||
* http://www.shaftnet.org/~pizza/software/capturefrm.txt
|
||||
*
|
||||
* but it might be used by some non-AVS drivers now or in the
|
||||
* future.
|
||||
*/
|
||||
#define DLT_IEEE802_11_RADIO_AVS 163 /* 802.11 plus AVS radio header */
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_MONITOR 164
|
||||
|
||||
/*
|
||||
* Reserved for BACnet MS/TP.
|
||||
*/
|
||||
#define DLT_BACNET_MS_TP 165
|
||||
|
||||
/*
|
||||
* Another PPP variant as per request from Karsten Keil <kkeil@suse.de>.
|
||||
*
|
||||
* This is used in some OSes to allow a kernel socket filter to distinguish
|
||||
* between incoming and outgoing packets, on a socket intended to
|
||||
* supply pppd with outgoing packets so it can do dial-on-demand and
|
||||
* hangup-on-lack-of-demand; incoming packets are filtered out so they
|
||||
* don't cause pppd to hold the connection up (you don't want random
|
||||
* input packets such as port scans, packets from old lost connections,
|
||||
* etc. to force the connection to stay up).
|
||||
*
|
||||
* The first byte of the PPP header (0xff03) is modified to accomodate
|
||||
* the direction - 0x00 = IN, 0x01 = OUT.
|
||||
*/
|
||||
#define DLT_PPP_PPPD 166
|
||||
|
||||
/*
|
||||
* Names for backwards compatibility with older versions of some PPP
|
||||
* software; new software should use DLT_PPP_PPPD.
|
||||
*/
|
||||
#define DLT_PPP_WITH_DIRECTION DLT_PPP_PPPD
|
||||
#define DLT_LINUX_PPP_WITHDIRECTION DLT_PPP_PPPD
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, cookies, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_PPPOE 167
|
||||
#define DLT_JUNIPER_PPPOE_ATM 168
|
||||
|
||||
#define DLT_GPRS_LLC 169 /* GPRS LLC */
|
||||
#define DLT_GPF_T 170 /* GPF-T (ITU-T G.7041/Y.1303) */
|
||||
#define DLT_GPF_F 171 /* GPF-F (ITU-T G.7041/Y.1303) */
|
||||
|
||||
/*
|
||||
* Requested by Oolan Zimmer <oz@gcom.com> for use in Gcom's T1/E1 line
|
||||
* monitoring equipment.
|
||||
*/
|
||||
#define DLT_GCOM_T1E1 172
|
||||
#define DLT_GCOM_SERIAL 173
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_ is used
|
||||
* for internal communication to Physical Interface Cards (PIC)
|
||||
*/
|
||||
#define DLT_JUNIPER_PIC_PEER 174
|
||||
|
||||
/*
|
||||
* Link types requested by Gregor Maier <gregor@endace.com> of Endace
|
||||
* Measurement Systems. They add an ERF header (see
|
||||
* http://www.endace.com/support/EndaceRecordFormat.pdf) in front of
|
||||
* the link-layer header.
|
||||
*/
|
||||
#define DLT_ERF_ETH 175 /* Ethernet */
|
||||
#define DLT_ERF_POS 176 /* Packet-over-SONET */
|
||||
|
||||
/*
|
||||
* Requested by Daniele Orlandi <daniele@orlandi.com> for raw LAPD
|
||||
* for vISDN (http://www.orlandi.com/visdn/). Its link-layer header
|
||||
* includes additional information before the LAPD header, so it's
|
||||
* not necessarily a generic LAPD header.
|
||||
*/
|
||||
#define DLT_LINUX_LAPD 177
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ are used for prepending meta-information
|
||||
* like interface index, interface name
|
||||
* before standard Ethernet, PPP, Frelay & C-HDLC Frames
|
||||
*/
|
||||
#define DLT_JUNIPER_ETHER 178
|
||||
#define DLT_JUNIPER_PPP 179
|
||||
#define DLT_JUNIPER_FRELAY 180
|
||||
#define DLT_JUNIPER_CHDLC 181
|
||||
|
||||
/*
|
||||
* Multi Link Frame Relay (FRF.16)
|
||||
*/
|
||||
#define DLT_MFR 182
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for internal communication with a
|
||||
* voice Adapter Card (PIC)
|
||||
*/
|
||||
#define DLT_JUNIPER_VP 183
|
||||
|
||||
/*
|
||||
* Arinc 429 frames.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Every frame contains a 32bit A429 label.
|
||||
* More documentation on Arinc 429 can be found at
|
||||
* http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf
|
||||
*/
|
||||
#define DLT_A429 184
|
||||
|
||||
/*
|
||||
* Arinc 653 Interpartition Communication messages.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Please refer to the A653-1 standard for more information.
|
||||
*/
|
||||
#define DLT_A653_ICM 185
|
||||
|
||||
/*
|
||||
* USB packets, beginning with a USB setup header; requested by
|
||||
* Paolo Abeni <paolo.abeni@email.it>.
|
||||
*/
|
||||
#define DLT_USB 186
|
||||
|
||||
/*
|
||||
* Bluetooth HCI UART transport layer (part H:4); requested by
|
||||
* Paolo Abeni.
|
||||
*/
|
||||
#define DLT_BLUETOOTH_HCI_H4 187
|
||||
|
||||
/*
|
||||
* IEEE 802.16 MAC Common Part Sublayer; requested by Maria Cruz
|
||||
* <cruz_petagay@bah.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_16_MAC_CPS 188
|
||||
|
||||
/*
|
||||
* USB packets, beginning with a Linux USB header; requested by
|
||||
* Paolo Abeni <paolo.abeni@email.it>.
|
||||
*/
|
||||
#define DLT_USB_LINUX 189
|
||||
|
||||
/*
|
||||
* Controller Area Network (CAN) v. 2.0B packets.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Used to dump CAN packets coming from a CAN Vector board.
|
||||
* More documentation on the CAN v2.0B frames can be found at
|
||||
* http://www.can-cia.org/downloads/?269
|
||||
*/
|
||||
#define DLT_CAN20B 190
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, with address fields padded, as is done by Linux
|
||||
* drivers; requested by Juergen Schimmer.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4_LINUX 191
|
||||
|
||||
/*
|
||||
* Per Packet Information encapsulated packets.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
*/
|
||||
#define DLT_PPI 192
|
||||
|
||||
/*
|
||||
* Header for 802.16 MAC Common Part Sublayer plus a radiotap radio header;
|
||||
* requested by Charles Clancy.
|
||||
*/
|
||||
#define DLT_IEEE802_16_MAC_CPS_RADIO 193
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for internal communication with a
|
||||
* integrated service module (ISM).
|
||||
*/
|
||||
#define DLT_JUNIPER_ISM 194
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
|
||||
* nothing); requested by Mikko Saarnivala <mikko.saarnivala@sensinode.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4 195
|
||||
|
||||
/*
|
||||
* Various link-layer types, with a pseudo-header, for SITA
|
||||
* (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com).
|
||||
*/
|
||||
#define DLT_SITA 196
|
||||
|
||||
/*
|
||||
* Various link-layer types, with a pseudo-header, for Endace DAG cards;
|
||||
* encapsulates Endace ERF records. Requested by Stephen Donnelly
|
||||
* <stephen@endace.com>.
|
||||
*/
|
||||
#define DLT_ERF 197
|
||||
|
||||
/*
|
||||
* Special header prepended to Ethernet packets when capturing from a
|
||||
* u10 Networks board. Requested by Phil Mulholland
|
||||
* <phil@u10networks.com>.
|
||||
*/
|
||||
#define DLT_RAIF1 198
|
||||
|
||||
/*
|
||||
* IPMB packet for IPMI, beginning with the I2C slave address, followed
|
||||
* by the netFn and LUN, etc.. Requested by Chanthy Toeung
|
||||
* <chanthy.toeung@ca.kontron.com>.
|
||||
*/
|
||||
#define DLT_IPMB 199
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for capturing data on a secure tunnel interface.
|
||||
*/
|
||||
#define DLT_JUNIPER_ST 200
|
||||
|
||||
/*
|
||||
* Bluetooth HCI UART transport layer (part H:4), with pseudo-header
|
||||
* that includes direction information; requested by Paolo Abeni.
|
||||
*/
|
||||
#define DLT_BLUETOOTH_HCI_H4_WITH_PHDR 201
|
||||
|
||||
/*
|
||||
* AX.25 packet with a 1-byte KISS header; see
|
||||
*
|
||||
* http://www.ax25.net/kiss.htm
|
||||
*
|
||||
* as per Richard Stearn <richard@rns-stearn.demon.co.uk>.
|
||||
*/
|
||||
#define DLT_AX25_KISS 202
|
||||
|
||||
/*
|
||||
* LAPD packets from an ISDN channel, starting with the address field,
|
||||
* with no pseudo-header.
|
||||
* Requested by Varuna De Silva <varunax@gmail.com>.
|
||||
*/
|
||||
#define DLT_LAPD 203
|
||||
|
||||
/*
|
||||
* Variants of various link-layer headers, with a one-byte direction
|
||||
* pseudo-header prepended - zero means "received by this host",
|
||||
* non-zero (any non-zero value) means "sent by this host" - as per
|
||||
* Will Barker <w.barker@zen.co.uk>.
|
||||
*/
|
||||
#define DLT_PPP_WITH_DIR 204 /* PPP - don't confuse with DLT_PPP_WITH_DIRECTION */
|
||||
#define DLT_C_HDLC_WITH_DIR 205 /* Cisco HDLC */
|
||||
#define DLT_FRELAY_WITH_DIR 206 /* Frame Relay */
|
||||
#define DLT_LAPB_WITH_DIR 207 /* LAPB */
|
||||
|
||||
/*
|
||||
* 208 is reserved for an as-yet-unspecified proprietary link-layer
|
||||
* type, as requested by Will Barker.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IPMB with a Linux-specific pseudo-header; as requested by Alexey Neyman
|
||||
* <avn@pigeonpoint.com>.
|
||||
*/
|
||||
#define DLT_IPMB_LINUX 209
|
||||
|
||||
/*
|
||||
* FlexRay automotive bus - http://www.flexray.com/ - as requested
|
||||
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_FLEXRAY 210
|
||||
|
||||
/*
|
||||
* Media Oriented Systems Transport (MOST) bus for multimedia
|
||||
* transport - http://www.mostcooperation.com/ - as requested
|
||||
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_MOST 211
|
||||
|
||||
/*
|
||||
* Local Interconnect Network (LIN) bus for vehicle networks -
|
||||
* http://www.lin-subbus.org/ - as requested by Hannes Kaelber
|
||||
* <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_LIN 212
|
||||
|
||||
/*
|
||||
* X2E-private data link type used for serial line capture,
|
||||
* as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_X2E_SERIAL 213
|
||||
|
||||
/*
|
||||
* X2E-private data link type used for the Xoraya data logger
|
||||
* family, as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_X2E_XORAYA 214
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
|
||||
* nothing), but with the PHY-level data for non-ASK PHYs (4 octets
|
||||
* of 0 as preamble, one octet of SFD, one octet of frame length+
|
||||
* reserved bit, and then the MAC-layer data, starting with the
|
||||
* frame control field).
|
||||
*
|
||||
* Requested by Max Filippov <jcmvbkbc@gmail.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4_NONASK_PHY 215
|
||||
|
||||
|
||||
/*
|
||||
* DLT and savefile link type values are split into a class and
|
||||
* a member of that class. A class value of 0 indicates a regular
|
||||
* DLT_/LINKTYPE_ value.
|
||||
*/
|
||||
#define DLT_CLASS(x) ((x) & 0x03ff0000)
|
||||
|
||||
/*
|
||||
* NetBSD-specific generic "raw" link type. The class value indicates
|
||||
* that this is the generic raw type, and the lower 16 bits are the
|
||||
* address family we're dealing with. Those values are NetBSD-specific;
|
||||
* do not assume that they correspond to AF_ values for your operating
|
||||
* system.
|
||||
*/
|
||||
#define DLT_CLASS_NETBSD_RAWAF 0x02240000
|
||||
#define DLT_NETBSD_RAWAF(af) (DLT_CLASS_NETBSD_RAWAF | (af))
|
||||
#define DLT_NETBSD_RAWAF_AF(x) ((x) & 0x0000ffff)
|
||||
#define DLT_IS_NETBSD_RAWAF(x) (DLT_CLASS(x) == DLT_CLASS_NETBSD_RAWAF)
|
||||
|
||||
|
||||
/*
|
||||
* The instruction encodings.
|
||||
*/
|
||||
/* instruction classes */
|
||||
#define BPF_CLASS(code) ((code) & 0x07)
|
||||
#define BPF_LD 0x00
|
||||
#define BPF_LDX 0x01
|
||||
#define BPF_ST 0x02
|
||||
#define BPF_STX 0x03
|
||||
#define BPF_ALU 0x04
|
||||
#define BPF_JMP 0x05
|
||||
#define BPF_RET 0x06
|
||||
#define BPF_MISC 0x07
|
||||
|
||||
/* ld/ldx fields */
|
||||
#define BPF_SIZE(code) ((code) & 0x18)
|
||||
#define BPF_W 0x00
|
||||
#define BPF_H 0x08
|
||||
#define BPF_B 0x10
|
||||
#define BPF_MODE(code) ((code) & 0xe0)
|
||||
#define BPF_IMM 0x00
|
||||
#define BPF_ABS 0x20
|
||||
#define BPF_IND 0x40
|
||||
#define BPF_MEM 0x60
|
||||
#define BPF_LEN 0x80
|
||||
#define BPF_MSH 0xa0
|
||||
|
||||
/* alu/jmp fields */
|
||||
#define BPF_OP(code) ((code) & 0xf0)
|
||||
#define BPF_ADD 0x00
|
||||
#define BPF_SUB 0x10
|
||||
#define BPF_MUL 0x20
|
||||
#define BPF_DIV 0x30
|
||||
#define BPF_OR 0x40
|
||||
#define BPF_AND 0x50
|
||||
#define BPF_LSH 0x60
|
||||
#define BPF_RSH 0x70
|
||||
#define BPF_NEG 0x80
|
||||
#define BPF_JA 0x00
|
||||
#define BPF_JEQ 0x10
|
||||
#define BPF_JGT 0x20
|
||||
#define BPF_JGE 0x30
|
||||
#define BPF_JSET 0x40
|
||||
#define BPF_SRC(code) ((code) & 0x08)
|
||||
#define BPF_K 0x00
|
||||
#define BPF_X 0x08
|
||||
|
||||
/* ret - BPF_K and BPF_X also apply */
|
||||
#define BPF_RVAL(code) ((code) & 0x18)
|
||||
#define BPF_A 0x10
|
||||
|
||||
/* misc */
|
||||
#define BPF_MISCOP(code) ((code) & 0xf8)
|
||||
#define BPF_TAX 0x00
|
||||
#define BPF_TXA 0x80
|
||||
|
||||
/*
|
||||
* The instruction data structure.
|
||||
*/
|
||||
struct bpf_insn {
|
||||
u_short code;
|
||||
u_char jt;
|
||||
u_char jf;
|
||||
bpf_u_int32 k;
|
||||
};
|
||||
|
||||
/*
|
||||
* Macros for insn array initializers.
|
||||
*/
|
||||
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
|
||||
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
|
||||
|
||||
#if __STDC__ || defined(__cplusplus)
|
||||
extern int bpf_validate(const struct bpf_insn *, int);
|
||||
extern u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
|
||||
#else
|
||||
extern int bpf_validate();
|
||||
extern u_int bpf_filter();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST).
|
||||
*/
|
||||
#define BPF_MEMWORDS 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/namedb.h,v 1.1 2006/10/04 18:09:22 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_namedb_h
|
||||
#define lib_pcap_namedb_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* As returned by the pcap_next_etherent()
|
||||
* XXX this stuff doesn't belong in this interface, but this
|
||||
* library already must do name to address translation, so
|
||||
* on systems that don't have support for /etc/ethers, we
|
||||
* export these hooks since they'll
|
||||
*/
|
||||
struct pcap_etherent {
|
||||
u_char addr[6];
|
||||
char name[122];
|
||||
};
|
||||
#ifndef PCAP_ETHERS_FILE
|
||||
#define PCAP_ETHERS_FILE "/etc/ethers"
|
||||
#endif
|
||||
struct pcap_etherent *pcap_next_etherent(FILE *);
|
||||
u_char *pcap_ether_hostton(const char*);
|
||||
u_char *pcap_ether_aton(const char *);
|
||||
|
||||
bpf_u_int32 **pcap_nametoaddr(const char *);
|
||||
#ifdef INET6
|
||||
struct addrinfo *pcap_nametoaddrinfo(const char *);
|
||||
#endif
|
||||
bpf_u_int32 pcap_nametonetaddr(const char *);
|
||||
|
||||
int pcap_nametoport(const char *, int *, int *);
|
||||
int pcap_nametoportrange(const char *, int *, int *, int *);
|
||||
int pcap_nametoproto(const char *);
|
||||
int pcap_nametoeproto(const char *);
|
||||
int pcap_nametollc(const char *);
|
||||
/*
|
||||
* If a protocol is unknown, PROTO_UNDEF is returned.
|
||||
* Also, pcap_nametoport() returns the protocol along with the port number.
|
||||
* If there are ambiguous entried in /etc/services (i.e. domain
|
||||
* can be either tcp or udp) PROTO_UNDEF is returned.
|
||||
*/
|
||||
#define PROTO_UNDEF -1
|
||||
|
||||
/* XXX move these to pcap-int.h? */
|
||||
int __pcap_atodn(const char *, bpf_u_int32 *);
|
||||
int __pcap_atoin(const char *, bpf_u_int32 *);
|
||||
u_short __pcap_nametodnaddr(const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,407 @@
|
||||
/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.4.2.11 2008-10-06 15:38:39 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_pcap_h
|
||||
#define lib_pcap_pcap_h
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <pcap-stdinc.h>
|
||||
#elif defined(MSDOS)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h> /* u_int, u_char etc. */
|
||||
#else /* UN*X */
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#endif /* WIN32/MSDOS/UN*X */
|
||||
|
||||
#ifndef PCAP_DONT_INCLUDE_PCAP_BPF_H
|
||||
#include <pcap/bpf.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
// We have to define the SOCKET here, although it has been defined in sockutils.h
|
||||
// This is to avoid the distribution of the 'sockutils.h' file around
|
||||
// (for example in the WinPcap developer's pack)
|
||||
#ifndef SOCKET
|
||||
#ifdef WIN32
|
||||
#define SOCKET unsigned int
|
||||
#else
|
||||
#define SOCKET int
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCAP_VERSION_MAJOR 2
|
||||
#define PCAP_VERSION_MINOR 4
|
||||
|
||||
#define PCAP_ERRBUF_SIZE 256
|
||||
|
||||
/*
|
||||
* Compatibility for systems that have a bpf.h that
|
||||
* predates the bpf typedefs for 64-bit support.
|
||||
*/
|
||||
#if BPF_RELEASE - 0 < 199406
|
||||
typedef int bpf_int32;
|
||||
typedef u_int bpf_u_int32;
|
||||
#endif
|
||||
|
||||
typedef struct pcap pcap_t;
|
||||
typedef struct pcap_dumper pcap_dumper_t;
|
||||
typedef struct pcap_if pcap_if_t;
|
||||
typedef struct pcap_addr pcap_addr_t;
|
||||
|
||||
/*
|
||||
* The first record in the file contains saved values for some
|
||||
* of the flags used in the printout phases of tcpdump.
|
||||
* Many fields here are 32 bit ints so compilers won't insert unwanted
|
||||
* padding; these files need to be interchangeable across architectures.
|
||||
*
|
||||
* Do not change the layout of this structure, in any way (this includes
|
||||
* changes that only affect the length of fields in this structure).
|
||||
*
|
||||
* Also, do not change the interpretation of any of the members of this
|
||||
* structure, in any way (this includes using values other than
|
||||
* LINKTYPE_ values, as defined in "savefile.c", in the "linktype"
|
||||
* field).
|
||||
*
|
||||
* Instead:
|
||||
*
|
||||
* introduce a new structure for the new format, if the layout
|
||||
* of the structure changed;
|
||||
*
|
||||
* send mail to "tcpdump-workers@lists.tcpdump.org", requesting
|
||||
* a new magic number for your new capture file format, and, when
|
||||
* you get the new magic number, put it in "savefile.c";
|
||||
*
|
||||
* use that magic number for save files with the changed file
|
||||
* header;
|
||||
*
|
||||
* make the code in "savefile.c" capable of reading files with
|
||||
* the old file header as well as files with the new file header
|
||||
* (using the magic number to determine the header format).
|
||||
*
|
||||
* Then supply the changes as a patch at
|
||||
*
|
||||
* http://sourceforge.net/projects/libpcap/
|
||||
*
|
||||
* so that future versions of libpcap and programs that use it (such as
|
||||
* tcpdump) will be able to read your new capture file format.
|
||||
*/
|
||||
struct pcap_file_header {
|
||||
bpf_u_int32 magic;
|
||||
u_short version_major;
|
||||
u_short version_minor;
|
||||
bpf_int32 thiszone; /* gmt to local correction */
|
||||
bpf_u_int32 sigfigs; /* accuracy of timestamps */
|
||||
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
|
||||
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Macros for the value returned by pcap_datalink_ext().
|
||||
*
|
||||
* If LT_FCS_LENGTH_PRESENT(x) is true, the LT_FCS_LENGTH(x) macro
|
||||
* gives the FCS length of packets in the capture.
|
||||
*/
|
||||
#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000)
|
||||
#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28)
|
||||
#define LT_FCS_DATALINK_EXT(x) ((((x) & 0xF) << 28) | 0x04000000)
|
||||
|
||||
typedef enum {
|
||||
PCAP_D_INOUT = 0,
|
||||
PCAP_D_IN,
|
||||
PCAP_D_OUT
|
||||
} pcap_direction_t;
|
||||
|
||||
/*
|
||||
* Generic per-packet information, as supplied by libpcap.
|
||||
*
|
||||
* The time stamp can and should be a "struct timeval", regardless of
|
||||
* whether your system supports 32-bit tv_sec in "struct timeval",
|
||||
* 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit
|
||||
* and 64-bit applications. The on-disk format of savefiles uses 32-bit
|
||||
* tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit
|
||||
* and 64-bit versions of libpcap, even if they're on the same platform,
|
||||
* should supply the appropriate version of "struct timeval", even if
|
||||
* that's not what the underlying packet capture mechanism supplies.
|
||||
*/
|
||||
struct pcap_pkthdr {
|
||||
struct timeval ts; /* time stamp */
|
||||
bpf_u_int32 caplen; /* length of portion present */
|
||||
bpf_u_int32 len; /* length this packet (off wire) */
|
||||
};
|
||||
|
||||
/*
|
||||
* As returned by the pcap_stats()
|
||||
*/
|
||||
struct pcap_stat {
|
||||
u_int ps_recv; /* number of packets received */
|
||||
u_int ps_drop; /* number of packets dropped */
|
||||
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
|
||||
#ifdef HAVE_REMOTE
|
||||
u_int ps_capt; /* number of packets that are received by the application; please get rid off the Win32 ifdef */
|
||||
u_int ps_sent; /* number of packets sent by the server on the network */
|
||||
u_int ps_netdrop; /* number of packets lost on the network */
|
||||
#endif /* HAVE_REMOTE */
|
||||
};
|
||||
|
||||
#ifdef MSDOS
|
||||
/*
|
||||
* As returned by the pcap_stats_ex()
|
||||
*/
|
||||
struct pcap_stat_ex {
|
||||
u_long rx_packets; /* total packets received */
|
||||
u_long tx_packets; /* total packets transmitted */
|
||||
u_long rx_bytes; /* total bytes received */
|
||||
u_long tx_bytes; /* total bytes transmitted */
|
||||
u_long rx_errors; /* bad packets received */
|
||||
u_long tx_errors; /* packet transmit problems */
|
||||
u_long rx_dropped; /* no space in Rx buffers */
|
||||
u_long tx_dropped; /* no space available for Tx */
|
||||
u_long multicast; /* multicast packets received */
|
||||
u_long collisions;
|
||||
|
||||
/* detailed rx_errors: */
|
||||
u_long rx_length_errors;
|
||||
u_long rx_over_errors; /* receiver ring buff overflow */
|
||||
u_long rx_crc_errors; /* recv'd pkt with crc error */
|
||||
u_long rx_frame_errors; /* recv'd frame alignment error */
|
||||
u_long rx_fifo_errors; /* recv'r fifo overrun */
|
||||
u_long rx_missed_errors; /* recv'r missed packet */
|
||||
|
||||
/* detailed tx_errors */
|
||||
u_long tx_aborted_errors;
|
||||
u_long tx_carrier_errors;
|
||||
u_long tx_fifo_errors;
|
||||
u_long tx_heartbeat_errors;
|
||||
u_long tx_window_errors;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Item in a list of interfaces.
|
||||
*/
|
||||
struct pcap_if {
|
||||
struct pcap_if *next;
|
||||
char *name; /* name to hand to "pcap_open_live()" */
|
||||
char *description; /* textual description of interface, or NULL */
|
||||
struct pcap_addr *addresses;
|
||||
bpf_u_int32 flags; /* PCAP_IF_ interface flags */
|
||||
};
|
||||
|
||||
#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */
|
||||
|
||||
/*
|
||||
* Representation of an interface address.
|
||||
*/
|
||||
struct pcap_addr {
|
||||
struct pcap_addr *next;
|
||||
struct sockaddr *addr; /* address */
|
||||
struct sockaddr *netmask; /* netmask for that address */
|
||||
struct sockaddr *broadaddr; /* broadcast address for that address */
|
||||
struct sockaddr *dstaddr; /* P2P destination address for that address */
|
||||
};
|
||||
|
||||
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
|
||||
const u_char *);
|
||||
|
||||
/*
|
||||
* Error codes for the pcap API.
|
||||
* These will all be negative, so you can check for the success or
|
||||
* failure of a call that returns these codes by checking for a
|
||||
* negative value.
|
||||
*/
|
||||
#define PCAP_ERROR -1 /* generic error code */
|
||||
#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */
|
||||
#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */
|
||||
#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */
|
||||
#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */
|
||||
#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */
|
||||
#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */
|
||||
#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */
|
||||
#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */
|
||||
|
||||
/*
|
||||
* Warning codes for the pcap API.
|
||||
* These will all be positive and non-zero, so they won't look like
|
||||
* errors.
|
||||
*/
|
||||
#define PCAP_WARNING 1 /* generic warning code */
|
||||
#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */
|
||||
|
||||
char *pcap_lookupdev(char *);
|
||||
int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *);
|
||||
|
||||
pcap_t *pcap_create(const char *, char *);
|
||||
int pcap_set_snaplen(pcap_t *, int);
|
||||
int pcap_set_promisc(pcap_t *, int);
|
||||
int pcap_can_set_rfmon(pcap_t *);
|
||||
int pcap_set_rfmon(pcap_t *, int);
|
||||
int pcap_set_timeout(pcap_t *, int);
|
||||
int pcap_set_buffer_size(pcap_t *, int);
|
||||
int pcap_activate(pcap_t *);
|
||||
|
||||
pcap_t *pcap_open_live(const char *, int, int, int, char *);
|
||||
pcap_t *pcap_open_dead(int, int);
|
||||
pcap_t *pcap_open_offline(const char *, char *);
|
||||
#if defined(WIN32)
|
||||
pcap_t *pcap_hopen_offline(intptr_t, char *);
|
||||
#if !defined(LIBPCAP_EXPORTS)
|
||||
#define pcap_fopen_offline(f,b) \
|
||||
pcap_hopen_offline(_get_osfhandle(_fileno(f)), b)
|
||||
#else /*LIBPCAP_EXPORTS*/
|
||||
static pcap_t *pcap_fopen_offline(FILE *, char *);
|
||||
#endif
|
||||
#else /*WIN32*/
|
||||
pcap_t *pcap_fopen_offline(FILE *, char *);
|
||||
#endif /*WIN32*/
|
||||
|
||||
void pcap_close(pcap_t *);
|
||||
int pcap_loop(pcap_t *, int, pcap_handler, u_char *);
|
||||
int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *);
|
||||
const u_char*
|
||||
pcap_next(pcap_t *, struct pcap_pkthdr *);
|
||||
int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **);
|
||||
void pcap_breakloop(pcap_t *);
|
||||
int pcap_stats(pcap_t *, struct pcap_stat *);
|
||||
int pcap_setfilter(pcap_t *, struct bpf_program *);
|
||||
int pcap_setdirection(pcap_t *, pcap_direction_t);
|
||||
int pcap_getnonblock(pcap_t *, char *);
|
||||
int pcap_setnonblock(pcap_t *, int, char *);
|
||||
int pcap_inject(pcap_t *, const void *, size_t);
|
||||
int pcap_sendpacket(pcap_t *, const u_char *, int);
|
||||
const char *pcap_statustostr(int);
|
||||
const char *pcap_strerror(int);
|
||||
char *pcap_geterr(pcap_t *);
|
||||
void pcap_perror(pcap_t *, char *);
|
||||
int pcap_compile(pcap_t *, struct bpf_program *, const char *, int,
|
||||
bpf_u_int32);
|
||||
int pcap_compile_nopcap(int, int, struct bpf_program *,
|
||||
const char *, int, bpf_u_int32);
|
||||
void pcap_freecode(struct bpf_program *);
|
||||
int pcap_offline_filter(struct bpf_program *, const struct pcap_pkthdr *,
|
||||
const u_char *);
|
||||
int pcap_datalink(pcap_t *);
|
||||
int pcap_datalink_ext(pcap_t *);
|
||||
int pcap_list_datalinks(pcap_t *, int **);
|
||||
int pcap_set_datalink(pcap_t *, int);
|
||||
void pcap_free_datalinks(int *);
|
||||
int pcap_datalink_name_to_val(const char *);
|
||||
const char *pcap_datalink_val_to_name(int);
|
||||
const char *pcap_datalink_val_to_description(int);
|
||||
int pcap_snapshot(pcap_t *);
|
||||
int pcap_is_swapped(pcap_t *);
|
||||
int pcap_major_version(pcap_t *);
|
||||
int pcap_minor_version(pcap_t *);
|
||||
|
||||
/* XXX */
|
||||
FILE *pcap_file(pcap_t *);
|
||||
int pcap_fileno(pcap_t *);
|
||||
|
||||
pcap_dumper_t *pcap_dump_open(pcap_t *, const char *);
|
||||
pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp);
|
||||
FILE *pcap_dump_file(pcap_dumper_t *);
|
||||
long pcap_dump_ftell(pcap_dumper_t *);
|
||||
int pcap_dump_flush(pcap_dumper_t *);
|
||||
void pcap_dump_close(pcap_dumper_t *);
|
||||
void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *);
|
||||
|
||||
int pcap_findalldevs(pcap_if_t **, char *);
|
||||
void pcap_freealldevs(pcap_if_t *);
|
||||
|
||||
const char *pcap_lib_version(void);
|
||||
|
||||
/* XXX this guy lives in the bpf tree */
|
||||
u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
|
||||
int bpf_validate(const struct bpf_insn *f, int len);
|
||||
char *bpf_image(const struct bpf_insn *, int);
|
||||
void bpf_dump(const struct bpf_program *, int);
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
/*
|
||||
* Win32 definitions
|
||||
*/
|
||||
|
||||
int pcap_setbuff(pcap_t *p, int dim);
|
||||
int pcap_setmode(pcap_t *p, int mode);
|
||||
int pcap_setmintocopy(pcap_t *p, int size);
|
||||
|
||||
#ifdef WPCAP
|
||||
/* Include file with the wpcap-specific extensions */
|
||||
#include <Win32-Extensions.h>
|
||||
#endif /* WPCAP */
|
||||
|
||||
#define MODE_CAPT 0
|
||||
#define MODE_STAT 1
|
||||
#define MODE_MON 2
|
||||
|
||||
#elif defined(MSDOS)
|
||||
|
||||
/*
|
||||
* MS-DOS definitions
|
||||
*/
|
||||
|
||||
int pcap_stats_ex (pcap_t *, struct pcap_stat_ex *);
|
||||
void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait);
|
||||
u_long pcap_mac_packets (void);
|
||||
|
||||
#else /* UN*X */
|
||||
|
||||
/*
|
||||
* UN*X definitions
|
||||
*/
|
||||
|
||||
int pcap_get_selectable_fd(pcap_t *);
|
||||
|
||||
#endif /* WIN32/MSDOS/UN*X */
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
/* Includes most of the public stuff that is needed for the remote capture */
|
||||
#include <remote-ext.h>
|
||||
#endif /* HAVE_REMOTE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/sll.h,v 1.2.2.1 2008-05-30 01:36:06 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For captures on Linux cooked sockets, we construct a fake header
|
||||
* that includes:
|
||||
*
|
||||
* a 2-byte "packet type" which is one of:
|
||||
*
|
||||
* LINUX_SLL_HOST packet was sent to us
|
||||
* LINUX_SLL_BROADCAST packet was broadcast
|
||||
* LINUX_SLL_MULTICAST packet was multicast
|
||||
* LINUX_SLL_OTHERHOST packet was sent to somebody else
|
||||
* LINUX_SLL_OUTGOING packet was sent *by* us;
|
||||
*
|
||||
* a 2-byte Ethernet protocol field;
|
||||
*
|
||||
* a 2-byte link-layer type;
|
||||
*
|
||||
* a 2-byte link-layer address length;
|
||||
*
|
||||
* an 8-byte source link-layer address, whose actual length is
|
||||
* specified by the previous value.
|
||||
*
|
||||
* All fields except for the link-layer address are in network byte order.
|
||||
*
|
||||
* DO NOT change the layout of this structure, or change any of the
|
||||
* LINUX_SLL_ values below. If you must change the link-layer header
|
||||
* for a "cooked" Linux capture, introduce a new DLT_ type (ask
|
||||
* "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it
|
||||
* a value that collides with a value already being used), and use the
|
||||
* new header in captures of that type, so that programs that can
|
||||
* handle DLT_LINUX_SLL captures will continue to handle them correctly
|
||||
* without any change, and so that capture files with different headers
|
||||
* can be told apart and programs that read them can dissect the
|
||||
* packets in them.
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_sll_h
|
||||
#define lib_pcap_sll_h
|
||||
|
||||
/*
|
||||
* A DLT_LINUX_SLL fake link-layer header.
|
||||
*/
|
||||
#define SLL_HDR_LEN 16 /* total header length */
|
||||
#define SLL_ADDRLEN 8 /* length of address field */
|
||||
|
||||
struct sll_header {
|
||||
u_int16_t sll_pkttype; /* packet type */
|
||||
u_int16_t sll_hatype; /* link-layer address type */
|
||||
u_int16_t sll_halen; /* link-layer address length */
|
||||
u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */
|
||||
u_int16_t sll_protocol; /* protocol */
|
||||
};
|
||||
|
||||
/*
|
||||
* The LINUX_SLL_ values for "sll_pkttype"; these correspond to the
|
||||
* PACKET_ values on Linux, but are defined here so that they're
|
||||
* available even on systems other than Linux, and so that they
|
||||
* don't change even if the PACKET_ values change.
|
||||
*/
|
||||
#define LINUX_SLL_HOST 0
|
||||
#define LINUX_SLL_BROADCAST 1
|
||||
#define LINUX_SLL_MULTICAST 2
|
||||
#define LINUX_SLL_OTHERHOST 3
|
||||
#define LINUX_SLL_OUTGOING 4
|
||||
|
||||
/*
|
||||
* The LINUX_SLL_ values for "sll_protocol"; these correspond to the
|
||||
* ETH_P_ values on Linux, but are defined here so that they're
|
||||
* available even on systems other than Linux. We assume, for now,
|
||||
* that the ETH_P_ values won't change in Linux; if they do, then:
|
||||
*
|
||||
* if we don't translate them in "pcap-linux.c", capture files
|
||||
* won't necessarily be readable if captured on a system that
|
||||
* defines ETH_P_ values that don't match these values;
|
||||
*
|
||||
* if we do translate them in "pcap-linux.c", that makes life
|
||||
* unpleasant for the BPF code generator, as the values you test
|
||||
* for in the kernel aren't the values that you test for when
|
||||
* reading a capture file, so the fixup code run on BPF programs
|
||||
* handed to the kernel ends up having to do more work.
|
||||
*
|
||||
* Add other values here as necessary, for handling packet types that
|
||||
* might show up on non-Ethernet, non-802.x networks. (Not all the ones
|
||||
* in the Linux "if_ether.h" will, I suspect, actually show up in
|
||||
* captures.)
|
||||
*/
|
||||
#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */
|
||||
#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Paolo Abeni (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Basic USB data struct
|
||||
* By Paolo Abeni <paolo.abeni@email.it>
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/usb.h,v 1.6 2007/09/22 02:06:08 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PCAP_USB_STRUCTS_H__
|
||||
#define _PCAP_USB_STRUCTS_H__
|
||||
|
||||
/*
|
||||
* possible transfer mode
|
||||
*/
|
||||
#define URB_TRANSFER_IN 0x80
|
||||
#define URB_ISOCHRONOUS 0x0
|
||||
#define URB_INTERRUPT 0x1
|
||||
#define URB_CONTROL 0x2
|
||||
#define URB_BULK 0x3
|
||||
|
||||
/*
|
||||
* possible event type
|
||||
*/
|
||||
#define URB_SUBMIT 'S'
|
||||
#define URB_COMPLETE 'C'
|
||||
#define URB_ERROR 'E'
|
||||
|
||||
/*
|
||||
* USB setup header as defined in USB specification.
|
||||
* Appears at the front of each packet in DLT_USB captures.
|
||||
*/
|
||||
typedef struct _usb_setup {
|
||||
u_int8_t bmRequestType;
|
||||
u_int8_t bRequest;
|
||||
u_int16_t wValue;
|
||||
u_int16_t wIndex;
|
||||
u_int16_t wLength;
|
||||
} pcap_usb_setup;
|
||||
|
||||
|
||||
/*
|
||||
* Header prepended by linux kernel to each event.
|
||||
* Appears at the front of each packet in DLT_USB_LINUX captures.
|
||||
*/
|
||||
typedef struct _usb_header {
|
||||
u_int64_t id;
|
||||
u_int8_t event_type;
|
||||
u_int8_t transfer_type;
|
||||
u_int8_t endpoint_number;
|
||||
u_int8_t device_address;
|
||||
u_int16_t bus_id;
|
||||
char setup_flag;/*if !=0 the urb setup header is not present*/
|
||||
char data_flag; /*if !=0 no urb data is present*/
|
||||
int64_t ts_sec;
|
||||
int32_t ts_usec;
|
||||
int32_t status;
|
||||
u_int32_t urb_len;
|
||||
u_int32_t data_len; /* amount of urb data really present in this event*/
|
||||
pcap_usb_setup setup;
|
||||
} pcap_usb_header;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/vlan.h,v 1.1.2.2 2008-08-06 07:45:59 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_vlan_h
|
||||
#define lib_pcap_vlan_h
|
||||
|
||||
struct vlan_tag {
|
||||
u_int16_t vlan_tpid; /* ETH_P_8021Q */
|
||||
u_int16_t vlan_tci; /* VLAN TCI */
|
||||
};
|
||||
|
||||
#define VLAN_TAG_LEN 4
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (c) 2002 - 2003
|
||||
* NetGroup, Politecnico di Torino (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __REMOTE_EXT_H__
|
||||
#define __REMOTE_EXT_H__
|
||||
|
||||
|
||||
#ifndef HAVE_REMOTE
|
||||
#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h
|
||||
#endif
|
||||
|
||||
// Definition for Microsoft Visual Studio
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\file remote-ext.h
|
||||
|
||||
The goal of this file it to include most of the new definitions that should be
|
||||
placed into the pcap.h file.
|
||||
|
||||
It includes all new definitions (structures and functions like pcap_open().
|
||||
Some of the functions are not really a remote feature, but, right now,
|
||||
they are placed here.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// All this stuff is public
|
||||
/*! \addtogroup remote_struct
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief Defines the maximum buffer size in which address, port, interface names are kept.
|
||||
|
||||
In case the adapter name or such is larger than this value, it is truncated.
|
||||
This is not used by the user; however it must be aware that an hostname / interface
|
||||
name longer than this value will be truncated.
|
||||
*/
|
||||
#define PCAP_BUF_SIZE 1024
|
||||
|
||||
|
||||
/*! \addtogroup remote_source_ID
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a file, i.e. the user want to open a capture from a local file.
|
||||
*/
|
||||
#define PCAP_SRC_FILE 2
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a local interface, i.e. the user want to open a capture from
|
||||
a local interface. This does not involve the RPCAP protocol.
|
||||
*/
|
||||
#define PCAP_SRC_IFLOCAL 3
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a remote interface, i.e. the user want to open a capture from
|
||||
an interface on a remote host. This does involve the RPCAP protocol.
|
||||
*/
|
||||
#define PCAP_SRC_IFREMOTE 4
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*! \addtogroup remote_source_string
|
||||
|
||||
The formats allowed by the pcap_open() are the following:
|
||||
- file://path_and_filename [opens a local file]
|
||||
- rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol]
|
||||
- rpcap://host/devicename [opens the selected device available on a remote host]
|
||||
- rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP]
|
||||
- adaptername [to open a local adapter; kept for compability, but it is strongly discouraged]
|
||||
- (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged]
|
||||
|
||||
The formats allowed by the pcap_findalldevs_ex() are the following:
|
||||
- file://folder/ [lists all the files in the given folder]
|
||||
- rpcap:// [lists all local adapters]
|
||||
- rpcap://host:port/ [lists the devices available on a remote host]
|
||||
|
||||
Referring to the 'host' and 'port' paramters, they can be either numeric or literal. Since
|
||||
IPv6 is fully supported, these are the allowed formats:
|
||||
|
||||
- host (literal): e.g. host.foo.bar
|
||||
- host (numeric IPv4): e.g. 10.11.12.13
|
||||
- host (numeric IPv4, IPv6 style): e.g. [10.11.12.13]
|
||||
- host (numeric IPv6): e.g. [1:2:3::4]
|
||||
- port: can be either numeric (e.g. '80') or literal (e.g. 'http')
|
||||
|
||||
Here you find some allowed examples:
|
||||
- rpcap://host.foo.bar/devicename [everything literal, no port number]
|
||||
- rpcap://host.foo.bar:1234/devicename [everything literal, with port number]
|
||||
- rpcap://10.11.12.13/devicename [IPv4 numeric, no port number]
|
||||
- rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number]
|
||||
- rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number]
|
||||
- rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number]
|
||||
- rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number]
|
||||
- rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number]
|
||||
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\brief String that will be used to determine the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This string will be prepended to the interface name in order to create a string
|
||||
that contains all the information required to open the source.
|
||||
|
||||
This string indicates that the user wants to open a capture from a local file.
|
||||
*/
|
||||
#define PCAP_SRC_FILE_STRING "file://"
|
||||
/*!
|
||||
\brief String that will be used to determine the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This string will be prepended to the interface name in order to create a string
|
||||
that contains all the information required to open the source.
|
||||
|
||||
This string indicates that the user wants to open a capture from a network interface.
|
||||
This string does not necessarily involve the use of the RPCAP protocol. If the
|
||||
interface required resides on the local host, the RPCAP protocol is not involved
|
||||
and the local functions are used.
|
||||
*/
|
||||
#define PCAP_SRC_IF_STRING "rpcap://"
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_open_flags
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief Defines if the adapter has to go in promiscuous mode.
|
||||
|
||||
It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise.
|
||||
Note that even if this parameter is false, the interface could well be in promiscuous
|
||||
mode for some other reason (for example because another capture process with
|
||||
promiscuous mode enabled is currently using that interface).
|
||||
On on Linux systems with 2.2 or later kernels (that have the "any" device), this
|
||||
flag does not work on the "any" device; if an argument of "any" is supplied,
|
||||
the 'promisc' flag is ignored.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_PROMISCUOUS 1
|
||||
|
||||
/*!
|
||||
\brief Defines if the data trasfer (in case of a remote
|
||||
capture) has to be done with UDP protocol.
|
||||
|
||||
If it is '1' if you want a UDP data connection, '0' if you want
|
||||
a TCP data connection; control connection is always TCP-based.
|
||||
A UDP connection is much lighter, but it does not guarantee that all
|
||||
the captured packets arrive to the client workstation. Moreover,
|
||||
it could be harmful in case of network congestion.
|
||||
This flag is meaningless if the source is not a remote interface.
|
||||
In that case, it is simply ignored.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_DATATX_UDP 2
|
||||
|
||||
|
||||
/*!
|
||||
\brief Defines if the remote probe will capture its own generated traffic.
|
||||
|
||||
In case the remote probe uses the same interface to capture traffic and to send
|
||||
data back to the caller, the captured traffic includes the RPCAP traffic as well.
|
||||
If this flag is turned on, the RPCAP traffic is excluded from the capture, so that
|
||||
the trace returned back to the collector is does not include this traffic.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4
|
||||
|
||||
/*!
|
||||
\brief Defines if the local adapter will capture its own generated traffic.
|
||||
|
||||
This flag tells the underlying capture driver to drop the packets that were sent by itself.
|
||||
This is usefult when building applications like bridges, that should ignore the traffic
|
||||
they just sent.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8
|
||||
|
||||
/*!
|
||||
\brief This flag configures the adapter for maximum responsiveness.
|
||||
|
||||
In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before
|
||||
copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage,
|
||||
i.e. better performance, which is good for applications like sniffers. If the user sets the
|
||||
PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application
|
||||
is ready to receive them. This is suggested for real time applications (like, for example, a bridge)
|
||||
that need the best responsiveness.*/
|
||||
#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_samp_methods
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief No sampling has to be done on the current capture.
|
||||
|
||||
In this case, no sampling algorithms are applied to the current capture.
|
||||
*/
|
||||
#define PCAP_SAMP_NOSAMP 0
|
||||
|
||||
/*!
|
||||
\brief It defines that only 1 out of N packets must be returned to the user.
|
||||
|
||||
In this case, the 'value' field of the 'pcap_samp' structure indicates the
|
||||
number of packets (minus 1) that must be discarded before one packet got accepted.
|
||||
In other words, if 'value = 10', the first packet is returned to the caller, while
|
||||
the following 9 are discarded.
|
||||
*/
|
||||
#define PCAP_SAMP_1_EVERY_N 1
|
||||
|
||||
/*!
|
||||
\brief It defines that we have to return 1 packet every N milliseconds.
|
||||
|
||||
In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting
|
||||
time' in milliseconds before one packet got accepted.
|
||||
In other words, if 'value = 10', the first packet is returned to the caller; the next
|
||||
returned one will be the first packet that arrives when 10ms have elapsed.
|
||||
*/
|
||||
#define PCAP_SAMP_FIRST_AFTER_N_MS 2
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_auth_methods
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief It defines the NULL authentication.
|
||||
|
||||
This value has to be used within the 'type' member of the pcap_rmtauth structure.
|
||||
The 'NULL' authentication has to be equal to 'zero', so that old applications
|
||||
can just put every field of struct pcap_rmtauth to zero, and it does work.
|
||||
*/
|
||||
#define RPCAP_RMTAUTH_NULL 0
|
||||
/*!
|
||||
\brief It defines the username/password authentication.
|
||||
|
||||
With this type of authentication, the RPCAP protocol will use the username/
|
||||
password provided to authenticate the user on the remote machine. If the
|
||||
authentication is successful (and the user has the right to open network devices)
|
||||
the RPCAP connection will continue; otherwise it will be dropped.
|
||||
|
||||
This value has to be used within the 'type' member of the pcap_rmtauth structure.
|
||||
*/
|
||||
#define RPCAP_RMTAUTH_PWD 1
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
|
||||
\brief This structure keeps the information needed to autheticate
|
||||
the user on a remote machine.
|
||||
|
||||
The remote machine can either grant or refuse the access according
|
||||
to the information provided.
|
||||
In case the NULL authentication is required, both 'username' and
|
||||
'password' can be NULL pointers.
|
||||
|
||||
This structure is meaningless if the source is not a remote interface;
|
||||
in that case, the functions which requires such a structure can accept
|
||||
a NULL pointer as well.
|
||||
*/
|
||||
struct pcap_rmtauth
|
||||
{
|
||||
/*!
|
||||
\brief Type of the authentication required.
|
||||
|
||||
In order to provide maximum flexibility, we can support different types
|
||||
of authentication based on the value of this 'type' variable. The currently
|
||||
supported authentication methods are defined into the
|
||||
\link remote_auth_methods Remote Authentication Methods Section\endlink.
|
||||
|
||||
*/
|
||||
int type;
|
||||
/*!
|
||||
\brief Zero-terminated string containing the username that has to be
|
||||
used on the remote machine for authentication.
|
||||
|
||||
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
|
||||
and it can be NULL.
|
||||
*/
|
||||
char *username;
|
||||
/*!
|
||||
\brief Zero-terminated string containing the password that has to be
|
||||
used on the remote machine for authentication.
|
||||
|
||||
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
|
||||
and it can be NULL.
|
||||
*/
|
||||
char *password;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
\brief This structure defines the information related to sampling.
|
||||
|
||||
In case the sampling is requested, the capturing device should read
|
||||
only a subset of the packets coming from the source. The returned packets depend
|
||||
on the sampling parameters.
|
||||
|
||||
\warning The sampling process is applied <strong>after</strong> the filtering process.
|
||||
In other words, packets are filtered first, then the sampling process selects a
|
||||
subset of the 'filtered' packets and it returns them to the caller.
|
||||
*/
|
||||
struct pcap_samp
|
||||
{
|
||||
/*!
|
||||
Method used for sampling. Currently, the supported methods are listed in the
|
||||
\link remote_samp_methods Sampling Methods Section\endlink.
|
||||
*/
|
||||
int method;
|
||||
|
||||
/*!
|
||||
This value depends on the sampling method defined. For its meaning, please check
|
||||
at the \link remote_samp_methods Sampling Methods Section\endlink.
|
||||
*/
|
||||
int value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//! Maximum length of an host name (needed for the RPCAP active mode)
|
||||
#define RPCAP_HOSTLIST_SIZE 1024
|
||||
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/ // end of public documentation
|
||||
|
||||
|
||||
// Exported functions
|
||||
|
||||
|
||||
|
||||
/** \name New WinPcap functions
|
||||
|
||||
This section lists the new functions that are able to help considerably in writing
|
||||
WinPcap programs because of their easiness of use.
|
||||
*/
|
||||
//\{
|
||||
pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
|
||||
int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf);
|
||||
int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf);
|
||||
int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
|
||||
struct pcap_samp *pcap_setsampling(pcap_t *p);
|
||||
|
||||
//\}
|
||||
// End of new winpcap functions
|
||||
|
||||
|
||||
|
||||
/** \name Remote Capture functions
|
||||
*/
|
||||
//\{
|
||||
SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf);
|
||||
int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf);
|
||||
int pcap_remoteact_close(const char *host, char *errbuf);
|
||||
void pcap_remoteact_cleanup();
|
||||
//\}
|
||||
// End of remote capture functions
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for all ethercat headers
|
||||
*/
|
||||
|
||||
#ifndef _EC_ETHERCAT_H
|
||||
#define _EC_ETHERCAT_H
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatbase.h"
|
||||
#include "ethercatmain.h"
|
||||
#include "ethercatdc.h"
|
||||
#include "ethercatcoe.h"
|
||||
#include "ethercatfoe.h"
|
||||
#include "ethercatsoe.h"
|
||||
#include "ethercateoe.h"
|
||||
#include "ethercatconfig.h"
|
||||
#include "ethercatprint.h"
|
||||
|
||||
#endif /* _EC_ETHERCAT_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user