feat(motor): 自适应 PDO 窗口与通道活动指示,新增原生 SOEM 时序诊断

后端 PDO 循环改为按在线槽收缩状态窗口,稀疏总线下单槽每周期都能刷新,
并补上尾部槽的发现扫描游标;新增自旋节拍源(阈值/显式覆盖)、P 核识别
与线程级钉核,自旋期间释放 GIL 以免 HTTP 线程被饿死;启动时先关停旧实例,
避免两个进程争用 Npcap 适配器。

记录每拍完整时间线、GC stop-the-world 事件与网格跳拍数,经 /api/motor/dbg
导出用于归因固定频率的超时;配
This commit is contained in:
cbb
2026-09-05 01:20:14 +08:00
parent 979da72181
commit 79633a2b0f
121 changed files with 33088 additions and 75 deletions
+14
View File
@@ -0,0 +1,14 @@
@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
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 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
+148
View File
@@ -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
+4
View File
@@ -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
+121
View File
@@ -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
View File
@@ -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())
+397
View File
@@ -0,0 +1,397 @@
/* ecat_native.c — 原型 A:原生 PDO 守护进程(SOEM 直用,无 Python/GIL)
*
* 目的:验证"换语言"能把 PDO 循环的天花板从 Python 的 ~5kHz 抬到多高、
* 尾抖动能多干净。总线核心收发本来就是 C(pysoem 只是 SOEM 的包装),
* 这里把热循环从解释器里拿出来,其余一切不动。
*
* 契约:复刻 ecat_server.py 的 3 条热路由(POST /api/motor/state|period|dbg),
* 只绑 127.0.0.1、无 CORS —— 与 Python 服务同口径,cadence_diag/rate_probe
* 直接 --base http://127.0.0.1:8091 就能打过来。其余路由仍留在 Python。
*
* 时间线元组与 Python 完全同构:(t0, ts, tm, tr, te, k)
* t0=循环顶 ts=send 前(原生无锁等待,≈t0) tm=send 后 tr=receive 后
* te=循环底(跳拍判定时刻) k=本拍网格跳过数
* 时刻用 QueryPerformanceCounter 秒 —— 与 Python perf_counter 同一时间基
* (实测差 ≤6µs),cadence_diag 的差分口径不变。
*/
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ethercat.h"
#define RING 2048 /* 与 Python _cyc_t maxlen 同 */
#define IO_MAP_SIZE 4096
#define STATS_N 400 /* state 里 cyc_us 分位数的样本窗口,同 _cyc_stats(n=400) */
/* ── 计时原语 ── */
static double g_qpc2s;
static inline double now_s(void)
{
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
return (double)c.QuadPart * g_qpc2s;
}
static inline void spin_until(double deadline)
{
while (now_s() < deadline)
YieldProcessor(); /* PAUSE:SMT 兄弟友好,不烧功耗 */
}
/* ── 共享状态 ── */
typedef struct { double t0, ts, tm, tr, te; int k; } beat_t;
static beat_t g_ring[RING];
static int g_ring_i; /* 写指针:仅 PDO 线程动 */
static long long g_total; /* 累计拍数 */
static long long g_skip_total; /* 累计网格跳过拍 */
static CRITICAL_SECTION g_ring_lock; /* 保护 ring/total/skip 的读侧 */
static CRITICAL_SECTION g_cfg_lock; /* 保护 g_period_ms */
static double g_period_ms = 0.25;
static volatile LONG g_stop;
static volatile LONG g_wkc;
static long long g_expected_wkc;
static int g_slavecount;
static char g_slave_names[16][64];
static uint8 g_iomap[IO_MAP_SIZE];
static ULONGLONG g_pcore_mask; /* --mask,0=不钉 */
/* ── PDO 线程:网格 + 自旋 + 分段计时,逻辑与 _pdo_worker 一一对应 ── */
static DWORD WINAPI pdo_worker(LPVOID arg)
{
double per, t0, ts, tm, tr, now, te, next_t = 0;
int have_next = 0, k, i, wkc;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
if (g_pcore_mask)
SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)g_pcore_mask);
while (!InterlockedOr(&g_stop, 0)) {
t0 = now_s();
EnterCriticalSection(&g_cfg_lock);
per = g_period_ms / 1000.0;
LeaveCriticalSection(&g_cfg_lock);
if (!have_next) { next_t = t0 + per; have_next = 1; }
ts = now_s(); /* 原生无锁无填充 → pre≈0 */
ec_send_processdata();
tm = now_s();
wkc = ec_receive_processdata(EC_TIMEOUTRET);
tr = now_s();
InterlockedExchange(&g_wkc, wkc);
/* 绝对网格截止点 + 超期跳过(不挪锚点),与 Python 同 */
now = now_s();
k = 0;
if (next_t <= now) {
while (next_t <= now) { next_t += per; k++; }
}
te = now;
EnterCriticalSection(&g_ring_lock);
i = g_ring_i;
g_ring_i = (i + 1) % RING;
g_ring[i].t0 = t0; g_ring[i].ts = ts; g_ring[i].tm = tm;
g_ring[i].tr = tr; g_ring[i].te = te; g_ring[i].k = k;
g_total++;
g_skip_total += k;
LeaveCriticalSection(&g_ring_lock);
if (k == 0) { /* 没超期才等下一拍;超期直接落下去补 */
spin_until(next_t);
next_t += per;
}
}
return 0;
}
/* ── 分位数(读侧现算,同 _cyc_fresh 的口径)── */
static int cmp_d(const void *a, const void *b)
{
double x = *(const double *)a, y = *(const double *)b;
return (x > y) - (x < y);
}
static void pct3(const double *v, int n, char *out, size_t osz)
{
double *s = (double *)malloc(n * sizeof(double));
int i;
if (!s) { snprintf(out, osz, "{}"); return; }
memcpy(s, v, n * sizeof(double));
qsort(s, n, sizeof(double), cmp_d);
snprintf(out, osz, "{\"p50\":%.1f,\"p90\":%.1f,\"max\":%.1f}",
s[(int)(0.5 * (n - 1))], s[(int)(0.9 * (n - 1))], s[n - 1]);
free(s);
}
/* 取最近 n 拍(按时间序)。ring 单写者,读侧加锁拷贝。 */
static int recent_beats(beat_t *dst, int n)
{
int have, start, i;
EnterCriticalSection(&g_ring_lock);
have = (g_total < RING) ? (int)g_total : RING;
if (n > have) n = have;
start = (g_ring_i - n + RING * 2) % RING;
for (i = 0; i < n; i++) dst[i] = g_ring[(start + i) % RING];
LeaveCriticalSection(&g_ring_lock);
return n;
}
static void build_cyc_us(char *out, size_t osz)
{
static beat_t b[RING];
static double work[STATS_N], send[STATS_N], rx[STATS_N], tot[STATS_N];
char sw[96], sr[96], st[96], swork[96];
int n = recent_beats(b, RING), i, m = n < STATS_N ? n : STATS_N;
if (m < 2) { snprintf(out, osz, "{}"); return; }
for (i = 0; i < m; i++) {
beat_t *p = &b[n - m + i];
work[i] = (p->te - p->t0) * 1e6;
send[i] = (p->tm - p->ts) * 1e6;
rx[i] = (p->tr - p->tm) * 1e6;
tot[i] = (p->tr - p->t0) * 1e6;
}
pct3(work, m, swork, sizeof swork);
pct3(send, m, sw, sizeof sw);
pct3(rx, m, sr, sizeof sr);
pct3(tot, m, st, sizeof st);
snprintf(out, osz,
"\"cyc_us\":{\"n\":%d,\"work\":%s,\"send\":%s,\"rx\":%s,\"total\":%s,"
"\"late\":0,\"lost\":0,\"gc\":{\"n\":[0,0,0],\"max_us\":[0,0,0],\"pdo\":0}}",
m, swork, sw, sr, st);
}
/* ── 极简 HTTP/1.1(POST-only JSON,thread-per-connection)── */
static int json_num(const char *body, const char *key, double *val)
{
const char *p = strstr(body, key);
if (!p) return 0;
p += strlen(key);
while (*p && *p != ':') p++;
if (*p != ':') return 0;
p++;
while (*p == ' ' || *p == '\t') p++;
char *end;
double v = strtod(p, &end);
if (end == p) return 0;
*val = v;
return 1;
}
static void handle_conn(SOCKET cs)
{
char *buf = (char *)malloc(65536);
int len = 0, i, cl = 0;
char *hdr_end, *body, path[128] = "";
char *resp = (char *)malloc(256 * 1024);
int rl = 0;
char extra[4096];
if (!buf || !resp) { closesocket(cs); free(buf); free(resp); return; }
/* 读到头 */
for (;;) {
int n = recv(cs, buf + len, 65535 - len, 0);
if (n <= 0) goto done;
len += n; buf[len] = 0;
hdr_end = strstr(buf, "\r\n\r\n");
if (hdr_end) break;
}
body = hdr_end + 4;
{ /* Content-Length + 收满 body */
char *p = buf;
for (p = strstr(p, "\n"); p && p < hdr_end; p = strstr(p + 1, "\n")) {
/* p 停在上一行的 \n 上,下一行从 p+1 开始 */
if (!_strnicmp(p + 1, "content-length:", 15)) { cl = atoi(p + 16); break; }
}
while ((body + cl) > (buf + len)) {
int need = (int)((body + cl) - (buf + len));
int r = recv(cs, buf + len, need > 65535 - len ? 65535 - len : need, 0);
if (r <= 0) goto done;
len += r; buf[len] = 0;
}
if (cl > 65535 - (int)(body - buf)) cl = (int)(buf + len - body);
body[cl] = 0;
}
if (sscanf(buf, "%*s %127s", path) != 1) goto done;
if (!strcmp(path, "/api/motor/state")) {
build_cyc_us(extra, sizeof extra);
rl = snprintf(resp, 256 * 1024,
"{\"ok\":true,\"log\":[],\"cycles\":%lld,\"period_ms\":%.4f,\"spin\":true,"
"\"wkc\":%d,\"expected_wkc\":%d,\"slot_period_ms\":%.4f,\"win_bases\":0,"
"\"queue_depth\":0,\"sent_seq\":0,\"sent_frames\":0,\"ts\":0,"
"\"head\":{},\"motors\":[",
g_total, g_period_ms, (int)g_wkc, (int)g_expected_wkc, g_period_ms);
for (i = 0; i < g_slavecount && i < 16; i++)
rl += snprintf(resp + rl, 256 * 1024 - rl, "%s{\"name\":\"%s\",\"valid\":true}",
i ? "," : "", g_slave_names[i]);
rl += snprintf(resp + rl, 256 * 1024 - rl, "],\"bridge\":{},\"xrx\":{},\"rx_batch\":{},%s}", extra);
} else if (!strcmp(path, "/api/motor/period")) {
double v = -1, old = g_period_ms;
if (json_num(body, "period_us", &v) && v > 0) v /= 1000.0;
else if (!json_num(body, "period_ms", &v)) v = -1;
if (v < 0.0625 || v > 1000.0) {
rl = snprintf(resp, 256 * 1024,
"{\"ok\":false,\"log\":[\"周期 %s 超出允许范围 [62.5us, 1000ms]\"]}", body);
} else {
EnterCriticalSection(&g_cfg_lock);
g_period_ms = v;
LeaveCriticalSection(&g_cfg_lock);
rl = snprintf(resp, 256 * 1024,
"{\"ok\":true,\"log\":[\"PDO 周期(native) %gms → %gms\"],\"period_ms\":%.4f}",
old, v, v);
}
} else if (!strcmp(path, "/api/motor/dbg")) {
static beat_t b[RING];
int n = recent_beats(b, RING);
rl = snprintf(resp, 256 * 1024,
"{\"ok\":true,\"period_ms\":%.4f,\"spin\":true,\"gc_enabled\":false,"
"\"cycles\":%lld,\"skip_total\":%lld,\"cyc\":[",
g_period_ms, g_total, g_skip_total);
for (i = 0; i < n; i++)
rl += snprintf(resp + rl, 256 * 1024 - rl,
"%s[%.7f,%.7f,%.7f,%.7f,%.7f,%d]",
i ? "," : "", b[i].t0, b[i].ts, b[i].tm, b[i].tr, b[i].te, b[i].k);
rl += snprintf(resp + rl, 256 * 1024 - rl, "],\"gc_events\":[],\"http\":[]}");
} else if (!strcmp(path, "/") || !strcmp(path, "/api/native/health")) {
rl = snprintf(resp, 256 * 1024,
"{\"ok\":true,\"name\":\"ecat_native\",\"slaves\":%d,\"wkc\":%d,\"cycles\":%lld}",
g_slavecount, (int)g_wkc, g_total);
} else {
rl = snprintf(resp, 256 * 1024,
"{\"ok\":false,\"log\":[\"接口不存在(native 原型只有 state/period/dbg): %s\"]}", path);
}
{ /* 发送 */
char hdr[256];
int hl = snprintf(hdr, sizeof hdr,
"HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\n"
"Cache-Control: no-store\r\nContent-Length: %d\r\nConnection: close\r\n\r\n", rl);
send(cs, hdr, hl, 0);
send(cs, resp, rl, 0);
}
done:
closesocket(cs);
free(buf);
free(resp);
}
static DWORD WINAPI conn_thread(LPVOID p)
{
handle_conn((SOCKET)(intptr_t)p);
return 0;
}
int main(int argc, char **argv)
{
const char *substr = "I226";
int port = 8091, i;
LARGE_INTEGER f;
ec_adaptert *ad, *pick = NULL;
WSADATA wsa;
SOCKET ls;
struct sockaddr_in sa;
HANDLE th;
for (i = 1; i < argc - 0; i++) {
if (!strcmp(argv[i], "--substr") && i + 1 < argc) substr = argv[++i];
else if (!strcmp(argv[i], "--port") && i + 1 < argc) port = atoi(argv[++i]);
else if (!strcmp(argv[i], "--mask") && i + 1 < argc)
g_pcore_mask = strtoull(argv[++i], NULL, 0);
}
QueryPerformanceFrequency(&f);
g_qpc2s = 1.0 / (double)f.QuadPart;
InitializeCriticalSection(&g_ring_lock);
InitializeCriticalSection(&g_cfg_lock);
/* 找网卡(desc/name 含 substr) */
for (ad = ec_find_adapters(); ad; ad = ad->next) {
if (strstr(ad->desc, substr) || strstr(ad->name, substr)) { pick = ad; break; }
}
if (!pick) {
fprintf(stderr, "[native] 找不到含 \"%s\" 的网卡\n", substr);
return 2;
}
if (!ec_init(pick->name)) {
fprintf(stderr, "[native] ec_init 失败: %s\n", pick->name);
return 3;
}
printf("[native] 打开网卡: %s\n", pick->desc);
/* 预清理:广播 INIT|ACK 清掉上一主站(可能被强杀)残留的 AL 错误位,再重枚举。
SSC 的 AL_ControlInd 对"错误位已置 + 请求不含 ACK(bit4)"的迁移直接忽略
(ecatslv.c),这是 native 之前卡 PREOP 的根因 —— 与 Python motor/start 的
预清理同理。config_init 会重写 SM0/SM1,等效"断开重连"。 */
ec_slave[0].state = EC_STATE_INIT | EC_STATE_ACK;
ec_writestate(0);
ec_statecheck(0, EC_STATE_INIT, 1000000);
if (ec_config_init(FALSE) <= 0) {
fprintf(stderr, "[native] 枚举从站失败(邮箱楔死需断电重启板子)\n");
ec_close();
return 4;
}
ec_config_map(&g_iomap);
ec_readstate();
/* LRW 的期望 WKC:每从站读输出+1、写输入+1、回读+1 → outputsWKC*2 + inputsWKC
(与 pysoem 同式;本机 1 从站 = 1*2+1 = 3,与实测 wkc 一致) */
g_expected_wkc = ec_group[0].outputsWKC * 2 + ec_group[0].inputsWKC;
g_slavecount = ec_slavecount;
for (i = 1; i <= ec_slavecount && i <= 16; i++)
snprintf(g_slave_names[i - 1], 64, "%s", ec_slave[i].name);
printf("[native] 枚举 %d 从站, config_map 后 AL=0x%02x, expected_wkc=%lld\n",
ec_slavecount, ec_slave[1].state & 0x0F, g_expected_wkc);
/* config_map 内部请求 SAFEOP 不带 ACK,残留错误位下会被忽略 → 显式补一次
SAFEOP|ACK 写,确保到 SAFEOP。 */
ec_slave[0].state = EC_STATE_SAFE_OP | EC_STATE_ACK;
ec_writestate(0);
ec_statecheck(0, EC_STATE_SAFE_OP, 2000000);
/* SAFEOP→OP:从站要先收到≥1帧过程数据才 ack OP,且带错误位时只有 OP|ACK
才清错并迁移 → 边发 PD 边带 ACK 重写 OP,每 200ms 一轮,readstate 拿真实态。
进 OP 后再起稳态线程(避免主线程与线程并发操作同一 ecx_port 打架)。 */
for (i = 0; i < 60; i++) {
ec_send_processdata();
ec_receive_processdata(EC_TIMEOUTRET);
ec_slave[0].state = EC_STATE_OPERATIONAL | EC_STATE_ACK;
ec_writestate(0);
ec_readstate();
if ((ec_slave[1].state & 0x0F) == EC_STATE_OPERATIONAL) break;
Sleep(200);
}
if ((ec_slave[1].state & 0x0F) != EC_STATE_OPERATIONAL) {
fprintf(stderr, "[native] 进 OP 失败(AL=0x%02x code=0x%04x)—— 若反复如此,"
"可能是上一主站退出后 SSC 邮箱楔死,需断电重启板子\n",
ec_slave[1].state & 0x0F, ec_slave[1].ALstatuscode);
ec_close();
return 5;
}
printf("[native] OP 达成: %d 从站\n", ec_slavecount);
th = CreateThread(NULL, 0, pdo_worker, NULL, 0, NULL);
SetThreadPriority(th, THREAD_PRIORITY_TIME_CRITICAL);
WSAStartup(MAKEWORD(2, 2), &wsa);
ls = socket(AF_INET, SOCK_STREAM, 0);
{ int one = 1; setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one); }
sa.sin_family = AF_INET;
sa.sin_port = htons((u_short)port);
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* 只绑 127.0.0.1,同 Python 口径 */
if (bind(ls, (struct sockaddr *)&sa, sizeof sa) == SOCKET_ERROR) {
fprintf(stderr, "[native] bind %d 失败: %d\n", port, WSAGetLastError());
return 6;
}
listen(ls, 32);
printf("[native] 监听 http://127.0.0.1:%d (state/period/dbg)\n", port);
for (;;) {
SOCKET cs = accept(ls, NULL, NULL);
HANDLE t;
if (cs == INVALID_SOCKET) continue;
t = CreateThread(NULL, 0, conn_thread, (LPVOID)(intptr_t)cs, 0, NULL);
if (t) CloseHandle(t); else closesocket(cs);
}
return 0;
}
+28
View File
@@ -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())
+156
View File
@@ -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())
+103
View File
@@ -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(&current_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 (&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp (&current_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);
}
+41
View File
@@ -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
+88
View File
@@ -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 (&current_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 (&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp (&current_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;
}
+43
View File
@@ -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
+144
View File
@@ -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, &current_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(&current_time);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp(&current_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;
}
+38
View File
@@ -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
+144
View File
@@ -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(&current_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(&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp(&current_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;
}
+38
View File
@@ -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
+59
View File
@@ -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
+144
View File
@@ -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(&current_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(&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp(&current_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;
}
+38
View File
@@ -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
+145
View File
@@ -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 (&current_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 (&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp (&current_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;
}
+37
View File
@@ -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
+187
View File
@@ -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(&current_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(&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp(&current_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;
}
+28
View File
@@ -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
+153
View File
@@ -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 (&current_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 (&current_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp (&current_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;
}
+45
View File
@@ -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
+36
View File
@@ -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
+561
View File
@@ -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
+122
View File
@@ -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
+79
View File
@@ -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;
+31
View File
@@ -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
+744
View File
@@ -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
+116
View File
@@ -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
+48
View File
@@ -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)
{
}
+31
View File
@@ -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
+648
View File
@@ -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
+120
View File
@@ -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
+117
View File
@@ -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);
}
}
}
+31
View File
@@ -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
+649
View File
@@ -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
+120
View File
@@ -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
+117
View File
@@ -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);
}
}
}
+31
View File
@@ -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
+696
View File
@@ -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
+120
View File
@@ -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
+117
View File
@@ -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);
}
}
}
+31
View File
@@ -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
+802
View File
@@ -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;
}
+56
View File
@@ -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 */
/**
* \}
*/
+423
View File
@@ -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;
}
+12
View File
@@ -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 */
+628
View File
@@ -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
+109
View File
@@ -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
+53
View File
@@ -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 */
}
+35
View File
@@ -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
+980
View File
@@ -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
+130
View File
@@ -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
+60
View File
@@ -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);
}
+23
View File
@@ -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
+648
View File
@@ -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
+123
View File
@@ -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
+121
View File
@@ -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);
}
}
}
+32
View File
@@ -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__*/
+45
View File
@@ -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
+26
View File
@@ -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 */
+637
View File
@@ -0,0 +1,637 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Base EtherCAT functions.
*
* Setting up a datagram in an ethernet frame.
* EtherCAT datagram primitives, broadcast, auto increment, configured and
* logical addressed data transfers. All base transfers are blocking, so
* wait for the frame to be returned to the master or timeout. If this is
* not acceptable build your own datagrams and use the functions from nicdrv.c.
*/
#include <stdio.h>
#include <string.h>
#include "oshw.h"
#include "osal.h"
#include "ethercattype.h"
#include "ethercatbase.h"
/** Write data to EtherCAT datagram.
*
* @param[out] datagramdata = data part of datagram
* @param[in] com = command
* @param[in] length = length of databuffer
* @param[in] data = databuffer to be copied into datagram
*/
static void ecx_writedatagramdata(void *datagramdata, ec_cmdtype com, uint16 length, const void * data)
{
if (length > 0)
{
switch (com)
{
case EC_CMD_NOP:
/* Fall-through */
case EC_CMD_APRD:
/* Fall-through */
case EC_CMD_FPRD:
/* Fall-through */
case EC_CMD_BRD:
/* Fall-through */
case EC_CMD_LRD:
/* no data to write. initialise data so frame is in a known state */
memset(datagramdata, 0, length);
break;
default:
memcpy(datagramdata, data, length);
break;
}
}
}
/** Generate and set EtherCAT datagram in a standard ethernet frame.
*
* @param[in] port = port context struct
* @param[out] frame = framebuffer
* @param[in] com = command
* @param[in] idx = index used for TX and RX buffers
* @param[in] ADP = Address Position
* @param[in] ADO = Address Offset
* @param[in] length = length of datagram excluding EtherCAT header
* @param[in] data = databuffer to be copied in datagram
* @return always 0
*/
int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
{
ec_comt *datagramP;
uint8 *frameP;
frameP = frame;
/* Ethernet header is preset and fixed in frame buffers
EtherCAT header needs to be added after that */
datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length);
datagramP->command = com;
datagramP->index = idx;
datagramP->ADP = htoes(ADP);
datagramP->ADO = htoes(ADO);
datagramP->dlength = htoes(length);
ecx_writedatagramdata(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], com, length, data);
/* set WKC to zero */
frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00;
frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00;
/* set size of frame in buffer array */
port->txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length;
return 0;
}
/** Add EtherCAT datagram to a standard ethernet frame with existing datagram(s).
*
* @param[in] port = port context struct
* @param[out] frame = framebuffer
* @param[in] com = command
* @param[in] idx = index used for TX and RX buffers
* @param[in] more = TRUE if still more datagrams to follow
* @param[in] ADP = Address Position
* @param[in] ADO = Address Offset
* @param[in] length = length of datagram excluding EtherCAT header
* @param[in] data = databuffer to be copied in datagram
* @return Offset to data in rx frame, usefull to retrieve data after RX.
*/
uint16 ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
{
ec_comt *datagramP;
uint8 *frameP;
uint16 prevlength;
frameP = frame;
/* copy previous frame size */
prevlength = (uint16)port->txbuflength[idx];
datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
/* add new datagram to ethernet frame size */
datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length );
/* add "datagram follows" flag to previous subframe dlength */
datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS );
/* set new EtherCAT header position */
datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE];
datagramP->command = com;
datagramP->index = idx;
datagramP->ADP = htoes(ADP);
datagramP->ADO = htoes(ADO);
if (more)
{
/* this is not the last datagram to add */
datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS);
}
else
{
/* this is the last datagram in the frame */
datagramP->dlength = htoes(length);
}
ecx_writedatagramdata(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], com, length, data);
/* set WKC to zero */
frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00;
frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00;
/* set size of frame in buffer array */
port->txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length;
/* return offset to data in rx frame
14 bytes smaller than tx frame due to stripping of ethernet header */
return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE;
}
/** BRW "broadcast write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, normally 0
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[in] data = databuffer to be written to slaves
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_BWR (ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
/* get fresh index */
idx = ecx_getindex (port);
/* setup datagram */
ecx_setupdatagram (port, &(port->txbuf[idx]), EC_CMD_BWR, idx, ADP, ADO, length, data);
/* send data and wait for answer */
wkc = ecx_srconfirm (port, idx, timeout);
/* clear buffer status */
ecx_setbufstat (port, idx, EC_BUF_EMPTY);
return wkc;
}
/** BRD "broadcast read" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, normally 0
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[out] data = databuffer to put slave data in
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_BRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
/* get fresh index */
idx = ecx_getindex(port);
/* setup datagram */
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_BRD, idx, ADP, ADO, length, data);
/* send data and wait for answer */
wkc = ecx_srconfirm (port, idx, timeout);
if (wkc > 0)
{
/* copy datagram to data buffer */
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
/* clear buffer status */
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** APRD "auto increment address read" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, each slave ++, slave that has 0 executes
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[out] data = databuffer to put slave data in
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
int wkc;
uint8 idx;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APRD, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if (wkc > 0)
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** APRMW "auto increment address read, multiple write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, each slave ++, slave that has 0 reads,
* following slaves write.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[out] data = databuffer to put slave data in
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
int wkc;
uint8 idx;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_ARMW, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if (wkc > 0)
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** FPRMW "configured address read, multiple write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, slave that has address reads,
* following slaves write.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[out] data = databuffer to put slave data in
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
int wkc;
uint8 idx;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if (wkc > 0)
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** APRDw "auto increment address read" word return primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, each slave ++, slave that has 0 reads.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return word data from slave
*/
uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
{
uint16 w;
w = 0;
ecx_APRD(port, ADP, ADO, sizeof(w), &w, timeout);
return w;
}
/** FPRD "configured address read" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, slave that has address reads.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[out] data = databuffer to put slave data in
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
int wkc;
uint8 idx;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if (wkc > 0)
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** FPRDw "configured address read" word return primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, slave that has address reads.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return word data from slave
*/
uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
{
uint16 w;
w = 0;
ecx_FPRD(port, ADP, ADO, sizeof(w), &w, timeout);
return w;
}
/** APWR "auto increment address write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, each slave ++, slave that has 0 writes.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[in] data = databuffer to write to slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APWR, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** APWRw "auto increment address write" word primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, each slave ++, slave that has 0 writes.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] data = word data to write to slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
{
return ecx_APWR(port, ADP, ADO, sizeof(data), &data, timeout);
}
/** FPWR "configured address write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, slave that has address writes.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] length = length of databuffer
* @param[in] data = databuffer to write to slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
int wkc;
uint8 idx;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPWR, idx, ADP, ADO, length, data);
wkc = ecx_srconfirm(port, idx, timeout);
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** FPWR "configured address write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] ADP = Address Position, slave that has address writes.
* @param[in] ADO = Address Offset, slave memory address
* @param[in] data = word to write to slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
{
return ecx_FPWR(port, ADP, ADO, sizeof(data), &data, timeout);
}
/** LRW "logical memory read / write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] LogAdr = Logical memory address
* @param[in] length = length of databuffer
* @param[in,out] data = databuffer to write to and read from slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** LRD "logical memory read" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] LogAdr = Logical memory address
* @param[in] length = length of bytes to read from slave.
* @param[out] data = databuffer to read from slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
wkc = ecx_srconfirm(port, idx, timeout);
if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD))
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** LWR "logical memory write" primitive. Blocking.
*
* @param[in] port = port context struct
* @param[in] LogAdr = Logical memory address
* @param[in] length = length of databuffer
* @param[in] data = databuffer to write to slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{
uint8 idx;
int wkc;
idx = ecx_getindex(port);
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
wkc = ecx_srconfirm(port, idx, timeout);
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
* Frame consists of two datagrams, one LRW and one FPRMW.
*
* @param[in] port = port context struct
* @param[in] LogAdr = Logical memory address
* @param[in] length = length of databuffer
* @param[in,out] data = databuffer to write to and read from slave.
* @param[in] DCrs = Distributed Clock reference slave address.
* @param[out] DCtime = DC time read from reference slave.
* @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
* @return Workcounter or EC_NOFRAME
*/
int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{
uint16 DCtO;
uint8 idx;
int wkc;
uint64 DCtE;
idx = ecx_getindex(port);
/* LRW in first datagram */
ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
/* FPRMW in second datagram */
DCtE = htoell(*DCtime);
DCtO = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
wkc = ecx_srconfirm(port, idx, timeout);
if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
{
memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
memcpy(&wkc, &(port->rxbuf[idx][EC_HEADERSIZE + length]), EC_WKCSIZE);
memcpy(&DCtE, &(port->rxbuf[idx][DCtO]), sizeof(*DCtime));
*DCtime = etohll(DCtE);
}
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
return wkc;
}
#ifdef EC_VER1
int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
{
return ecx_setupdatagram (&ecx_port, frame, com, idx, ADP, ADO, length, data);
}
uint16 ec_adddatagram (void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
{
return ecx_adddatagram (&ecx_port, frame, com, idx, more, ADP, ADO, length, data);
}
int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_BWR (&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_BRD(&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_APRD(&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_ARMW(&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_FRMW(&ecx_port, ADP, ADO, length, data, timeout);
}
uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout)
{
uint16 w;
w = 0;
ec_APRD(ADP, ADO, sizeof(w), &w, timeout);
return w;
}
int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_FPRD(&ecx_port, ADP, ADO, length, data, timeout);
}
uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout)
{
uint16 w;
w = 0;
ec_FPRD(ADP, ADO, sizeof(w), &w, timeout);
return w;
}
int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_APWR(&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
{
return ec_APWR(ADP, ADO, sizeof(data), &data, timeout);
}
int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
return ecx_FPWR(&ecx_port, ADP, ADO, length, data, timeout);
}
int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
{
return ec_FPWR(ADP, ADO, sizeof(data), &data, timeout);
}
int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout)
{
return ecx_LRW(&ecx_port, LogAdr, length, data, timeout);
}
int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
{
return ecx_LRD(&ecx_port, LogAdr, length, data, timeout);
}
int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
{
return ecx_LWR(&ecx_port, LogAdr, length, data, timeout);
}
int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{
return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout);
}
#endif
+63
View File
@@ -0,0 +1,63 @@
/*
* 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 _ethercatbase_
#define _ethercatbase_
#ifdef __cplusplus
extern "C"
{
#endif
int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
uint16 ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
int ecx_BWR(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
int ecx_BRD(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
#ifdef EC_VER1
int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
uint16 ec_adddatagram(void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
int ec_BWR(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
int ec_BRD(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout);
int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout);
int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout);
int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout);
int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout);
int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
#endif
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
/*
* 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 ethercatcoe.c
*/
#ifndef _ethercatcoe_
#define _ethercatcoe_
#ifdef __cplusplus
extern "C"
{
#endif
/** max entries in Object Description list */
#define EC_MAXODLIST 1024
/** max entries in Object Entry list */
#define EC_MAXOELIST 256
/* Storage for object description list */
typedef struct
{
/** slave number */
uint16 Slave;
/** number of entries in list */
uint16 Entries;
/** array of indexes */
uint16 Index[EC_MAXODLIST];
/** array of datatypes, see EtherCAT specification */
uint16 DataType[EC_MAXODLIST];
/** array of object codes, see EtherCAT specification */
uint8 ObjectCode[EC_MAXODLIST];
/** number of subindexes for each index */
uint8 MaxSub[EC_MAXODLIST];
/** textual description of each index */
char Name[EC_MAXODLIST][EC_MAXNAME+1];
} ec_ODlistt;
/* storage for object list entry information */
typedef struct
{
/** number of entries in list */
uint16 Entries;
/** array of value infos, see EtherCAT specification */
uint8 ValueInfo[EC_MAXOELIST];
/** array of value infos, see EtherCAT specification */
uint16 DataType[EC_MAXOELIST];
/** array of bit lengths, see EtherCAT specification */
uint16 BitLength[EC_MAXOELIST];
/** array of object access bits, see EtherCAT specification */
uint16 ObjAccess[EC_MAXOELIST];
/** textual description of each index */
char Name[EC_MAXOELIST][EC_MAXNAME+1];
} ec_OElistt;
#ifdef EC_VER1
void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
boolean CA, int *psize, void *p, int timeout);
int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
boolean CA, int psize, const void *p, int Timeout);
int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber , int psize, const void *p);
int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
int ec_readPDOmap(uint16 Slave, uint32 *Osize, uint32 *Isize);
int ec_readPDOmapCA(uint16 Slave, int Thread_n, uint32 *Osize, uint32 *Isize);
int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist);
int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist);
int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
#endif
void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
boolean CA, int *psize, void *p, int timeout);
int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
boolean CA, int psize, const void *p, int Timeout);
int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber , int psize, const void *p);
int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, uint32 *Osize, uint32 *Isize);
int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, uint32 *Osize, uint32 *Isize);
int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist);
int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist);
int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/*
* 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 ethercatconfig.c
*/
#ifndef _ethercatconfig_
#define _ethercatconfig_
#ifdef __cplusplus
extern "C"
{
#endif
#define EC_NODEOFFSET 0x1000
#define EC_TEMPNODE 0xffff
#ifdef EC_VER1
int ec_config_init(uint8 usetable);
int ec_config_map(void *pIOmap);
int ec_config_overlap_map(void *pIOmap);
int ec_config_map_aligned(void *pIOmap);
int ec_config_map_group(void *pIOmap, uint8 group);
int ec_config_overlap_map_group(void *pIOmap, uint8 group);
int ec_config_map_group_aligned(void *pIOmap, uint8 group);
int ec_config(uint8 usetable, void *pIOmap);
int ec_config_overlap(uint8 usetable, void *pIOmap);
int ec_recover_slave(uint16 slave, int timeout);
int ec_reconfig_slave(uint16 slave, int timeout);
#endif
int ecx_config_init(ecx_contextt *context, uint8 usetable);
int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
int ecx_config_map_group_aligned(ecx_contextt *context, void *pIOmap, uint8 group);
int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout);
int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout);
#ifdef __cplusplus
}
#endif
#endif
+66
View File
@@ -0,0 +1,66 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* DEPRECATED Configuration list of known EtherCAT slave devices.
*
* If a slave is found in this list it is configured according to the parameters
* in the list. Otherwise the configuration info is read directly from the slave
* EEPROM (SII or Slave Information Interface).
*/
#ifndef _ethercatconfiglist_
#define _ethercatconfiglist_
#ifdef __cplusplus
extern "C"
{
#endif
/*
explanation of dev:
1: static device with no IO mapping ie EK1100
2: input device no mailbox ie simple IO device
3: output device no mailbox
4: input device with mailbox configuration
5: output device with mailbox configuration
6: input/output device no mailbox
7: input.output device with mailbox configuration
*/
#define EC_CONFIGEND 0xffffffff
ec_configlist_t ec_configlist[] = {
{/*Man=*/0x00000000,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x044c2c52,/*Name=*/"EK1100" ,/*dtype=*/1,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x03ea3052,/*Name=*/"EL1002" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x03ec3052,/*Name=*/"EL1004" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x03f43052,/*Name=*/"EL1012" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x03f63052,/*Name=*/"EL1014" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x03fa3052,/*Name=*/"EL1018" ,/*dtype=*/2,/*Ibits=*/ 8,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x07d23052,/*Name=*/"EL2002" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x07d43052,/*Name=*/"EL2004" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 4,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x07d83052,/*Name=*/"EL2008" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 8,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x07f03052,/*Name=*/"EL2032" ,/*dtype=*/6,/*Ibits=*/ 2,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x0c1e3052,/*Name=*/"EL3102" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0c283052,/*Name=*/"EL3112" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0c323052,/*Name=*/"EL3122" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0c463052,/*Name=*/"EL3142" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0c503052,/*Name=*/"EL3152" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0c5a3052,/*Name=*/"EL3162" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
{/*Man=*/0x00000002,/*ID=*/0x0fc03052,/*Name=*/"EL4032" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1100,/*SM2f*/0x00010024,/*SM3a*/0x1180,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x10063052,/*Name=*/"EL4102" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x10103052,/*Name=*/"EL4112" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x101a3052,/*Name=*/"EL4122" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x10243052,/*Name=*/"EL4132" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
{/*Man=*/0x00000002,/*ID=*/0x13ed3052,/*Name=*/"EL5101" ,/*dtype=*/7,/*Ibits=*/40,/*Obits=*/24,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/1,/*FM1ac*/1},
{/*Man=*/EC_CONFIGEND,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}
};
#ifdef __cplusplus
}
#endif
#endif
+448
View File
@@ -0,0 +1,448 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Distributed Clock EtherCAT functions.
*
*/
#include "oshw.h"
#include "osal.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatdc.h"
#define PORTM0 0x01
#define PORTM1 0x02
#define PORTM2 0x04
#define PORTM3 0x08
/** 1st sync pulse delay in ns here 100ms */
#define SyncDelay ((int32)100000000)
/**
* Set DC of slave to fire sync0 at CyclTime interval with CyclShift offset.
*
* @param[in] context = context struct
* @param [in] slave Slave number.
* @param [in] act TRUE = active, FALSE = deactivated
* @param [in] CyclTime Cycltime in ns.
* @param [in] CyclShift CyclShift in ns.
*/
void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
{
uint8 h, RA;
uint16 slaveh;
int64 t, t1;
int32 tc;
slaveh = context->slavelist[slave].configadr;
RA = 0;
/* stop cyclic operation, ready for next trigger */
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
if (act)
{
RA = 1 + 2; /* act cyclic operation and sync0, sync1 deactivated */
}
h = 0;
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
t1 = 0;
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
t1 = etohll(t1);
/* Calculate first trigger time, always a whole multiple of CyclTime rounded up
plus the shifttime (can be negative)
This insures best synchronization between slaves, slaves with the same CyclTime
will sync at the same moment (you can use CyclShift to shift the sync) */
if (CyclTime > 0)
{
t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
}
else
{
t = t1 + SyncDelay + CyclShift;
/* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
}
t = htoell(t);
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
tc = htoel(CyclTime);
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
// update ec_slave state
context->slavelist[slave].DCactive = (uint8)act;
context->slavelist[slave].DCshift = CyclShift;
context->slavelist[slave].DCcycle = CyclTime;
}
/**
* Set DC of slave to fire sync0 and sync1 at CyclTime interval with CyclShift offset.
*
* @param[in] context = context struct
* @param [in] slave Slave number.
* @param [in] act TRUE = active, FALSE = deactivated
* @param [in] CyclTime0 Cycltime SYNC0 in ns.
* @param [in] CyclTime1 Cycltime SYNC1 in ns. This time is a delta time in relation to
the SYNC0 fire. If CylcTime1 = 0 then SYNC1 fires a the same time
as SYNC0.
* @param [in] CyclShift CyclShift in ns.
*/
void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
{
uint8 h, RA;
uint16 slaveh;
int64 t, t1;
int32 tc;
uint32 TrueCyclTime;
/* Sync1 can be used as a multiple of Sync0, use true cycle time */
TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0;
slaveh = context->slavelist[slave].configadr;
RA = 0;
/* stop cyclic operation, ready for next trigger */
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
if (act)
{
RA = 1 + 2 + 4; /* act cyclic operation and sync0 + sync1 */
}
h = 0;
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
t1 = 0;
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
t1 = etohll(t1);
/* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up
plus the shifttime (can be negative)
This insures best synchronization between slaves, slaves with the same CyclTime
will sync at the same moment (you can use CyclShift to shift the sync) */
if (CyclTime0 > 0)
{
t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift;
}
else
{
t = t1 + SyncDelay + CyclShift;
/* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
}
t = htoell(t);
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
tc = htoel(CyclTime0);
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
tc = htoel(CyclTime1);
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
// update ec_slave state
context->slavelist[slave].DCactive = (uint8)act;
context->slavelist[slave].DCshift = CyclShift;
context->slavelist[slave].DCcycle = CyclTime0;
}
/* latched port time of slave */
static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
{
int32 ts;
switch (port)
{
case 0:
ts = context->slavelist[slave].DCrtA;
break;
case 1:
ts = context->slavelist[slave].DCrtB;
break;
case 2:
ts = context->slavelist[slave].DCrtC;
break;
case 3:
ts = context->slavelist[slave].DCrtD;
break;
default:
ts = 0;
break;
}
return ts;
}
/* calculate previous active port of a slave */
static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
{
uint8 pport = port;
uint8 aport = context->slavelist[slave].activeports;
switch(port)
{
case 0:
if(aport & PORTM2)
pport = 2;
else if (aport & PORTM1)
pport = 1;
else if (aport & PORTM3)
pport = 3;
break;
case 1:
if(aport & PORTM3)
pport = 3;
else if (aport & PORTM0)
pport = 0;
else if (aport & PORTM2)
pport = 2;
break;
case 2:
if(aport & PORTM1)
pport = 1;
else if (aport & PORTM3)
pport = 3;
else if (aport & PORTM0)
pport = 0;
break;
case 3:
if(aport & PORTM0)
pport = 0;
else if (aport & PORTM2)
pport = 2;
else if (aport & PORTM1)
pport = 1;
break;
}
return pport;
}
/* search unconsumed ports in parent, consume and return first open port */
static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
{
uint8 parentport = 0;
uint8 b;
/* search order is important, here 3 - 1 - 2 - 0 */
b = context->slavelist[parent].consumedports;
if (b & PORTM3)
{
parentport = 3;
b &= (uint8)~PORTM3;
}
else if (b & PORTM1)
{
parentport = 1;
b &= (uint8)~PORTM1;
}
else if (b & PORTM2)
{
parentport = 2;
b &= (uint8)~PORTM2;
}
else if (b & PORTM0)
{
parentport = 0;
b &= (uint8)~PORTM0;
}
context->slavelist[parent].consumedports = b;
return parentport;
}
/**
* Locate DC slaves, measure propagation delays.
*
* @param[in] context = context struct
* @return boolean if slaves are found with DC
*/
boolean ecx_configdc(ecx_contextt *context)
{
uint16 i, slaveh, parent, child;
uint16 parenthold = 0;
uint16 prevDCslave = 0;
int32 ht, dt1, dt2, dt3;
int64 hrt;
uint8 entryport;
int8 nlist;
int8 plist[4];
int32 tlist[4];
ec_timet mastertime;
uint64 mastertime64;
context->slavelist[0].hasdc = FALSE;
context->grouplist[0].hasdc = FALSE;
ht = 0;
ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); /* latch DCrecvTimeA of all slaves */
mastertime = osal_current_time();
mastertime.sec -= 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */
mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000;
for (i = 1; i <= *(context->slavecount); i++)
{
context->slavelist[i].consumedports = context->slavelist[i].activeports;
if (context->slavelist[i].hasdc)
{
if (!context->slavelist[0].hasdc)
{
context->slavelist[0].hasdc = TRUE;
context->slavelist[0].DCnext = i;
context->slavelist[i].DCprevious = 0;
context->grouplist[context->slavelist[i].group].hasdc = TRUE;
context->grouplist[context->slavelist[i].group].DCnext = i;
}
else
{
context->slavelist[prevDCslave].DCnext = i;
context->slavelist[i].DCprevious = prevDCslave;
}
/* this branch has DC slave so remove parenthold */
parenthold = 0;
prevDCslave = i;
slaveh = context->slavelist[i].configadr;
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
context->slavelist[i].DCrtA = etohl(ht);
/* 64bit latched DCrecvTimeA of each specific slave */
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
/* use it as offset in order to set local time around 0 + mastertime */
hrt = htoell(-etohll(hrt) + mastertime64);
/* save it in the offset register */
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
context->slavelist[i].DCrtB = etohl(ht);
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
context->slavelist[i].DCrtC = etohl(ht);
(void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
context->slavelist[i].DCrtD = etohl(ht);
/* make list of active ports and their time stamps */
nlist = 0;
if (context->slavelist[i].activeports & PORTM0)
{
plist[nlist] = 0;
tlist[nlist] = context->slavelist[i].DCrtA;
nlist++;
}
if (context->slavelist[i].activeports & PORTM3)
{
plist[nlist] = 3;
tlist[nlist] = context->slavelist[i].DCrtD;
nlist++;
}
if (context->slavelist[i].activeports & PORTM1)
{
plist[nlist] = 1;
tlist[nlist] = context->slavelist[i].DCrtB;
nlist++;
}
if (context->slavelist[i].activeports & PORTM2)
{
plist[nlist] = 2;
tlist[nlist] = context->slavelist[i].DCrtC;
nlist++;
}
/* entryport is port with the lowest timestamp */
entryport = 0;
if((nlist > 1) && (tlist[1] < tlist[entryport]))
{
entryport = 1;
}
if((nlist > 2) && (tlist[2] < tlist[entryport]))
{
entryport = 2;
}
if((nlist > 3) && (tlist[3] < tlist[entryport]))
{
entryport = 3;
}
entryport = plist[entryport];
context->slavelist[i].entryport = entryport;
/* consume entryport from activeports */
context->slavelist[i].consumedports &= (uint8)~(1 << entryport);
/* finding DC parent of current */
parent = i;
do
{
child = parent;
parent = context->slavelist[parent].parent;
}
while (!((parent == 0) || (context->slavelist[parent].hasdc)));
/* only calculate propagation delay if slave is not the first */
if (parent > 0)
{
/* find port on parent this slave is connected to */
context->slavelist[i].parentport = ecx_parentport(context, parent);
if (context->slavelist[parent].topology == 1)
{
context->slavelist[i].parentport = context->slavelist[parent].entryport;
}
dt1 = 0;
dt2 = 0;
/* delta time of (parentport - 1) - parentport */
/* note: order of ports is 0 - 3 - 1 -2 */
/* non active ports are skipped */
dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
ecx_porttime(context, parent,
ecx_prevport(context, parent, context->slavelist[i].parentport));
/* current slave has children */
/* those children's delays need to be subtracted */
if (context->slavelist[i].topology > 1)
{
dt1 = ecx_porttime(context, i,
ecx_prevport(context, i, context->slavelist[i].entryport)) -
ecx_porttime(context, i, context->slavelist[i].entryport);
}
/* we are only interested in positive difference */
if (dt1 > dt3) dt1 = -dt1;
/* current slave is not the first child of parent */
/* previous child's delays need to be added */
if ((child - parent) > 1)
{
dt2 = ecx_porttime(context, parent,
ecx_prevport(context, parent, context->slavelist[i].parentport)) -
ecx_porttime(context, parent, context->slavelist[parent].entryport);
}
if (dt2 < 0) dt2 = -dt2;
/* calculate current slave delay from delta times */
/* assumption : forward delay equals return delay */
context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
context->slavelist[parent].pdelay;
ht = htoel(context->slavelist[i].pdelay);
/* write propagation delay*/
(void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
}
}
else
{
context->slavelist[i].DCrtA = 0;
context->slavelist[i].DCrtB = 0;
context->slavelist[i].DCrtC = 0;
context->slavelist[i].DCrtD = 0;
parent = context->slavelist[i].parent;
/* if non DC slave found on first position on branch hold root parent */
if ( (parent > 0) && (context->slavelist[parent].topology > 2))
parenthold = parent;
/* if branch has no DC slaves consume port on root parent */
if ( parenthold && (context->slavelist[i].topology == 1))
{
ecx_parentport(context, parenthold);
parenthold = 0;
}
}
}
return context->slavelist[0].hasdc;
}
#ifdef EC_VER1
void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
{
ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift);
}
void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
{
ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift);
}
boolean ec_configdc(void)
{
return ecx_configdc(&ecx_context);
}
#endif
+33
View File
@@ -0,0 +1,33 @@
/*
* 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 ethercatdc.c
*/
#ifndef _EC_ECATDC_H
#define _EC_ECATDC_H
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef EC_VER1
boolean ec_configdc();
void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
#endif
boolean ecx_configdc(ecx_contextt *context);
void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
#ifdef __cplusplus
}
#endif
#endif /* _EC_ECATDC_H */
+634
View File
@@ -0,0 +1,634 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Ethernet over EtherCAT (EoE) module.
*
* Set / Get IP functions
* Blocking send/receive Ethernet Frame
* Read incoming EoE fragment to Ethernet Frame
*/
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercat.h"
/** EoE utility function to convert uint32 to eoe ip bytes.
* @param[in] ip = ip in uint32
* @param[out] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
*/
static void EOE_ip_uint32_to_byte(eoe_ip4_addr_t * ip, uint8_t * byte_ip)
{
byte_ip[3] = eoe_ip4_addr1(ip); /* 1st octet */
byte_ip[2] = eoe_ip4_addr2(ip); /* 2nd octet */
byte_ip[1] = eoe_ip4_addr3(ip); /* 3ed octet */
byte_ip[0] = eoe_ip4_addr4(ip); /* 4th octet */
}
/** EoE utility function to convert eoe ip bytes to uint32.
* @param[in] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
* @param[out] ip = ip in uint32
*/
static void EOE_ip_byte_to_uint32(uint8_t * byte_ip, eoe_ip4_addr_t * ip)
{
EOE_IP4_ADDR_TO_U32(ip,
byte_ip[3], /* 1st octet */
byte_ip[2], /* 2nd octet */
byte_ip[1], /* 3ed octet */
byte_ip[0]); /* 4th octet */
}
/** EoE fragment data handler hook. Should not block.
*
* @param[in] context = context struct
* @param[in] hook = Pointer to hook function.
* @return 1
*/
int ecx_EOEdefinehook(ecx_contextt *context, void *hook)
{
context->EOEhook = hook;
return 1;
}
/** EoE EOE set IP, blocking. Waits for response from the slave.
*
* @param[in] context = Context struct
* @param[in] slave = Slave number
* @param[in] port = Port number on slave if applicable
* @param[in] ipparam = IP parameter data to be sent
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response or returned result code
*/
int ecx_EOEsetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
{
ec_EOEt *EOEp, *aEOEp;
ec_mbxbuft MbxIn, MbxOut;
uint16 frameinfo1, result;
uint8 cnt, data_offset;
uint8 flags = 0;
int wkc;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aEOEp = (ec_EOEt *)&MbxIn;
EOEp = (ec_EOEt *)&MbxOut;
EOEp->mbxheader.address = htoes(0x0000);
EOEp->mbxheader.priority = 0x00;
data_offset = EOE_PARAM_OFFSET;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + MBX_HDR_SET_CNT(cnt); /* EoE */
EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_INIT_REQ) |
EOE_HDR_FRAME_PORT_SET(port) |
EOE_HDR_LAST_FRAGMENT);
EOEp->frameinfo2 = 0;
if (ipparam->mac_set)
{
flags |= EOE_PARAM_MAC_INCLUDE;
memcpy(&EOEp->data[data_offset], ipparam->mac.addr, EOE_ETHADDR_LENGTH);
data_offset += EOE_ETHADDR_LENGTH;
}
if (ipparam->ip_set)
{
flags |= EOE_PARAM_IP_INCLUDE;
EOE_ip_uint32_to_byte(&ipparam->ip, &EOEp->data[data_offset]);
data_offset += EOE_IP4_LENGTH;
}
if (ipparam->subnet_set)
{
flags |= EOE_PARAM_SUBNET_IP_INCLUDE;
EOE_ip_uint32_to_byte(&ipparam->subnet, &EOEp->data[data_offset]);
data_offset += EOE_IP4_LENGTH;
}
if (ipparam->default_gateway_set)
{
flags |= EOE_PARAM_DEFAULT_GATEWAY_INCLUDE;
EOE_ip_uint32_to_byte(&ipparam->default_gateway, &EOEp->data[data_offset]);
data_offset += EOE_IP4_LENGTH;
}
if (ipparam->dns_ip_set)
{
flags |= EOE_PARAM_DNS_IP_INCLUDE;
EOE_ip_uint32_to_byte(&ipparam->dns_ip, &EOEp->data[data_offset]);
data_offset += EOE_IP4_LENGTH;
}
if (ipparam->dns_name_set)
{
/* TwinCAT include EOE_DNS_NAME_LENGTH chars even if name is shorter */
flags |= EOE_PARAM_DNS_NAME_INCLUDE;
memcpy(&EOEp->data[data_offset], (void *)ipparam->dns_name, EOE_DNS_NAME_LENGTH);
data_offset += EOE_DNS_NAME_LENGTH;
}
EOEp->mbxheader.length = htoes(EOE_PARAM_OFFSET + data_offset);
EOEp->data[0] = flags;
/* send EoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
{
frameinfo1 = etohs(aEOEp->frameinfo1);
result = etohs(aEOEp->result);
if ((EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_INIT_RESP) ||
(result != EOE_RESULT_SUCCESS))
{
wkc = -result;
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
}
return wkc;
}
/** EoE EOE get IP, blocking. Waits for response from the slave.
*
* @param[in] context = Context struct
* @param[in] slave = Slave number
* @param[in] port = Port number on slave if applicable
* @param[out] ipparam = IP parameter data retrived from slave
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response or returned result code
*/
int ecx_EOEgetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
{
ec_EOEt *EOEp, *aEOEp;
ec_mbxbuft MbxIn, MbxOut;
uint16 frameinfo1, eoedatasize;
uint8 cnt, data_offset;
uint8 flags = 0;
int wkc;
/* Empty slave out mailbox if something is in. Timout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aEOEp = (ec_EOEt *)&MbxIn;
EOEp = (ec_EOEt *)&MbxOut;
EOEp->mbxheader.address = htoes(0x0000);
EOEp->mbxheader.priority = 0x00;
data_offset = EOE_PARAM_OFFSET;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + MBX_HDR_SET_CNT(cnt); /* EoE */
EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_GET_IP_PARAM_REQ) |
EOE_HDR_FRAME_PORT_SET(port) |
EOE_HDR_LAST_FRAGMENT);
EOEp->frameinfo2 = 0;
EOEp->mbxheader.length = htoes(0x0004);
EOEp->data[0] = flags;
/* send EoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
{
frameinfo1 = etohs(aEOEp->frameinfo1);
eoedatasize = etohs(aEOEp->mbxheader.length) - 0x0004;
if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_GET_IP_PARAM_RESP)
{
wkc = -EOE_RESULT_UNSUPPORTED_FRAME_TYPE;
}
else
{
flags = aEOEp->data[0];
if (flags & EOE_PARAM_MAC_INCLUDE)
{
memcpy(ipparam->mac.addr,
&aEOEp->data[data_offset],
EOE_ETHADDR_LENGTH);
ipparam->mac_set = 1;
data_offset += EOE_ETHADDR_LENGTH;
}
if (flags & EOE_PARAM_IP_INCLUDE)
{
EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
&ipparam->ip);
ipparam->ip_set = 1;
data_offset += EOE_IP4_LENGTH;
}
if (flags & EOE_PARAM_SUBNET_IP_INCLUDE)
{
EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
&ipparam->subnet);
ipparam->subnet_set = 1;
data_offset += EOE_IP4_LENGTH;
}
if (flags & EOE_PARAM_DEFAULT_GATEWAY_INCLUDE)
{
EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
&ipparam->default_gateway);
ipparam->default_gateway_set = 1;
data_offset += EOE_IP4_LENGTH;
}
if (flags & EOE_PARAM_DNS_IP_INCLUDE)
{
EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
&ipparam->dns_ip);
ipparam->dns_ip_set = 1;
data_offset += EOE_IP4_LENGTH;
}
if (flags & EOE_PARAM_DNS_NAME_INCLUDE)
{
uint16_t dns_len;
if ((eoedatasize - data_offset) < EOE_DNS_NAME_LENGTH)
{
dns_len = (eoedatasize - data_offset);
}
else
{
dns_len = EOE_DNS_NAME_LENGTH;
}
/* Assume ZERO terminated string */
memcpy(ipparam->dns_name, &aEOEp->data[data_offset], dns_len);
ipparam->dns_name_set = 1;
data_offset += EOE_DNS_NAME_LENGTH;
}
/* Something os not correct, flag the error */
if(data_offset > eoedatasize)
{
wkc = -EC_ERR_TYPE_MBX_ERROR;
}
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
}
return wkc;
}
/** EoE ethernet buffer write, blocking.
*
* If the buffer is larger than the mailbox size then the buffer is sent in
* several fragments. The function will split the buf data in fragments and
* send them to the slave one by one.
*
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[in] port = Port number on slave if applicable
* @param[in] psize = Size in bytes of parameter buffer.
* @param[in] p = Pointer to parameter buffer
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave transmission
*/
int ecx_EOEsend(ecx_contextt *context, uint16 slave, uint8 port, int psize, void *p, int timeout)
{
ec_EOEt *EOEp;
ec_mbxbuft MbxOut;
uint16 frameinfo1, frameinfo2;
uint8 cnt, txfragmentno;
boolean NotLast;
int wkc, maxdata, txframesize, txframeoffset;
const uint8 * buf = p;
static uint8_t txframeno = 0;
ec_clearmbx(&MbxOut);
EOEp = (ec_EOEt *)&MbxOut;
EOEp->mbxheader.address = htoes(0x0000);
EOEp->mbxheader.priority = 0x00;
/* data section=mailbox size - 6 mbx - 4 EoEh */
maxdata = context->slavelist[slave].mbx_l - 0x0A;
txframesize = psize;
txfragmentno = 0;
txframeoffset = 0;
NotLast = TRUE;
do
{
txframesize = psize - txframeoffset;
if (txframesize > maxdata)
{
/* Adjust to even 32-octect blocks */
txframesize = ((maxdata >> 5) << 5);
}
if (txframesize == (psize - txframeoffset))
{
frameinfo1 = (EOE_HDR_LAST_FRAGMENT_SET(1) | EOE_HDR_FRAME_PORT_SET(port));
NotLast = FALSE;
}
else
{
frameinfo1 = EOE_HDR_FRAME_PORT_SET(port);
}
frameinfo2 = EOE_HDR_FRAG_NO_SET(txfragmentno);
if (txfragmentno > 0)
{
frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET((txframeoffset >> 5)));
}
else
{
frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET(((psize + 31) >> 5)));
txframeno++;
}
frameinfo2 = frameinfo2 | EOE_HDR_FRAME_NO_SET(txframeno);
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
EOEp->mbxheader.length = htoes((uint16)(4 + txframesize)); /* no timestamp */
EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + MBX_HDR_SET_CNT(cnt); /* EoE */
EOEp->frameinfo1 = htoes(frameinfo1);
EOEp->frameinfo2 = htoes(frameinfo2);
memcpy(EOEp->data, &buf[txframeoffset], txframesize);
/* send EoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, timeout);
if ((NotLast == TRUE) && (wkc > 0))
{
txframeoffset += txframesize;
txfragmentno++;
}
} while ((NotLast == TRUE) && (wkc > 0));
return wkc;
}
/** EoE ethernet buffer read, blocking.
*
* If the buffer is larger than the mailbox size then the buffer is received
* by several fragments. The function will assamble the fragments into
* a complete Ethernet buffer.
*
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[in] port = Port number on slave if applicable
* @param[in,out] psize = Size in bytes of parameter buffer.
* @param[in] p = Pointer to parameter buffer
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response or error code
*/
int ecx_EOErecv(ecx_contextt *context, uint16 slave, uint8 port, int * psize, void *p, int timeout)
{
ec_EOEt *aEOEp;
ec_mbxbuft MbxIn;
uint16 frameinfo1, frameinfo2;
uint8 rxfragmentno, rxframeno;
boolean NotLast;
int wkc, buffersize, rxframesize, rxframeoffset, eoedatasize;
uint8 * buf = p;
ec_clearmbx(&MbxIn);
aEOEp = (ec_EOEt *)&MbxIn;
NotLast = TRUE;
buffersize = *psize;
rxfragmentno = 0;
rxframeno = 0xff;
rxframeoffset = 0;
/* Hang for a while if nothing is in */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
while ((wkc > 0) && (NotLast == TRUE))
{
/* slave response should be FoE */
if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
{
eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
frameinfo1 = etohs(aEOEp->frameinfo1);
frameinfo2 = etohs(aEOEp->frameinfo2);
if (rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
{
if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
/* Exit here*/
break;
}
}
if (rxfragmentno == 0)
{
rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
if (rxframesize > buffersize)
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
/* Exit here*/
break;
}
if (port != EOE_HDR_FRAME_PORT_GET(frameinfo1))
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
/* Exit here*/
break;
}
}
else
{
if (rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
/* Exit here*/
break;
}
else if (rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
/* Exit here*/
break;
}
}
if ((rxframeoffset + eoedatasize) <= buffersize)
{
memcpy(&buf[rxframeoffset], aEOEp->data, eoedatasize);
rxframeoffset += eoedatasize;
rxfragmentno++;
}
if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
{
/* Remove timestamp */
if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
{
rxframeoffset -= 4;
}
NotLast = FALSE;
*psize = rxframeoffset;
}
else
{
/* Hang for a while if nothing is in */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
return wkc;
}
/** EoE mailbox fragment read
*
* Will take the data in incoming mailbox buffer and copy to destination
* Ethernet frame buffer at given offset and update current fragment variables
*
* @param[in] MbxIn = Received mailbox containing fragment data
* @param[in,out] rxfragmentno = Fragment number
* @param[in,out] rxframesize = Frame size
* @param[in,out] rxframeoffset = Frame offset
* @param[in,out] rxframeno = Frame number
* @param[in,out] psize = Size in bytes of frame buffer.
* @param[out] p = Pointer to frame buffer
* @return 0= if fragment OK, >0 if last fragment, <0 on error
*/
int ecx_EOEreadfragment(
ec_mbxbuft * MbxIn,
uint8 * rxfragmentno,
uint16 * rxframesize,
uint16 * rxframeoffset,
uint16 * rxframeno,
int * psize,
void *p)
{
uint16 frameinfo1, frameinfo2, eoedatasize;
int wkc;
ec_EOEt * aEOEp;
uint8 * buf;
aEOEp = (ec_EOEt *)MbxIn;
buf = p;
wkc = 0;
/* slave response should be EoE */
if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
{
eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
frameinfo1 = etohs(aEOEp->frameinfo1);
frameinfo2 = etohs(aEOEp->frameinfo2);
/* Retrive fragment number, is it what we expect? */
if (*rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
{
/* If expected fragment number is not 0, reset working variables */
if (*rxfragmentno != 0)
{
*rxfragmentno = 0;
*rxframesize = 0;
*rxframeoffset = 0;
*rxframeno = 0;
}
/* If incoming fragment number is not 0 we can't recover, exit */
if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
{
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
return wkc;
}
}
/* Is it a new frame?*/
if (*rxfragmentno == 0)
{
*rxframesize = (uint16)(EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
*rxframeoffset = 0;
*rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
}
else
{
/* If we're inside a frame, make sure it is the same */
if (*rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
{
*rxfragmentno = 0;
*rxframesize = 0;
*rxframeoffset = 0;
*rxframeno = 0;
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
return wkc;
}
else if (*rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
{
*rxfragmentno = 0;
*rxframesize = 0;
*rxframeoffset = 0;
*rxframeno = 0;
wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
return wkc;
}
}
/* Make sure we're inside expected frame size */
if (((*rxframeoffset + eoedatasize) <= *rxframesize) &&
((*rxframeoffset + eoedatasize) <= *psize))
{
memcpy(&buf[*rxframeoffset], aEOEp->data, eoedatasize);
*rxframeoffset += eoedatasize;
*rxfragmentno += 1;
}
/* Is it the last fragment */
if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
{
/* Remove timestamp */
if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
{
*rxframeoffset -= 4;
}
*psize = *rxframeoffset;
*rxfragmentno = 0;
*rxframesize = 0;
*rxframeoffset = 0;
*rxframeno = 0;
wkc = 1;
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
return wkc;
}
+213
View File
@@ -0,0 +1,213 @@
/*
* 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 ethercatfoe.c
*/
#ifndef _ethercateoe_
#define _ethercateoe_
#ifdef __cplusplus
extern "C"
{
#endif
#include "ethercat.h"
/* use maximum size for EOE mailbox data - mbxheader and 2x frameinfo */
#define EC_MAXEOEDATA (EC_MAXMBX - (sizeof(ec_mbxheadert) +\
sizeof(uint16_t) +\
sizeof(uint16_t)))
/** DNS length according to ETG 1000.6 */
#define EOE_DNS_NAME_LENGTH 32
/** Ethernet address length not including VLAN */
#define EOE_ETHADDR_LENGTH 6
/** IPv4 address length */
#define EOE_IP4_LENGTH sizeof(uint32_t)
#define EOE_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \
((uint32_t)((b) & 0xff) << 16) | \
((uint32_t)((c) & 0xff) << 8) | \
(uint32_t)((d) & 0xff))
#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
#define EOE_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
#define EOE_NTOHS(x) EOE_HTONS(x)
#define EOE_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
(((x) & 0x0000ff00UL) << 8) | \
(((x) & 0x00ff0000UL) >> 8) | \
(((x) & 0xff000000UL) >> 24))
#define EOE_NTOHL(x) EOE_HTONL(x)
#else
#define EOE_HTONS(x) (x)
#define EOE_NTOHS(x) (x)
#define EOE_HTONL(x) (x)
#define EOE_NTOHL(x) (x)
#endif /* !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) */
/** Get one byte from the 4-byte address */
#define eoe_ip4_addr1(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[0])
#define eoe_ip4_addr2(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[1])
#define eoe_ip4_addr3(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[2])
#define eoe_ip4_addr4(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[3])
/** Set an IP address given by the four byte-parts */
#define EOE_IP4_ADDR_TO_U32(ipaddr,a,b,c,d) \
(ipaddr)->addr = EOE_HTONL(EOE_MAKEU32(a,b,c,d))
/** Header frame info 1 */
#define EOE_HDR_FRAME_TYPE_OFFSET 0
#define EOE_HDR_FRAME_TYPE (0xF << 0)
#define EOE_HDR_FRAME_TYPE_SET(x) (((x) & 0xF) << 0)
#define EOE_HDR_FRAME_TYPE_GET(x) (((x) >> 0) & 0xF)
#define EOE_HDR_FRAME_PORT_OFFSET 4
#define EOE_HDR_FRAME_PORT (0xF << 4)
#define EOE_HDR_FRAME_PORT_SET(x) ((uint16)(((x) & 0xF) << 4))
#define EOE_HDR_FRAME_PORT_GET(x) (((x) >> 4) & 0xF)
#define EOE_HDR_LAST_FRAGMENT_OFFSET 8
#define EOE_HDR_LAST_FRAGMENT (0x1 << 8)
#define EOE_HDR_LAST_FRAGMENT_SET(x) (((x) & 0x1) << 8)
#define EOE_HDR_LAST_FRAGMENT_GET(x) (((x) >> 8) & 0x1)
#define EOE_HDR_TIME_APPEND_OFFSET 9
#define EOE_HDR_TIME_APPEND (0x1 << 9)
#define EOE_HDR_TIME_APPEND_SET(x) (((x) & 0x1) << 9)
#define EOE_HDR_TIME_APPEND_GET(x) (((x) >> 9) & 0x1)
#define EOE_HDR_TIME_REQUEST_OFFSET 10
#define EOE_HDR_TIME_REQUEST (0x1 << 10)
#define EOE_HDR_TIME_REQUEST_SET(x) (((x) & 0x1) << 10)
#define EOE_HDR_TIME_REQUEST_GET(x) (((x) >> 10) & 0x1)
/** Header frame info 2 */
#define EOE_HDR_FRAG_NO_OFFSET 0
#define EOE_HDR_FRAG_NO (0x3F << 0)
#define EOE_HDR_FRAG_NO_SET(x) ((uint16)(((x) & 0x3F) << 0))
#define EOE_HDR_FRAG_NO_GET(x) (((x) >> 0) & 0x3F)
#define EOE_HDR_FRAME_OFFSET_OFFSET 6
#define EOE_HDR_FRAME_OFFSET (0x3F << 6)
#define EOE_HDR_FRAME_OFFSET_SET(x) ((uint16)(((x) & 0x3F) << 6))
#define EOE_HDR_FRAME_OFFSET_GET(x) (((x) >> 6) & 0x3F)
#define EOE_HDR_FRAME_NO_OFFSET 12
#define EOE_HDR_FRAME_NO (0xF << 12)
#define EOE_HDR_FRAME_NO_SET(x) ((uint16)(((x) & 0xF) << 12))
#define EOE_HDR_FRAME_NO_GET(x) (((x) >> 12) & 0xF)
/** EOE param */
#define EOE_PARAM_OFFSET 4
#define EOE_PARAM_MAC_INCLUDE (0x1 << 0)
#define EOE_PARAM_IP_INCLUDE (0x1 << 1)
#define EOE_PARAM_SUBNET_IP_INCLUDE (0x1 << 2)
#define EOE_PARAM_DEFAULT_GATEWAY_INCLUDE (0x1 << 3)
#define EOE_PARAM_DNS_IP_INCLUDE (0x1 << 4)
#define EOE_PARAM_DNS_NAME_INCLUDE (0x1 << 5)
/** EoE frame types */
#define EOE_FRAG_DATA 0
#define EOE_INIT_RESP_TIMESTAMP 1
#define EOE_INIT_REQ 2 /* Spec SET IP REQ */
#define EOE_INIT_RESP 3 /* Spec SET IP RESP */
#define EOE_SET_ADDR_FILTER_REQ 4
#define EOE_SET_ADDR_FILTER_RESP 5
#define EOE_GET_IP_PARAM_REQ 6
#define EOE_GET_IP_PARAM_RESP 7
#define EOE_GET_ADDR_FILTER_REQ 8
#define EOE_GET_ADDR_FILTER_RESP 9
/** EoE parameter result codes */
#define EOE_RESULT_SUCCESS 0x0000
#define EOE_RESULT_UNSPECIFIED_ERROR 0x0001
#define EOE_RESULT_UNSUPPORTED_FRAME_TYPE 0x0002
#define EOE_RESULT_NO_IP_SUPPORT 0x0201
#define EOE_RESULT_NO_DHCP_SUPPORT 0x0202
#define EOE_RESULT_NO_FILTER_SUPPORT 0x0401
/** EOE ip4 address in network order */
typedef struct eoe_ip4_addr {
uint32_t addr;
}eoe_ip4_addr_t;
/** EOE ethernet address */
PACKED_BEGIN
typedef struct PACKED eoe_ethaddr
{
uint8_t addr[EOE_ETHADDR_LENGTH];
} eoe_ethaddr_t;
PACKED_END
/** EoE IP request structure, storage only, no need to pack */
typedef struct eoe_param
{
uint8_t mac_set : 1;
uint8_t ip_set : 1;
uint8_t subnet_set : 1;
uint8_t default_gateway_set : 1;
uint8_t dns_ip_set : 1;
uint8_t dns_name_set : 1;
eoe_ethaddr_t mac;
eoe_ip4_addr_t ip;
eoe_ip4_addr_t subnet;
eoe_ip4_addr_t default_gateway;
eoe_ip4_addr_t dns_ip;
char dns_name[EOE_DNS_NAME_LENGTH];
} eoe_param_t;
/** EOE structure.
* Used to interpret EoE mailbox packets.
*/
PACKED_BEGIN
typedef struct PACKED
{
ec_mbxheadert mbxheader;
uint16_t frameinfo1;
union
{
uint16_t frameinfo2;
uint16_t result;
};
uint8 data[EC_MAXEOEDATA];
} ec_EOEt;
PACKED_END
int ecx_EOEdefinehook(ecx_contextt *context, void *hook);
int ecx_EOEsetIp(ecx_contextt *context,
uint16 slave,
uint8 port,
eoe_param_t * ipparam,
int timeout);
int ecx_EOEgetIp(ecx_contextt *context,
uint16 slave,
uint8 port,
eoe_param_t * ipparam,
int timeout);
int ecx_EOEsend(ecx_contextt *context,
uint16 slave,
uint8 port,
int psize,
void *p,
int timeout);
int ecx_EOErecv(ecx_contextt *context,
uint16 slave,
uint8 port,
int * psize,
void *p,
int timeout);
int ecx_EOEreadfragment(
ec_mbxbuft * MbxIn,
uint8 * rxfragmentno,
uint16 * rxframesize,
uint16 * rxframeoffset,
uint16 * rxframeno,
int * psize,
void *p);
#ifdef __cplusplus
}
#endif
#endif
+416
View File
@@ -0,0 +1,416 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* File over EtherCAT (FoE) module.
*
* SDO read / write and SDO service functions
*/
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatfoe.h"
/* use maximum size for FOE mailbox data - header and metadata */
#define EC_MAXFOEDATA \
(EC_MAXMBX - \
(sizeof(ec_mbxheadert) + \
sizeof(uint8_t) + \
sizeof(uint8_t) + \
sizeof(uint32_t)))
/** FOE structure.
* Used for Read, Write, Data, Ack and Error mailbox packets.
*/
PACKED_BEGIN
typedef struct PACKED
{
ec_mbxheadert MbxHeader;
uint8 OpCode;
uint8 Reserved;
union
{
uint32 Password;
uint32 PacketNumber;
uint32 ErrorCode;
};
union
{
char FileName[EC_MAXFOEDATA];
uint8 Data[EC_MAXFOEDATA];
char ErrorText[EC_MAXFOEDATA];
};
} ec_FOEt;
PACKED_END
/** FoE progress hook.
*
* @param[in] context = context struct
* @param[in] hook = Pointer to hook function.
* @return 1
*/
int ecx_FOEdefinehook(ecx_contextt *context, void *hook)
{
context->FOEhook = hook;
return 1;
}
/** FoE read, blocking.
*
* @param[in] context = context struct
* @param[in] slave = Slave number.
* @param[in] filename = Filename of file to read.
* @param[in] password = password.
* @param[in,out] psize = Size in bytes of file buffer, returns bytes read from file.
* @param[out] p = Pointer to file buffer
* @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
{
ec_FOEt *FOEp, *aFOEp;
int wkc;
int32 dataread = 0;
int32 buffersize, packetnumber, prevpacket = 0;
uint16 fnsize, maxdata, segmentdata;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean worktodo;
buffersize = *psize;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timeout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aFOEp = (ec_FOEt *)&MbxIn;
FOEp = (ec_FOEt *)&MbxOut;
fnsize = (uint16)strlen(filename);
if (fnsize > EC_MAXFOEDATA)
{
fnsize = EC_MAXFOEDATA;
}
maxdata = context->slavelist[slave].mbx_l - 12;
if (fnsize > maxdata)
{
fnsize = maxdata;
}
FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
FOEp->OpCode = ECT_FOE_READ;
FOEp->Password = htoel(password);
/* copy filename in mailbox */
memcpy(&FOEp->FileName[0], filename, fnsize);
/* send FoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
do
{
worktodo = FALSE;
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
{
if(aFOEp->OpCode == ECT_FOE_DATA)
{
segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
packetnumber = etohl(aFOEp->PacketNumber);
if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
{
memcpy(p, &aFOEp->Data[0], segmentdata);
dataread += segmentdata;
p = (uint8 *)p + segmentdata;
if (segmentdata == maxdata)
{
worktodo = TRUE;
}
FOEp->MbxHeader.length = htoes(0x0006);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
FOEp->OpCode = ECT_FOE_ACK;
FOEp->PacketNumber = htoel(packetnumber);
/* send FoE ack to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc <= 0)
{
worktodo = FALSE;
}
if (context->FOEhook)
{
context->FOEhook(slave, packetnumber, dataread);
}
}
else
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_BUF2SMALL;
}
}
else
{
if(aFOEp->OpCode == ECT_FOE_ERROR)
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_ERROR;
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
*psize = dataread;
}
} while (worktodo);
}
return wkc;
}
/** FoE write, blocking.
*
* @param[in] context = context struct
* @param[in] slave = Slave number.
* @param[in] filename = Filename of file to write.
* @param[in] password = password.
* @param[in] psize = Size in bytes of file buffer.
* @param[out] p = Pointer to file buffer
* @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
{
ec_FOEt *FOEp, *aFOEp;
int wkc;
int32 packetnumber, sendpacket = 0;
uint16 fnsize, maxdata;
int segmentdata;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean worktodo, dofinalzero;
int tsize;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timeout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aFOEp = (ec_FOEt *)&MbxIn;
FOEp = (ec_FOEt *)&MbxOut;
dofinalzero = TRUE;
fnsize = (uint16)strlen(filename);
if (fnsize > EC_MAXFOEDATA)
{
fnsize = EC_MAXFOEDATA;
}
maxdata = context->slavelist[slave].mbx_l - 12;
if (fnsize > maxdata)
{
fnsize = maxdata;
}
FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
FOEp->OpCode = ECT_FOE_WRITE;
FOEp->Password = htoel(password);
/* copy filename in mailbox */
memcpy(&FOEp->FileName[0], filename, fnsize);
/* send FoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
do
{
worktodo = FALSE;
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
{
switch (aFOEp->OpCode)
{
case ECT_FOE_ACK:
{
packetnumber = etohl(aFOEp->PacketNumber);
if (packetnumber == sendpacket)
{
if (context->FOEhook)
{
context->FOEhook(slave, packetnumber, psize);
}
tsize = psize;
if (tsize > maxdata)
{
tsize = maxdata;
}
if(tsize || dofinalzero)
{
worktodo = TRUE;
dofinalzero = FALSE;
segmentdata = tsize;
psize -= segmentdata;
/* if last packet was full size, add a zero size packet as final */
/* EOF is defined as packetsize < full packetsize */
if (!psize && (segmentdata == maxdata))
{
dofinalzero = TRUE;
}
FOEp->MbxHeader.length = htoes((uint16)(0x0006 + segmentdata));
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
FOEp->OpCode = ECT_FOE_DATA;
sendpacket++;
FOEp->PacketNumber = htoel(sendpacket);
memcpy(&FOEp->Data[0], p, segmentdata);
p = (uint8 *)p + segmentdata;
/* send FoE data to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc <= 0)
{
worktodo = FALSE;
}
}
}
else
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_PACKETNUMBER;
}
break;
}
case ECT_FOE_BUSY:
{
/* resend if data has been send before */
/* otherwise ignore */
if (sendpacket)
{
psize += segmentdata;
p = (uint8 *)p - segmentdata;
--sendpacket;
tsize = psize;
if (tsize > maxdata)
{
tsize = maxdata;
}
if(tsize || dofinalzero)
{
worktodo = TRUE;
dofinalzero = FALSE;
segmentdata = tsize;
psize -= segmentdata;
/* if last packet was full size, add a zero size packet as final */
/* EOF is defined as packetsize < full packetsize */
if (!psize && (segmentdata == maxdata))
{
dofinalzero = TRUE;
}
FOEp->MbxHeader.length = htoes((uint16)(0x0006 + segmentdata));
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
FOEp->OpCode = ECT_FOE_DATA;
sendpacket++;
FOEp->PacketNumber = htoel(sendpacket);
memcpy(&FOEp->Data[0], p, segmentdata);
p = (uint8 *)p + segmentdata;
/* send FoE data to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc <= 0)
{
worktodo = FALSE;
}
}
}
break;
}
case ECT_FOE_ERROR:
{
/* FoE error */
if (aFOEp->ErrorCode == 0x8001)
{
wkc = -EC_ERR_TYPE_FOE_FILE_NOTFOUND;
}
else
{
wkc = -EC_ERR_TYPE_FOE_ERROR;
}
break;
}
default:
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
break;
}
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
} while (worktodo);
}
return wkc;
}
#ifdef EC_VER1
int ec_FOEdefinehook(void *hook)
{
return ecx_FOEdefinehook(&ecx_context, hook);
}
int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
{
return ecx_FOEread(&ecx_context, slave, filename, password, psize, p, timeout);
}
int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
{
return ecx_FOEwrite(&ecx_context, slave, filename, password, psize, p, timeout);
}
#endif
+33
View File
@@ -0,0 +1,33 @@
/*
* 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 ethercatfoe.c
*/
#ifndef _ethercatfoe_
#define _ethercatfoe_
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef EC_VER1
int ec_FOEdefinehook(void *hook);
int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
#endif
int ecx_FOEdefinehook(ecx_contextt *context, void *hook);
int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+539
View File
@@ -0,0 +1,539 @@
/*
* 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 ethercatmain.c
*/
#ifndef _ethercatmain_
#define _ethercatmain_
#ifdef __cplusplus
extern "C"
{
#endif
/** max. entries in EtherCAT error list */
#define EC_MAXELIST 64
/** max. length of readable name in slavelist and Object Description List */
#define EC_MAXNAME 40
/** max. number of slaves in array */
#define EC_MAXSLAVE 200
/** max. number of groups */
#define EC_MAXGROUP 2
/** max. number of IO segments per group */
#define EC_MAXIOSEGMENTS 64
/** max. mailbox size */
#define EC_MAXMBX 1486
/** max. eeprom PDO entries */
#define EC_MAXEEPDO 0x200
/** max. SM used */
#define EC_MAXSM 8
/** max. FMMU used */
#define EC_MAXFMMU 4
/** max. Adapter */
#define EC_MAXLEN_ADAPTERNAME 128
/** define maximum number of concurrent threads in mapping */
#define EC_MAX_MAPT 1
typedef struct ec_adapter ec_adaptert;
struct ec_adapter
{
char name[EC_MAXLEN_ADAPTERNAME];
char desc[EC_MAXLEN_ADAPTERNAME];
ec_adaptert *next;
};
/** record for FMMU */
PACKED_BEGIN
typedef struct PACKED ec_fmmu
{
uint32 LogStart;
uint16 LogLength;
uint8 LogStartbit;
uint8 LogEndbit;
uint16 PhysStart;
uint8 PhysStartBit;
uint8 FMMUtype;
uint8 FMMUactive;
uint8 unused1;
uint16 unused2;
} ec_fmmut;
PACKED_END
/** record for sync manager */
PACKED_BEGIN
typedef struct PACKED ec_sm
{
uint16 StartAddr;
uint16 SMlength;
uint32 SMflags;
} ec_smt;
PACKED_END
PACKED_BEGIN
typedef struct PACKED ec_state_status
{
uint16 State;
uint16 Unused;
uint16 ALstatuscode;
} ec_state_status;
PACKED_END
#define ECT_MBXPROT_AOE 0x0001
#define ECT_MBXPROT_EOE 0x0002
#define ECT_MBXPROT_COE 0x0004
#define ECT_MBXPROT_FOE 0x0008
#define ECT_MBXPROT_SOE 0x0010
#define ECT_MBXPROT_VOE 0x0020
#define ECT_COEDET_SDO 0x01
#define ECT_COEDET_SDOINFO 0x02
#define ECT_COEDET_PDOASSIGN 0x04
#define ECT_COEDET_PDOCONFIG 0x08
#define ECT_COEDET_UPLOAD 0x10
#define ECT_COEDET_SDOCA 0x20
#define EC_SMENABLEMASK 0xfffeffff
typedef struct ecx_context ecx_contextt;
/** for list of ethercat slaves detected */
typedef struct ec_slave
{
/** state of slave */
uint16 state;
/** AL status code */
uint16 ALstatuscode;
/** Configured address */
uint16 configadr;
/** Alias address */
uint16 aliasadr;
/** Manufacturer from EEprom */
uint32 eep_man;
/** ID from EEprom */
uint32 eep_id;
/** revision from EEprom */
uint32 eep_rev;
/** serial number from EEprom */
uint32 eep_sn;
/** Interface type */
uint16 Itype;
/** Device type */
uint16 Dtype;
/** output bits */
uint16 Obits;
/** output bytes, if Obits < 8 then Obytes = 0 */
uint32 Obytes;
/** output pointer in IOmap buffer */
uint8 *outputs;
/** startbit in first output byte */
uint8 Ostartbit;
/** input bits */
uint16 Ibits;
/** input bytes, if Ibits < 8 then Ibytes = 0 */
uint32 Ibytes;
/** input pointer in IOmap buffer */
uint8 *inputs;
/** startbit in first input byte */
uint8 Istartbit;
/** SM structure */
ec_smt SM[EC_MAXSM];
/** SM type 0=unused 1=MbxWr 2=MbxRd 3=Outputs 4=Inputs */
uint8 SMtype[EC_MAXSM];
/** FMMU structure */
ec_fmmut FMMU[EC_MAXFMMU];
/** FMMU0 function */
uint8 FMMU0func;
/** FMMU1 function */
uint8 FMMU1func;
/** FMMU2 function */
uint8 FMMU2func;
/** FMMU3 function */
uint8 FMMU3func;
/** length of write mailbox in bytes, if no mailbox then 0 */
uint16 mbx_l;
/** mailbox write offset */
uint16 mbx_wo;
/** length of read mailbox in bytes */
uint16 mbx_rl;
/** mailbox read offset */
uint16 mbx_ro;
/** mailbox supported protocols */
uint16 mbx_proto;
/** Counter value of mailbox link layer protocol 1..7 */
uint8 mbx_cnt;
/** has DC capability */
boolean hasdc;
/** Physical type; Ebus, EtherNet combinations */
uint8 ptype;
/** topology: 1 to 3 links */
uint8 topology;
/** active ports bitmap : ....3210 , set if respective port is active **/
uint8 activeports;
/** consumed ports bitmap : ....3210, used for internal delay measurement **/
uint8 consumedports;
/** slave number for parent, 0=master */
uint16 parent;
/** port number on parent this slave is connected to **/
uint8 parentport;
/** port number on this slave the parent is connected to **/
uint8 entryport;
/** DC receivetimes on port A */
int32 DCrtA;
/** DC receivetimes on port B */
int32 DCrtB;
/** DC receivetimes on port C */
int32 DCrtC;
/** DC receivetimes on port D */
int32 DCrtD;
/** propagation delay */
int32 pdelay;
/** next DC slave */
uint16 DCnext;
/** previous DC slave */
uint16 DCprevious;
/** DC cycle time in ns */
int32 DCcycle;
/** DC shift from clock modulus boundary */
int32 DCshift;
/** DC sync activation, 0=off, 1=on */
uint8 DCactive;
/** link to config table */
uint16 configindex;
/** link to SII config */
uint16 SIIindex;
/** 1 = 8 bytes per read, 0 = 4 bytes per read */
uint8 eep_8byte;
/** 0 = eeprom to master , 1 = eeprom to PDI */
uint8 eep_pdi;
/** CoE details */
uint8 CoEdetails;
/** FoE details */
uint8 FoEdetails;
/** EoE details */
uint8 EoEdetails;
/** SoE details */
uint8 SoEdetails;
/** E-bus current */
int16 Ebuscurrent;
/** if >0 block use of LRW in processdata */
uint8 blockLRW;
/** group */
uint8 group;
/** first unused FMMU */
uint8 FMMUunused;
/** Boolean for tracking whether the slave is (not) responding, not used/set by the SOEM library */
boolean islost;
/** registered configuration function PO->SO, (DEPRECATED)*/
int (*PO2SOconfig)(uint16 slave, void *user);
/** registered configuration function PO->SO */
int (*PO2SOconfigx)(ecx_contextt * context, uint16 slave);
/** user data for PO2SOconfig */
void* user;
/** readable name */
char name[EC_MAXNAME + 1];
} ec_slavet;
/** for list of ethercat slave groups */
typedef struct ec_group
{
/** logical start address for this group */
uint32 logstartaddr;
/** output bytes, if Obits < 8 then Obytes = 0 */
uint32 Obytes;
/** output pointer in IOmap buffer */
uint8 *outputs;
/** input bytes, if Ibits < 8 then Ibytes = 0 */
uint32 Ibytes;
/** input pointer in IOmap buffer */
uint8 *inputs;
/** has DC capabillity */
boolean hasdc;
/** next DC slave */
uint16 DCnext;
/** E-bus current */
int16 Ebuscurrent;
/** if >0 block use of LRW in processdata */
uint8 blockLRW;
/** IO segments used */
uint16 nsegments;
/** 1st input segment */
uint16 Isegment;
/** Offset in input segment */
uint16 Ioffset;
/** Expected workcounter outputs */
uint16 outputsWKC;
/** Expected workcounter inputs */
uint16 inputsWKC;
/** check slave states */
boolean docheckstate;
/** IO segmentation list. Datagrams must not break SM in two. */
uint32 IOsegment[EC_MAXIOSEGMENTS];
} ec_groupt;
/** SII FMMU structure */
typedef struct ec_eepromFMMU
{
uint16 Startpos;
uint8 nFMMU;
uint8 FMMU0;
uint8 FMMU1;
uint8 FMMU2;
uint8 FMMU3;
} ec_eepromFMMUt;
/** SII SM structure */
typedef struct ec_eepromSM
{
uint16 Startpos;
uint8 nSM;
uint16 PhStart;
uint16 Plength;
uint8 Creg;
uint8 Sreg; /* don't care */
uint8 Activate;
uint8 PDIctrl; /* don't care */
} ec_eepromSMt;
/** record to store rxPDO and txPDO table from eeprom */
typedef struct ec_eepromPDO
{
uint16 Startpos;
uint16 Length;
uint16 nPDO;
uint16 Index[EC_MAXEEPDO];
uint16 SyncM[EC_MAXEEPDO];
uint16 BitSize[EC_MAXEEPDO];
uint16 SMbitsize[EC_MAXSM];
} ec_eepromPDOt;
/** mailbox buffer array */
typedef uint8 ec_mbxbuft[EC_MAXMBX + 1];
/** standard ethercat mailbox header */
PACKED_BEGIN
typedef struct PACKED ec_mbxheader
{
uint16 length;
uint16 address;
uint8 priority;
uint8 mbxtype;
} ec_mbxheadert;
PACKED_END
/** ALstatus and ALstatus code */
PACKED_BEGIN
typedef struct PACKED ec_alstatus
{
uint16 alstatus;
uint16 unused;
uint16 alstatuscode;
} ec_alstatust;
PACKED_END
/** stack structure to store segmented LRD/LWR/LRW constructs */
typedef struct ec_idxstack
{
uint8 pushed;
uint8 pulled;
uint8 idx[EC_MAXBUF];
void *data[EC_MAXBUF];
uint16 length[EC_MAXBUF];
uint16 dcoffset[EC_MAXBUF];
} ec_idxstackT;
/** ringbuf for error storage */
typedef struct ec_ering
{
int16 head;
int16 tail;
ec_errort Error[EC_MAXELIST + 1];
} ec_eringt;
/** SyncManager Communication Type structure for CA */
PACKED_BEGIN
typedef struct PACKED ec_SMcommtype
{
uint8 n;
uint8 nu1;
uint8 SMtype[EC_MAXSM];
} ec_SMcommtypet;
PACKED_END
/** SDO assign structure for CA */
PACKED_BEGIN
typedef struct PACKED ec_PDOassign
{
uint8 n;
uint8 nu1;
uint16 index[256];
} ec_PDOassignt;
PACKED_END
/** SDO description structure for CA */
PACKED_BEGIN
typedef struct PACKED ec_PDOdesc
{
uint8 n;
uint8 nu1;
uint32 PDO[256];
} ec_PDOdesct;
PACKED_END
/** Context structure , referenced by all ecx functions*/
struct ecx_context
{
/** port reference, may include red_port */
ecx_portt *port;
/** slavelist reference */
ec_slavet *slavelist;
/** number of slaves found in configuration */
int *slavecount;
/** maximum number of slaves allowed in slavelist */
int maxslave;
/** grouplist reference */
ec_groupt *grouplist;
/** maximum number of groups allowed in grouplist */
int maxgroup;
/** internal, reference to eeprom cache buffer */
uint8 *esibuf;
/** internal, reference to eeprom cache map */
uint32 *esimap;
/** internal, current slave for eeprom cache */
uint16 esislave;
/** internal, reference to error list */
ec_eringt *elist;
/** internal, reference to processdata stack buffer info */
ec_idxstackT *idxstack;
/** reference to ecaterror state */
boolean *ecaterror;
/** internal, position of DC datagram in process data packet */
uint16 DCtO;
/** internal, length of DC datagram */
uint16 DCl;
/** reference to last DC time from slaves */
int64 *DCtime;
/** internal, SM buffer */
ec_SMcommtypet *SMcommtype;
/** internal, PDO assign list */
ec_PDOassignt *PDOassign;
/** internal, PDO description list */
ec_PDOdesct *PDOdesc;
/** internal, SM list from eeprom */
ec_eepromSMt *eepSM;
/** internal, FMMU list from eeprom */
ec_eepromFMMUt *eepFMMU;
/** registered FoE hook */
int (*FOEhook)(uint16 slave, int packetnumber, int datasize);
/** registered EoE hook */
int (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx);
/** flag to control legacy automatic state change or manual state change */
int manualstatechange;
/** userdata, promotes application configuration esp. in EC_VER2 with multiple
* ec_context instances. Note: userdata memory is managed by application, not SOEM */
void *userdata;
};
#ifdef EC_VER1
/** global struct to hold default master context */
extern ecx_contextt ecx_context;
/** main slave data structure array */
extern ec_slavet ec_slave[EC_MAXSLAVE];
/** number of slaves found by configuration function */
extern int ec_slavecount;
/** slave group structure */
extern ec_groupt ec_group[EC_MAXGROUP];
extern boolean EcatError;
extern int64 ec_DCtime;
void ec_pusherror(const ec_errort *Ec);
boolean ec_poperror(ec_errort *Ec);
boolean ec_iserror(void);
void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
int ec_init(const char * ifname);
int ec_init_redundant(const char *ifname, char *if2name);
void ec_close(void);
uint8 ec_siigetbyte(uint16 slave, uint16 address);
int16 ec_siifind(uint16 slave, uint16 cat);
void ec_siistring(char *str, uint16 slave, uint16 Sn);
uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU);
uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM);
uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n);
uint32 ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t);
int ec_readstate(void);
int ec_writestate(uint16 slave);
uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout);
int ec_mbxempty(uint16 slave, int timeout);
int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout);
int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout);
void ec_esidump(uint16 slave, uint8 *esibuf);
uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout);
int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout);
int ec_eeprom2master(uint16 slave);
int ec_eeprom2pdi(uint16 slave);
uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout);
int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout);
int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout);
void ec_readeeprom1(uint16 slave, uint16 eeproma);
uint32 ec_readeeprom2(uint16 slave, int timeout);
int ec_send_processdata_group(uint8 group);
int ec_send_overlap_processdata_group(uint8 group);
int ec_receive_processdata_group(uint8 group, int timeout);
int ec_send_processdata(void);
int ec_send_overlap_processdata(void);
int ec_receive_processdata(int timeout);
#endif
ec_adaptert * ec_find_adapters(void);
void ec_free_adapters(ec_adaptert * adapter);
uint8 ec_nextmbxcnt(uint8 cnt);
void ec_clearmbx(ec_mbxbuft *Mbx);
void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec);
boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec);
boolean ecx_iserror(ecx_contextt *context);
void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
int ecx_init(ecx_contextt *context, const char * ifname);
int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name);
void ecx_close(ecx_contextt *context);
uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address);
int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat);
void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn);
uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU);
uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM);
uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n);
uint32 ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t);
int ecx_readstate(ecx_contextt *context);
int ecx_writestate(ecx_contextt *context, uint16 slave);
uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout);
int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout);
int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout);
int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout);
void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf);
uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout);
int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout);
int ecx_eeprom2master(ecx_contextt *context, uint16 slave);
int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave);
uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout);
int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout);
int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout);
void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma);
uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout);
int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group);
int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout);
int ecx_send_processdata(ecx_contextt *context);
int ecx_send_overlap_processdata(ecx_contextt *context);
int ecx_receive_processdata(ecx_contextt *context, int timeout);
int ecx_send_processdata_group(ecx_contextt *context, uint8 group);
#ifdef __cplusplus
}
#endif
#endif
+375
View File
@@ -0,0 +1,375 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Module to convert EtherCAT errors to readable messages.
*
* SDO abort messages and AL status codes are used to relay slave errors to
* the user application. This module converts the binary codes to readable text.
* For the defined error codes see the EtherCAT protocol documentation.
*/
#include <stdio.h>
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatmain.h"
#define EC_MAXERRORNAME 127
/** SDO error list type definition */
typedef struct
{
/** Error code returned from SDO */
uint32 errorcode;
/** Readable error description */
char errordescription[EC_MAXERRORNAME + 1];
} ec_sdoerrorlist_t;
/** AL status code list type definition */
typedef struct
{
/** AL status code */
uint16 ALstatuscode;
/** Readable description */
char ALstatuscodedescription[EC_MAXERRORNAME + 1];
} ec_ALstatuscodelist_t;
/** SoE error list type definition */
typedef struct
{
/** SoE error code */
uint16 errorcode;
/** Readable description */
char errordescription[EC_MAXERRORNAME + 1];
} ec_soeerrorlist_t;
/** MBX error list type definition */
typedef struct
{
/** MBX error code */
uint16 errorcode;
/** Readable description */
char errordescription[EC_MAXERRORNAME + 1];
} ec_mbxerrorlist_t;
char estring[EC_MAXERRORNAME];
/** SDO error list definition */
const ec_sdoerrorlist_t ec_sdoerrorlist[] = {
{0x00000000, "No error" },
{0x05030000, "Toggle bit not changed" },
{0x05040000, "SDO protocol timeout" },
{0x05040001, "Client/Server command specifier not valid or unknown" },
{0x05040005, "Out of memory" },
{0x06010000, "Unsupported access to an object" },
{0x06010001, "Attempt to read to a write only object" },
{0x06010002, "Attempt to write to a read only object" },
{0x06010003, "Subindex can not be written, SI0 must be 0 for write access" },
{0x06010004, "SDO Complete access not supported for variable length objects" },
{0x06010005, "Object length exceeds mailbox size" },
{0x06010006, "Object mapped to RxPDO, SDO download blocked" },
{0x06020000, "The object does not exist in the object directory" },
{0x06040041, "The object can not be mapped into the PDO" },
{0x06040042, "The number and length of the objects to be mapped would exceed the PDO length" },
{0x06040043, "General parameter incompatibility reason" },
{0x06040047, "General internal incompatibility in the device" },
{0x06060000, "Access failed due to a hardware error" },
{0x06070010, "Data type does not match, length of service parameter does not match" },
{0x06070012, "Data type does not match, length of service parameter too high" },
{0x06070013, "Data type does not match, length of service parameter too low" },
{0x06090011, "Subindex does not exist" },
{0x06090030, "Value range of parameter exceeded (only for write access)" },
{0x06090031, "Value of parameter written too high" },
{0x06090032, "Value of parameter written too low" },
{0x06090036, "Maximum value is less than minimum value" },
{0x08000000, "General error" },
{0x08000020, "Data cannot be transferred or stored to the application" },
{0x08000021, "Data cannot be transferred or stored to the application because of local control" },
{0x08000022, "Data cannot be transferred or stored to the application because of the present device state" },
{0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present" },
{0xffffffff, "Unknown" }
};
/** AL status code list definition */
const ec_ALstatuscodelist_t ec_ALstatuscodelist[] = {
{0x0000 , "No error" },
{0x0001 , "Unspecified error" },
{0x0002 , "No memory" },
{0x0011 , "Invalid requested state change" },
{0x0012 , "Unknown requested state" },
{0x0013 , "Bootstrap not supported" },
{0x0014 , "No valid firmware" },
{0x0015 , "Invalid mailbox configuration" },
{0x0016 , "Invalid mailbox configuration" },
{0x0017 , "Invalid sync manager configuration" },
{0x0018 , "No valid inputs available" },
{0x0019 , "No valid outputs" },
{0x001A , "Synchronization error" },
{0x001B , "Sync manager watchdog" },
{0x001C , "Invalid sync Manager types" },
{0x001D , "Invalid output configuration" },
{0x001E , "Invalid input configuration" },
{0x001F , "Invalid watchdog configuration" },
{0x0020 , "Slave needs cold start" },
{0x0021 , "Slave needs INIT" },
{0x0022 , "Slave needs PREOP" },
{0x0023 , "Slave needs SAFEOP" },
{0x0024 , "Invalid input mapping" },
{0x0025 , "Invalid output mapping" },
{0x0026 , "Inconsistent settings" },
{0x0027 , "Freerun not supported" },
{0x0028 , "Synchronisation not supported" },
{0x0029 , "Freerun needs 3buffer mode" },
{0x002A , "Background watchdog" },
{0x002B , "No valid Inputs and Outputs" },
{0x002C , "Fatal sync error" },
{0x002D , "No sync error" }, // was "Invalid Output FMMU Configuration"
{0x002E , "Invalid input FMMU configuration" },
{0x0030 , "Invalid DC SYNC configuration" },
{0x0031 , "Invalid DC latch configuration" },
{0x0032 , "PLL error" },
{0x0033 , "DC sync IO error" },
{0x0034 , "DC sync timeout error" },
{0x0035 , "DC invalid sync cycle time" },
{0x0036 , "DC invalid sync0 cycle time" },
{0x0037 , "DC invalid sync1 cycle time" },
{0x0041 , "MBX_AOE" },
{0x0042 , "MBX_EOE" },
{0x0043 , "MBX_COE" },
{0x0044 , "MBX_FOE" },
{0x0045 , "MBX_SOE" },
{0x004F , "MBX_VOE" },
{0x0050 , "EEPROM no access" },
{0x0051 , "EEPROM error" },
{0x0060 , "Slave restarted locally" },
{0x0061 , "Device identification value updated" },
{0x00f0 , "Application controller available" },
{0xffff , "Unknown" }
};
/** SoE error list definition */
const ec_soeerrorlist_t ec_soeerrorlist[] = {
{0x0000, "No error" },
{0x1001, "No IDN" },
{0x1009, "Invalid access to element 1" },
{0x2001, "No Name" },
{0x2002, "Name transmission too short" },
{0x2003, "Name transmission too long" },
{0x2004, "Name cannot be changed (read only)" },
{0x2005, "Name is write-protected at this time" },
{0x3002, "Attribute transmission too short" },
{0x3003, "Attribute transmission too long" },
{0x3004, "Attribute cannot be changed (read only)" },
{0x3005, "Attribute is write-protected at this time" },
{0x4001, "No units" },
{0x4002, "Unit transmission too short" },
{0x4003, "Unit transmission too long" },
{0x4004, "Unit cannot be changed (read only)" },
{0x4005, "Unit is write-protected at this time" },
{0x5001, "No minimum input value" },
{0x5002, "Minimum input value transmission too short" },
{0x5003, "Minimum input value transmission too long" },
{0x5004, "Minimum input value cannot be changed (read only)" },
{0x5005, "Minimum input value is write-protected at this time" },
{0x6001, "No maximum input value" },
{0x6002, "Maximum input value transmission too short" },
{0x6003, "Maximum input value transmission too long" },
{0x6004, "Maximum input value cannot be changed (read only)" },
{0x6005, "Maximum input value is write-protected at this time" },
{0x7002, "Operation data transmission too short" },
{0x7003, "Operation data transmission too long" },
{0x7004, "Operation data cannot be changed (read only)" },
{0x7005, "Operation data is write-protected at this time (state)" },
{0x7006, "Operation data is smaller than the minimum input value" },
{0x7007, "Operation data is smaller than the maximum input value" },
{0x7008, "Invalid operation data:Configured IDN will not be supported" },
{0x7009, "Operation data write protected by a password" },
{0x700A, "Operation data is write protected, it is configured cyclically" },
{0x700B, "Invalid indirect addressing: (e.g., data container, list handling)" },
{0x700C, "Operation data is write protected, due to other settings" },
{0x700D, "Reserved" },
{0x7010, "Procedure command already active" },
{0x7011, "Procedure command not interruptible" },
{0x7012, "Procedure command at this time not executable (state)" },
{0x7013, "Procedure command not executable (invalid or false parameters)" },
{0x7014, "No data state" },
{0x8001, "No default value" },
{0x8002, "Default value transmission too long" },
{0x8004, "Default value cannot be changed, read only" },
{0x800A, "Invalid drive number" },
{0x800A, "General error" },
{0x800A, "No element addressed" },
{0xffff, "Unknown" }
};
/** MBX error list definition */
const ec_mbxerrorlist_t ec_mbxerrorlist[] = {
{0x0000, "No error" },
{0x0001, "Syntax of 6 octet Mailbox Header is wrong" },
{0x0002, "The mailbox protocol is not supported" },
{0x0003, "Channel Field contains wrong value"},
{0x0004, "The service is no supported"},
{0x0005, "Invalid mailbox header"},
{0x0006, "Length of received mailbox data is too short"},
{0x0007, "No more memory in slave"},
{0x0008, "The length of data is inconsistent"},
{0xffff, "Unknown"}
};
/** Look up text string that belongs to SDO error code.
*
* @param[in] sdoerrorcode = SDO error code as defined in EtherCAT protocol
* @return readable string
*/
const char* ec_sdoerror2string( uint32 sdoerrorcode)
{
int i = 0;
while ( (ec_sdoerrorlist[i].errorcode != 0xffffffffUL) &&
(ec_sdoerrorlist[i].errorcode != sdoerrorcode) )
{
i++;
}
return ec_sdoerrorlist[i].errordescription;
}
/** Look up text string that belongs to AL status code.
*
* @param[in] ALstatuscode = AL status code as defined in EtherCAT protocol
* @return readable string
*/
char* ec_ALstatuscode2string( uint16 ALstatuscode)
{
int i = 0;
while ( (ec_ALstatuscodelist[i].ALstatuscode != 0xffff) &&
(ec_ALstatuscodelist[i].ALstatuscode != ALstatuscode) )
{
i++;
}
return (char *) ec_ALstatuscodelist[i].ALstatuscodedescription;
}
/** Look up text string that belongs to SoE error code.
*
* @param[in] errorcode = SoE error code as defined in EtherCAT protocol
* @return readable string
*/
char* ec_soeerror2string( uint16 errorcode)
{
int i = 0;
while ( (ec_soeerrorlist[i].errorcode != 0xffff) &&
(ec_soeerrorlist[i].errorcode != errorcode) )
{
i++;
}
return (char *) ec_soeerrorlist[i].errordescription;
}
/** Look up text string that belongs to MBX error code.
*
* @param[in] errorcode = MBX error code as defined in EtherCAT protocol
* @return readable string
*/
char* ec_mbxerror2string( uint16 errorcode)
{
int i = 0;
while ( (ec_mbxerrorlist[i].errorcode != 0xffff) &&
(ec_mbxerrorlist[i].errorcode != errorcode) )
{
i++;
}
return (char *) ec_mbxerrorlist[i].errordescription;
}
/** Convert an error to text string.
*
* @param[in] Ec = Struct describing the error.
* @return readable string
*/
char* ecx_err2string(const ec_errort Ec)
{
char timestr[20];
sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) );
switch (Ec.Etype)
{
case EC_ERR_TYPE_SDO_ERROR:
{
sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
break;
}
case EC_ERR_TYPE_EMERGENCY:
{
sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n",
timestr, Ec.Slave, Ec.ErrorCode);
break;
}
case EC_ERR_TYPE_PACKET_ERROR:
{
sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n",
timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode);
break;
}
case EC_ERR_TYPE_SDOINFO_ERROR:
{
sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
break;
}
case EC_ERR_TYPE_SOE_ERROR:
{
sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n",
timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode));
break;
}
case EC_ERR_TYPE_MBX_ERROR:
{
sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n",
timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode));
break;
}
default:
{
sprintf(estring, "%s error:%8.8x\n",
timestr, (unsigned)Ec.AbortCode);
break;
}
}
return (char*) estring;
}
/** Look up error in ec_errorlist and convert to text string.
*
* @param[in] context = context struct
* @return readable string
*/
char* ecx_elist2string(ecx_contextt *context)
{
ec_errort Ec;
if (ecx_poperror(context, &Ec))
{
return ecx_err2string(Ec);
}
else
{
return "";
}
}
#ifdef EC_VER1
char* ec_elist2string(void)
{
return ecx_elist2string(&ecx_context);
}
#endif
+34
View File
@@ -0,0 +1,34 @@
/*
* 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 ethercatprint.c
*/
#ifndef _ethercatprint_
#define _ethercatprint_
#ifdef __cplusplus
extern "C"
{
#endif
char* ec_sdoerror2string( uint32 sdoerrorcode);
char* ec_ALstatuscode2string( uint16 ALstatuscode);
char* ec_soeerror2string( uint16 errorcode);
char* ec_mbxerror2string( uint16 errorcode);
char* ecx_err2string(const ec_errort Ec);
char* ecx_elist2string(ecx_contextt *context);
#ifdef EC_VER1
char* ec_elist2string(void);
#endif
#ifdef __cplusplus
}
#endif
#endif
+389
View File
@@ -0,0 +1,389 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Servo over EtherCAT (SoE) Module.
*/
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatsoe.h"
#define EC_SOE_MAX_DRIVES 8
/** SoE (Servo over EtherCAT) mailbox structure */
PACKED_BEGIN
typedef struct PACKED
{
ec_mbxheadert MbxHeader;
uint8 opCode :3;
uint8 incomplete :1;
uint8 error :1;
uint8 driveNo :3;
uint8 elementflags;
union
{
uint16 idn;
uint16 fragmentsleft;
};
} ec_SoEt;
PACKED_END
/** Report SoE error.
*
* @param[in] context = context struct
* @param[in] Slave = Slave number
* @param[in] idn = IDN that generated error
* @param[in] Error = Error code, see EtherCAT documentation for list
*/
void ecx_SoEerror(ecx_contextt *context, uint16 Slave, uint16 idn, uint16 Error)
{
ec_errort Ec;
memset(&Ec, 0, sizeof(Ec));
Ec.Time = osal_current_time();
Ec.Slave = Slave;
Ec.Index = idn;
Ec.SubIdx = 0;
*(context->ecaterror) = TRUE;
Ec.Etype = EC_ERR_TYPE_SOE_ERROR;
Ec.ErrorCode = Error;
ecx_pusherror(context, &Ec);
}
/** SoE read, blocking.
*
* The IDN object of the selected slave and DriveNo is read. If a response
* is larger than the mailbox size then the response is segmented. The function
* will combine all segments and copy them to the parameter buffer.
*
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[in] driveNo = Drive number in slave
* @param[in] elementflags = Flags to select what properties of IDN are to be transferred.
* @param[in] idn = IDN.
* @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SoE.
* @param[out] p = Pointer to parameter buffer
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
{
ec_SoEt *SoEp, *aSoEp;
int totalsize, framedatasize;
int wkc;
uint8 *bp;
uint8 *mp;
uint16 *errorcode;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean NotLast;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timeout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aSoEp = (ec_SoEt *)&MbxIn;
SoEp = (ec_SoEt *)&MbxOut;
SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert));
SoEp->MbxHeader.address = htoes(0x0000);
SoEp->MbxHeader.priority = 0x00;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + MBX_HDR_SET_CNT(cnt); /* SoE */
SoEp->opCode = ECT_SOE_READREQ;
SoEp->incomplete = 0;
SoEp->error = 0;
SoEp->driveNo = driveNo;
SoEp->elementflags = elementflags;
SoEp->idn = htoes(idn);
totalsize = 0;
bp = p;
mp = (uint8 *)&MbxIn + sizeof(ec_SoEt);
NotLast = TRUE;
/* send SoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
while (NotLast)
{
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be SoE, ReadRes */
if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
(aSoEp->opCode == ECT_SOE_READRES) &&
(aSoEp->error == 0) &&
(aSoEp->driveNo == driveNo) &&
(aSoEp->elementflags == elementflags))
{
framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt) + sizeof(ec_mbxheadert);
totalsize += framedatasize;
/* Does parameter fit in parameter buffer ? */
if (totalsize <= *psize)
{
/* copy parameter data in parameter buffer */
memcpy(bp, mp, framedatasize);
/* increment buffer pointer */
bp += framedatasize;
}
else
{
framedatasize -= totalsize - *psize;
totalsize = *psize;
/* copy parameter data in parameter buffer */
if (framedatasize > 0) memcpy(bp, mp, framedatasize);
}
if (!aSoEp->incomplete)
{
NotLast = FALSE;
*psize = totalsize;
}
}
/* other slave response */
else
{
NotLast = FALSE;
if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
(aSoEp->opCode == ECT_SOE_READRES) &&
(aSoEp->error == 1))
{
mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
errorcode = (uint16 *)mp;
ecx_SoEerror(context, slave, idn, *errorcode);
}
else
{
ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
}
wkc = 0;
}
}
else
{
NotLast = FALSE;
ecx_packeterror(context, slave, idn, 0, 4); /* no response */
}
}
}
return wkc;
}
/** SoE write, blocking.
*
* The IDN object of the selected slave and DriveNo is written. If a response
* is larger than the mailbox size then the response is segmented.
*
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[in] driveNo = Drive number in slave
* @param[in] elementflags = Flags to select what properties of IDN are to be transferred.
* @param[in] idn = IDN.
* @param[in] psize = Size in bytes of parameter buffer.
* @param[out] p = Pointer to parameter buffer
* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
{
ec_SoEt *SoEp, *aSoEp;
int framedatasize, maxdata;
int wkc;
uint8 *mp;
uint8 *hp;
uint16 *errorcode;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean NotLast;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timeout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aSoEp = (ec_SoEt *)&MbxIn;
SoEp = (ec_SoEt *)&MbxOut;
SoEp->MbxHeader.address = htoes(0x0000);
SoEp->MbxHeader.priority = 0x00;
SoEp->opCode = ECT_SOE_WRITEREQ;
SoEp->error = 0;
SoEp->driveNo = driveNo;
SoEp->elementflags = elementflags;
hp = p;
mp = (uint8 *)&MbxOut + sizeof(ec_SoEt);
maxdata = context->slavelist[slave].mbx_l - sizeof(ec_SoEt);
NotLast = TRUE;
while (NotLast)
{
framedatasize = psize;
NotLast = FALSE;
SoEp->idn = htoes(idn);
SoEp->incomplete = 0;
if (framedatasize > maxdata)
{
framedatasize = maxdata; /* segmented transfer needed */
NotLast = TRUE;
SoEp->incomplete = 1;
SoEp->fragmentsleft = (uint16)(psize / maxdata);
}
SoEp->MbxHeader.length = htoes((uint16)(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize));
/* get new mailbox counter, used for session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + MBX_HDR_SET_CNT(cnt); /* SoE */
/* copy parameter data to mailbox */
memcpy(mp, hp, framedatasize);
hp += framedatasize;
psize -= framedatasize;
/* send SoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
if (!NotLast || !ecx_mbxempty(context, slave, timeout))
{
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
NotLast = FALSE;
/* slave response should be SoE, WriteRes */
if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
(aSoEp->opCode == ECT_SOE_WRITERES) &&
(aSoEp->error == 0) &&
(aSoEp->driveNo == driveNo) &&
(aSoEp->elementflags == elementflags))
{
/* SoE write succeeded */
}
/* other slave response */
else
{
if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
(aSoEp->opCode == ECT_SOE_READRES) &&
(aSoEp->error == 1))
{
mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
errorcode = (uint16 *)mp;
ecx_SoEerror(context, slave, idn, *errorcode);
}
else
{
ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
}
wkc = 0;
}
}
else
{
ecx_packeterror(context, slave, idn, 0, 4); /* no response */
}
}
}
}
return wkc;
}
/** SoE read AT and MTD mapping.
*
* SoE has standard indexes defined for mapping. This function
* tries to read them and collect a full input and output mapping size
* of designated slave.
*
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[out] Osize = Size in bits of output mapping (MTD) found
* @param[out] Isize = Size in bits of input mapping (AT) found
* @return >0 if mapping successful.
*/
int ecx_readIDNmap(ecx_contextt *context, uint16 slave, uint32 *Osize, uint32 *Isize)
{
int retVal = 0;
int wkc;
int psize;
uint8 driveNr;
uint16 entries, itemcount;
ec_SoEmappingt SoEmapping;
ec_SoEattributet SoEattribute;
*Isize = 0;
*Osize = 0;
for(driveNr = 0; driveNr < EC_SOE_MAX_DRIVES; driveNr++)
{
psize = sizeof(SoEmapping);
/* read output mapping via SoE */
wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_MDTCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
{
/* command word (uint16) is always mapped but not in list */
*Osize += 16;
for (itemcount = 0 ; itemcount < entries ; itemcount++)
{
psize = sizeof(SoEattribute);
/* read attribute of each IDN in mapping list */
wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
if ((wkc > 0) && (!SoEattribute.list))
{
/* length : 0 = 8bit, 1 = 16bit .... */
*Osize += (int)8 << SoEattribute.length;
}
}
}
psize = sizeof(SoEmapping);
/* read input mapping via SoE */
wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_ATCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
{
/* status word (uint16) is always mapped but not in list */
*Isize += 16;
for (itemcount = 0 ; itemcount < entries ; itemcount++)
{
psize = sizeof(SoEattribute);
/* read attribute of each IDN in mapping list */
wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
if ((wkc > 0) && (!SoEattribute.list))
{
/* length : 0 = 8bit, 1 = 16bit .... */
*Isize += (int)8 << SoEattribute.length;
}
}
}
}
/* found some I/O bits ? */
if ((*Isize > 0) || (*Osize > 0))
{
retVal = 1;
}
return retVal;
}
#ifdef EC_VER1
int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
{
return ecx_SoEread(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
}
int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
{
return ecx_SoEwrite(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
}
int ec_readIDNmap(uint16 slave, uint32 *Osize, uint32 *Isize)
{
return ecx_readIDNmap(&ecx_context, slave, Osize, Isize);
}
#endif
+130
View File
@@ -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 ethercatsoe.c
*/
#ifndef _ethercatsoe_
#define _ethercatsoe_
#ifdef __cplusplus
extern "C"
{
#endif
#define EC_SOE_DATASTATE_B 0x01
#define EC_SOE_NAME_B 0x02
#define EC_SOE_ATTRIBUTE_B 0x04
#define EC_SOE_UNIT_B 0x08
#define EC_SOE_MIN_B 0x10
#define EC_SOE_MAX_B 0x20
#define EC_SOE_VALUE_B 0x40
#define EC_SOE_DEFAULT_B 0x80
#define EC_SOE_MAXNAME 60
#define EC_SOE_MAXMAPPING 64
#define EC_IDN_MDTCONFIG 24
#define EC_IDN_ATCONFIG 16
/** SoE name structure */
PACKED_BEGIN
typedef struct PACKED
{
/** current length in bytes of list */
uint16 currentlength;
/** maximum length in bytes of list */
uint16 maxlength;
char name[EC_SOE_MAXNAME];
} ec_SoEnamet;
PACKED_END
/** SoE list structure */
PACKED_BEGIN
typedef struct PACKED
{
/** current length in bytes of list */
uint16 currentlength;
/** maximum length in bytes of list */
uint16 maxlength;
union
{
uint8 byte[8];
uint16 word[4];
uint32 dword[2];
uint64 lword[1];
};
} ec_SoElistt;
PACKED_END
/** SoE IDN mapping structure */
PACKED_BEGIN
typedef struct PACKED
{
/** current length in bytes of list */
uint16 currentlength;
/** maximum length in bytes of list */
uint16 maxlength;
uint16 idn[EC_SOE_MAXMAPPING];
} ec_SoEmappingt;
PACKED_END
#define EC_SOE_LENGTH_1 0x00
#define EC_SOE_LENGTH_2 0x01
#define EC_SOE_LENGTH_4 0x02
#define EC_SOE_LENGTH_8 0x03
#define EC_SOE_TYPE_BINARY 0x00
#define EC_SOE_TYPE_UINT 0x01
#define EC_SOE_TYPE_INT 0x02
#define EC_SOE_TYPE_HEX 0x03
#define EC_SOE_TYPE_STRING 0x04
#define EC_SOE_TYPE_IDN 0x05
#define EC_SOE_TYPE_FLOAT 0x06
#define EC_SOE_TYPE_PARAMETER 0x07
/** SoE attribute structure */
PACKED_BEGIN
typedef struct PACKED
{
/** evaluation factor for display purposes */
uint32 evafactor :16;
/** length of IDN element(s) */
uint32 length :2;
/** IDN is list */
uint32 list :1;
/** IDN is command */
uint32 command :1;
/** datatype */
uint32 datatype :3;
uint32 reserved1 :1;
/** decimals to display if float datatype */
uint32 decimals :4;
/** write protected in pre-op */
uint32 wppreop :1;
/** write protected in safe-op */
uint32 wpsafeop :1;
/** write protected in op */
uint32 wpop :1;
uint32 reserved2 :1;
} ec_SoEattributet;
PACKED_END
#ifdef EC_VER1
int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
int ec_readIDNmap(uint16 slave, uint32 *Osize, uint32 *Isize);
#endif
int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
int ecx_readIDNmap(ecx_contextt *context, uint16 slave, uint32 *Osize, uint32 *Isize);
#ifdef __cplusplus
}
#endif
#endif
+581
View File
@@ -0,0 +1,581 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* General typedefs and defines for EtherCAT.
*
* Defines that could need optimisation for specific applications
* are the EC_TIMEOUTxxx. Assumptions for the standard settings are a
* standard linux PC or laptop and a wired connection to maximal 100 slaves.
* For use with wireless connections or lots of slaves the timeouts need
* increasing. For fast systems running Xenomai and RT-net or alike the
* timeouts need to be shorter.
*/
#ifndef _EC_TYPE_H
#define _EC_TYPE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "osal.h"
#ifdef USE_SOEM_CONFIG_H
#include "soem_config.h"
#endif
/** define EC_VER1 if version 1 default context and functions are needed
* define EC_VER2 if application uses only ecx_ functions and own context */
#if !defined(EC_VER1) && !defined(EC_VER2)
# define EC_VER1
#endif
/** Define little endian target by default if no endian is set */
#if !defined(EC_LITTLE_ENDIAN) && !defined(EC_BIG_ENDIAN)
# define EC_LITTLE_ENDIAN
#endif
/** return value no frame returned */
#define EC_NOFRAME -1
/** return value unknown frame received */
#define EC_OTHERFRAME -2
/** return value general error */
#define EC_ERROR -3
/** return value too many slaves */
#define EC_SLAVECOUNTEXCEEDED -4
/** return value request timeout */
#define EC_TIMEOUT -5
/** maximum EtherCAT frame length in bytes */
#define EC_MAXECATFRAME 1518
/** maximum EtherCAT LRW frame length in bytes */
/* MTU - Ethernet header - length - datagram header - WCK - FCS */
#define EC_MAXLRWDATA (EC_MAXECATFRAME - 14 - 2 - 10 - 2 - 4)
/** size of DC datagram used in first LRW frame */
#define EC_FIRSTDCDATAGRAM 20
/** standard frame buffer size in bytes */
#define EC_BUFSIZE EC_MAXECATFRAME
/** datagram type EtherCAT */
#define EC_ECATTYPE 0x1000
/** number of frame buffers per channel (tx, rx1 rx2) */
#define EC_MAXBUF 16
#ifndef USE_SOEM_CONFIG_H
/** timeout value in us for tx frame to return to rx */
#define EC_TIMEOUTRET 2000
/** timeout value in us for safe data transfer, max. triple retry */
#define EC_TIMEOUTRET3 (EC_TIMEOUTRET * 3)
/** timeout value in us for return "safe" variant (f.e. wireless) */
#define EC_TIMEOUTSAFE 20000
/** timeout value in us for EEPROM access */
#define EC_TIMEOUTEEP 20000
/** timeout value in us for tx mailbox cycle */
#define EC_TIMEOUTTXM 20000
/** timeout value in us for rx mailbox cycle */
#define EC_TIMEOUTRXM 700000
/** timeout value in us for check statechange */
#define EC_TIMEOUTSTATE 2000000
#endif
/** size of EEPROM bitmap cache */
#define EC_MAXEEPBITMAP 128
/** size of EEPROM cache buffer */
#define EC_MAXEEPBUF EC_MAXEEPBITMAP << 5
/** default number of retries if wkc <= 0 */
#define EC_DEFAULTRETRIES 3
/** default group size in 2^x */
#define EC_LOGGROUPOFFSET 16
/** definition for frame buffers */
typedef uint8 ec_bufT[EC_BUFSIZE];
/** ethernet header definition */
PACKED_BEGIN
typedef struct PACKED
{
/** destination MAC */
uint16 da0,da1,da2;
/** source MAC */
uint16 sa0,sa1,sa2;
/** ethernet type */
uint16 etype;
} ec_etherheadert;
PACKED_END
/** ethernet header size */
#define ETH_HEADERSIZE sizeof(ec_etherheadert)
/** EtherCAT datagram header definition */
PACKED_BEGIN
typedef struct PACKED
{
/** length of EtherCAT datagram */
uint16 elength;
/** EtherCAT command, see ec_cmdtype */
uint8 command;
/** index, used in SOEM for Tx to Rx recombination */
uint8 index;
/** ADP */
uint16 ADP;
/** ADO */
uint16 ADO;
/** length of data portion in datagram */
uint16 dlength;
/** interrupt, currently unused */
uint16 irpt;
} ec_comt;
PACKED_END
/** EtherCAT header size */
#define EC_HEADERSIZE sizeof(ec_comt)
/** size of ec_comt.elength item in EtherCAT header */
#define EC_ELENGTHSIZE sizeof(uint16)
/** offset position of command in EtherCAT header */
#define EC_CMDOFFSET EC_ELENGTHSIZE
/** size of workcounter item in EtherCAT datagram */
#define EC_WKCSIZE sizeof(uint16)
/** definition of datagram follows bit in ec_comt.dlength */
#define EC_DATAGRAMFOLLOWS (1 << 15)
/** Possible error codes returned. */
typedef enum
{
/** No error */
EC_ERR_OK = 0,
/** Library already initialized. */
EC_ERR_ALREADY_INITIALIZED,
/** Library not initialized. */
EC_ERR_NOT_INITIALIZED,
/** Timeout occurred during execution of the function. */
EC_ERR_TIMEOUT,
/** No slaves were found. */
EC_ERR_NO_SLAVES,
/** Function failed. */
EC_ERR_NOK
} ec_err;
/** Possible EtherCAT slave states */
typedef enum
{
/** No valid state. */
EC_STATE_NONE = 0x00,
/** Init state*/
EC_STATE_INIT = 0x01,
/** Pre-operational. */
EC_STATE_PRE_OP = 0x02,
/** Boot state*/
EC_STATE_BOOT = 0x03,
/** Safe-operational. */
EC_STATE_SAFE_OP = 0x04,
/** Operational */
EC_STATE_OPERATIONAL = 0x08,
/** Error or ACK error */
EC_STATE_ACK = 0x10,
EC_STATE_ERROR = 0x10
} ec_state;
/** Possible buffer states */
typedef enum
{
/** Empty */
EC_BUF_EMPTY = 0x00,
/** Allocated, but not filled */
EC_BUF_ALLOC = 0x01,
/** Transmitted */
EC_BUF_TX = 0x02,
/** Received, but not consumed */
EC_BUF_RCVD = 0x03,
/** Cycle completed */
EC_BUF_COMPLETE = 0x04
} ec_bufstate;
/** Ethercat data types */
typedef enum
{
ECT_BOOLEAN = 0x0001,
ECT_INTEGER8 = 0x0002,
ECT_INTEGER16 = 0x0003,
ECT_INTEGER32 = 0x0004,
ECT_UNSIGNED8 = 0x0005,
ECT_UNSIGNED16 = 0x0006,
ECT_UNSIGNED32 = 0x0007,
ECT_REAL32 = 0x0008,
ECT_VISIBLE_STRING = 0x0009,
ECT_OCTET_STRING = 0x000A,
ECT_UNICODE_STRING = 0x000B,
ECT_TIME_OF_DAY = 0x000C,
ECT_TIME_DIFFERENCE = 0x000D,
ECT_DOMAIN = 0x000F,
ECT_INTEGER24 = 0x0010,
ECT_REAL64 = 0x0011,
ECT_INTEGER64 = 0x0015,
ECT_UNSIGNED24 = 0x0016,
ECT_UNSIGNED64 = 0x001B,
ECT_BIT1 = 0x0030,
ECT_BIT2 = 0x0031,
ECT_BIT3 = 0x0032,
ECT_BIT4 = 0x0033,
ECT_BIT5 = 0x0034,
ECT_BIT6 = 0x0035,
ECT_BIT7 = 0x0036,
ECT_BIT8 = 0x0037
} ec_datatype;
/** Ethercat command types */
typedef enum
{
/** No operation */
EC_CMD_NOP = 0x00,
/** Auto Increment Read */
EC_CMD_APRD,
/** Auto Increment Write */
EC_CMD_APWR,
/** Auto Increment Read Write */
EC_CMD_APRW,
/** Configured Address Read */
EC_CMD_FPRD,
/** Configured Address Write */
EC_CMD_FPWR,
/** Configured Address Read Write */
EC_CMD_FPRW,
/** Broadcast Read */
EC_CMD_BRD,
/** Broadcast Write */
EC_CMD_BWR,
/** Broadcast Read Write */
EC_CMD_BRW,
/** Logical Memory Read */
EC_CMD_LRD,
/** Logical Memory Write */
EC_CMD_LWR,
/** Logical Memory Read Write */
EC_CMD_LRW,
/** Auto Increment Read Multiple Write */
EC_CMD_ARMW,
/** Configured Read Multiple Write */
EC_CMD_FRMW
/** Reserved */
} ec_cmdtype;
/** Ethercat EEprom command types */
typedef enum
{
/** No operation */
EC_ECMD_NOP = 0x0000,
/** Read */
EC_ECMD_READ = 0x0100,
/** Write */
EC_ECMD_WRITE = 0x0201,
/** Reload */
EC_ECMD_RELOAD = 0x0300
} ec_ecmdtype;
/** EEprom state machine read size */
#define EC_ESTAT_R64 0x0040
/** EEprom state machine busy flag */
#define EC_ESTAT_BUSY 0x8000
/** EEprom state machine error flag mask */
#define EC_ESTAT_EMASK 0x7800
/** EEprom state machine error acknowledge */
#define EC_ESTAT_NACK 0x2000
/* Ethercat SSI (Slave Information Interface) */
/** Start address SII sections in Eeprom */
#define ECT_SII_START 0x0040
enum
{
/** SII category strings */
ECT_SII_STRING = 10,
/** SII category general */
ECT_SII_GENERAL = 30,
/** SII category FMMU */
ECT_SII_FMMU = 40,
/** SII category SM */
ECT_SII_SM = 41,
/** SII category PDO */
ECT_SII_PDO = 50
};
/** Item offsets in SII general section */
enum
{
ECT_SII_MANUF = 0x0008,
ECT_SII_ID = 0x000a,
ECT_SII_REV = 0x000c,
ECT_SII_SN = 0x000e,
ECT_SII_BOOTRXMBX = 0x0014,
ECT_SII_BOOTTXMBX = 0x0016,
ECT_SII_MBXSIZE = 0x0019,
ECT_SII_TXMBXADR = 0x001a,
ECT_SII_RXMBXADR = 0x0018,
ECT_SII_MBXPROTO = 0x001c
};
/** Mailbox types definitions */
enum
{
/** Error mailbox type */
ECT_MBXT_ERR = 0x00,
/** ADS over EtherCAT mailbox type */
ECT_MBXT_AOE,
/** Ethernet over EtherCAT mailbox type */
ECT_MBXT_EOE,
/** CANopen over EtherCAT mailbox type */
ECT_MBXT_COE,
/** File over EtherCAT mailbox type */
ECT_MBXT_FOE,
/** Servo over EtherCAT mailbox type */
ECT_MBXT_SOE,
/** Vendor over EtherCAT mailbox type */
ECT_MBXT_VOE = 0x0f
};
/** CoE mailbox types */
enum
{
ECT_COES_EMERGENCY = 0x01,
ECT_COES_SDOREQ,
ECT_COES_SDORES,
ECT_COES_TXPDO,
ECT_COES_RXPDO,
ECT_COES_TXPDO_RR,
ECT_COES_RXPDO_RR,
ECT_COES_SDOINFO
};
/** CoE SDO commands */
enum
{
ECT_SDO_DOWN_INIT = 0x21,
ECT_SDO_DOWN_EXP = 0x23,
ECT_SDO_DOWN_INIT_CA = 0x31,
ECT_SDO_UP_REQ = 0x40,
ECT_SDO_UP_REQ_CA = 0x50,
ECT_SDO_SEG_UP_REQ = 0x60,
ECT_SDO_ABORT = 0x80
};
/** CoE Object Description commands */
enum
{
ECT_GET_ODLIST_REQ = 0x01,
ECT_GET_ODLIST_RES = 0x02,
ECT_GET_OD_REQ = 0x03,
ECT_GET_OD_RES = 0x04,
ECT_GET_OE_REQ = 0x05,
ECT_GET_OE_RES = 0x06,
ECT_SDOINFO_ERROR = 0x07
};
/** FoE opcodes */
enum
{
ECT_FOE_READ = 0x01,
ECT_FOE_WRITE,
ECT_FOE_DATA,
ECT_FOE_ACK,
ECT_FOE_ERROR,
ECT_FOE_BUSY
};
/** SoE opcodes */
enum
{
ECT_SOE_READREQ = 0x01,
ECT_SOE_READRES,
ECT_SOE_WRITEREQ,
ECT_SOE_WRITERES,
ECT_SOE_NOTIFICATION,
ECT_SOE_EMERGENCY
};
/** Ethercat registers */
enum
{
ECT_REG_TYPE = 0x0000,
ECT_REG_PORTDES = 0x0007,
ECT_REG_ESCSUP = 0x0008,
ECT_REG_STADR = 0x0010,
ECT_REG_ALIAS = 0x0012,
ECT_REG_DLCTL = 0x0100,
ECT_REG_DLPORT = 0x0101,
ECT_REG_DLALIAS = 0x0103,
ECT_REG_DLSTAT = 0x0110,
ECT_REG_ALCTL = 0x0120,
ECT_REG_ALSTAT = 0x0130,
ECT_REG_ALSTATCODE = 0x0134,
ECT_REG_PDICTL = 0x0140,
ECT_REG_IRQMASK = 0x0200,
ECT_REG_RXERR = 0x0300,
ECT_REG_FRXERR = 0x0308,
ECT_REG_EPUECNT = 0x030C,
ECT_REG_PECNT = 0x030D,
ECT_REG_PECODE = 0x030E,
ECT_REG_LLCNT = 0x0310,
ECT_REG_WDCNT = 0x0442,
ECT_REG_EEPCFG = 0x0500,
ECT_REG_EEPCTL = 0x0502,
ECT_REG_EEPSTAT = 0x0502,
ECT_REG_EEPADR = 0x0504,
ECT_REG_EEPDAT = 0x0508,
ECT_REG_FMMU0 = 0x0600,
ECT_REG_FMMU1 = ECT_REG_FMMU0 + 0x10,
ECT_REG_FMMU2 = ECT_REG_FMMU1 + 0x10,
ECT_REG_FMMU3 = ECT_REG_FMMU2 + 0x10,
ECT_REG_SM0 = 0x0800,
ECT_REG_SM1 = ECT_REG_SM0 + 0x08,
ECT_REG_SM2 = ECT_REG_SM1 + 0x08,
ECT_REG_SM3 = ECT_REG_SM2 + 0x08,
ECT_REG_SM0STAT = ECT_REG_SM0 + 0x05,
ECT_REG_SM1STAT = ECT_REG_SM1 + 0x05,
ECT_REG_SM1ACT = ECT_REG_SM1 + 0x06,
ECT_REG_SM1CONTR = ECT_REG_SM1 + 0x07,
ECT_REG_DCTIME0 = 0x0900,
ECT_REG_DCTIME1 = 0x0904,
ECT_REG_DCTIME2 = 0x0908,
ECT_REG_DCTIME3 = 0x090C,
ECT_REG_DCSYSTIME = 0x0910,
ECT_REG_DCSOF = 0x0918,
ECT_REG_DCSYSOFFSET = 0x0920,
ECT_REG_DCSYSDELAY = 0x0928,
ECT_REG_DCSYSDIFF = 0x092C,
ECT_REG_DCSPEEDCNT = 0x0930,
ECT_REG_DCTIMEFILT = 0x0934,
ECT_REG_DCCUC = 0x0980,
ECT_REG_DCSYNCACT = 0x0981,
ECT_REG_DCSTART0 = 0x0990,
ECT_REG_DCCYCLE0 = 0x09A0,
ECT_REG_DCCYCLE1 = 0x09A4
};
/** standard SDO Sync Manager Communication Type */
#define ECT_SDO_SMCOMMTYPE 0x1c00
/** standard SDO PDO assignment */
#define ECT_SDO_PDOASSIGN 0x1c10
/** standard SDO RxPDO assignment */
#define ECT_SDO_RXPDOASSIGN 0x1c12
/** standard SDO TxPDO assignment */
#define ECT_SDO_TXPDOASSIGN 0x1c13
/** Ethercat packet type */
#define ETH_P_ECAT 0x88A4
/** Error types */
typedef enum
{
EC_ERR_TYPE_SDO_ERROR = 0,
EC_ERR_TYPE_EMERGENCY = 1,
EC_ERR_TYPE_PACKET_ERROR = 3,
EC_ERR_TYPE_SDOINFO_ERROR = 4,
EC_ERR_TYPE_FOE_ERROR = 5,
EC_ERR_TYPE_FOE_BUF2SMALL = 6,
EC_ERR_TYPE_FOE_PACKETNUMBER = 7,
EC_ERR_TYPE_SOE_ERROR = 8,
EC_ERR_TYPE_MBX_ERROR = 9,
EC_ERR_TYPE_FOE_FILE_NOTFOUND = 10,
EC_ERR_TYPE_EOE_INVALID_RX_DATA = 11
} ec_err_type;
/** Struct to retrieve errors. */
typedef struct
{
/** Time at which the error was generated. */
ec_timet Time;
/** Signal bit, error set but not read */
boolean Signal;
/** Slave number that generated the error */
uint16 Slave;
/** CoE SDO index that generated the error */
uint16 Index;
/** CoE SDO subindex that generated the error */
uint8 SubIdx;
/** Type of error */
ec_err_type Etype;
union
{
/** General abortcode */
int32 AbortCode;
/** Specific error for Emergency mailbox */
struct
{
uint16 ErrorCode;
uint8 ErrorReg;
uint8 b1;
uint16 w1;
uint16 w2;
};
};
} ec_errort;
/** Helper macros */
/** Set the count value in the Mailbox header */
#define MBX_HDR_SET_CNT(cnt) ((uint8)((cnt) << 4))
/** Macro to make a word from 2 bytes */
#define MK_WORD(msb, lsb) ((((uint16)(msb))<<8) | (lsb))
/** Macro to get hi byte of a word */
#define HI_BYTE(w) ((w) >> 8)
/** Macro to get low byte of a word */
#define LO_BYTE(w) ((w) & 0x00ff)
/** Macro to swap hi and low byte of a word */
#define SWAP(w) ((((w)& 0xff00) >> 8) | (((w) & 0x00ff) << 8))
/** Macro to get hi word of a dword */
#define LO_WORD(l) ((l) & 0xffff)
/** Macro to get hi word of a dword */
#define HI_WORD(l) ((l) >> 16)
#define get_unaligned(ptr) \
({ __typeof__(*(ptr)) __tmp; memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
#define put_unaligned32(val, ptr) \
(memcpy((ptr), &(val), 4))
#define put_unaligned64(val, ptr) \
(memcpy((ptr), &(val), 8))
#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
#define htoes(A) (A)
#define htoel(A) (A)
#define htoell(A) (A)
#define etohs(A) (A)
#define etohl(A) (A)
#define etohll(A) (A)
#elif !defined(EC_LITTLE_ENDIAN) && defined(EC_BIG_ENDIAN)
#define htoes(A) ((((uint16)(A) & 0xff00) >> 8) | \
(((uint16)(A) & 0x00ff) << 8))
#define htoel(A) ((((uint32)(A) & 0xff000000) >> 24) | \
(((uint32)(A) & 0x00ff0000) >> 8) | \
(((uint32)(A) & 0x0000ff00) << 8) | \
(((uint32)(A) & 0x000000ff) << 24))
#define htoell(A) ((((uint64)(A) & (uint64)0xff00000000000000ULL) >> 56) | \
(((uint64)(A) & (uint64)0x00ff000000000000ULL) >> 40) | \
(((uint64)(A) & (uint64)0x0000ff0000000000ULL) >> 24) | \
(((uint64)(A) & (uint64)0x000000ff00000000ULL) >> 8) | \
(((uint64)(A) & (uint64)0x00000000ff000000ULL) << 8) | \
(((uint64)(A) & (uint64)0x0000000000ff0000ULL) << 24) | \
(((uint64)(A) & (uint64)0x000000000000ff00ULL) << 40) | \
(((uint64)(A) & (uint64)0x00000000000000ffULL) << 56))
#define etohs htoes
#define etohl htoel
#define etohll htoell
#else
#error "Must define one of EC_BIG_ENDIAN or EC_LITTLE_ENDIAN"
#endif
#ifdef __cplusplus
}
#endif
#endif /* _EC_TYPE_H */
+362
View File
@@ -0,0 +1,362 @@
/*****************************************************************************
*
* FILE NAME: ec_master.c
*
* DESCRIPTION: This is the main program module.
*
\*****************************************************************************/
#include <stdio.h>
/*****************************************************************************
*
* FUNCTION: main
*
* DESCRIPTION:
* This is the main program module.
*
\*****************************************************************************/
/** \file
* \brief Example code for Simple Open EtherCAT master
*
* Usage : slaveinfo [ifname] [-sdo] [-map]
* Ifname is NIC interface, f.e. eth0.
* Optional -sdo to display CoE object dictionary.
* Optional -map to display slave PDO mapping
*
* This shows the configured slave data.
*
* (c)Arthur Ketels 2010 - 2011
*/
#include <stdio.h>
#include <string.h>
#include <rt.h>
#include <traceapi.h>
#include "ethercat.h"
char IOmap[4096];
char IOmap2[4096];
/** Main slave data array.
* Each slave found on the network gets its own record.
* ec_slave[0] is reserved for the master. Structure gets filled
* in by the configuration function ec_config().
*/
ec_slavet ec_slave[EC_MAXSLAVE];
ec_slavet ec_slave2[EC_MAXSLAVE];
/** number of slaves found on the network */
int ec_slavecount;
int ec_slavecount2;
/** slave group structure */
ec_groupt ec_groups[EC_MAXGROUP];
ec_groupt ec_groups2[EC_MAXGROUP];
/** cache for EEPROM read functions */
static uint8 esibuf[EC_MAXEEPBUF];
static uint8 esibuf2[EC_MAXEEPBUF];
/** bitmap for filled cache buffer bytes */
static uint32 esimap[EC_MAXEEPBITMAP];
static uint32 esimap2[EC_MAXEEPBITMAP];
/** current slave for EEPROM cache buffer */
static ec_eringt ec_elist;
static ec_eringt ec_elist2;
static ec_idxstackT ec_idxstack;
static ec_idxstackT ec_idxstack2;
/** SyncManager Communication Type struct to store data of one slave */
static ec_SMcommtypet ec_SMcommtype;
static ec_SMcommtypet ec_SMcommtype2;
/** PDO assign struct to store data of one slave */
static ec_PDOassignt ec_PDOassign;
static ec_PDOassignt ec_PDOassign2;
/** PDO description struct to store data of one slave */
static ec_PDOdesct ec_PDOdesc;
static ec_PDOdesct ec_PDOdesc2;
/** buffer for EEPROM SM data */
static ec_eepromSMt ec_SM;
static ec_eepromSMt ec_SM2;
/** buffer for EEPROM FMMU data */
static ec_eepromFMMUt ec_FMMU;
static ec_eepromFMMUt ec_FMMU2;
/** Global variable TRUE if error available in error stack */
static boolean EcatError = FALSE;
static boolean EcatError2 = FALSE;
int64 ec_DCtime;
int64 ec_DCtime2;
static ecx_portt ecx_port;
static ecx_portt ecx_port2;
static ecx_contextt ctx [] = {
{
&ecx_port,
&ec_slave[0],
&ec_slavecount,
EC_MAXSLAVE,
&ec_groups[0],
EC_MAXGROUP,
&esibuf[0],
&esimap[0],
0,
&ec_elist,
&ec_idxstack,
&EcatError,
0,
0,
&ec_DCtime,
&ec_SMcommtype,
&ec_PDOassign,
&ec_PDOdesc,
&ec_SM,
&ec_FMMU
},
{
&ecx_port2,
&ec_slave2[0],
&ec_slavecount2,
EC_MAXSLAVE,
&ec_groups2[0],
EC_MAXGROUP,
&esibuf2[0],
&esimap2[0],
0,
&ec_elist2,
&ec_idxstack2,
&EcatError2,
0,
0,
&ec_DCtime2,
&ec_SMcommtype2,
&ec_PDOassign2,
&ec_PDOdesc2,
&ec_SM2,
&ec_FMMU2
}
};
void slaveinfo(char *ifname)
{
int cnt, i, j, nSM;
int ctx_count;
uint16 ssigen;
int expectedWKC[2];
int wkc_count;
int chk;
volatile int wkc[2];
boolean inOP;
printf("Starting slaveinfo\n");
/* initialise SOEM, bind socket to ifname */
if (ecx_init(&ctx[0],"ie1g1") && ecx_init(&ctx[1],"ie1g0"))
{
printf("ec_init on %s succeeded.\n",ifname);
/* find and auto-config slaves */
if ( ecx_config_init(&ctx[0],FALSE) > 0 && ecx_config_init(&ctx[1],FALSE) > 0 )
{
for (ctx_count = 0; ctx_count < 2; ctx_count++)
{
ecx_config_map_group(&ctx[ctx_count], IOmap, 0);
ecx_configdc(&ctx[ctx_count]);
while(*(ctx[ctx_count].ecaterror)) printf("%s", ecx_elist2string(&ctx[ctx_count]));
printf("%d slaves found and configured.\n",*(ctx[ctx_count].slavecount));
expectedWKC[ctx_count] = ( ctx[ctx_count].grouplist[0].outputsWKC * 2) + ctx[ctx_count].grouplist[0].inputsWKC;
printf("Calculated workcounter %d\n", expectedWKC[ctx_count]);
/* wait for all slaves to reach SAFE_OP state */
ecx_statecheck(&ctx[ctx_count],0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 3);
if (ctx[ctx_count].slavelist[ctx_count].state != EC_STATE_SAFE_OP )
{
printf("Not all slaves reached safe operational state.\n");
ecx_readstate(&ctx[ctx_count]);
for(i = 1; i <= *(ctx[ctx_count].slavecount) ; i++)
{
if(ctx[ctx_count].slavelist[i].state != EC_STATE_SAFE_OP)
{
printf("Slave %d State=%2x StatusCode=%4x : %s\n",
i, ctx[ctx_count].slavelist[i].state, ctx[ctx_count].slavelist[i].ALstatuscode, ec_ALstatuscode2string(ctx[ctx_count].slavelist[i].ALstatuscode));
}
}
}
ecx_readstate(&ctx[ctx_count]);
for( cnt = 1 ; cnt <= *(ctx[ctx_count].slavecount) ; cnt++)
{
printf("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n",
cnt, ctx[ctx_count].slavelist[cnt].name, ctx[ctx_count].slavelist[cnt].Obits, ctx[ctx_count].slavelist[cnt].Ibits,
ctx[ctx_count].slavelist[cnt].state, ctx[ctx_count].slavelist[cnt].pdelay, ctx[ctx_count].slavelist[cnt].hasdc);
if (ctx[ctx_count].slavelist[cnt].hasdc)
{
printf(" DCParentport:%d\n", ctx[ctx_count].slavelist[cnt].parentport);
}
printf(" Activeports:%d.%d.%d.%d\n", (ctx[ctx_count].slavelist[cnt].activeports & 0x01) > 0 ,
(ctx[ctx_count].slavelist[cnt].activeports & 0x02) > 0 ,
(ctx[ctx_count].slavelist[cnt].activeports & 0x04) > 0 ,
(ctx[ctx_count].slavelist[cnt].activeports & 0x08) > 0 );
printf(" Configured address: %4.4x\n", ctx[ctx_count].slavelist[cnt].configadr);
printf(" Man: %8.8x ID: %8.8x Rev: %8.8x\n", (int)ctx[ctx_count].slavelist[cnt].eep_man,
(int)ctx[ctx_count].slavelist[cnt].eep_id, (int)ctx[ctx_count].slavelist[cnt].eep_rev);
for(nSM = 0 ; nSM < EC_MAXSM ; nSM++)
{
if(ctx[ctx_count].slavelist[cnt].SM[nSM].StartAddr > 0)
printf(" SM%1d A:%4.4x L:%4d F:%8.8x Type:%d\n",nSM, ctx[ctx_count].slavelist[cnt].SM[nSM].StartAddr,
ctx[ctx_count].slavelist[cnt].SM[nSM].SMlength,(int)ctx[ctx_count].slavelist[cnt].SM[nSM].SMflags,
ctx[ctx_count].slavelist[cnt].SMtype[nSM]);
}
for(j = 0 ; j < ctx[ctx_count].slavelist[cnt].FMMUunused ; j++)
{
printf(" FMMU%1d Ls:%8.8x Ll:%4d Lsb:%d Leb:%d Ps:%4.4x Psb:%d Ty:%2.2x Act:%2.2x\n", j,
(int)ctx[ctx_count].slavelist[cnt].FMMU[j].LogStart, ctx[ctx_count].slavelist[cnt].FMMU[j].LogLength,
ctx[ctx_count].slavelist[cnt].FMMU[j].LogStartbit, ctx[ctx_count].slavelist[cnt].FMMU[j].LogEndbit,
ctx[ctx_count].slavelist[cnt].FMMU[j].PhysStart, ctx[ctx_count].slavelist[cnt].FMMU[j].PhysStartBit,
ctx[ctx_count].slavelist[cnt].FMMU[j].FMMUtype, ctx[ctx_count].slavelist[cnt].FMMU[j].FMMUactive);
}
printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n",
ctx[ctx_count].slavelist[cnt].FMMU0func, ctx[ctx_count].slavelist[cnt].FMMU1func, ctx[ctx_count].slavelist[cnt].FMMU2func,
ctx[ctx_count].slavelist[cnt].FMMU3func);
printf(" MBX length wr: %d rd: %d MBX protocols : %2.2x\n", ctx[ctx_count].slavelist[cnt].mbx_l,
ctx[ctx_count].slavelist[cnt].mbx_rl, ctx[ctx_count].slavelist[cnt].mbx_proto);
ssigen = ecx_siifind(&ctx[ctx_count], cnt, ECT_SII_GENERAL);
/* SII general section */
if (ssigen)
{
ctx[ctx_count].slavelist[cnt].CoEdetails = ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x07);
ctx[ctx_count].slavelist[cnt].FoEdetails = ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x08);
ctx[ctx_count].slavelist[cnt].EoEdetails = ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x09);
ctx[ctx_count].slavelist[cnt].SoEdetails = ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x0a);
if((ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x0d) & 0x02) > 0)
{
ctx[ctx_count].slavelist[cnt].blockLRW = 1;
ctx[ctx_count].slavelist[0].blockLRW++;
}
ctx[ctx_count].slavelist[cnt].Ebuscurrent = ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x0e);
ctx[ctx_count].slavelist[cnt].Ebuscurrent += ecx_siigetbyte(&ctx[ctx_count], cnt, ssigen + 0x0f) << 8;
ctx[ctx_count].slavelist[0].Ebuscurrent += ctx[ctx_count].slavelist[cnt].Ebuscurrent;
}
printf(" CoE details: %2.2x FoE details: %2.2x EoE details: %2.2x SoE details: %2.2x\n",
ctx[ctx_count].slavelist[cnt].CoEdetails, ctx[ctx_count].slavelist[cnt].FoEdetails, ctx[ctx_count].slavelist[cnt].EoEdetails, ctx[ctx_count].slavelist[cnt].SoEdetails);
printf(" Ebus current: %d[mA]\n only LRD/LWR:%d\n",
ctx[ctx_count].slavelist[cnt].Ebuscurrent, ctx[ctx_count].slavelist[cnt].blockLRW);
}
}
inOP = FALSE;
printf("Request operational state for all slaves\n");
expectedWKC[0] = (ctx[0].grouplist[0].outputsWKC * 2) + ctx[0].grouplist[0].inputsWKC;
expectedWKC[1] = (ctx[1].grouplist[0].outputsWKC * 2) + ctx[1].grouplist[0].inputsWKC;
printf("Calculated workcounter master 1 %d\n", expectedWKC[0]);
printf("Calculated workcounter master 2 %d\n", expectedWKC[1]);
ctx[0].slavelist[0].state = EC_STATE_OPERATIONAL;
ctx[1].slavelist[0].state = EC_STATE_OPERATIONAL;
/* send one valid process data to make outputs in slaves happy*/
ecx_send_processdata(&ctx[0]);
ecx_send_processdata(&ctx[1]);
ecx_receive_processdata(&ctx[0],EC_TIMEOUTRET);
ecx_receive_processdata(&ctx[1],EC_TIMEOUTRET);
/* request OP state for all slaves */
ecx_writestate(&ctx[0], 0);
ecx_writestate(&ctx[1], 0);
chk = 40;
/* wait for all slaves to reach OP state */
do
{
ecx_send_processdata(&ctx[0]);
ecx_send_processdata(&ctx[1]);
ecx_receive_processdata(&ctx[0],EC_TIMEOUTRET);
ecx_receive_processdata(&ctx[1],EC_TIMEOUTRET);
ecx_statecheck(&ctx[0],0, EC_STATE_OPERATIONAL, 50000);
ecx_statecheck(&ctx[1],0, EC_STATE_OPERATIONAL, 50000);
}
while (chk-- && (ctx[0].slavelist[0].state != EC_STATE_OPERATIONAL) && (ctx[1].slavelist[0].state != EC_STATE_OPERATIONAL));
*(ctx[0].slavelist[6].outputs) = 0xFF;
*(ctx[1].slavelist[1].outputs) = 0x0F;
if (ctx[0].slavelist[0].state == EC_STATE_OPERATIONAL && ctx[1].slavelist[0].state == EC_STATE_OPERATIONAL )
{
printf("Operational state reached for all slaves.\n");
/* cyclic loop */
start_RT_trace (1);
wkc_count = 0;
inOP = TRUE;
for(i = 1; i <= 10000; i++)
{
for (ctx_count = 0; ctx_count < 2; ctx_count++)
{
/* Receive processdata */
log_RT_event('R',(WORD)(1 + ctx_count * 10));
wkc[ctx_count] = ecx_receive_processdata(&ctx[ctx_count], 0);
log_RT_event('R',(WORD)(99 + ctx_count * 100));
}
for (ctx_count = 0; ctx_count < 2; ctx_count++)
{
/* Send processdata */
log_RT_event('S',(WORD)(1 + ctx_count * 10));
if (!ecx_send_processdata(&ctx[ctx_count]))
{
printf (" Frame no: %d on master %d,Not sentOK\n", i,ctx_count);
}
log_RT_event('S',(WORD)(99 + ctx_count * 100));
}
knRtSleep(1);
for (ctx_count = 0; ctx_count < 2; ctx_count++)
{
if(wkc[ctx_count] >= expectedWKC[ctx_count])
{
}
else
{
printf (" Frame no: %d on master %d , wc not wkc >= expectedWKC, %d\n",i, ctx_count, wkc[ctx_count]);
}
}
}
stop_RT_trace ();
inOP = FALSE;
}
}
else
{
printf("No slaves found!\n");
}
printf("End slaveinfo, close socket\n");
/* stop SOEM, close socket */
ecx_close(&ctx[0]);
ecx_close(&ctx[1]);
}
else
{
printf("No socket connection on %s\nExcecute as root\n",ifname);
}
}
void main(int argc, char* argv[])
{
printf("SOEM (Simple Open EtherCAT Master)\nSlaveinfo\n");
if (argc > 1)
{
/* start slaveinfo */
slaveinfo(argv[1]);
}
else
{
printf("Usage: slaveinfo ifname [options]\nifname = eth0 for example\nOptions :\n -sdo : print SDO info\n -map : print mapping\n");
}
printf("End program\n");
}

Some files were not shown because too many files have changed in this diff Show More