feat(motor): 自适应 PDO 窗口与通道活动指示,新增原生 SOEM 时序诊断
后端 PDO 循环改为按在线槽收缩状态窗口,稀疏总线下单槽每周期都能刷新, 并补上尾部槽的发现扫描游标;新增自旋节拍源(阈值/显式覆盖)、P 核识别 与线程级钉核,自旋期间释放 GIL 以免 HTTP 线程被饿死;启动时先关停旧实例, 避免两个进程争用 Npcap 适配器。 记录每拍完整时间线、GC stop-the-world 事件与网格跳拍数,经 /api/motor/dbg 导出用于归因固定频率的超时;配
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,148 @@
|
||||
Microsoft (R) COFF/PE Dumper Version 14.43.34809.0
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
|
||||
Dump of file C:\Windows\System32\Npcap\wpcap.dll
|
||||
|
||||
File Type: DLL
|
||||
|
||||
Section contains the following exports for wpcap.dll
|
||||
|
||||
00000000 characteristics
|
||||
FFFFFFFF time date stamp
|
||||
0.00 version
|
||||
1 ordinal base
|
||||
119 number of functions
|
||||
119 number of names
|
||||
|
||||
ordinal hint RVA name
|
||||
|
||||
1 0 00001010 bpf_dump
|
||||
2 1 00001170 bpf_filter
|
||||
3 2 00001C40 bpf_image
|
||||
4 3 00001190 bpf_validate
|
||||
5 4 00073000 eproto_db
|
||||
6 5 00018850 pcap_activate
|
||||
7 6 000189C0 pcap_breakloop
|
||||
8 7 000189E0 pcap_bufsize
|
||||
9 8 00018A00 pcap_can_set_rfmon
|
||||
10 9 00018A40 pcap_close
|
||||
11 A 00012DD0 pcap_compile
|
||||
12 B 000131B0 pcap_compile_nopcap
|
||||
13 C 00018A70 pcap_create
|
||||
14 D 00018BC0 pcap_createsrcstr
|
||||
15 E 00018BF0 pcap_datalink
|
||||
16 F 00018C10 pcap_datalink_ext
|
||||
17 10 00018C30 pcap_datalink_name_to_val
|
||||
18 11 00018D10 pcap_datalink_val_to_description
|
||||
19 12 00018D60 pcap_datalink_val_to_description_or_dlt
|
||||
20 13 00018E00 pcap_datalink_val_to_name
|
||||
21 14 00018E50 pcap_dispatch
|
||||
22 15 0001DED0 pcap_dump
|
||||
23 16 0001DF60 pcap_dump_close
|
||||
24 17 0001DF90 pcap_dump_file
|
||||
25 18 0001DFA0 pcap_dump_flush
|
||||
26 19 0001DFC0 pcap_dump_ftell
|
||||
27 1A 0001DFD0 pcap_dump_ftell64
|
||||
28 1B 0001DFE0 pcap_dump_hopen
|
||||
29 1C 0001E0F0 pcap_dump_open
|
||||
30 1D 0001E240 pcap_dump_open_append
|
||||
31 1E 000136B0 pcap_ether_aton
|
||||
32 1F 00013720 pcap_ether_hostton
|
||||
33 20 00018E70 pcap_file
|
||||
34 21 00018E80 pcap_fileno
|
||||
35 22 00018EA0 pcap_findalldevs
|
||||
36 23 000220E0 pcap_findalldevs_ex
|
||||
37 24 00019070 pcap_free_datalinks
|
||||
38 25 00019070 pcap_free_tstamp_types
|
||||
39 26 00019080 pcap_freealldevs
|
||||
40 27 00013230 pcap_freecode
|
||||
41 28 00019140 pcap_get_airpcap_handle
|
||||
42 29 000191D0 pcap_get_tstamp_precision
|
||||
43 2A 000191E0 pcap_geterr
|
||||
44 2B 000191F0 pcap_getevent
|
||||
45 2C 00019270 pcap_getnonblock
|
||||
46 2D 0001C130 pcap_hopen_offline
|
||||
47 2E 0001C140 pcap_hopen_offline_with_tstamp_precision
|
||||
48 2F 00019340 pcap_init
|
||||
49 30 000193D0 pcap_inject
|
||||
50 31 00019490 pcap_is_swapped
|
||||
51 32 0001FF90 pcap_lib_version
|
||||
52 33 000194B0 pcap_list_datalinks
|
||||
53 34 00019580 pcap_list_tstamp_types
|
||||
54 35 00019640 pcap_live_dump
|
||||
55 36 00019690 pcap_live_dump_ended
|
||||
56 37 00020050 pcap_lookupdev
|
||||
57 38 000202B0 pcap_lookupnet
|
||||
58 39 000196A0 pcap_loop
|
||||
59 3A 00019720 pcap_major_version
|
||||
60 3B 00019740 pcap_minor_version
|
||||
61 3C 00013800 pcap_nametoaddr
|
||||
62 3D 00013870 pcap_nametoaddrinfo
|
||||
63 3E 000138C0 pcap_nametoeproto
|
||||
64 3F 00013920 pcap_nametollc
|
||||
65 40 000136A0 pcap_nametonetaddr
|
||||
66 41 00013980 pcap_nametoport
|
||||
67 42 00013B40 pcap_nametoportrange
|
||||
68 43 00013BF0 pcap_nametoproto
|
||||
69 44 00019760 pcap_next
|
||||
70 45 00002360 pcap_next_etherent
|
||||
71 46 000197B0 pcap_next_ex
|
||||
72 47 00019810 pcap_offline_filter
|
||||
73 48 00019830 pcap_oid_get_request
|
||||
74 49 00019880 pcap_oid_set_request
|
||||
75 4A 000225D0 pcap_open
|
||||
76 4B 000198D0 pcap_open_dead
|
||||
77 4C 000198E0 pcap_open_dead_with_tstamp_precision
|
||||
78 4D 00019B10 pcap_open_live
|
||||
79 4E 0001C200 pcap_open_offline
|
||||
80 4F 0001C210 pcap_open_offline_with_tstamp_precision
|
||||
81 50 0001A3D0 pcap_parsesrcstr
|
||||
82 51 0001A6D0 pcap_perror
|
||||
83 52 00023F70 pcap_remoteact_accept
|
||||
84 53 00023FA0 pcap_remoteact_accept_ex
|
||||
85 54 00024350 pcap_remoteact_cleanup
|
||||
86 55 00024380 pcap_remoteact_close
|
||||
87 56 00024510 pcap_remoteact_list
|
||||
88 57 0001A740 pcap_sendpacket
|
||||
89 58 0001A7A0 pcap_sendqueue_alloc
|
||||
90 59 0001A800 pcap_sendqueue_destroy
|
||||
91 5A 0001A820 pcap_sendqueue_queue
|
||||
92 5B 0001A890 pcap_sendqueue_transmit
|
||||
93 5C 0001A8D0 pcap_set_buffer_size
|
||||
94 5D 0001A910 pcap_set_datalink
|
||||
95 5E 0001AA80 pcap_set_immediate_mode
|
||||
96 5F 0001AAC0 pcap_set_promisc
|
||||
97 60 0001AB00 pcap_set_rfmon
|
||||
98 61 0001AB40 pcap_set_snaplen
|
||||
99 62 0001AB80 pcap_set_timeout
|
||||
100 63 0001ABC0 pcap_set_tstamp_precision
|
||||
101 64 0001AC50 pcap_set_tstamp_type
|
||||
102 65 0001ACE0 pcap_setbuff
|
||||
103 66 0001AD20 pcap_setdirection
|
||||
104 67 0001ADD0 pcap_setfilter
|
||||
105 68 0001AE10 pcap_setmintocopy
|
||||
106 69 0001AE50 pcap_setmode
|
||||
107 6A 0001AE90 pcap_setnonblock
|
||||
108 6B 00022850 pcap_setsampling
|
||||
109 6C 0001AF00 pcap_setuserbuffer
|
||||
110 6D 0001AF40 pcap_snapshot
|
||||
111 6E 0001AF60 pcap_stats
|
||||
112 6F 0001AFA0 pcap_stats_ex
|
||||
113 70 0001AFE0 pcap_statustostr
|
||||
114 71 0001B150 pcap_strerror
|
||||
115 72 0001B1B0 pcap_tstamp_type_name_to_val
|
||||
116 73 0001B290 pcap_tstamp_type_val_to_description
|
||||
117 74 0001B2E0 pcap_tstamp_type_val_to_name
|
||||
118 75 00073120 pcap_version
|
||||
119 76 000136A0 pcap_wsockinit
|
||||
|
||||
Summary
|
||||
|
||||
A000 .data
|
||||
1000 .fptable
|
||||
4000 .pdata
|
||||
26000 .rdata
|
||||
1000 .reloc
|
||||
1000 .rsrc
|
||||
4C000 .text
|
||||
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul
|
||||
cd /d G:\Robot_v2\DCU\tools\ecat
|
||||
cl /nologo /c /O2 /W3 /utf-8 /D_CRT_SECURE_NO_WARNINGS /I soem-src\soem /I soem-src\osal /I soem-src\osal\win32 /I soem-src\oshw\win32 /I soem-src\oshw\win32\wpcap\Include /Fobuild_native\ soem-src\oshw\win32\nicdrv.c 2>&1
|
||||
@@ -0,0 +1,121 @@
|
||||
LIBRARY wpcap
|
||||
EXPORTS
|
||||
bpf_dump
|
||||
bpf_filter
|
||||
bpf_image
|
||||
bpf_validate
|
||||
eproto_db
|
||||
pcap_activate
|
||||
pcap_breakloop
|
||||
pcap_bufsize
|
||||
pcap_can_set_rfmon
|
||||
pcap_close
|
||||
pcap_compile
|
||||
pcap_compile_nopcap
|
||||
pcap_create
|
||||
pcap_createsrcstr
|
||||
pcap_datalink
|
||||
pcap_datalink_ext
|
||||
pcap_datalink_name_to_val
|
||||
pcap_datalink_val_to_description
|
||||
pcap_datalink_val_to_description_or_dlt
|
||||
pcap_datalink_val_to_name
|
||||
pcap_dispatch
|
||||
pcap_dump
|
||||
pcap_dump_close
|
||||
pcap_dump_file
|
||||
pcap_dump_flush
|
||||
pcap_dump_ftell
|
||||
pcap_dump_ftell64
|
||||
pcap_dump_hopen
|
||||
pcap_dump_open
|
||||
pcap_dump_open_append
|
||||
pcap_ether_aton
|
||||
pcap_ether_hostton
|
||||
pcap_file
|
||||
pcap_fileno
|
||||
pcap_findalldevs
|
||||
pcap_findalldevs_ex
|
||||
pcap_free_datalinks
|
||||
pcap_free_tstamp_types
|
||||
pcap_freealldevs
|
||||
pcap_freecode
|
||||
pcap_get_airpcap_handle
|
||||
pcap_get_tstamp_precision
|
||||
pcap_geterr
|
||||
pcap_getevent
|
||||
pcap_getnonblock
|
||||
pcap_hopen_offline
|
||||
pcap_hopen_offline_with_tstamp_precision
|
||||
pcap_init
|
||||
pcap_inject
|
||||
pcap_is_swapped
|
||||
pcap_lib_version
|
||||
pcap_list_datalinks
|
||||
pcap_list_tstamp_types
|
||||
pcap_live_dump
|
||||
pcap_live_dump_ended
|
||||
pcap_lookupdev
|
||||
pcap_lookupnet
|
||||
pcap_loop
|
||||
pcap_major_version
|
||||
pcap_minor_version
|
||||
pcap_nametoaddr
|
||||
pcap_nametoaddrinfo
|
||||
pcap_nametoeproto
|
||||
pcap_nametollc
|
||||
pcap_nametonetaddr
|
||||
pcap_nametoport
|
||||
pcap_nametoportrange
|
||||
pcap_nametoproto
|
||||
pcap_next
|
||||
pcap_next_etherent
|
||||
pcap_next_ex
|
||||
pcap_offline_filter
|
||||
pcap_oid_get_request
|
||||
pcap_oid_set_request
|
||||
pcap_open
|
||||
pcap_open_dead
|
||||
pcap_open_dead_with_tstamp_precision
|
||||
pcap_open_live
|
||||
pcap_open_offline
|
||||
pcap_open_offline_with_tstamp_precision
|
||||
pcap_parsesrcstr
|
||||
pcap_perror
|
||||
pcap_remoteact_accept
|
||||
pcap_remoteact_accept_ex
|
||||
pcap_remoteact_cleanup
|
||||
pcap_remoteact_close
|
||||
pcap_remoteact_list
|
||||
pcap_sendpacket
|
||||
pcap_sendqueue_alloc
|
||||
pcap_sendqueue_destroy
|
||||
pcap_sendqueue_queue
|
||||
pcap_sendqueue_transmit
|
||||
pcap_set_buffer_size
|
||||
pcap_set_datalink
|
||||
pcap_set_immediate_mode
|
||||
pcap_set_promisc
|
||||
pcap_set_rfmon
|
||||
pcap_set_snaplen
|
||||
pcap_set_timeout
|
||||
pcap_set_tstamp_precision
|
||||
pcap_set_tstamp_type
|
||||
pcap_setbuff
|
||||
pcap_setdirection
|
||||
pcap_setfilter
|
||||
pcap_setmintocopy
|
||||
pcap_setmode
|
||||
pcap_setnonblock
|
||||
pcap_setsampling
|
||||
pcap_setuserbuffer
|
||||
pcap_snapshot
|
||||
pcap_stats
|
||||
pcap_stats_ex
|
||||
pcap_statustostr
|
||||
pcap_strerror
|
||||
pcap_tstamp_type_name_to_val
|
||||
pcap_tstamp_type_val_to_description
|
||||
pcap_tstamp_type_val_to_name
|
||||
pcap_version
|
||||
pcap_wsockinit
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""归因"某一拍为什么超时":把 PDO 时间线与 GC / HTTP 事件时间线做交集。
|
||||
|
||||
背景:250us 和 200us 档的达成率都是 96~97%,几乎不随档位变化 —— 丢拍不像
|
||||
"总线差几十 us"这种比例性原因,更像固定频率的事件(GC 停止世界、HTTP 线程
|
||||
持 GIL 序列化快照、系统杂活)一撞就是一整个时隙。本脚本从 /api/motor/dbg
|
||||
拉三条原始时间线,对每一个超时拍给出责任归属。
|
||||
|
||||
用法(目标服务必须已在跑,本脚本自己不会拉服务):
|
||||
python cadence_diag.py --period 0.25 --sample 6
|
||||
python cadence_diag.py --period 0.2 --gc off # A/B:关掉 GC 看超时拍是否消失
|
||||
python cadence_diag.py --period 0.2 --gc on --hammer 2 # 再叠 2 路 /motor/state 轮询
|
||||
|
||||
判读:
|
||||
超时拍 = loop_end-t0(work) 超过设定周期的拍;它们正是网格跳掉的拍。
|
||||
归因窗口 = 上一拍 loop_end → 本拍 loop_end;GC/HTTP 事件落在窗口内即认领。
|
||||
"裸超时"占大头才说明是内核/中断层面的抢占,而不是进程内流程问题。
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import threading
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
BASE_DEFAULT = "http://127.0.0.1:8090"
|
||||
|
||||
|
||||
def api(base, path, req):
|
||||
r = urllib.request.Request(base + "/api/" + path,
|
||||
data=json.dumps(req).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(r, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def hammer_worker(base, stop, path="motor/state"):
|
||||
while not stop.is_set():
|
||||
try:
|
||||
api(base, path, {})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def classify(beats, gc_ev, http_ev, period_us):
|
||||
"""给每个超时拍贴标签。返回 (late列表, 计数统计)。beats: 按 t0 排序的样本。"""
|
||||
counts = {"GC": 0, "HTTP": 0, "GC+HTTP": 0, "裸超时": 0}
|
||||
late = []
|
||||
for i in range(1, len(beats)):
|
||||
t0, ts, tm, tr, te, k = beats[i]
|
||||
work = (te - t0) * 1e6
|
||||
if work <= period_us:
|
||||
continue
|
||||
w0, w1 = beats[i - 1][4], te # 上一拍结束 → 本拍结束
|
||||
g = [e for e in gc_ev if w0 <= e[0] <= w1 and e[1] > 20.0]
|
||||
h = [x for x in http_ev if x[0] < w1 and x[1] > w0 and (x[1] - x[0]) * 1e6 > 20.0]
|
||||
if g and h:
|
||||
tag = "GC+HTTP"
|
||||
elif g:
|
||||
tag = "GC(gen%d %.0fus)" % (g[-1][2], g[-1][1])
|
||||
counts["GC"] += 1
|
||||
elif h:
|
||||
tag = "HTTP(%s %.0fus)" % (h[-1][2].replace("/api/", ""), (h[-1][1] - h[-1][0]) * 1e6)
|
||||
counts["HTTP"] += 1
|
||||
else:
|
||||
tag = "裸超时"
|
||||
counts["裸超时"] += 1
|
||||
if g and h:
|
||||
counts["GC+HTTP"] += 1
|
||||
late.append((work, k, tag, (t0 - beats[i - 1][4]) * 1e6))
|
||||
return late, counts
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--base", default=BASE_DEFAULT)
|
||||
ap.add_argument("--period", type=float, required=True, help="周期 ms(0.25=250us)")
|
||||
ap.add_argument("--sample", type=float, default=6.0, help="设周期后预热秒数,之后才取窗口")
|
||||
ap.add_argument("--gc", choices=["on", "off", "keep"], default="keep")
|
||||
ap.add_argument("--hammer", type=int, default=0, help="叠加 N 路 /motor/state 轮询线程")
|
||||
args = ap.parse_args()
|
||||
per_us = args.period * 1000.0
|
||||
|
||||
if args.gc != "keep":
|
||||
r = api(args.base, "motor/dbg", {"action": "gc", "enable": args.gc == "on"})
|
||||
print(f"GC → {r.get('gc_enabled')}")
|
||||
|
||||
r = api(args.base, "motor/period", {"period_ms": args.period})
|
||||
print("period:", r.get("log"))
|
||||
if r.get("spin") is False and args.period < 1:
|
||||
print("!! 自旋没生效,这组数据没有意义")
|
||||
return 1
|
||||
|
||||
stop = threading.Event()
|
||||
threads = [threading.Thread(target=hammer_worker, args=(args.base, stop), daemon=True)
|
||||
for _ in range(args.hammer)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
try:
|
||||
time.sleep(args.sample)
|
||||
d = api(args.base, "motor/dbg", {})
|
||||
finally:
|
||||
stop.set()
|
||||
for t in threads:
|
||||
t.join(timeout=1)
|
||||
|
||||
beats = sorted(d["cyc"])[:-50] # 尾部几十拍被 dump 自身的 GIL 占用污染,丢弃
|
||||
if len(beats) < 100:
|
||||
print("样本不足", len(beats))
|
||||
return 1
|
||||
iv = [(beats[i][0] - beats[i - 1][0]) * 1e6 for i in range(1, len(beats))]
|
||||
hz = 1e6 / statistics.fmean(iv)
|
||||
work = sorted((e - t) * 1e6 for t, _, _, _, e, _ in beats)
|
||||
print(f"\n窗口 {len(beats)} 拍 ≈ {sum(iv) / 1e6:.2f}s | 周期 {per_us:.0f}us | "
|
||||
f"实测 {hz:.0f}Hz (达成 {hz / (1e6 / per_us) * 100:.1f}%) | "
|
||||
f"跳拍合计 {sum(k for *_, k in beats)}")
|
||||
gc_in_win = [e[2] for e in d["gc_events"] if beats[0][0] <= e[0] <= beats[-1][4]]
|
||||
print(f"work p50/p90/max = {work[len(work) // 2]:.0f}/{work[int(len(work) * .9)]:.0f}/"
|
||||
f"{work[-1]:.0f}us | 窗口内 GC g0/g1/g2 = "
|
||||
f"{gc_in_win.count(0)}/{gc_in_win.count(1)}/{gc_in_win.count(2)}")
|
||||
|
||||
# 事件按窗口时间过滤,再交集
|
||||
w0, w1 = beats[0][0], beats[-1][4]
|
||||
gc_ev = [e for e in d["gc_events"] if w0 <= e[0] <= w1]
|
||||
http_ev = [x for x in d["http"] if x[1] >= w0 and x[0] <= w1]
|
||||
late, counts = classify(beats, gc_ev, http_ev, per_us)
|
||||
n_late = len(late)
|
||||
print(f"超时拍 {n_late}/{len(beats)} = {n_late / len(beats) * 100:.2f}% | "
|
||||
+ " | ".join(f"{k}: {v}" for k, v in counts.items()))
|
||||
if late:
|
||||
lateness = sorted(x[0] for x in late)
|
||||
print(f"超时拍的 work: 中位 {statistics.median(lateness):.0f}us, "
|
||||
f"p90 {lateness[int(len(lateness) * .9)]:.0f}us, max {lateness[-1]:.0f}us")
|
||||
print("最狠的 8 拍:")
|
||||
for work_us, k, tag, gap in sorted(late, reverse=True)[:8]:
|
||||
print(f" work={work_us:7.0f}us 跳{k}拍 上游空闲{gap:6.0f}us {tag}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+397
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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())
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#include "ee_x86_64_tsc.h"
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
#define NSECS_PER_SEC 1000000000
|
||||
|
||||
uint64_t osEE_x86_64_tsc_read(void);
|
||||
|
||||
void ee_usleep(uint32 usec);
|
||||
|
||||
inline int osal_usleep (uint32 usec)
|
||||
{
|
||||
ee_usleep(usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
uint64_t time = osEE_x86_64_tsc_read();
|
||||
tv->tv_sec = time/NSECS_PER_SEC;
|
||||
tv->tv_sec += 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start */
|
||||
tv->tv_usec = (time%NSECS_PER_SEC)/1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet ret;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
ret.sec = current_time.tv_sec;
|
||||
ret.usec = current_time.tv_usec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + USECS_PER_SEC - start->usec;
|
||||
} else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert *self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert *self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
/* OSEE_PRINT("current: %d:%d -- expire: %d:%d -- result: %d\n", */
|
||||
/* current_time.tv_sec, */
|
||||
/* current_time.tv_usec, */
|
||||
/* stop_time.tv_sec, */
|
||||
/* stop_time.tv_usec, */
|
||||
/* is_not_yet_expired); */
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdlib.h>
|
||||
#include <ee.h>
|
||||
|
||||
// define if debug print is needed
|
||||
#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT OSEE_PRINT
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||
void *osal_malloc(size_t size);
|
||||
void osal_free(void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <rt.h>
|
||||
#include <sys/time.h>
|
||||
#include <osal.h>
|
||||
|
||||
static int64_t sysfrequency;
|
||||
static double qpc2usec;
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_gettimeofday (struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
return gettimeofday (tv, tz);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
int osal_usleep(uint32 usec)
|
||||
{
|
||||
RtSleepEx (usec / 1000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mutex is not needed when running single threaded */
|
||||
|
||||
void osal_mtx_lock(osal_mutex_t * mtx)
|
||||
{
|
||||
/* RtWaitForSingleObject((HANDLE)mtx, INFINITE); */
|
||||
}
|
||||
|
||||
void osal_mtx_unlock(osal_mutex_t * mtx)
|
||||
{
|
||||
/* RtReleaseMutex((HANDLE)mtx); */
|
||||
}
|
||||
|
||||
int osal_mtx_lock_timeout(osal_mutex_t * mtx, uint32_t time_ms)
|
||||
{
|
||||
/* return RtWaitForSingleObject((HANDLE)mtx, time_ms); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
osal_mutex_t * osal_mtx_create(void)
|
||||
{
|
||||
/* return (void*)RtCreateMutex(NULL, FALSE, NULL); */
|
||||
return (void *)0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#ifdef _MSC_VER
|
||||
#define PACKED_BEGIN __pragma(pack(push, 1))
|
||||
#define PACKED
|
||||
#define PACKED_END __pragma(pack(pop))
|
||||
#elif defined(__GNUC__)
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE RTHANDLE
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timespec current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_nsec / 1000;
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns time from some unspecified moment in past,
|
||||
* strictly increasing, used for time intervals measurement. */
|
||||
static void osal_getrelativetime(struct timeval *tv)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_getrelativetime(&start_time);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_getrelativetime(¤t_time);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_
|
||||
#define _osal_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "osal_defs.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* General types */
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
typedef uint8_t boolean;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef int64_t int64;
|
||||
typedef uint64_t uint64;
|
||||
typedef float float32;
|
||||
typedef double float64;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 sec; /*< Seconds elapsed since the Epoch (Jan 1, 1970) */
|
||||
uint32 usec; /*< Microseconds elapsed since last second boundary */
|
||||
} ec_timet;
|
||||
|
||||
typedef struct osal_timer
|
||||
{
|
||||
ec_timet stop_time;
|
||||
} osal_timert;
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_us);
|
||||
boolean osal_timer_is_expired(osal_timert * self);
|
||||
int osal_usleep(uint32 usec);
|
||||
ec_timet osal_current_time(void);
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff);
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param);
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <osal.h>
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
/* usleep is deprecated, use nanosleep instead */
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime to prevent possible live-lock.
|
||||
* Gettimeofday uses CLOCK_REALTIME that can get NTP timeadjust.
|
||||
* If this function preempts timeadjust and it uses vpage it live-locks.
|
||||
* Also when using XENOMAI, only clock_gettime is RT safe */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
pthread_attr_t attr;
|
||||
struct sched_param schparam;
|
||||
pthread_t *threadp;
|
||||
|
||||
threadp = thandle;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, stacksize);
|
||||
ret = pthread_create(threadp, &attr, func, param);
|
||||
pthread_attr_destroy(&attr);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
memset(&schparam, 0, sizeof(schparam));
|
||||
schparam.sched_priority = 40;
|
||||
ret = pthread_setschedparam(*threadp, SCHED_FIFO, &schparam);
|
||||
if(ret < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#define OSAL_THREAD_HANDLE pthread_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <osal.h>
|
||||
#include <kern.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <config.h>
|
||||
|
||||
#define timercmp(a, b, CMP) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? \
|
||||
((a)->tv_usec CMP (b)->tv_usec) : \
|
||||
((a)->tv_sec CMP (b)->tv_sec))
|
||||
#define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
#define USECS_PER_TICK (USECS_PER_SEC / CFG_TICKS_PER_SECOND)
|
||||
|
||||
|
||||
/* Workaround for rt-labs defect 776.
|
||||
* Default implementation of udelay() didn't work correctly when tick was
|
||||
* shorter than one millisecond.
|
||||
*/
|
||||
void udelay (uint32_t us)
|
||||
{
|
||||
tick_t ticks = (us / USECS_PER_TICK) + 1;
|
||||
task_delay (ticks);
|
||||
}
|
||||
|
||||
int gettimeofday(struct timeval *tp, void *tzp)
|
||||
{
|
||||
tick_t tick = tick_get();
|
||||
tick_t ticks_left;
|
||||
|
||||
ASSERT (tp != NULL);
|
||||
|
||||
tp->tv_sec = tick / CFG_TICKS_PER_SECOND;
|
||||
|
||||
ticks_left = tick % CFG_TICKS_PER_SECOND;
|
||||
tp->tv_usec = ticks_left * USECS_PER_TICK;
|
||||
ASSERT (tp->tv_usec < USECS_PER_SEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
udelay(usec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
return gettimeofday(tv, tz);
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
gettimeofday (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
gettimeofday (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == false;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
thandle = task_spawn ("worker", func, 6,stacksize, param);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
thandle = task_spawn ("worker_rt", func, 15 ,stacksize, param);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE task_t *
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <osal.h>
|
||||
#include <vxWorks.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
|
||||
#define timercmp(a, b, CMP) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? \
|
||||
((a)->tv_usec CMP (b)->tv_usec) : \
|
||||
((a)->tv_sec CMP (b)->tv_sec))
|
||||
#define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
/* OBS! config worker threads must have higher prio that task running ec_configuration */
|
||||
#define ECAT_TASK_PRIO_HIGH 20 /* Priority for high performance network task */
|
||||
#define ECAT_TASK_PRIO_LOW 80 /* Priority for high performance network task */
|
||||
#define ECAT_STACK_SIZE 10000 /* Stack size for high performance task */
|
||||
static int ecatTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
|
||||
static int ecatTaskIndex = 0;
|
||||
|
||||
#ifndef use_task_delay
|
||||
#define use_task_delay 1
|
||||
#endif
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
#if (use_task_delay == 1)
|
||||
/* Task delay 0 only yields */
|
||||
taskDelay(usec / 1000);
|
||||
return 0;
|
||||
#else
|
||||
/* The suspension may be longer than requested due to the rounding up of
|
||||
* the request to the timer's resolution or to other scheduling activities
|
||||
* (e.g., a higher priority task intervenes).
|
||||
*/
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
return nanosleep(&ts, NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
struct timespec ts;
|
||||
int return_value;
|
||||
(void)tz; /* Not used */
|
||||
|
||||
/* Use clock_gettime CLOCK_MONOTONIC to a avoid NTP time adjustments */
|
||||
return_value = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
tv->tv_sec = ts.tv_sec;
|
||||
tv->tv_usec = ts.tv_nsec / 1000;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start(osal_timert * self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_gettimeofday(&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd(&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert * self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_gettimeofday(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_LOW,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_rt_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_HIGH,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE TASK_ID
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <osal.h>
|
||||
#include "osal_win32.h"
|
||||
|
||||
static int64_t sysfrequency;
|
||||
static double qpc2usec;
|
||||
|
||||
#define USECS_PER_SEC 1000000
|
||||
|
||||
static int osal_getrelativetime(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
int64_t wintime, usecs;
|
||||
(void)tz;
|
||||
if(!sysfrequency)
|
||||
{
|
||||
timeBeginPeriod(1);
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&sysfrequency);
|
||||
qpc2usec = 1000000.0 / sysfrequency;
|
||||
}
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&wintime);
|
||||
usecs = (int64_t)((double)wintime * qpc2usec);
|
||||
tv->tv_sec = (long)(usecs / 1000000);
|
||||
tv->tv_usec = (long)(usecs - (tv->tv_sec * 1000000));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
FILETIME system_time;
|
||||
int64 system_time64, usecs;
|
||||
(void)tz;
|
||||
/* The offset variable is required to switch from Windows epoch (January 1, 1601) to
|
||||
* Unix epoch (January 1, 1970). Number of days between both epochs: 134.774
|
||||
*
|
||||
* The time returned by GetSystemTimeAsFileTime() changes in 100 ns steps, so the
|
||||
* following factors are required for the conversion from days to 100 ns steps:
|
||||
*
|
||||
* 86.400 seconds per day; 1.000.000 microseconds per second; 10 * 100 ns per microsecond
|
||||
*/
|
||||
int64 offset = -134774LL * 86400LL * 1000000LL * 10LL;
|
||||
|
||||
GetSystemTimeAsFileTime(&system_time);
|
||||
|
||||
system_time64 = ((int64)(system_time.dwHighDateTime) << 32) + (int64)system_time.dwLowDateTime;
|
||||
system_time64 += offset;
|
||||
usecs = system_time64 / 10;
|
||||
|
||||
tv->tv_sec = (long)(usecs / 1000000);
|
||||
tv->tv_usec = (long)(usecs - (tv->tv_sec * 1000000));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time (void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday (¤t_time, 0);
|
||||
return_value.sec = current_time.tv_sec;
|
||||
return_value.usec = current_time.tv_usec;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
|
||||
{
|
||||
if (end->usec < start->usec) {
|
||||
diff->sec = end->sec - start->sec - 1;
|
||||
diff->usec = end->usec + 1000000 - start->usec;
|
||||
}
|
||||
else {
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_timer_start (osal_timert *self, uint32 timeout_usec)
|
||||
{
|
||||
struct timeval start_time;
|
||||
struct timeval timeout;
|
||||
struct timeval stop_time;
|
||||
|
||||
osal_getrelativetime (&start_time, 0);
|
||||
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
|
||||
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
|
||||
timeradd (&start_time, &timeout, &stop_time);
|
||||
|
||||
self->stop_time.sec = stop_time.tv_sec;
|
||||
self->stop_time.usec = stop_time.tv_usec;
|
||||
}
|
||||
|
||||
boolean osal_timer_is_expired (osal_timert *self)
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timeval stop_time;
|
||||
int is_not_yet_expired;
|
||||
|
||||
osal_getrelativetime (¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
int osal_usleep(uint32 usec)
|
||||
{
|
||||
osal_timert qtime;
|
||||
osal_timer_start(&qtime, usec);
|
||||
if(usec >= 1000)
|
||||
{
|
||||
SleepEx(usec / 1000, FALSE);
|
||||
}
|
||||
while(!osal_timer_is_expired(&qtime));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
*(OSAL_THREAD_HANDLE*)thandle = CreateThread(NULL, stacksize, func, param, 0, NULL);
|
||||
if(!thandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
int ret;
|
||||
ret = osal_thread_create(thandle, stacksize, func, param);
|
||||
if (ret)
|
||||
{
|
||||
ret = SetThreadPriority(thandle, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude some conflicting definitions in windows header
|
||||
#include <windows.h>
|
||||
// define if debug printf is needed
|
||||
//#define EC_DEBUG
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
#define EC_PRINT printf
|
||||
#else
|
||||
#define EC_PRINT(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED
|
||||
#ifdef __GNUC__
|
||||
#define PACKED_BEGIN _Pragma("pack(push,1)")
|
||||
#define PACKED_END _Pragma("pack(pop)")
|
||||
#else
|
||||
#define PACKED_BEGIN __pragma(pack(push, 1))
|
||||
#define PACKED_END __pragma(pack(pop))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE HANDLE
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#ifndef _osal_win32_
|
||||
#define _osal_win32_
|
||||
|
||||
/* Convenience macros for operations on timevals.
|
||||
NOTE: `timercmp' does not work for >= or <=. */
|
||||
# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
|
||||
# define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
# define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct timezone;
|
||||
// currently the tz parameter is ignored in the win32 implmentation.
|
||||
int osal_gettimeofday (struct timeval *tv, struct timezone *tz);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ee.h"
|
||||
#include "intel_i210.h"
|
||||
#include "ee_x86_64_tsc.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
void ee_port_lock(void);
|
||||
void ee_port_unlock(void);
|
||||
|
||||
static inline void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int d;
|
||||
struct eth_device *dev;
|
||||
OSEE_PRINT("ecx_setupnic() searching %s...\n", ifname);
|
||||
|
||||
for (d = 0;; ++d) {
|
||||
dev = eth_get_device(d);
|
||||
|
||||
if (dev == NULL)
|
||||
break; // ERROR: device not found
|
||||
|
||||
if (!strncmp(dev->name, ifname, MAX_DEVICE_NAME)){
|
||||
// Device found
|
||||
int i;
|
||||
|
||||
eth_setup_device(d, 1, 100);
|
||||
port->dev_id = d;
|
||||
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
break; // device found
|
||||
}
|
||||
}
|
||||
|
||||
return (dev != NULL);
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
inline int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
// Nothing to do
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt = 0;
|
||||
|
||||
ee_port_lock();
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
idx = 0;
|
||||
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF)) {
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
idx = 0;
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
ee_port_unlock();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
|
||||
eth_send_packet(port->dev_id, (*stack->txbuf)[idx], lp, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
ee_port_lock();
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
eth_send_packet(port->dev_id, &(port->txbuf2), port->txbuflength2, 1);
|
||||
ee_port_unlock();
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = eth_receive_packet(port->dev_id, (*stack->tempbuf), lp, 1, 0);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
stack = &(port->stack);
|
||||
else
|
||||
stack = &(port->redport->stack);
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD)) {
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
} else {
|
||||
ee_port_lock();
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
|
||||
|
||||
while (1) {
|
||||
if (ecx_recvpkt(port, stacknumber)) {
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT)) {
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx) {
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
break;
|
||||
} else if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX) {
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
break;
|
||||
} else {
|
||||
OSEE_PRINT("ecx_inframe(): WARNING: strange things happened\n");
|
||||
/* strange things happened */
|
||||
}
|
||||
} else {
|
||||
OSEE_PRINT("ecx_inframe(): WARNING it is NOT an EtherCAT frame!\n");
|
||||
}
|
||||
} else {
|
||||
// WARNING: no messages received.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ee_port_unlock();
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do {
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE) {
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) ) {
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) ) {
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) ) {
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do {
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME) {
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do {
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET) {
|
||||
osal_timer_start (&timer2, timeout);
|
||||
} else {
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly
|
||||
from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
int ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
|
||||
/** Device id in the device pool */
|
||||
int dev_id;
|
||||
|
||||
// TODO: add mutex support
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
int ec_inframe(uint8 idx, int stacknumber);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "intel_i210.h"
|
||||
#include "ethercat.h"
|
||||
|
||||
#if !defined(__gnu_linux__)
|
||||
#include <machine/endian.h>
|
||||
#else
|
||||
#include <endian.h>
|
||||
#define __htons(x) htobe16(x)
|
||||
#define __ntohs(x) be16toh(x)
|
||||
#endif
|
||||
|
||||
ec_adaptert adapters [DEVS_MAX_NB];
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
inline uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
// __htons() is provided by the bare-metal x86 compiler
|
||||
return __htons(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
inline uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
// __ntohs() is provided by the bare-metal x86 compiler
|
||||
return __ntohs(network);
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert* oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert *ret = NULL;
|
||||
if (eth_discover_devices() >= 0) {
|
||||
for (int i = 0;; ++i) {
|
||||
struct eth_device *dev = eth_get_device(i);
|
||||
if (dev == NULL) {
|
||||
adapters[i-1].next = NULL;
|
||||
break;
|
||||
}
|
||||
strncpy(adapters[i].name, dev->name, MAX_DEVICE_NAME);
|
||||
adapters[i].next = &adapters[i+1];
|
||||
}
|
||||
ret = &(adapters[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert *adapter)
|
||||
{
|
||||
}
|
||||
|
||||
extern int ec_slavecount;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert* oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only sent by the master,
|
||||
* and the sent packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original sent packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <rt.h>
|
||||
#include <traceapi.h>
|
||||
#include "hpeif2.h"
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "oshw.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
ecx_portt ecx_port;
|
||||
ecx_redportt ecx_redport;
|
||||
|
||||
#endif
|
||||
|
||||
#define ECAT_PRINT_INFO printf
|
||||
#define ECAT_PRINT_WARN printf
|
||||
#define ECAT_PRINT_ERROR printf
|
||||
|
||||
/* HPE port settings */
|
||||
const unsigned long interrupt_mode = NO_INTERRUPT;
|
||||
const unsigned long phy_settings = SPEED_100 | DUPLEX_FULL;
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
HPESTATUS status;
|
||||
HPEMEDIASTATUS mstat;
|
||||
time_t now, t;
|
||||
unsigned char mac[6];
|
||||
int i;
|
||||
int dontWaitForLink = 0;
|
||||
int result = 1;
|
||||
HPE_CONFIG_OPTIONS conf = { 0, 0, NULL};
|
||||
|
||||
status = hpeOpen(ifname, phy_settings, interrupt_mode, &(port->handle));
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeOpen failed with status %04x ", status);
|
||||
if(status == E_EXIST) ECAT_PRINT_ERROR("E_EXIST\n");
|
||||
else if(status == E_STATE) ECAT_PRINT_ERROR("E_STATE\n");
|
||||
else if(status == E_PARAM) ECAT_PRINT_ERROR("E_PARAM\n");
|
||||
else if(status == E_INVALID_ADDR) ECAT_PRINT_ERROR("E_INVALID_ADDR\n");
|
||||
else if(status == E_IO) ECAT_PRINT_ERROR("E_IO\n");
|
||||
else if(status == E_TIME) ECAT_PRINT_ERROR("E_TIME\n");
|
||||
else ECAT_PRINT_ERROR("UNKNOWN\n");
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
conf.option_flags |= OPT_PROMISC;
|
||||
|
||||
status = hpeConfigOptions(port->handle, &conf, sizeof(conf));
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeConfigOptions failed with status %04x\n", status);
|
||||
// NOTE: HPE driver for Intel 10/100 Mbps device currently doesn't support multicast.
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
do
|
||||
{
|
||||
status = hpeGetMediaStatus(port->handle, &mstat);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeGetMediaStatus failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
if (mstat.media_speed == SPEED_NONE)
|
||||
{
|
||||
RtSleepEx(1000);
|
||||
time(&t);
|
||||
}
|
||||
} while (mstat.media_speed == SPEED_NONE && t < (now+10));
|
||||
|
||||
if (((mstat.media_speed & phy_settings) == 0) || ((mstat.media_duplex & phy_settings) == 0)) {
|
||||
ECAT_PRINT_ERROR("Media not connected as requested: speed=%u, duplex=%u\n",
|
||||
mstat.media_speed, mstat.media_duplex);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status= hpeGetMacAddress(port->handle, mac);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeGetMacAddress failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* allocate 2 receive buffers and attach them */
|
||||
status = hpeAllocateReceiveBufferSet(port->handle, &(port->rx_buffers), EC_MAXBUF, EC_BUFSIZE);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeAllocateReceiveBufferSet failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = hpeAttachReceiveBufferSet(port->handle, port->rx_buffers);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeAttachReceiveBufferSet failed with status %04x\n", status);
|
||||
result = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->redstate = ECT_RED_NONE;
|
||||
/* Init regions */
|
||||
port->getindex_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->rx_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->tx_region = CreateRtRegion (PRIORITY_QUEUING);
|
||||
port->lastidx = 0;
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
// allocate 1 transmit buffer of two fragments and 500 bytes and attach it
|
||||
port->tx_buffers[i] = (HPETXBUFFERSET *)AllocateRtMemory(sizeof(HPETXBUFFER) + sizeof(HPETXBUFFERSET));
|
||||
port->tx_buffers[i]->buffer_count = 1;
|
||||
port->tx_buffers[i]->buffers[0].fragment_count = 1;
|
||||
// first fragment is the fixed header, second is dynamic data
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].ptr = port->txbuf[i];
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].size = EC_BUFSIZE;
|
||||
port->tx_buffers[i]->buffers[0].fragments[0].paddr = 0;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
HPESTATUS status;
|
||||
int i;
|
||||
|
||||
if (port->tx_buffers)
|
||||
{
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
FreeRtMemory(port->tx_buffers[i]);
|
||||
port->tx_buffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (port->rx_buffers)
|
||||
{
|
||||
hpeFreeReceiveBufferSet(port->handle, &(port->rx_buffers));
|
||||
}
|
||||
|
||||
status= hpeClose(port->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
ECAT_PRINT_ERROR("hpeClose failed with status %04x\n", status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with Ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = (ec_etherheadert *)p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
WaitForRtControl(port->getindex_region);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if ( port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
}
|
||||
|
||||
port->lastidx = idx;
|
||||
ReleaseRtControl();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
HPESTATUS status;
|
||||
DWORD txstate;
|
||||
int lp;
|
||||
ec_stackT *stack;
|
||||
int result = 0;
|
||||
int retries = 1024;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
port->tx_buffers[idx]->buffer_count = 1;
|
||||
port->tx_buffers[idx]->buffers[0].fragments[0].size = lp;
|
||||
|
||||
// wait for transmit to complete
|
||||
do
|
||||
{
|
||||
result = hpeGetTransmitterState(port->handle, &txstate) == E_OK && txstate == HPE_TXBUSY;
|
||||
} while (result && retries-- > 0);
|
||||
|
||||
if (result)
|
||||
{
|
||||
result = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_RT_event('S',(WORD)2);
|
||||
status = hpeAttachTransmitBufferSet(port->handle, port->tx_buffers[idx]);
|
||||
if (status != E_OK)
|
||||
{
|
||||
result = -2;
|
||||
goto end;
|
||||
}
|
||||
log_RT_event('S',(WORD)3);
|
||||
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
status = hpeStartTransmitter(port->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
result = -3;
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_RT_event('S',(WORD)4);
|
||||
result = lp;
|
||||
|
||||
end:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
HPESTATUS status;
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2)[ETH_HEADERSIZE];
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
|
||||
// OBS! redundant not ACTIVE for BFIN, just added to compile
|
||||
//ASSERT (0);
|
||||
hpeAttachTransmitBufferSet(port->redport->handle, port->tx_buffers[idx]);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
status = hpeStartTransmitter(port->redport->handle);
|
||||
if (status != E_OK)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
HPEBUFFER *rxbuffer;
|
||||
HPESTATUS status;
|
||||
ec_stackT *stack;
|
||||
int bytesrx = 0;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
|
||||
log_RT_event('R',(WORD)2);
|
||||
|
||||
status = hpeGetReceiveBuffer(port->handle, &rxbuffer);
|
||||
if (status == E_OK)
|
||||
{
|
||||
memcpy(stack->tempbuf,rxbuffer->ptr, rxbuffer->used);
|
||||
bytesrx = rxbuffer->used;
|
||||
port->tempinbufs = bytesrx;
|
||||
// TODO case no interrupt
|
||||
}
|
||||
log_RT_event('R',(WORD)3);
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForRtControl(port->rx_region);
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp = (ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseRtControl();
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
{
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port,idx,1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
if (timeout == 0 && (port->redstate == ECT_RED_NONE))
|
||||
{
|
||||
int loop_cnt = 0;
|
||||
/* Allow us to consume MAX buffer number of frames */
|
||||
do
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
loop_cnt++;
|
||||
} while (wkc <= EC_NOFRAME && (loop_cnt <= EC_MAXBUF));
|
||||
}
|
||||
else
|
||||
{
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
}
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer1);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#define HAVE_REMOTE
|
||||
|
||||
#include <rt.h>
|
||||
#include "hpeif2.h"
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/* Intime */
|
||||
HPEHANDLE handle;
|
||||
HPERXBUFFERSET *rx_buffers;
|
||||
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
|
||||
} ecx_redportt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
RTHANDLE getindex_region;
|
||||
RTHANDLE tx_region;
|
||||
RTHANDLE rx_region;
|
||||
/* Intime */
|
||||
HPEHANDLE handle;
|
||||
HPERXBUFFERSET *rx_buffers;
|
||||
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
//extern ecx_portt ecx_port;
|
||||
//extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setupheader(void *p);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/endian.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons (uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs (uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters (ec_adaptert * adapter)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "ethercatmain.h"
|
||||
#include "nicdrv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint16 oshw_htons (uint16 hostshort);
|
||||
uint16 oshw_ntohs (uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters (void);
|
||||
void oshw_free_adapters (ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int r, rval, ifindex;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_ll sll;
|
||||
int *psock;
|
||||
pthread_mutexattr_t mutexattr;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_init(&mutexattr);
|
||||
pthread_mutexattr_setprotocol(&mutexattr , PTHREAD_PRIO_INHERIT);
|
||||
pthread_mutex_init(&(port->getindex_mutex), &mutexattr);
|
||||
pthread_mutex_init(&(port->tx_mutex) , &mutexattr);
|
||||
pthread_mutex_init(&(port->rx_mutex) , &mutexattr);
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
/* we use RAW packet socket, with packet type ETH_P_ECAT */
|
||||
*psock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ECAT));
|
||||
if(*psock < 0)
|
||||
return 0;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1;
|
||||
r = 0;
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
i = 1;
|
||||
r |= setsockopt(*psock, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i));
|
||||
/* connect socket to NIC by name */
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
r |= ioctl(*psock, SIOCGIFINDEX, &ifr);
|
||||
ifindex = ifr.ifr_ifindex;
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
ifr.ifr_flags = 0;
|
||||
/* reset flags of NIC interface */
|
||||
r |= ioctl(*psock, SIOCGIFFLAGS, &ifr);
|
||||
/* set flags of NIC interface, here promiscuous and broadcast */
|
||||
ifr.ifr_flags = ifr.ifr_flags | IFF_PROMISC | IFF_BROADCAST;
|
||||
r |= ioctl(*psock, SIOCSIFFLAGS, &ifr);
|
||||
/* bind socket to protocol, in this case RAW EtherCAT */
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifindex;
|
||||
sll.sll_protocol = htons(ETH_P_ECAT);
|
||||
r |= bind(*psock, (struct sockaddr *)&sll, sizeof(sll));
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
if (r == 0) rval = 1;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
close(port->sockhandle);
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
close(port->redport->sockhandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock( &(port->getindex_mutex) );
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock( &(port->getindex_mutex) );
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
|
||||
if (rval == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0) == -1)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter = NULL;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (prev_adapter)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in Linux we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to fund out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i, rval;
|
||||
pcap_t **psock;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = NULL;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_init(&(port->getindex_mutex), NULL);
|
||||
pthread_mutex_init(&(port->tx_mutex), NULL);
|
||||
pthread_mutex_init(&(port->rx_mutex), NULL);
|
||||
port->sockhandle = NULL;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
*psock = pcap_open_live(ifname, 65536, 1, 0, errbuf);
|
||||
if (NULL == *psock)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rval = pcap_setdirection(*psock, PCAP_D_IN);
|
||||
if (rval != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rval = pcap_setnonblock(*psock, 1, errbuf);
|
||||
if (rval != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle != NULL)
|
||||
{
|
||||
pthread_mutex_destroy(&(port->getindex_mutex));
|
||||
pthread_mutex_destroy(&(port->tx_mutex));
|
||||
pthread_mutex_destroy(&(port->rx_mutex));
|
||||
pcap_close(port->sockhandle);
|
||||
port->sockhandle = NULL;
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle != NULL))
|
||||
{
|
||||
pcap_close(port->redport->sockhandle);
|
||||
port->redport->sockhandle = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock(&(port->getindex_mutex));
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock(&(port->getindex_mutex));
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
|
||||
if (rval == PCAP_ERROR)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
struct pcap_pkthdr * header;
|
||||
unsigned char const * pkt_data;
|
||||
int res;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
|
||||
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
|
||||
if (res <=0 )
|
||||
{
|
||||
port->tempinbufs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytesrx = header->len;
|
||||
if (bytesrx > lp)
|
||||
{
|
||||
bytesrx = lp;
|
||||
}
|
||||
memcpy(*stack->tempbuf, pkt_data, bytesrx);
|
||||
port->tempinbufs = bytesrx;
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] tvs = timeout
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pcap/pcap.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
pcap_t **sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lenghts */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in macosx we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int r, rval;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
int *bpf;
|
||||
// const uint8_t bpffnamelen = 12;
|
||||
char fname[13] = {0};
|
||||
const int maxbpffile = 1000;
|
||||
uint true_val = 1;
|
||||
|
||||
rval = 0;
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
bpf = &(port->redport->sockhandle);
|
||||
*bpf = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_init(&(port->getindex_mutex), NULL);
|
||||
pthread_mutex_init(&(port->tx_mutex) , NULL);
|
||||
pthread_mutex_init(&(port->rx_mutex) , NULL);
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
bpf = &(port->sockhandle);
|
||||
}
|
||||
|
||||
/* Open a bpf file descriptor */
|
||||
*bpf = -1;
|
||||
for(i = 0; *bpf == -1 && i < maxbpffile; ++i)
|
||||
{
|
||||
sprintf(fname, "/dev/bpf%i", i);
|
||||
*bpf = open(fname, O_RDWR);
|
||||
}
|
||||
|
||||
if(*bpf == -1)
|
||||
{
|
||||
/* Failed to open bpf */
|
||||
perror("Could not open bpf\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Need to hand the same buffer size as bpf expects,
|
||||
force bpf to use our buffer size! */
|
||||
uint buffer_len = sizeof(ec_bufT);
|
||||
if (ioctl(*bpf, BIOCSBLEN, &buffer_len) == -1) {
|
||||
perror("BIOCGBLEN");
|
||||
}
|
||||
|
||||
if (buffer_len < 1518) {
|
||||
printf("buffer_len %d < 1518\n", buffer_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* connect bpf to NIC by name */
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
if(ioctl(*bpf, BIOCSETIF, &ifr) == -1) {
|
||||
perror("BIOCSETIF");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set required bpf options */
|
||||
|
||||
/* Activate immediate mode */
|
||||
if (ioctl(*bpf, BIOCIMMEDIATE, &true_val) == -1) {
|
||||
perror("BIOCIMMEDIATE");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set interface in promiscuous mode */
|
||||
if (ioctl(*bpf, BIOCPROMISC, &true_val) == -1) {
|
||||
perror("BIOCPROMISC");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow to have custom source address */
|
||||
if (ioctl(*bpf, BIOCSHDRCMPLT, &true_val) == -1) {
|
||||
perror("BIOCSHDRCMPLT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Listen only to incomming messages */
|
||||
uint direction = BPF_D_IN;
|
||||
if (ioctl(*bpf, BIOCSDIRECTION, &direction) == -1) {
|
||||
perror("BIOCSDIRECTION");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set read timeout */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 11000;
|
||||
if (ioctl(*bpf, BIOCSRTIMEOUT, &timeout) == -1) {
|
||||
perror("BIOCSRTIMEOUT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
close(port->sockhandle);
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
close(port->redport->sockhandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
pthread_mutex_lock( &(port->getindex_mutex) );
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
pthread_mutex_unlock( &(port->getindex_mutex) );
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
//rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = write (*stack->sock,(*stack->txbuf)[idx], lp);
|
||||
if (rval == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
pthread_mutex_lock( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (write(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2) == -1)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
pthread_mutex_unlock( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
//bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
|
||||
bytesrx = read(*stack->sock, (*stack->tempbuf), lp);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
/* The data read from /dev/bpf includes an extra header, skip it. */
|
||||
struct bpf_hdr *bpfh = (struct bpf_hdr *)(stack->tempbuf);
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(*stack->tempbuf + bpfh->bh_hdrlen);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
/* The EtherCAT header follows the ethernet frame header. */
|
||||
ecp =(ec_comt*)(&ehp[1]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip headers) */
|
||||
memcpy(rxbuf, &(ehp[1]), port->tempinbufs - ((uint32_t)ecp - (uint32_t)*stack->tempbuf));
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
pthread_mutex_t getindex_mutex;
|
||||
pthread_mutex_t tx_mutex;
|
||||
pthread_mutex_t rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
int i;
|
||||
struct if_nameindex *ids;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
|
||||
ids = if_nameindex ();
|
||||
for(i = 0; ids[i].if_index != 0; i++)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name, in rtems we use the same on both */
|
||||
adapter->next = NULL;
|
||||
|
||||
if (ids[i].if_name)
|
||||
{
|
||||
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
|
||||
prev_adapter = adapter;
|
||||
}
|
||||
|
||||
if_freenameindex (ids);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,802 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: fec_ecat.c 138 2014-04-11 09:11:48Z rtlfrm $
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "fec_ecat.h"
|
||||
|
||||
#include <dev.h>
|
||||
#include <kern.h>
|
||||
#include <string.h>
|
||||
#include <uassert.h>
|
||||
#include <eth/fec_buffer.h>
|
||||
#include <eth/phy/mii.h>
|
||||
#include <bsp.h>
|
||||
#include <config.h>
|
||||
|
||||
#undef RTK_DEBUG /* Print debugging info? */
|
||||
#undef RTK_DEBUG_DATA /* Print packets? */
|
||||
|
||||
#ifdef RTK_DEBUG
|
||||
#define DPRINT(...) rprintp ("FEC: "__VA_ARGS__)
|
||||
#else
|
||||
#define DPRINT(...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef RTK_DEBUG_DATA
|
||||
#define DUMP_PACKET(p, l) fec_ecat_dump_packet (p, l)
|
||||
#else
|
||||
#define DUMP_PACKET(p, l)
|
||||
#endif /* RTK_DEBUG_DATA */
|
||||
|
||||
#define ETHERNET_FCS_SIZE_IN_BYTES 4 /* Frame Check Sequence (32-bit CRC) */
|
||||
|
||||
#define DELAY(ms) task_delay (tick_from_ms (ms) + 1)
|
||||
|
||||
#define ETH_PHY_ADDRESS 0x1 /* Default jumper setting on TWR-K60F */
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
#define FEC_ALLIGNED __attribute__((section(".dma"),aligned(16)))
|
||||
|
||||
/*
|
||||
* "The device's system clock is connected to the module clock,
|
||||
* as named in the Ethernet chapter. The minimum system clock frequency
|
||||
* for 100 Mbps operation is 25 MHz."
|
||||
*
|
||||
* - Kinetis K60 manual ch. 3.9.1.1 "Ethernet Clocking Options"
|
||||
*/
|
||||
#define FEC_MIN_MODULE_CLOCK_Hz (25 * 1000 * 1000)
|
||||
|
||||
/* Note that the core clock and the system clock has the same clock frequency */
|
||||
#define FEC_MODULE_CLOCK_Hz CFG_IPG_CLOCK
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
typedef struct fec_mac_t
|
||||
{
|
||||
uint32_t lower; /* octets 0, 1, 2, 3 */
|
||||
uint32_t upper; /* octets 4, 5 */
|
||||
} fec_mac_t;
|
||||
|
||||
/* Message Information Block (MIB) Counters */
|
||||
typedef struct fec_mib
|
||||
{
|
||||
uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */
|
||||
uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */
|
||||
uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */
|
||||
uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */
|
||||
uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */
|
||||
uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */
|
||||
uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */
|
||||
uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */
|
||||
uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */
|
||||
uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */
|
||||
uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */
|
||||
uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */
|
||||
uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */
|
||||
uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */
|
||||
uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */
|
||||
uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */
|
||||
uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */
|
||||
uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */
|
||||
uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */
|
||||
uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */
|
||||
uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */
|
||||
uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */
|
||||
uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */
|
||||
uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */
|
||||
uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */
|
||||
uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */
|
||||
uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */
|
||||
uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */
|
||||
uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */
|
||||
uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */
|
||||
uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */
|
||||
uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */
|
||||
uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */
|
||||
uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */
|
||||
uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */
|
||||
uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */
|
||||
uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */
|
||||
uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */
|
||||
uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */
|
||||
uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */
|
||||
uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */
|
||||
uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */
|
||||
uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */
|
||||
uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */
|
||||
uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */
|
||||
uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */
|
||||
uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */
|
||||
uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */
|
||||
uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */
|
||||
uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */
|
||||
uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */
|
||||
uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */
|
||||
uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */
|
||||
uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */
|
||||
uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */
|
||||
uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */
|
||||
uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */
|
||||
} fec_mib_t;
|
||||
|
||||
COMPILETIME_ASSERT (sizeof (fec_mib_t) == 0x100);
|
||||
|
||||
/* Register read/write struct */
|
||||
typedef struct reg_fec
|
||||
{
|
||||
uint32_t resv0;
|
||||
uint32_t eir;
|
||||
uint32_t eimr;
|
||||
uint32_t resv1;
|
||||
uint32_t rdar;
|
||||
uint32_t tdar;
|
||||
uint32_t resv2[3];
|
||||
uint32_t ecr;
|
||||
uint32_t resv3[6];
|
||||
uint32_t mmfr;
|
||||
uint32_t mscr;
|
||||
uint32_t resv4[7];
|
||||
uint32_t mibc;
|
||||
uint32_t resv5[7];
|
||||
uint32_t rcr;
|
||||
uint32_t resv6[15];
|
||||
uint32_t tcr;
|
||||
uint32_t resv7[7];
|
||||
fec_mac_t pa; /* Physical address. PALR/PAUR */
|
||||
uint32_t opd;
|
||||
uint32_t resv8[10];
|
||||
uint32_t iaur;
|
||||
uint32_t ialr;
|
||||
uint32_t gaur;
|
||||
uint32_t galr;
|
||||
uint32_t resv9[7];
|
||||
uint32_t tfwr;
|
||||
uint32_t resv10;
|
||||
uint32_t frbr;
|
||||
uint32_t frsr;
|
||||
uint32_t resv11[11];
|
||||
uint32_t erdsr;
|
||||
uint32_t etdsr;
|
||||
uint32_t emrbr;
|
||||
uint32_t resv14;
|
||||
uint32_t rx_section_full; /* Not present on all controllers */
|
||||
uint32_t rx_section_empty; /* Not present on all controllers */
|
||||
uint32_t rx_almost_empty; /* Not present on all controllers */
|
||||
uint32_t rx_almost_full; /* Not present on all controllers */
|
||||
uint32_t tx_section_empty; /* Not present on all controllers */
|
||||
uint32_t tx_almost_empty; /* Not present on all controllers */
|
||||
uint32_t tx_almost_full; /* Not present on all controllers */
|
||||
uint32_t resv12[21];
|
||||
const fec_mib_t mib;
|
||||
uint32_t fec_miigsk_cfgr; /* Not present on all controllers */
|
||||
uint32_t resv13;
|
||||
uint32_t fec_miigsk_enr; /* Not present on all controllers */
|
||||
uint32_t resv15[0x7d];
|
||||
fec_mac_t smac[4]; /* Not present on all controllers */
|
||||
} reg_fec_t;
|
||||
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, emrbr) == 0x188);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, tx_almost_full) == 0x1A8);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, fec_miigsk_cfgr) == 0x300);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac) == 0x500);
|
||||
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac[3]) == 0x518);
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* Bit definitions and macros for FEC_EIR */
|
||||
#define FEC_EIR_CLEAR_ALL (0xFFF80000)
|
||||
#define FEC_EIR_HBERR (0x80000000)
|
||||
#define FEC_EIR_BABR (0x40000000)
|
||||
#define FEC_EIR_BABT (0x20000000)
|
||||
#define FEC_EIR_GRA (0x10000000)
|
||||
#define FEC_EIR_TXF (0x08000000)
|
||||
#define FEC_EIR_TXB (0x04000000)
|
||||
#define FEC_EIR_RXF (0x02000000)
|
||||
#define FEC_EIR_RXB (0x01000000)
|
||||
#define FEC_EIR_MII (0x00800000)
|
||||
#define FEC_EIR_EBERR (0x00400000)
|
||||
#define FEC_EIR_LC (0x00200000)
|
||||
#define FEC_EIR_RL (0x00100000)
|
||||
#define FEC_EIR_UN (0x00080000)
|
||||
|
||||
/* Bit definitions and macros for FEC_RDAR */
|
||||
#define FEC_RDAR_R_DES_ACTIVE (0x01000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_TDAR */
|
||||
#define FEC_TDAR_X_DES_ACTIVE (0x01000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_ECR */
|
||||
#define FEC_ECR_DBSWP (0x00000100)
|
||||
#define FEC_ECR_ETHER_EN (0x00000002)
|
||||
#define FEC_ECR_RESET (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_MMFR */
|
||||
#define FEC_MMFR_DATA(x) (((x)&0xFFFF))
|
||||
#define FEC_MMFR_ST(x) (((x)&0x03)<<30)
|
||||
#define FEC_MMFR_ST_01 (0x40000000)
|
||||
#define FEC_MMFR_OP_RD (0x20000000)
|
||||
#define FEC_MMFR_OP_WR (0x10000000)
|
||||
#define FEC_MMFR_PA(x) (((x)&0x1F)<<23)
|
||||
#define FEC_MMFR_RA(x) (((x)&0x1F)<<18)
|
||||
#define FEC_MMFR_TA(x) (((x)&0x03)<<16)
|
||||
#define FEC_MMFR_TA_10 (0x00020000)
|
||||
|
||||
/* Bit definitions and macros for FEC_MSCR */
|
||||
#define FEC_MSCR_DIS_PREAMBLE (0x00000080)
|
||||
#define FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<1)
|
||||
|
||||
/* Bit definitions and macros for FEC_MIBC */
|
||||
#define FEC_MIBC_MIB_DISABLE (0x80000000)
|
||||
#define FEC_MIBC_MIB_IDLE (0x40000000)
|
||||
|
||||
/* Bit definitions and macros for FEC_RCR */
|
||||
#define FEC_RCR_GRS (0x80000000)
|
||||
#define FEC_RCR_NO_LGTH_CHECK (0x40000000)
|
||||
#define FEC_RCR_MAX_FL(x) (((x)&0x7FF)<<16)
|
||||
#define FEC_RCR_CNTL_FRM_ENA (0x00008000)
|
||||
#define FEC_RCR_CRC_FWD (0x00004000)
|
||||
#define FEC_RCR_PAUSE_FWD (0x00002000)
|
||||
#define FEC_RCR_PAD_EN (0x00001000)
|
||||
#define FEC_RCR_RMII_ECHO (0x00000800)
|
||||
#define FEC_RCR_RMII_LOOP (0x00000400)
|
||||
#define FEC_RCR_RMII_10T (0x00000200)
|
||||
#define FEC_RCR_RMII_MODE (0x00000100)
|
||||
#define FEC_RCR_SGMII_ENA (0x00000080)
|
||||
#define FEC_RCR_RGMII_ENA (0x00000040)
|
||||
#define FEC_RCR_FCE (0x00000020)
|
||||
#define FEC_RCR_BC_REJ (0x00000010)
|
||||
#define FEC_RCR_PROM (0x00000008)
|
||||
#define FEC_RCR_MII_MODE (0x00000004)
|
||||
#define FEC_RCR_DRT (0x00000002)
|
||||
#define FEC_RCR_LOOP (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_TCR */
|
||||
#define FEC_TCR_RFC_PAUSE (0x00000010)
|
||||
#define FEC_TCR_TFC_PAUSE (0x00000008)
|
||||
#define FEC_TCR_FDEN (0x00000004)
|
||||
#define FEC_TCR_HBC (0x00000002)
|
||||
#define FEC_TCR_GTS (0x00000001)
|
||||
|
||||
/* Bit definitions and macros for FEC_PAUR */
|
||||
#define FEC_PAUR_PADDR2(x) (((x)&0xFFFF)<<16)
|
||||
#define FEC_PAUR_TYPE(x) ((x)&0xFFFF)
|
||||
|
||||
/* Bit definitions and macros for FEC_OPD */
|
||||
#define FEC_OPD_PAUSE_DUR(x) (((x)&0x0000FFFF)<<0)
|
||||
#define FEC_OPD_OPCODE(x) (((x)&0x0000FFFF)<<16)
|
||||
|
||||
/* Bit definitions and macros for FEC_TFWR */
|
||||
#define FEC_TFWR_STR_FWD BIT (8) /* Present on ENET but not on FEC */
|
||||
#define FEC_TFWR_X_WMRK(x) ((x)&0x03)
|
||||
#define FEC_TFWR_X_WMRK_64 (0x01)
|
||||
#define FEC_TFWR_X_WMRK_128 (0x02)
|
||||
#define FEC_TFWR_X_WMRK_192 (0x03)
|
||||
|
||||
/* Bit definitions and macros for FEC_FRBR */
|
||||
#define FEC_FRBR_R_BOUND(x) (((x)&0xFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_FRSR */
|
||||
#define FEC_FRSR_R_FSTART(x) (((x)&0xFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_ERDSR */
|
||||
#define FEC_ERDSR_R_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_ETDSR */
|
||||
#define FEC_ETDSR_X_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
||||
|
||||
/* Bit definitions and macros for FEC_EMRBR */
|
||||
#define FEC_EMRBR_R_BUF_SIZE(x) (((x)&0x7F)<<4)
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* the defines of MII operation */
|
||||
#define FEC_MII_ST 0x40000000
|
||||
#define FEC_MII_OP_OFF 28
|
||||
#define FEC_MII_OP_MASK 0x03
|
||||
#define FEC_MII_OP_RD 0x02
|
||||
#define FEC_MII_OP_WR 0x01
|
||||
#define FEC_MII_PA_OFF 23
|
||||
#define FEC_MII_PA_MASK 0xFF
|
||||
#define FEC_MII_RA_OFF 18
|
||||
#define FEC_MII_RA_MASK 0xFF
|
||||
#define FEC_MII_TA 0x00020000
|
||||
#define FEC_MII_DATA_OFF 0
|
||||
#define FEC_MII_DATA_MASK 0x0000FFFF
|
||||
|
||||
#define FEC_MII_FRAME (FEC_MII_ST | FEC_MII_TA)
|
||||
#define FEC_MII_OP(x) (((x) & FEC_MII_OP_MASK) << FEC_MII_OP_OFF)
|
||||
#define FEC_MII_PA(pa) (((pa) & FEC_MII_PA_MASK) << FEC_MII_PA_OFF)
|
||||
#define FEC_MII_RA(ra) (((ra) & FEC_MII_RA_MASK) << FEC_MII_RA_OFF)
|
||||
#define FEC_MII_SET_DATA(v) (((v) & FEC_MII_DATA_MASK) << FEC_MII_DATA_OFF)
|
||||
#define FEC_MII_GET_DATA(v) (((v) >> FEC_MII_DATA_OFF) & FEC_MII_DATA_MASK)
|
||||
#define FEC_MII_READ(pa, ra) ((FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_RD)) |\
|
||||
FEC_MII_PA(pa) | FEC_MII_RA(ra))
|
||||
#define FEC_MII_WRITE(pa, ra, v) (FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_WR)|\
|
||||
FEC_MII_PA(pa) | FEC_MII_RA(ra) | FEC_MII_SET_DATA(v))
|
||||
|
||||
#define FEC_MII_TIMEOUT 10 // 1 * 10 ms
|
||||
#define FEC_MII_TICK 1 // 1 ms
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
/* Ethernet Transmit and Receive Buffers */
|
||||
#define PKT_MAXBUF_SIZE 1518
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
typedef enum fec_phy_inteface
|
||||
{
|
||||
/**
|
||||
* MII, Media Independent Interface
|
||||
* 4-bit operating at 25 Mhz
|
||||
*/
|
||||
FEC_PHY_MII,
|
||||
/**
|
||||
* RMII, Reduced Media Independent Interface
|
||||
* 2-bit operating at 50 Mhz
|
||||
*/
|
||||
FEC_PHY_RMII,
|
||||
FEC_PHY_RGMII
|
||||
} fec_phy_inteface_t;
|
||||
|
||||
typedef struct fec_cfg
|
||||
{
|
||||
uint32_t base;
|
||||
uint32_t clock;
|
||||
fec_buffer_bd_t * tx_bd_base;
|
||||
fec_buffer_bd_t * rx_bd_base;
|
||||
fec_phy_inteface_t phy_interface;
|
||||
} fec_cfg_t;
|
||||
|
||||
typedef struct fec
|
||||
{
|
||||
uint32_t clock;
|
||||
fec_buffer_bd_t * tx_bd_base;
|
||||
fec_buffer_bd_t * rx_bd_base;
|
||||
volatile reg_fec_t *base;
|
||||
phy_t * phy;
|
||||
fec_phy_inteface_t phy_interface;
|
||||
} fec_t;
|
||||
|
||||
/* Buffer descriptors. */
|
||||
static fec_buffer_bd_t fec_tx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
||||
static fec_buffer_bd_t fec_rx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
||||
|
||||
/* Data buffers. */
|
||||
static uint8_t fec_tx_data[NUM_BUFFERS * TX_BUFFER_SIZE] FEC_ALLIGNED;
|
||||
static uint8_t fec_rx_data[NUM_BUFFERS * RX_BUFFER_SIZE] FEC_ALLIGNED;
|
||||
|
||||
static fec_t *fec;
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#ifdef RTK_DEBUG
|
||||
const char * fec_ecat_link_duplex_name (uint8_t link_state)
|
||||
{
|
||||
if (link_state & PHY_LINK_OK)
|
||||
{
|
||||
if (link_state & PHY_LINK_FULL_DUPLEX)
|
||||
{
|
||||
return "full duplex";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "half duplex";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "no duplex (link down)";
|
||||
}
|
||||
}
|
||||
|
||||
const char * fec_ecat_link_speed_name (uint8_t link_state)
|
||||
{
|
||||
if (link_state & PHY_LINK_OK)
|
||||
{
|
||||
if (link_state & PHY_LINK_10MBIT)
|
||||
{
|
||||
return "10 Mbps";
|
||||
}
|
||||
else if (link_state & PHY_LINK_100MBIT)
|
||||
{
|
||||
return "100 Mbps";
|
||||
}
|
||||
else if (link_state & PHY_LINK_1000MBIT)
|
||||
{
|
||||
return "1 Gbps";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "unknown speed";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "0 Mbps (link down)";
|
||||
}
|
||||
}
|
||||
#endif /* RTK_DEBUG */
|
||||
|
||||
#ifdef RTK_DEBUG_DATA
|
||||
#include <ctype.h>
|
||||
static void fec_ecat_dump_packet (const uint8_t * payload, size_t len)
|
||||
{
|
||||
size_t i, j, n;
|
||||
char s[80];
|
||||
|
||||
ASSERT (payload != NULL);
|
||||
|
||||
for (i = 0; i < len; i += 16)
|
||||
{
|
||||
n = 0;
|
||||
for (j = 0; j < 16 && (i + j) < len; j++)
|
||||
{
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "%02x ", payload[i + j]);
|
||||
}
|
||||
for (; j < 16; j++)
|
||||
{
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, " ");
|
||||
}
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "|");
|
||||
for (j = 0; j < 16 && (i + j) < len; j++)
|
||||
{
|
||||
uint8_t c = payload[i + j];
|
||||
c = (isprint (c)) ? c : '.';
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "%c", c);
|
||||
}
|
||||
ASSERT (n <= sizeof(s));
|
||||
n += rsnprintf (s + n, sizeof(s) - n, "|\n");
|
||||
ASSERT (n <= sizeof(s));
|
||||
DPRINT ("%s", s);
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_DATA */
|
||||
|
||||
static uint16_t fec_ecat_read_phy (void * arg, uint8_t address, uint8_t reg)
|
||||
{
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
fec->base->mmfr = FEC_MII_READ(address, reg); // Write read command.
|
||||
while (!(fec->base->eir & FEC_EIR_MII))
|
||||
{
|
||||
; // Wait for interrupt.
|
||||
}
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
uint16_t data = FEC_MII_GET_DATA(fec->base->mmfr);
|
||||
return data; // Return read data.
|
||||
}
|
||||
|
||||
static void fec_ecat_write_phy (void * arg, uint8_t address, uint8_t reg,
|
||||
uint16_t value)
|
||||
{
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
fec->base->mmfr = FEC_MII_WRITE(address, reg, value); // Write data.
|
||||
while (!(fec->base->eir & FEC_EIR_MII))
|
||||
{
|
||||
; // Wait for interrupt.
|
||||
}
|
||||
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
||||
}
|
||||
|
||||
static void fec_ecat_init_hw (const fec_mac_address_t * mac_address)
|
||||
{
|
||||
uint32_t mii_speed;
|
||||
|
||||
/* Hard reset */
|
||||
fec->base->ecr = FEC_ECR_RESET;
|
||||
while (fec->base->ecr & FEC_ECR_RESET)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Configure MDC clock.
|
||||
mii_speed = (fec->clock + 499999) / 5000000;
|
||||
fec->base->mscr = (fec->base->mscr & (~0x7E)) | (mii_speed << 1);
|
||||
|
||||
// Receive control register
|
||||
fec->base->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE;
|
||||
|
||||
// set RMII mode in RCR register.
|
||||
if (fec->phy_interface == FEC_PHY_RMII)
|
||||
{
|
||||
fec->base->rcr |= FEC_RCR_RMII_MODE;
|
||||
}
|
||||
else if(fec->phy_interface == FEC_PHY_RGMII)
|
||||
{
|
||||
fec->base->rcr &= ~(FEC_RCR_RMII_MODE | FEC_RCR_MII_MODE );
|
||||
fec->base->rcr |= (FEC_RCR_RGMII_ENA | FEC_RCR_MII_MODE);
|
||||
}
|
||||
|
||||
// Reset phy
|
||||
if (fec->phy->ops->reset)
|
||||
{
|
||||
fec->phy->ops->reset (fec->phy);
|
||||
}
|
||||
|
||||
/* Don't receive any unicast frames except those whose destination address
|
||||
* equals our physical address.
|
||||
*/
|
||||
fec->base->iaur = 0;
|
||||
fec->base->ialr = 0;
|
||||
|
||||
/* Receive all multicast frames. */
|
||||
fec->base->gaur = UINT32_MAX;
|
||||
fec->base->galr = UINT32_MAX;
|
||||
|
||||
/* Set our physical address. */
|
||||
fec->base->pa.lower = (mac_address->octet[0] << 24) +
|
||||
(mac_address->octet[1] << 16) +
|
||||
(mac_address->octet[2] << 8) +
|
||||
(mac_address->octet[3] << 0);
|
||||
fec->base->pa.upper = (mac_address->octet[4] << 24) +
|
||||
(mac_address->octet[5] << 16) +
|
||||
0x8808;
|
||||
|
||||
/* Start link autonegotiation */
|
||||
fec->phy->ops->start (fec->phy);
|
||||
}
|
||||
|
||||
int fec_ecat_send (const void *payload, size_t tot_len)
|
||||
{
|
||||
fec_buffer_bd_t * bd;
|
||||
|
||||
/* Frames larger than the maximum Ethernet frame size are not allowed. */
|
||||
ASSERT (tot_len <= PKT_MAXBUF_SIZE);
|
||||
ASSERT (tot_len <= TX_BUFFER_SIZE);
|
||||
|
||||
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
||||
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
||||
*/
|
||||
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
||||
|
||||
/* Allocate a transmit buffer. We wait here until the MAC has released at
|
||||
* least one buffer. This could take a couple of microseconds.
|
||||
*/
|
||||
bd = NULL;
|
||||
while (bd == NULL)
|
||||
{
|
||||
bd = fec_buffer_get_tx ();
|
||||
}
|
||||
|
||||
DPRINT ("out (%u):\n", tot_len);
|
||||
DUMP_PACKET (payload, tot_len);
|
||||
|
||||
/* Copy frame to allocated buffer */
|
||||
memcpy (bd->data, payload, tot_len);
|
||||
bd->length = tot_len;
|
||||
|
||||
fec_buffer_produce_tx (bd);
|
||||
|
||||
/* Wait for previous transmissions to complete.
|
||||
*
|
||||
* This is a workaround for Freescale Kinetis errata e6358.
|
||||
* See "Mask Set Errata for Mask 3N96B".
|
||||
*/
|
||||
while (fec->base->tdar)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
/* Transmit frame */
|
||||
fec->base->tdar = 1;
|
||||
|
||||
return tot_len;
|
||||
}
|
||||
|
||||
int fec_ecat_recv (void * buffer, size_t buffer_length)
|
||||
{
|
||||
fec_buffer_bd_t * bd;
|
||||
int return_value;
|
||||
size_t frame_length_without_fcs;
|
||||
|
||||
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
||||
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
||||
*/
|
||||
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
||||
|
||||
bd = fec_buffer_get_rx ();
|
||||
if (bd == NULL)
|
||||
{
|
||||
/* No frame received. Not an error. */
|
||||
return_value = 0;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/* The FCS CRC should not be returned to user, so subtract that. */
|
||||
frame_length_without_fcs = bd->length - ETHERNET_FCS_SIZE_IN_BYTES;
|
||||
|
||||
/* A frame was received. Handle it and then return its buffer to hardware */
|
||||
if ((bd->status & BD_RX_L)== 0){
|
||||
/* Buffer is not last buffer in frame. This really should never
|
||||
* happen as our buffers are large enough to contain any
|
||||
* Ethernet frame. Nevertheless, is has been observed to happen.
|
||||
* Drop the packet and hope for the best.
|
||||
*/
|
||||
DPRINT ("recv(): End of frame not found. Status: 0x%x\n", bd->status);
|
||||
return_value = -1;
|
||||
}
|
||||
else if (bd->status & (BD_RX_LG | BD_RX_NO | BD_RX_CR | BD_RX_OV))
|
||||
{
|
||||
DPRINT ("recv(): Frame is damaged. Status: 0x%x\n", bd->status);
|
||||
return_value = -1;
|
||||
}
|
||||
else if (buffer_length >= frame_length_without_fcs)
|
||||
{
|
||||
/* No errors detected, so frame should be valid. */
|
||||
ASSERT (frame_length_without_fcs > 0);
|
||||
|
||||
memcpy (buffer, bd->data, frame_length_without_fcs);
|
||||
DPRINT ("in (%u):\n", frame_length_without_fcs);
|
||||
DUMP_PACKET (buffer, frame_length_without_fcs);
|
||||
return_value = frame_length_without_fcs;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT ("received_frame: User buffer is too small.\n");
|
||||
return_value = -1;
|
||||
}
|
||||
|
||||
/* Tell the HW that there are new free RX buffers. */
|
||||
fec_buffer_produce_rx (bd);
|
||||
fec->base->rdar = 1;
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static void fec_ecat_hotplug (void)
|
||||
{
|
||||
uint8_t link_state = 0;
|
||||
|
||||
/* Disable frame reception/transmission */
|
||||
fec->base->ecr &= ~FEC_ECR_ETHER_EN;
|
||||
|
||||
/* Set duplex mode according to link state */
|
||||
|
||||
link_state = fec->phy->ops->get_link_state (fec->phy);
|
||||
|
||||
/* Set duplex */
|
||||
if (link_state & PHY_LINK_FULL_DUPLEX)
|
||||
{
|
||||
fec->base->tcr |= FEC_TCR_FDEN;
|
||||
fec->base->rcr &= ~FEC_RCR_DRT;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec->base->tcr &= ~FEC_TCR_FDEN;
|
||||
fec->base->rcr |= FEC_RCR_DRT;
|
||||
}
|
||||
|
||||
/* Set RMII 10-Mbps mode. */
|
||||
if (link_state & PHY_LINK_10MBIT)
|
||||
{
|
||||
fec->base->rcr |= FEC_RCR_RMII_10T;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec->base->rcr &= ~FEC_RCR_RMII_10T;
|
||||
}
|
||||
|
||||
/* Clear any pending interrupt */
|
||||
fec->base->eir = 0xffffffff;
|
||||
|
||||
// Disable all interrupts.
|
||||
fec->base->eimr = 0x0;
|
||||
|
||||
/* Configure the amount of data required in the
|
||||
* transmit FIFO before transmission of a frame can begin.
|
||||
*/
|
||||
/* All data in frame (i.e. store-and-forward mode). */
|
||||
fec->base->tfwr = FEC_TFWR_STR_FWD;
|
||||
|
||||
// Reset buffers.
|
||||
fec_buffer_reset ();
|
||||
|
||||
// FEC_ERDSR - Receive buffer descriptor ring start register
|
||||
fec->base->erdsr = (uint32_t)fec->rx_bd_base;
|
||||
|
||||
// FEC_ETDSR - Transmit buffer descriptor ring start register
|
||||
fec->base->etdsr = (uint32_t)fec->tx_bd_base;
|
||||
|
||||
// FEC_EMRBR - Maximum receive buffer size register
|
||||
fec->base->emrbr = RX_BUFFER_SIZE - 1;
|
||||
|
||||
/* Let controller count frames. Useful for debugging */
|
||||
fec->base->mibc = 0x0;
|
||||
|
||||
/* Now enable the transmit and receive processing.
|
||||
*
|
||||
* Also let MAC hardware convert DMA descriptor fields between little endian
|
||||
* (as accessed by ARM core) and big endian (as used internally by the
|
||||
* MAC hardware. Software would otherwise need to do the conversions.
|
||||
*/
|
||||
fec->base->ecr = FEC_ECR_ETHER_EN | FEC_ECR_DBSWP;
|
||||
|
||||
/* Indicate that there have been empty receive buffers produced */
|
||||
// FEC_RDAR - Receive Descriptor ring - Receive descriptor active register
|
||||
fec->base->rdar = 1;
|
||||
|
||||
DPRINT ("Link up. Speed: %s. Mode: %s.\n", fec_ecat_link_speed_name (link_state),
|
||||
fec_ecat_link_duplex_name (link_state));
|
||||
}
|
||||
|
||||
static dev_state_t fec_ecat_probe (void)
|
||||
{
|
||||
uint8_t link_state;
|
||||
|
||||
link_state = fec->phy->ops->get_link_state (fec->phy);
|
||||
|
||||
return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
|
||||
}
|
||||
|
||||
COMPILETIME_ASSERT (FEC_MODULE_CLOCK_Hz >= FEC_MIN_MODULE_CLOCK_Hz);
|
||||
|
||||
int fec_ecat_init (const fec_mac_address_t * mac_address,
|
||||
bool phy_loopback_mode)
|
||||
{
|
||||
dev_state_t state;
|
||||
static const fec_cfg_t eth_cfg =
|
||||
{
|
||||
.base = ENET_BASE,
|
||||
.clock = FEC_MODULE_CLOCK_Hz,
|
||||
.tx_bd_base = fec_tx_bd,
|
||||
.rx_bd_base = fec_rx_bd,
|
||||
.phy_interface = FEC_PHY_RMII,
|
||||
};
|
||||
static phy_cfg_t phy_cfg =
|
||||
{
|
||||
.address = ETH_PHY_ADDRESS,
|
||||
.read = NULL, /* Set by MAC driver */
|
||||
.write = NULL, /* Set by MAC driver */
|
||||
};
|
||||
|
||||
phy_cfg.loopback_mode = phy_loopback_mode;
|
||||
|
||||
/* Initialise buffers and buffer descriptors.*/
|
||||
fec_buffer_init_tx (fec_tx_bd, fec_tx_data, NUM_BUFFERS);
|
||||
fec_buffer_init_rx (fec_rx_bd, fec_rx_data, NUM_BUFFERS);
|
||||
|
||||
fec = malloc (sizeof (fec_t));
|
||||
UASSERT (fec != NULL, EMEM);
|
||||
|
||||
/* Initialise driver state */
|
||||
fec->rx_bd_base = eth_cfg.rx_bd_base;
|
||||
fec->tx_bd_base = eth_cfg.tx_bd_base;
|
||||
fec->clock = eth_cfg.clock;
|
||||
fec->base = (reg_fec_t *)eth_cfg.base;
|
||||
fec->phy_interface = eth_cfg.phy_interface;
|
||||
fec->phy = mii_init (&phy_cfg);
|
||||
fec->phy->arg = fec;
|
||||
fec->phy->read = fec_ecat_read_phy;
|
||||
fec->phy->write = fec_ecat_write_phy;
|
||||
|
||||
/* Initialize hardware */
|
||||
fec_ecat_init_hw (mac_address);
|
||||
state = StateDetached;
|
||||
while (state == StateDetached)
|
||||
{
|
||||
state = fec_ecat_probe ();
|
||||
}
|
||||
fec_ecat_hotplug ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: fec_ecat.h 91 2014-04-02 13:32:29Z rtlfrm $
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup fec EtherCat Ethernet MAC driver for Frescale K60 SoCs.
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef FEC_H
|
||||
#define FEC_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct fec_mac_address
|
||||
{
|
||||
uint8_t octet[6];
|
||||
} fec_mac_address_t;
|
||||
|
||||
int fec_ecat_init (const fec_mac_address_t * mac_address, bool phy_loopback_mode);
|
||||
|
||||
int fec_ecat_send (const void *payload, size_t tot_len);
|
||||
|
||||
int fec_ecat_recv (void * buffer, size_t buffer_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FEC_H */
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
@@ -0,0 +1,423 @@
|
||||
/******************************************************************************
|
||||
* * *** ***
|
||||
* *** *** ***
|
||||
* *** **** ********** *** ***** *** **** *****
|
||||
* ********* ********** *** ********* ************ *********
|
||||
* **** *** *** *** *** **** ***
|
||||
* *** *** ****** *** *********** *** **** *****
|
||||
* *** *** ****** *** ************* *** **** *****
|
||||
* *** **** **** *** *** *** **** ***
|
||||
* *** ******* ***** ************** ************* *********
|
||||
* *** ***** *** ******* ** ** ****** *****
|
||||
* t h e r e a l t i m e t a r g e t e x p e r t s
|
||||
*
|
||||
* http://www.rt-labs.com
|
||||
* Copyright (C) 2006. rt-labs AB, Sweden. All rights reserved.
|
||||
*------------------------------------------------------------------------------
|
||||
* $Id: lw_emac.c 348 2012-10-18 16:41:14Z rtlfrm $
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <kern.h>
|
||||
#include <config.h>
|
||||
#include <bfin_dma.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lw_emac.h"
|
||||
|
||||
/* MII standard registers.
|
||||
See IEEE Std 802.3-2005 clause 22:
|
||||
"Reconciliation Sublayer (RS) and Media Independent Interface (MII)",
|
||||
section 2.4.1 to 3.
|
||||
http://standards.ieee.org/getieee802/download/802.3-2005_section2.pdf */
|
||||
#define MII_BMCR 0x00 /* Basic Mode Control Register */
|
||||
#define MII_BMSR 0x01 /* Basic Mode Status Register */
|
||||
#define MII_PHYIDR1 0x02 /* PHY Identifier Register 1 */
|
||||
#define MII_PHYIDR2 0x03 /* PHY Identifier Register 2 */
|
||||
#define MII_LPAR 0x05 /* Auto-Negotiation Link Partner Ability Register */
|
||||
|
||||
/* MII basic mode control register */
|
||||
#define MII_BMCR_RST BIT (15)
|
||||
#define MII_BMCR_ANEG_EN BIT (12)
|
||||
#define MII_BMCR_ANEG_RST BIT (9)
|
||||
|
||||
/* MII basic mode status register */
|
||||
#define MII_BMSR_ANEGACK BIT (5)
|
||||
#define MII_BMSR_LINK BIT (2)
|
||||
|
||||
/* MII auto-negotiation advertisement register */
|
||||
#define MII_ANAR_100_FD BIT (8) /* Can do 100BASE-TX full duplex */
|
||||
#define MII_ANAR_10_FD BIT (6) /* Can do 10BASE-T full duplex */
|
||||
|
||||
/* Management Data Clock (MDC) frequency.
|
||||
* 2.5Mz corresponds to the minimum period for MDC as defined by the standard.
|
||||
* This is independent of whether the PHY is in 10Mbit/s or 100Mbit/s mode.
|
||||
* See Std. IEEE 802.3 (Ethernet) Clause 22.2.2.11 ("management data clock"). */
|
||||
#define PHY_MDC_Hz 2500000
|
||||
#define PHY_RETRIES 3000
|
||||
/* TODO: This might need changing for different boards. Fix? */
|
||||
#define PHY_ADDR 0
|
||||
|
||||
#define ETH_RX_BUF_SIZE 2
|
||||
#define ETH_TX_BUF_SIZE 1
|
||||
#define ETH_FRAME_SIZE 0x614
|
||||
|
||||
/* Ethernet MAC register structure */
|
||||
typedef struct bfin_emac_regs
|
||||
{
|
||||
/* Control-Status Register Group */
|
||||
uint32_t opmode; /* operating mode */
|
||||
uint32_t addrlo; /* address low */
|
||||
uint32_t addrhi; /* address high */
|
||||
uint32_t hashlo; /* multicast hash table low */
|
||||
uint32_t hashhi; /* multicast hash table high */
|
||||
uint32_t staadd; /* station management address */
|
||||
uint32_t stadat; /* station management data */
|
||||
uint32_t flc; /* flow control */
|
||||
uint32_t vlan1; /* VLAN1 tag */
|
||||
uint32_t vlan2; /* VLAN2 tag */
|
||||
uint32_t _reserved1;
|
||||
uint32_t wkup_ctl; /* wakeup frame control and status */
|
||||
uint32_t wkup_ffmsk[4]; /* wakeup frame n byte mask (n == 0,..,3) */
|
||||
uint32_t wkup_ffcmd; /* wakeup frame filter commands */
|
||||
uint32_t wkup_ffoff; /* wakeup frame filter offsets */
|
||||
uint32_t wkup_ffcrc0; /* wakeup frame filter CRC0/1 */
|
||||
uint32_t wkup_ffcrc1; /* wakeup frame filter CRC2/3 */
|
||||
uint32_t _reserved2[4];
|
||||
/* System Interface Register Group */
|
||||
uint32_t sysctl; /* system control */
|
||||
uint32_t systat; /* system status */
|
||||
/* Ethernet Frame Status Register Group */
|
||||
uint32_t rx_stat; /* RX Current Frame Status */
|
||||
uint32_t rx_stky; /* RX Sticky Frame Status */
|
||||
uint32_t rx_irqe; /* RX Frame Status Interrupt Enable */
|
||||
uint32_t tx_stat; /* TX Current Frame Status Register */
|
||||
uint32_t tx_stky; /* TT Sticky Frame Status */
|
||||
uint32_t tx_irqe; /* TX Frame Status Interrupt Enable */
|
||||
/* MAC Management Counter Register Group */
|
||||
uint32_t mmc_ctl; /* Management Counters Control */
|
||||
uint32_t mmc_rirqs; /* MMC RX Interrupt Status */
|
||||
uint32_t mmc_rirqe; /* MMC RX Interrupt Enable */
|
||||
uint32_t mmc_tirqs; /* MMC TX Interrupt Status */
|
||||
uint32_t mmc_tirqe; /* MMC TX Interrupt Enable */
|
||||
uint32_t _reserved3[27];
|
||||
uint32_t mmc_rxc_ok; /* FramesReceivedOK */
|
||||
uint32_t mmc_rxc_fcs; /* FrameCheckSequenceErrors */
|
||||
uint32_t mmc_rxc_align; /* AlignmentErrors */
|
||||
uint32_t mmc_rxc_octet; /* OctetsReceivedOK */
|
||||
uint32_t mmc_rxc_dmaovf; /* FramesLostDueToIntMACRcvError */
|
||||
uint32_t mmc_rxc_unicst; /* UnicastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_multi; /* MulticastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_broad; /* BroadcastFramesReceivedOK */
|
||||
uint32_t mmc_rxc_lnerri; /* InRangeLengthErrors */
|
||||
uint32_t mmc_rxc_lnerro; /* OutOfRangeLengthField */
|
||||
uint32_t mmc_rxc_long; /* FrameTooLongErrors */
|
||||
uint32_t mmc_rxc_macctl; /* MACControlFramesReceived */
|
||||
uint32_t mmc_rxc_opcode; /* UnsupportedOpcodesReceived */
|
||||
uint32_t mmc_rxc_pause; /* PAUSEMACCtrlFramesReceived */
|
||||
uint32_t mmc_rxc_allfrm; /* FramesReceivedAll */
|
||||
uint32_t mmc_rxc_alloct; /* OctetsReceivedAll */
|
||||
uint32_t mmc_rxc_typed; /* TypedFramesReceived */
|
||||
uint32_t mmc_rxc_short; /* FramesLenLt64Received */
|
||||
uint32_t mmc_rxc_eq64; /* FramesLenEq64Received */
|
||||
uint32_t mmc_rxc_lt128; /* FramesLen65_127Received */
|
||||
uint32_t mmc_rxc_lt256; /* FramesLen128_255Received */
|
||||
uint32_t mmc_rxc_lt512; /* FramesLen256_511Received */
|
||||
uint32_t mmc_rxc_lt1024; /* FramesLen512_1023Received */
|
||||
uint32_t mmc_rxc_ge1024; /* FramesLen1024_MaxReceived */
|
||||
/* TODO: add all registers */
|
||||
} bfin_emac_regs_t;
|
||||
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, opmode) == 0x0);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, staadd) == 0x14);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ctl) == 0x2c);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ffcrc1) == 0x4c);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, sysctl) == 0x60);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, rx_stat) == 0x68);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_ctl) == 0x80);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_tirqe) == 0x90);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ok) == 0x100);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_dmaovf) == 0x110);
|
||||
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ge1024) == 0x15c);
|
||||
|
||||
/* 32-bit aligned struct for ethernet data */
|
||||
typedef struct ethernet_data {
|
||||
uint16_t length; /* When TX = frame data length in bytes, not including 'length'
|
||||
* When RX = 0x0000 padding when using RXDWA
|
||||
*/
|
||||
uint8_t data[ETH_FRAME_SIZE]; /* Hold the ethernet frame */
|
||||
uint16_t padding; /* To make the struct 32-bit aligned */
|
||||
} ethernet_data_t;
|
||||
|
||||
COMPILETIME_ASSERT (sizeof (ethernet_data_t) == 0x618) ;
|
||||
|
||||
static volatile bfin_emac_regs_t * pEth = (bfin_emac_regs_t *) EMAC_OPMODE;
|
||||
|
||||
/* Buffers for rx and tx DMA operations */
|
||||
static ethernet_data_t rxBuffer[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static ethernet_data_t txBuffer[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static volatile uint32_t rxStatusWord[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static volatile uint32_t txStatusWord[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
|
||||
static bfin_dma_descriptor_t rxDMADesc[ETH_RX_BUF_SIZE * 2] __attribute__((section(".dma")));
|
||||
static bfin_dma_descriptor_t txDMADesc[ETH_TX_BUF_SIZE * 2] __attribute__((section(".dma")));
|
||||
|
||||
/* Hold the current index of rx and tx buffers */
|
||||
static uint8_t rxIdx, txIdx;
|
||||
|
||||
|
||||
/* Internal function that writes to a PHY register */
|
||||
static void lw_emac_write_phy_reg(uint8_t phy_addr, uint8_t reg_addr, uint32_t data) {
|
||||
/* Set the flags that should be set reg_addr on PHY */
|
||||
pEth->stadat = data;
|
||||
|
||||
/* Wait for our turn then initiate writing of stadat to PHY register */
|
||||
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STAOP | STABUSY;
|
||||
while (pEth->staadd & STABUSY) ;
|
||||
}
|
||||
|
||||
/* Internal function that reads from; and returns a PHY register */
|
||||
static uint32_t lw_emac_read_phy_reg(uint8_t phy_addr, uint8_t reg_addr) {
|
||||
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STABUSY;
|
||||
while (pEth->staadd & STABUSY) ;
|
||||
|
||||
return pEth->stadat;
|
||||
}
|
||||
|
||||
/* Internal function that sets the MAC address */
|
||||
static void lw_emac_set_mac_addr(uint8_t * ethAddr)
|
||||
{
|
||||
pEth->addrlo =
|
||||
ethAddr[0] |
|
||||
ethAddr[1] << 8 |
|
||||
ethAddr[2] << 16 |
|
||||
ethAddr[3] << 24;
|
||||
pEth->addrhi =
|
||||
ethAddr[4] |
|
||||
ethAddr[5] << 8;
|
||||
}
|
||||
|
||||
static uint8_t lw_emac_init_registers(uint8_t * ethAddr) {
|
||||
uint32_t clock_divisor, sysctl_mdcdiv, phy_stadat, counter;
|
||||
|
||||
/* CONFIGURE MAC MII PINS */
|
||||
|
||||
/* Enable PHY Clock Output */
|
||||
*(volatile uint16_t *) VR_CTL |= PHYCLKOE;
|
||||
|
||||
/* Set all bits to 1 to use MII mode */
|
||||
*(uint16_t *) PORTH_FER = 0xFFFF;
|
||||
|
||||
/* CONFIGURE MAC REGISTERS */
|
||||
|
||||
/*
|
||||
* Set the MMC (MAC Management Counter) control register
|
||||
* RTSC = Clear all counters
|
||||
*
|
||||
* Note that counters are not enabled at this time
|
||||
*/
|
||||
pEth->mmc_ctl = RSTC;
|
||||
|
||||
/* Set MAC address */
|
||||
lw_emac_set_mac_addr (ethAddr);
|
||||
|
||||
clock_divisor = SCLK / PHY_MDC_Hz;
|
||||
sysctl_mdcdiv = clock_divisor / 2 - 1;
|
||||
ASSERT (sysctl_mdcdiv <= 0x3f);
|
||||
|
||||
rprintp ("PHY ID: %04x %04x\n",
|
||||
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR1),
|
||||
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR2)
|
||||
);
|
||||
|
||||
/*
|
||||
* Set the system control register
|
||||
* SET_MDCDIV(x) = Set MDC to 2.5 MHz
|
||||
* RXDWA = Pad incoming frame with 0x0000 as to make the data-part 32-bit aligned
|
||||
* RXCKS = Enable Receive Frame TCP/UDP Checksum Computation
|
||||
*/
|
||||
pEth->sysctl = SET_MDCDIV(sysctl_mdcdiv) | RXDWA ;
|
||||
|
||||
/* CONFIGURE PHY */
|
||||
|
||||
/*
|
||||
* Set the PHY basic control register
|
||||
* MII_BMCR_ANEG_EN = Auto negotiation on
|
||||
* MII_BMCR_ANEG_RST = Restart the auto-neg process by setting
|
||||
* Speed handled by auto negotiation
|
||||
*/
|
||||
phy_stadat = MII_BMCR_ANEG_EN | MII_BMCR_ANEG_RST;
|
||||
lw_emac_write_phy_reg(PHY_ADDR, MII_BMCR, phy_stadat);
|
||||
|
||||
/* Loop until link is up or time out after PHY_RETRIES */
|
||||
counter = 0;
|
||||
do {
|
||||
if (counter > PHY_RETRIES) {
|
||||
rprintp("Ethernet link is down\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
task_delay (tick_from_ms (10));
|
||||
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_BMSR);
|
||||
|
||||
++counter;
|
||||
} while (!(phy_stadat & MII_BMSR_LINK)) ;
|
||||
|
||||
/* Check whether link partner can do full duplex or not */
|
||||
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_LPAR);
|
||||
|
||||
if (phy_stadat & (MII_ANAR_100_FD | MII_ANAR_10_FD) ) {
|
||||
pEth->opmode = FDMODE;
|
||||
}
|
||||
else {
|
||||
pEth->opmode = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup DMA MAC receive/transfer channels with XCOUNT 0 (which we use
|
||||
* together with description based DMA) and XMODIFY 4 (bytes per transfer)
|
||||
*/
|
||||
bfin_dma_channel_init(DMA_CHANNEL_EMAC_RX, 0, 4);
|
||||
bfin_dma_channel_init(DMA_CHANNEL_EMAC_TX, 0, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bfin_EMAC_init (uint8_t *ethAddr)
|
||||
{
|
||||
rxIdx = txIdx = 0;
|
||||
|
||||
if (lw_emac_init_registers(ethAddr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reset status words
|
||||
memset ((uint8_t *)rxStatusWord, 0, sizeof(rxStatusWord));
|
||||
memset ((uint8_t *)txStatusWord, 0, sizeof(txStatusWord));
|
||||
|
||||
txDMADesc[0].next = &txDMADesc[1];
|
||||
txDMADesc[0].start_addr = &txBuffer[0];
|
||||
txDMADesc[0].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
txDMADesc[1].next = &txDMADesc[0];
|
||||
txDMADesc[1].start_addr = &txStatusWord[0];
|
||||
txDMADesc[1].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(0) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_STOP);
|
||||
|
||||
rxDMADesc[0].next = &rxDMADesc[1];
|
||||
rxDMADesc[0].start_addr = &rxBuffer[0];
|
||||
rxDMADesc[0].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[1].next = &rxDMADesc[2];
|
||||
rxDMADesc[1].start_addr = &rxStatusWord[0];
|
||||
rxDMADesc[1].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[2].next = &rxDMADesc[3];
|
||||
rxDMADesc[2].start_addr = &rxBuffer[1];
|
||||
rxDMADesc[2].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
rxDMADesc[3].next = &rxDMADesc[0];
|
||||
rxDMADesc[3].start_addr = &rxStatusWord[1];
|
||||
rxDMADesc[3].config = DMA_CONFIG_DMA_EN |
|
||||
DMA_CONFIG_WNR |
|
||||
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
|
||||
DMA_CONFIG_NDSIZE(5) |
|
||||
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
|
||||
|
||||
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_RX, &rxDMADesc[0]);
|
||||
|
||||
/* Enable Receiving, Automatic Pad Stripping and receiving of frames with length < 64 butes */
|
||||
pEth->opmode |= ASTP | PSF | RE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int bfin_EMAC_send (void *packet, int length)
|
||||
{
|
||||
UASSERT(length > 0, EARG);
|
||||
UASSERT(length < ETH_FRAME_SIZE, EARG);
|
||||
|
||||
/* TODO: Check DMA Error in IRQ status */
|
||||
|
||||
while (bfin_dma_channel_interrupt_is_active (DMA_CHANNEL_EMAC_TX));
|
||||
|
||||
txBuffer[txIdx].length = length;
|
||||
memcpy(txBuffer[txIdx].data, packet, length);
|
||||
|
||||
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_TX, txDMADesc);
|
||||
pEth->opmode |= TE;
|
||||
|
||||
while ((txStatusWord[txIdx] & TX_COMP) == 0);
|
||||
|
||||
ASSERT(txStatusWord[txIdx] & TX_OK);
|
||||
|
||||
txStatusWord[txIdx] = 0;
|
||||
|
||||
++txIdx;
|
||||
|
||||
if (txIdx == ETH_TX_BUF_SIZE) {
|
||||
txIdx = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bfin_EMAC_recv (uint8_t * packet, size_t size)
|
||||
{
|
||||
uint32_t length;
|
||||
uint32_t status = rxStatusWord[rxIdx];
|
||||
|
||||
/* Check if rx frame is completed */
|
||||
if ((status & RX_COMP) == 0) {
|
||||
return -1;
|
||||
}
|
||||
else if ((status & RX_OK) == 0) {
|
||||
ASSERT(0);
|
||||
/* TODO: Handle error */
|
||||
return -1;
|
||||
}
|
||||
else if (status & RX_DMAO) {
|
||||
ASSERT(0);
|
||||
/* TODO: Handle overrun */
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = status & RX_FRLEN;
|
||||
|
||||
if (size < length) {
|
||||
length = size;
|
||||
}
|
||||
|
||||
memcpy(packet, rxBuffer[rxIdx].data, length);
|
||||
|
||||
bfin_dma_channel_interrupt_clear (DMA_CHANNEL_EMAC_RX);
|
||||
rxStatusWord[rxIdx] = 0;
|
||||
|
||||
++rxIdx;
|
||||
|
||||
if (rxIdx == ETH_RX_BUF_SIZE) {
|
||||
rxIdx = 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* author: Tomas Vestelind
|
||||
*/
|
||||
|
||||
#ifndef LWIP_MAC_H
|
||||
#define LWIP_MAC_H
|
||||
|
||||
int bfin_EMAC_init(uint8_t *enetaddr);
|
||||
int bfin_EMAC_send(void *packet, int length);
|
||||
int bfin_EMAC_recv(uint8_t * packet, size_t size);
|
||||
|
||||
#endif /* LWIP_MAC_H */
|
||||
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <kern.h>
|
||||
#include <ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "osal.h"
|
||||
#include "oshw.h"
|
||||
|
||||
|
||||
#include "lw_mac/lw_emac.h"
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
int rVal;
|
||||
int *psock;
|
||||
|
||||
port->getindex_mutex = mtx_create();
|
||||
port->tx_mutex = mtx_create();
|
||||
port->rx_mutex = mtx_create();
|
||||
|
||||
rVal = bfin_EMAC_init((uint8_t *)priMAC);
|
||||
if (rVal != 0)
|
||||
return 0;
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = -1;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->getindex_mutex = mtx_create();
|
||||
port->tx_mutex = mtx_create();
|
||||
port->rx_mutex = mtx_create();
|
||||
port->sockhandle = -1;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
if (port->sockhandle >= 0)
|
||||
{
|
||||
close(port->sockhandle);
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle >= 0))
|
||||
{
|
||||
close(port->redport->sockhandle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = oshw_htons(0xffff);
|
||||
bp->da1 = oshw_htons(0xffff);
|
||||
bp->da2 = oshw_htons(0xffff);
|
||||
bp->sa0 = oshw_htons(priMAC[0]);
|
||||
bp->sa1 = oshw_htons(priMAC[1]);
|
||||
bp->sa2 = oshw_htons(priMAC[2]);
|
||||
bp->etype = oshw_htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
mtx_lock (port->getindex_mutex);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
}
|
||||
port->lastidx = idx;
|
||||
|
||||
mtx_unlock (port->getindex_mutex);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = bfin_EMAC_send((*stack->txbuf)[idx], lp);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = oshw_htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
mtx_lock (port->tx_mutex);
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = oshw_htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
|
||||
// OBS! redundant not ACTIVE for BFIN, just added to compile
|
||||
ASSERT (0);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
bfin_EMAC_send(&(port->txbuf2), port->txbuflength2);
|
||||
mtx_unlock (port->tx_mutex);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
bytesrx = bfin_EMAC_recv((*stack->tempbuf), lp);
|
||||
port->tempinbufs = bytesrx;
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtx_lock (port->rx_mutex);
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == oshw_htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mtx_unlock (port->rx_mutex);
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert timer)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
}
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME)
|
||||
{
|
||||
primrx = port->rxsa[idx];
|
||||
}
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
secrx = port->redport->rxsa[idx];
|
||||
}
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
osal_timert read_timer;
|
||||
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start(&read_timer, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start(&timer, timeout);
|
||||
do
|
||||
{
|
||||
osal_timert read_timer;
|
||||
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET));
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, read_timer);
|
||||
/* wait for answer with WKC>0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
int *sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
int sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
mtx_t * getindex_mutex;
|
||||
mtx_t * tx_mutex;
|
||||
mtx_t * rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int stacknumber);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include "oshw.h"
|
||||
#include <stdlib.h>
|
||||
#include <lwip/inet.h>
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(const uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(const uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
|
||||
/* TODO if needed */
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
/* TODO if needed */
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <kern.h>
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
|
||||
uint16 oshw_htons(uint16 host);
|
||||
uint16 oshw_ntohs(uint16 network);
|
||||
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT VxWorks SNARF Mux driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* If EtherCAT is run in parallel with normal IP traffic and EtherCAT have a
|
||||
* dedicated NIC, instantiate an extra tNetX task and redirect the NIC workQ
|
||||
* to be handle by the extra tNetX task, if needed raise the tNetX task prio.
|
||||
* This prevents from having tNet0 becoming a bottleneck.
|
||||
*
|
||||
* The "redundant" option will configure two Mux drivers and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resend through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <muxLib.h>
|
||||
#include <ipProto.h>
|
||||
#include <wvLib.h>
|
||||
#include <sysLib.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
#include "nicdrv.h"
|
||||
|
||||
#define NIC_DEBUG /* Print debugging info? */
|
||||
|
||||
// wvEvent flags
|
||||
#define ECAT_RECV_FAILED 0x664
|
||||
#define ECAT_RECV_OK 0x665
|
||||
#define ECAT_RECV_RETRY_OK 0x666
|
||||
#define ECAT_STACK_RECV 0x667
|
||||
#define ECAT_SEND_START 0x675
|
||||
#define ECAT_SEND_COMPLETE 0x676
|
||||
#define ECAT_SEND_FAILED 0x677
|
||||
|
||||
#ifdef NIC_DEBUG
|
||||
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f) \
|
||||
do { \
|
||||
logMsg (x,a,b,c,d,e,f); \
|
||||
} while (0)
|
||||
#define NIC_WVEVENT(a,b,c) \
|
||||
do { \
|
||||
wvEvent(a, b, c); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#else
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f)
|
||||
#define NIC_WVEVENT(a,b,c)
|
||||
#endif /* NIC_DEBUG */
|
||||
|
||||
#define IF_NAME_SIZE 8
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to find out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
/* usec per tick for timeconversion, default to 1kHz */
|
||||
#define USECS_PER_SEC 1000000
|
||||
static unsigned int usec_per_tick = 1000;
|
||||
|
||||
/** Receive hook called by Mux driver. */
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg);
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
void print_nicversion(void)
|
||||
{
|
||||
printf("Generic is used\n");
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "gei0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
char ifn[IF_NAME_SIZE];
|
||||
int unit_no = -1;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
/* Get systick info, sysClkRateGet return ticks per second */
|
||||
usec_per_tick = USECS_PER_SEC / sysClkRateGet();
|
||||
/* Don't allow 0 since it is used in DIV */
|
||||
if(usec_per_tick == 0)
|
||||
usec_per_tick = 1;
|
||||
/* Make reference to packet device struct, keep track if the packet
|
||||
* device is the redundant or not.
|
||||
*/
|
||||
if (secondary)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
pPktDev->redundant = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPktDev = &(port->pktDev);
|
||||
pPktDev->redundant = 0;
|
||||
}
|
||||
|
||||
/* Clear frame counters*/
|
||||
pPktDev->tx_count = 0;
|
||||
pPktDev->rx_count = 0;
|
||||
pPktDev->overrun_count = 0;
|
||||
|
||||
/* Create multi-thread support semaphores */
|
||||
port->sem_get_index = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
|
||||
|
||||
/* Get the dev name and unit from ifname
|
||||
* We assume form gei1, fei0...
|
||||
*/
|
||||
memset(ifn,0x0,sizeof(ifn));
|
||||
|
||||
for(i=0; i < strlen(ifname);i++)
|
||||
{
|
||||
if(isdigit(ifname[i]))
|
||||
{
|
||||
strncpy(ifn, ifname, i);
|
||||
unit_no = atoi(&ifname[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach IP stack */
|
||||
//ipDetach(pktDev.unit,pktDev.name);
|
||||
|
||||
pPktDev->port = port;
|
||||
|
||||
/* Bind to mux driver for given interface, include ethercat driver pointer
|
||||
* as user reference
|
||||
*/
|
||||
/* Bind to mux */
|
||||
pPktDev->pCookie = muxBind(ifn,
|
||||
unit_no,
|
||||
mux_rx_callback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
MUX_PROTO_SNARF,
|
||||
"ECAT SNARF",
|
||||
pPktDev);
|
||||
|
||||
if (pPktDev->pCookie == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Get reference tp END obje */
|
||||
pPktDev->endObj = endFindByName(ifn, unit_no);
|
||||
|
||||
if (port->pktDev.endObj == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->redport->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: Redundant port not allocated",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
}
|
||||
|
||||
/* setup ethernet headers in tx buffers so we don't have to repeat it */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
|
||||
exit:
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
int i;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
M_BLK_ID trash_can;
|
||||
|
||||
pPktDev = &(port->pktDev);
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
if (msgQReceive(port->msgQId[i],
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
|
||||
2, 3, 4, 5, 6);
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
msgQDelete(port->msgQId[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
|
||||
/* Clean redundant resources if available*/
|
||||
if (port->redport)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->redport->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
if (msgQReceive(port->redport->msgQId[i],
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
|
||||
2, 3, 4, 5, 6);
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
msgQDelete(port->redport->msgQId[i]);
|
||||
}
|
||||
}
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
semTake(port->sem_get_index, WAIT_FOREVER);
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
semGive(port->sem_get_index);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Low level transmit buffer over mux layer 2 driver
|
||||
*
|
||||
* @param[in] pPktDev = packet device to send buffer over
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] buf = buff to send
|
||||
* @param[in] len = bytes to send
|
||||
* @return driver send result
|
||||
*/
|
||||
static int ec_outfram_send(ETHERCAT_PKT_DEV * pPktDev, uint8 idx, void * buf, int len)
|
||||
{
|
||||
STATUS status = OK;
|
||||
M_BLK_ID pPacket = NULL;
|
||||
int rval = 0;
|
||||
END_OBJ *endObj = (END_OBJ *)pPktDev->endObj;
|
||||
MSG_Q_ID msgQId;
|
||||
|
||||
/* Clean up any abandoned frames and re-use the allocated buffer*/
|
||||
msgQId = pPktDev->port->msgQId[idx];
|
||||
if(msgQNumMsgs(msgQId) > 0)
|
||||
{
|
||||
pPktDev->abandoned_count++;
|
||||
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ abandoned\n", idx,
|
||||
2, 3, 4, 5, 6);
|
||||
if (msgQReceive(msgQId,
|
||||
(char *)&pPacket,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) == ERROR)
|
||||
{
|
||||
pPacket = NULL;
|
||||
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ mBlk handled by receiver\n", idx,
|
||||
2, 3, 4, 5, 6);
|
||||
}
|
||||
}
|
||||
|
||||
if (pPacket == NULL)
|
||||
{
|
||||
/* Allocate m_blk to send */
|
||||
if ((pPacket = netTupleGet(endObj->pNetPool,
|
||||
len,
|
||||
M_DONTWAIT,
|
||||
MT_DATA,
|
||||
TRUE)) == NULL)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: Could not allocate MBLK memory!\n", 1, 2, 3, 4, 5, 6);
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pPacket->mBlkHdr.mLen = len;
|
||||
pPacket->mBlkHdr.mFlags |= M_PKTHDR;
|
||||
pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf;
|
||||
pPacket->mBlkPktHdr.len = pPacket->m_len;
|
||||
|
||||
netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL);
|
||||
status = muxSend(endObj, pPacket);
|
||||
|
||||
if (status == OK)
|
||||
{
|
||||
rval = len;
|
||||
NIC_WVEVENT(ECAT_SEND_COMPLETE, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
else
|
||||
{
|
||||
netMblkClChainFree(pPacket);
|
||||
/* fail */
|
||||
static int print_once;
|
||||
if (print_once == 0)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: failed\n",
|
||||
1, 2, 3, 4, 5, 6);
|
||||
print_once = 1;
|
||||
}
|
||||
NIC_WVEVENT(ECAT_SEND_FAILED, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit buffer over mux layer 2 driver, passing buffer
|
||||
* and packet device to send on as arguments
|
||||
* @param[in] port = port context holding reference to packet device
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int rval = 0;
|
||||
ec_stackT *stack;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
pPktDev = &(port->pktDev);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
}
|
||||
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = ec_outfram_send(pPktDev, idx, (char*)(*stack->txbuf)[idx],
|
||||
(*stack->txbuflength)[idx]);
|
||||
if (rval > 0)
|
||||
{
|
||||
port->pktDev.tx_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit frame to send as index.
|
||||
* @param[in] port = port context
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary interface */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
rval = ec_outfram_send(&(port->redport->pktDev), idx, &(port->txbuf2), port->txbuflength2);
|
||||
if (rval <= 0)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
/** Call back routine registered as hook with mux layer 2 driver
|
||||
* @param[in] pCookie = Mux cookie
|
||||
* @param[in] type = received type
|
||||
* @param[in] pMblk = the received packet reference
|
||||
* @param[in] llHdrInfo = header info
|
||||
* @param[in] muxUserArg = assigned reference to packet device when init called
|
||||
* @return TRUE if frame was successfully read and passed to MsgQ
|
||||
*/
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
uint8 idxf;
|
||||
ec_comt *ecp;
|
||||
ec_bufT * tempbuf;
|
||||
ecx_portt * port;
|
||||
MSG_Q_ID msgQId;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
int length;
|
||||
int bufstat;
|
||||
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (type == ETH_P_ECAT)
|
||||
{
|
||||
length = pMblk->mBlkHdr.mLen;
|
||||
tempbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
pPktDev = (ETHERCAT_PKT_DEV *)muxUserArg;
|
||||
port = pPktDev->port;
|
||||
|
||||
/* Get ethercat frame header */
|
||||
ecp = (ec_comt*)&(*tempbuf)[ETH_HEADERSIZE];
|
||||
idxf = ecp->index;
|
||||
if (idxf >= EC_MAXBUF)
|
||||
{
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d out of bounds\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if it is the redundant port or not */
|
||||
if (pPktDev->redundant == 1)
|
||||
{
|
||||
bufstat = port->redport->rxbufstat[idxf];
|
||||
msgQId = port->redport->msgQId[idxf];
|
||||
}
|
||||
else
|
||||
{
|
||||
bufstat = port->rxbufstat[idxf];
|
||||
msgQId = port->msgQId[idxf];
|
||||
}
|
||||
|
||||
/* Check length and if someone expects the frame */
|
||||
if (length > 0 && bufstat == EC_BUF_TX)
|
||||
{
|
||||
/* Post the frame to the receive Q for the EtherCAT stack */
|
||||
STATUS status;
|
||||
status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID),
|
||||
NO_WAIT, MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
else if ((status == ERROR) && (errno == S_objLib_OBJ_UNAVAILABLE))
|
||||
{
|
||||
/* Try to empty the MSGQ since we for some strange reason
|
||||
* already have a frame in the MsqQ,
|
||||
* is it due to timeout when receiving?
|
||||
* We want the latest received frame in the buffer
|
||||
*/
|
||||
port->pktDev.overrun_count++;
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d MsgQ overrun\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
M_BLK_ID trash_can;
|
||||
if (msgQReceive(msgQId,
|
||||
(char *)&trash_can,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT) != ERROR)
|
||||
{
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
status = msgQSend(msgQId,
|
||||
(char *)&pMblk,
|
||||
sizeof(M_BLK_ID),
|
||||
NO_WAIT,
|
||||
MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Non blocking or blocking read, if we use timeout we pass minimum 1 tick.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] pMblk = mBlk for received frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, uint8 idx, int stacknumber, M_BLK_ID * pMblk, int timeout)
|
||||
{
|
||||
int bytesrx = 0;
|
||||
MSG_Q_ID msgQId;
|
||||
int tick_timeout = max((timeout / usec_per_tick), 1);
|
||||
|
||||
if (stacknumber == 1)
|
||||
{
|
||||
msgQId = port->redport->msgQId[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
msgQId = port->msgQId[idx];
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), NO_WAIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), tick_timeout);
|
||||
}
|
||||
|
||||
if (bytesrx > 0)
|
||||
{
|
||||
bytesrx = (*pMblk)->mBlkHdr.mLen;
|
||||
NIC_WVEVENT(ECAT_STACK_RECV, (char *)&bytesrx, sizeof(bytesrx));
|
||||
}
|
||||
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. Frames are received by separate receiver
|
||||
* task tNet0 (default), tNet0 fetch what frame index and store a reference to the
|
||||
* received frame in matching MsgQ. The stack user tasks fetch the frame
|
||||
* reference and copies the frame the the RX buffer, when done it free
|
||||
* the frame buffer allocated by the Mux.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber, int timeout)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
ec_bufT *tempinbuf;
|
||||
M_BLK_ID pMblk;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* (non-) blocking call to retrieve frame from Muxlayer */
|
||||
if (ecx_recvpkt(port, idx, stacknumber, &pMblk, timeout))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
|
||||
/* Get pointer to the frame */
|
||||
tempinbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
/* Get pointer to the Ethernet header */
|
||||
ehp = (ec_etherheadert*)tempinbuf;
|
||||
/* Get pointer to the EtherCAT frame as ethernet payload */
|
||||
ecp = (ec_comt*)&(*tempinbuf)[ETH_HEADERSIZE];
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
netMblkOffsetToBufCopy(pMblk, ETH_HEADERSIZE,(void *) rxbuf,
|
||||
(*stack->txbuflength)[idx] - ETH_HEADERSIZE, NULL);
|
||||
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
netMblkClChainFree(pMblk);
|
||||
port->pktDev.rx_count++;
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timer = absolute timeout time
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer, int timeout)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
{
|
||||
wkc2 = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
{
|
||||
wkc = ecx_inframe(port, idx, 0, timeout);
|
||||
}
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
{
|
||||
wkc2 = ecx_inframe(port, idx, 1, timeout);
|
||||
}
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1, timeout);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer, timeout);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2, timeout);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
#ifdef EC_VER1
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber, int timeout)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber, timeout);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <vxWorks.h>
|
||||
|
||||
/** structure to connect EtherCAT stack and VxWorks device */
|
||||
typedef struct ETHERCAT_PKT_DEV
|
||||
{
|
||||
struct ecx_port *port;
|
||||
void *pCookie;
|
||||
void *endObj;
|
||||
UINT32 redundant;
|
||||
UINT32 tx_count;
|
||||
UINT32 rx_count;
|
||||
UINT32 overrun_count;
|
||||
UINT32 abandoned_count;
|
||||
}ETHERCAT_PKT_DEV;
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct ecx_redport
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct ecx_port
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
/** Semaphore to protect single resources */
|
||||
SEM_ID sem_get_index;
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "oshw.h"
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons(uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs(uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters(void)
|
||||
{
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters(ec_adaptert * adapter)
|
||||
{
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons(uint16 hostshort);
|
||||
uint16 oshw_ntohs(uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters(void);
|
||||
void oshw_free_adapters(ec_adaptert * adapter);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT RAW socket driver.
|
||||
*
|
||||
* Low level interface functions to send and receive EtherCAT packets.
|
||||
* EtherCAT has the property that packets are only send by the master,
|
||||
* and the send packets always return in the receive buffer.
|
||||
* There can be multiple packets "on the wire" before they return.
|
||||
* To combine the received packets with the original send packets a buffer
|
||||
* system is installed. The identifier is put in the index item of the
|
||||
* EtherCAT header. The index is stored and compared when a frame is received.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* The socket layer can exhibit a reversal in the packet order (rare).
|
||||
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
|
||||
* will reorder the packets automatically.
|
||||
*
|
||||
* The "redundant" option will configure two sockets and two NIC interfaces.
|
||||
* Slaves are connected to both interfaces, one on the IN port and one on the
|
||||
* OUT port. Packets are send via both interfaces. Any one of the connections
|
||||
* (also an interconnect) can be removed and the slaves are still serviced with
|
||||
* packets. The software layer will detect the possible failure modes and
|
||||
* compensate. If needed the packets from interface A are resent through interface B.
|
||||
* This layer is fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
#include "ethercattype.h"
|
||||
#include <Mmsystem.h>
|
||||
#include "nicdrv.h"
|
||||
#include "osal_win32.h"
|
||||
|
||||
#endif
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connection */
|
||||
ECT_RED_DOUBLE
|
||||
};
|
||||
|
||||
/** Primary source MAC address used for EtherCAT.
|
||||
* This address is not the MAC address used from the NIC.
|
||||
* EtherCAT does not care about MAC addressing, but it is used here to
|
||||
* differentiate the route the packet traverses through the EtherCAT
|
||||
* segment. This is needed to fund out the packet flow in redundant
|
||||
* configurations. */
|
||||
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
|
||||
/** Secondary source MAC address used for EtherCAT. */
|
||||
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
|
||||
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_PRIM priMAC[1]
|
||||
/** second MAC word is used for identification */
|
||||
#define RX_SEC secMAC[1]
|
||||
|
||||
static char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "eth0"
|
||||
* @param[in] secondary = if >0 then use secondary stack instead of primary
|
||||
* @return >0 if succeeded
|
||||
*/
|
||||
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
||||
{
|
||||
int i;
|
||||
pcap_t **psock;
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
/* when using secondary socket it is automatically a redundant setup */
|
||||
psock = &(port->redport->sockhandle);
|
||||
*psock = NULL;
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.sock = &(port->redport->sockhandle);
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeCriticalSection(&(port->getindex_mutex));
|
||||
InitializeCriticalSection(&(port->tx_mutex));
|
||||
InitializeCriticalSection(&(port->rx_mutex));
|
||||
port->sockhandle = NULL;
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.sock = &(port->sockhandle);
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.tempbuf = &(port->tempinbuf);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
|
||||
psock = &(port->sockhandle);
|
||||
}
|
||||
/* we use pcap socket to send RAW packets in windows user mode*/
|
||||
*psock = pcap_open(ifname, 65536, PCAP_OPENFLAG_PROMISCUOUS |
|
||||
PCAP_OPENFLAG_MAX_RESPONSIVENESS |
|
||||
PCAP_OPENFLAG_NOCAPTURE_LOCAL, -1, NULL , errbuf);
|
||||
if (NULL == *psock)
|
||||
{
|
||||
printf("interface %s could not open with pcap\n", ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
ec_setupheader(&(port->txbuf[i]));
|
||||
port->rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
ec_setupheader(&(port->txbuf2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
timeEndPeriod(1);
|
||||
|
||||
if (port->sockhandle != NULL)
|
||||
{
|
||||
DeleteCriticalSection(&(port->getindex_mutex));
|
||||
DeleteCriticalSection(&(port->tx_mutex));
|
||||
DeleteCriticalSection(&(port->rx_mutex));
|
||||
pcap_close(port->sockhandle);
|
||||
port->sockhandle = NULL;
|
||||
}
|
||||
if ((port->redport) && (port->redport->sockhandle != NULL))
|
||||
{
|
||||
pcap_close(port->redport->sockhandle);
|
||||
port->redport->sockhandle = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is always broadcast.
|
||||
* Ethertype is always ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = htons(ETH_P_ECAT);
|
||||
}
|
||||
|
||||
/** Get new frame identifier index and allocate corresponding rx buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @return new index.
|
||||
*/
|
||||
uint8 ecx_getindex(ecx_portt *port)
|
||||
{
|
||||
uint8 idx;
|
||||
uint8 cnt;
|
||||
|
||||
EnterCriticalSection(&(port->getindex_mutex));
|
||||
|
||||
idx = port->lastidx + 1;
|
||||
/* index can't be larger than buffer array */
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
/* try to find unused index */
|
||||
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
||||
{
|
||||
idx++;
|
||||
cnt++;
|
||||
if (idx >= EC_MAXBUF)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
port->lastidx = idx;
|
||||
|
||||
LeaveCriticalSection(&(port->getindex_mutex));
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** Set rx buffer status.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in buffer array
|
||||
* @param[in] bufstat = status to set
|
||||
*/
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
|
||||
{
|
||||
port->rxbufstat[idx] = bufstat;
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
port->redport->rxbufstat[idx] = bufstat;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @param[in] stacknumber = 0=Primary 1=Secondary stack
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
int lp, rval;
|
||||
ec_stackT *stack;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = (*stack->txbuflength)[idx];
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
|
||||
if (rval == PCAP_ERROR)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Transmit buffer over socket (non blocking).
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index in tx buffer array
|
||||
* @return socket send result
|
||||
*/
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx)
|
||||
{
|
||||
ec_comt *datagramP;
|
||||
ec_etherheadert *ehp;
|
||||
int rval;
|
||||
|
||||
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
|
||||
/* rewrite MAC source address 1 to primary */
|
||||
ehp->sa1 = htons(priMAC[1]);
|
||||
/* transmit over primary socket*/
|
||||
rval = ecx_outframe(port, idx, 0);
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
EnterCriticalSection( &(port->tx_mutex) );
|
||||
ehp = (ec_etherheadert *)&(port->txbuf2);
|
||||
/* use dummy frame for secondary socket transmit (BRD) */
|
||||
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
|
||||
/* write index to frame */
|
||||
datagramP->index = idx;
|
||||
/* rewrite MAC source address 1 to secondary */
|
||||
ehp->sa1 = htons(secMAC[1]);
|
||||
/* transmit over secondary socket */
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
|
||||
}
|
||||
LeaveCriticalSection( &(port->tx_mutex) );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Non blocking read of socket. Put frame in temporary buffer.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
|
||||
{
|
||||
int lp, bytesrx;
|
||||
ec_stackT *stack;
|
||||
struct pcap_pkthdr * header;
|
||||
unsigned char const * pkt_data;
|
||||
int res;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
lp = sizeof(port->tempinbuf);
|
||||
|
||||
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
|
||||
if (res <=0 )
|
||||
{
|
||||
port->tempinbufs = 0;
|
||||
return 0;
|
||||
}
|
||||
bytesrx = header->len;
|
||||
if (bytesrx > lp)
|
||||
{
|
||||
bytesrx = lp;
|
||||
}
|
||||
memcpy(*stack->tempbuf, pkt_data, bytesrx);
|
||||
port->tempinbufs = bytesrx;
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. To compensate for received frames that
|
||||
* are out-of-order all frames are stored in their respective indexed buffer.
|
||||
* If a frame was placed in the buffer previously, the function retrieves it
|
||||
* from that buffer index without calling ec_recvpkt. If the requested index
|
||||
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
|
||||
* three options now, 1 no frame read, so exit. 2 frame read but other
|
||||
* than requested index, store in buffer and exit. 3 frame read with matching
|
||||
* index, store in buffer, set completed flag in buffer status and exit.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME or EC_OTHERFRAME.
|
||||
*/
|
||||
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
uint8 idxf;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* check if requested index is already in buffer ? */
|
||||
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
||||
{
|
||||
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&(port->rx_mutex));
|
||||
/* non blocking call to retrieve frame from socket */
|
||||
if (ecx_recvpkt(port, stacknumber))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
ehp =(ec_etherheadert*)(stack->tempbuf);
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (ehp->etype == htons(ETH_P_ECAT))
|
||||
{
|
||||
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
idxf = ecp->index;
|
||||
/* found index equals requested index ? */
|
||||
if (idxf == idx)
|
||||
{
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
|
||||
/* return WKC */
|
||||
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
|
||||
/* mark as completed */
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
|
||||
/* store MAC source word 1 for redundant routing info */
|
||||
(*stack->rxsa)[idx] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if index exist and someone is waiting for it */
|
||||
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
|
||||
{
|
||||
rxbuf = &(*stack->rxbuf)[idxf];
|
||||
/* put it in the buffer array (strip ethernet header) */
|
||||
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
|
||||
/* mark as received */
|
||||
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
|
||||
(*stack->rxsa)[idxf] = ntohs(ehp->sa1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* strange things happened */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection( &(port->rx_mutex) );
|
||||
|
||||
}
|
||||
|
||||
/* WKC if matching frame found */
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Blocking redundant receive frame function. If redundant mode is not active then
|
||||
* it skips the secondary stack and redundancy functions. In redundant mode it waits
|
||||
* for both (primary and secondary) frames to come in. The result goes in an decision
|
||||
* tree that decides, depending on the route of the packet and its possible missing arrival,
|
||||
* how to reroute the original packet to get the data in an other try.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] tvs = timeout
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
|
||||
{
|
||||
osal_timert timer2;
|
||||
int wkc = EC_NOFRAME;
|
||||
int wkc2 = EC_NOFRAME;
|
||||
int primrx, secrx;
|
||||
|
||||
/* if not in redundant mode then always assume secondary is OK */
|
||||
if (port->redstate == ECT_RED_NONE)
|
||||
wkc2 = 0;
|
||||
do
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc <= EC_NOFRAME)
|
||||
wkc = ecx_inframe(port, idx, 0);
|
||||
/* only try secondary if in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* only read frame if not already in */
|
||||
if (wkc2 <= EC_NOFRAME)
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
}
|
||||
/* wait for both frames to arrive or timeout */
|
||||
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
|
||||
/* only do redundant functions when in redundant mode */
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
/* primrx if the received MAC source on primary socket */
|
||||
primrx = 0;
|
||||
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
|
||||
/* secrx if the received MAC source on psecondary socket */
|
||||
secrx = 0;
|
||||
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
|
||||
|
||||
/* primary socket got secondary frame and secondary socket got primary frame */
|
||||
/* normal situation in redundant mode */
|
||||
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
|
||||
{
|
||||
/* copy secondary buffer to primary */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
||||
/* we need to resend TX packet */
|
||||
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
|
||||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
|
||||
{
|
||||
/* If both primary and secondary have partial connection retransmit the primary received
|
||||
* frame over the secondary socket. The result from the secondary received frame is a combined
|
||||
* frame that traversed all slaves in standard order. */
|
||||
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
|
||||
{
|
||||
/* copy primary rx to tx buffer */
|
||||
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
}
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
/* resend secondary tx */
|
||||
ecx_outframe(port, idx, 1);
|
||||
do
|
||||
{
|
||||
/* retrieve frame */
|
||||
wkc2 = ecx_inframe(port, idx, 1);
|
||||
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
||||
if (wkc2 > EC_NOFRAME)
|
||||
{
|
||||
/* copy secondary result to primary rx buffer */
|
||||
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
||||
wkc = wkc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return WKC or EC_NOFRAME */
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter if a frame is found with corresponding index, otherwise
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc;
|
||||
osal_timert timer;
|
||||
|
||||
osal_timer_start (&timer, timeout);
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer);
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
/** Blocking send and receive frame function. Used for non processdata frames.
|
||||
* A datagram is build into a frame and transmitted via this function. It waits
|
||||
* for an answer and returns the workcounter. The function retries if time is
|
||||
* left and the result is WKC=0 or no frame received.
|
||||
*
|
||||
* The function calls ec_outframe_red() and ec_waitinframe_red().
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = index of frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return Workcounter or EC_NOFRAME
|
||||
*/
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
|
||||
{
|
||||
int wkc = EC_NOFRAME;
|
||||
osal_timert timer1, timer2;
|
||||
|
||||
osal_timer_start (&timer1, timeout);
|
||||
do
|
||||
{
|
||||
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
||||
ecx_outframe_red(port, idx);
|
||||
if (timeout < EC_TIMEOUTRET)
|
||||
{
|
||||
osal_timer_start (&timer2, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normally use partial timeout for rx */
|
||||
osal_timer_start (&timer2, EC_TIMEOUTRET);
|
||||
}
|
||||
/* get frame from primary or if in redundant mode possibly from secondary */
|
||||
wkc = ecx_waitinframe_red(port, idx, &timer2);
|
||||
/* wait for answer with WKC>=0 or otherwise retry until timeout */
|
||||
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
|
||||
|
||||
return wkc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EC_VER1
|
||||
|
||||
int ec_setupnic(const char *ifname, int secondary)
|
||||
{
|
||||
return ecx_setupnic(&ecx_port, ifname, secondary);
|
||||
}
|
||||
|
||||
int ec_closenic(void)
|
||||
{
|
||||
return ecx_closenic(&ecx_port);
|
||||
}
|
||||
|
||||
uint8 ec_getindex(void)
|
||||
{
|
||||
return ecx_getindex(&ecx_port);
|
||||
}
|
||||
|
||||
void ec_setbufstat(uint8 idx, int bufstat)
|
||||
{
|
||||
ecx_setbufstat(&ecx_port, idx, bufstat);
|
||||
}
|
||||
|
||||
int ec_outframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_outframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_outframe_red(uint8 idx)
|
||||
{
|
||||
return ecx_outframe_red(&ecx_port, idx);
|
||||
}
|
||||
|
||||
int ec_inframe(uint8 idx, int stacknumber)
|
||||
{
|
||||
return ecx_inframe(&ecx_port, idx, stacknumber);
|
||||
}
|
||||
|
||||
int ec_waitinframe(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_waitinframe(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
int ec_srconfirm(uint8 idx, int timeout)
|
||||
{
|
||||
return ecx_srconfirm(&ecx_port, idx, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define HAVE_REMOTE
|
||||
|
||||
#include <pcap.h>
|
||||
#include <Packet32.h>
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** socket connection used */
|
||||
pcap_t **sock;
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** temporary receive buffer */
|
||||
ec_bufT *tempbuf;
|
||||
/** rx buffers */
|
||||
ec_bufT (*rxbuf)[EC_MAXBUF];
|
||||
/** rx buffer status fields */
|
||||
int (*rxbufstat)[EC_MAXBUF];
|
||||
/** received MAC source address (middle word) */
|
||||
int (*rxsa)[EC_MAXBUF];
|
||||
} ec_stackT;
|
||||
|
||||
/** pointer structure to buffers for redundant port */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct
|
||||
{
|
||||
ec_stackT stack;
|
||||
pcap_t *sockhandle;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** temporary rx buffer */
|
||||
ec_bufT tempinbuf;
|
||||
/** temporary rx buffer status */
|
||||
int tempinbufs;
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lengths */
|
||||
int txbuflength[EC_MAXBUF];
|
||||
/** temporary tx buffer */
|
||||
ec_bufT txbuf2;
|
||||
/** temporary tx buffer length */
|
||||
int txbuflength2;
|
||||
/** last used frame index */
|
||||
uint8 lastidx;
|
||||
/** current redundancy state */
|
||||
int redstate;
|
||||
/** pointer to redundancy port and buffers */
|
||||
ecx_redportt *redport;
|
||||
CRITICAL_SECTION getindex_mutex;
|
||||
CRITICAL_SECTION tx_mutex;
|
||||
CRITICAL_SECTION rx_mutex;
|
||||
} ecx_portt;
|
||||
|
||||
extern const uint16 priMAC[3];
|
||||
extern const uint16 secMAC[3];
|
||||
|
||||
#ifdef EC_VER1
|
||||
extern ecx_portt ecx_port;
|
||||
extern ecx_redportt ecx_redport;
|
||||
|
||||
int ec_setupnic(const char * ifname, int secondary);
|
||||
int ec_closenic(void);
|
||||
void ec_setbufstat(uint8 idx, int bufstat);
|
||||
uint8 ec_getindex(void);
|
||||
int ec_outframe(uint8 idx, int sock);
|
||||
int ec_outframe_red(uint8 idx);
|
||||
int ec_waitinframe(uint8 idx, int timeout);
|
||||
int ec_srconfirm(uint8 idx,int timeout);
|
||||
#endif
|
||||
|
||||
void ec_setupheader(void *p);
|
||||
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
|
||||
int ecx_closenic(ecx_portt *port);
|
||||
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
|
||||
uint8 ecx_getindex(ecx_portt *port);
|
||||
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
|
||||
int ecx_outframe_red(ecx_portt *port, uint8 idx);
|
||||
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
|
||||
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
#include "oshw.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Host to Network byte order (i.e. to big endian).
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_htons (uint16 host)
|
||||
{
|
||||
uint16 network = htons (host);
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network (i.e. big endian) to Host byte order.
|
||||
*
|
||||
* Note that Ethercat uses little endian byte order, except for the Ethernet
|
||||
* header which is big endian as usual.
|
||||
*/
|
||||
uint16 oshw_ntohs (uint16 network)
|
||||
{
|
||||
uint16 host = ntohs (network);
|
||||
return host;
|
||||
}
|
||||
|
||||
/* Create list over available network adapters.
|
||||
* @return First element in linked list of adapters
|
||||
*/
|
||||
ec_adaptert * oshw_find_adapters (void)
|
||||
{
|
||||
int i = 0;
|
||||
pcap_if_t *alldevs;
|
||||
pcap_if_t *d;
|
||||
ec_adaptert * adapter;
|
||||
ec_adaptert * prev_adapter;
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
/* find all devices */
|
||||
if (pcap_findalldevs(&alldevs, errbuf) == -1)
|
||||
{
|
||||
fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf);
|
||||
return (NULL);
|
||||
}
|
||||
/* Iterate all devices and create a local copy holding the name and
|
||||
* description.
|
||||
*/
|
||||
for(d= alldevs; d != NULL; d= d->next)
|
||||
{
|
||||
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
|
||||
/* If we got more than one adapter save link list pointer to previous
|
||||
* adapter.
|
||||
* Else save as pointer to return.
|
||||
*/
|
||||
if (i)
|
||||
{
|
||||
prev_adapter->next = adapter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_adapter = adapter;
|
||||
}
|
||||
|
||||
/* fetch description and name of the device from libpcap */
|
||||
adapter->next = NULL;
|
||||
if (d->name)
|
||||
{
|
||||
strncpy(adapter->name, d->name, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->name[0] = '\0';
|
||||
}
|
||||
if (d->description)
|
||||
{
|
||||
strncpy(adapter->desc, d->description, EC_MAXLEN_ADAPTERNAME);
|
||||
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter->desc[0] = '\0';
|
||||
}
|
||||
prev_adapter = adapter;
|
||||
i++;
|
||||
}
|
||||
/* free all devices allocated */
|
||||
pcap_freealldevs(alldevs);
|
||||
|
||||
return ret_adapter;
|
||||
}
|
||||
|
||||
/** Free memory allocated memory used by adapter collection.
|
||||
* @param[in] adapter = First element in linked list of adapters
|
||||
* EC_NOFRAME.
|
||||
*/
|
||||
void oshw_free_adapters (ec_adaptert * adapter)
|
||||
{
|
||||
ec_adaptert * next_adapter;
|
||||
/* Iterate the linked list and free all elements holding
|
||||
* adapter information
|
||||
*/
|
||||
if(adapter)
|
||||
{
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
while (next_adapter)
|
||||
{
|
||||
adapter = next_adapter;
|
||||
next_adapter = adapter->next;
|
||||
free (adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed under the GNU General Public License version 2 with exceptions. See
|
||||
* LICENSE file in the project root for full license information
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for ethercatbase.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "ethercattype.h"
|
||||
#include "nicdrv.h"
|
||||
#include "ethercatmain.h"
|
||||
|
||||
uint16 oshw_htons (uint16 hostshort);
|
||||
uint16 oshw_ntohs (uint16 networkshort);
|
||||
ec_adaptert * oshw_find_adapters (void);
|
||||
void oshw_free_adapters (ec_adaptert * adapter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2007 CACE Technologies, Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @ingroup packetapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup packet32h Packet.dll definitions and data structures
|
||||
* Packet32.h contains the data structures and the definitions used by packet.dll.
|
||||
* The file is used both by the Win9x and the WinNTx versions of packet.dll, and can be included
|
||||
* by the applications that use the functions of this library
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __PACKET32
|
||||
#define __PACKET32
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#ifdef HAVE_AIRPCAP_API
|
||||
#include <airpcap.h>
|
||||
#else
|
||||
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
|
||||
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
|
||||
typedef struct _AirpcapHandle *PAirpcapHandle;
|
||||
#endif /* AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ */
|
||||
#endif /* HAVE_AIRPCAP_API */
|
||||
|
||||
#ifdef HAVE_DAG_API
|
||||
#include <dagc.h>
|
||||
#endif /* HAVE_DAG_API */
|
||||
|
||||
// Working modes
|
||||
#define PACKET_MODE_CAPT 0x0 ///< Capture mode
|
||||
#define PACKET_MODE_STAT 0x1 ///< Statistical mode
|
||||
#define PACKET_MODE_MON 0x2 ///< Monitoring mode
|
||||
#define PACKET_MODE_DUMP 0x10 ///< Dump mode
|
||||
#define PACKET_MODE_STAT_DUMP MODE_DUMP | MODE_STAT ///< Statistical dump Mode
|
||||
|
||||
|
||||
/// Alignment macro. Defines the alignment size.
|
||||
#define Packet_ALIGNMENT sizeof(int)
|
||||
/// Alignment macro. Rounds up to the next even multiple of Packet_ALIGNMENT.
|
||||
#define Packet_WORDALIGN(x) (((x)+(Packet_ALIGNMENT-1))&~(Packet_ALIGNMENT-1))
|
||||
|
||||
#define NdisMediumNull -1 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumCHDLC -2 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumPPPSerial -3 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumBare80211 -4 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumRadio80211 -5 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
#define NdisMediumPpi -6 ///< Custom linktype: NDIS doesn't provide an equivalent
|
||||
|
||||
// Loopback behaviour definitions
|
||||
#define NPF_DISABLE_LOOPBACK 1 ///< Drop the packets sent by the NPF driver
|
||||
#define NPF_ENABLE_LOOPBACK 2 ///< Capture the packets sent by the NPF driver
|
||||
|
||||
/*!
|
||||
\brief Network type structure.
|
||||
|
||||
This structure is used by the PacketGetNetType() function to return information on the current adapter's type and speed.
|
||||
*/
|
||||
typedef struct NetType
|
||||
{
|
||||
UINT LinkType; ///< The MAC of the current network adapter (see function PacketGetNetType() for more information)
|
||||
ULONGLONG LinkSpeed; ///< The speed of the network in bits per second
|
||||
}NetType;
|
||||
|
||||
|
||||
//some definitions stolen from libpcap
|
||||
|
||||
#ifndef BPF_MAJOR_VERSION
|
||||
|
||||
/*!
|
||||
\brief A BPF pseudo-assembly program.
|
||||
|
||||
The program will be injected in the kernel by the PacketSetBPF() function and applied to every incoming packet.
|
||||
*/
|
||||
struct bpf_program
|
||||
{
|
||||
UINT bf_len; ///< Indicates the number of instructions of the program, i.e. the number of struct bpf_insn that will follow.
|
||||
struct bpf_insn *bf_insns; ///< A pointer to the first instruction of the program.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief A single BPF pseudo-instruction.
|
||||
|
||||
bpf_insn contains a single instruction for the BPF register-machine. It is used to send a filter program to the driver.
|
||||
*/
|
||||
struct bpf_insn
|
||||
{
|
||||
USHORT code; ///< Instruction type and addressing mode.
|
||||
UCHAR jt; ///< Jump if true
|
||||
UCHAR jf; ///< Jump if false
|
||||
int k; ///< Generic field used for various purposes.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Structure that contains a couple of statistics values on the current capture.
|
||||
|
||||
It is used by packet.dll to return statistics about a capture session.
|
||||
*/
|
||||
struct bpf_stat
|
||||
{
|
||||
UINT bs_recv; ///< Number of packets that the driver received from the network adapter
|
||||
///< from the beginning of the current capture. This value includes the packets
|
||||
///< lost by the driver.
|
||||
UINT bs_drop; ///< number of packets that the driver lost from the beginning of a capture.
|
||||
///< Basically, a packet is lost when the the buffer of the driver is full.
|
||||
///< In this situation the packet cannot be stored and the driver rejects it.
|
||||
UINT ps_ifdrop; ///< drops by interface. XXX not yet supported
|
||||
UINT bs_capt; ///< number of packets that pass the filter, find place in the kernel buffer and
|
||||
///< thus reach the application.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Packet header.
|
||||
|
||||
This structure defines the header associated with every packet delivered to the application.
|
||||
*/
|
||||
struct bpf_hdr
|
||||
{
|
||||
struct timeval bh_tstamp; ///< The timestamp associated with the captured packet.
|
||||
///< It is stored in a TimeVal structure.
|
||||
UINT bh_caplen; ///< Length of captured portion. The captured portion <b>can be different</b>
|
||||
///< from the original packet, because it is possible (with a proper filter)
|
||||
///< to instruct the driver to capture only a portion of the packets.
|
||||
UINT bh_datalen; ///< Original length of packet
|
||||
USHORT bh_hdrlen; ///< Length of bpf header (this struct plus alignment padding). In some cases,
|
||||
///< a padding could be added between the end of this structure and the packet
|
||||
///< data for performance reasons. This filed can be used to retrieve the actual data
|
||||
///< of the packet.
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Dump packet header.
|
||||
|
||||
This structure defines the header associated with the packets in a buffer to be used with PacketSendPackets().
|
||||
It is simpler than the bpf_hdr, because it corresponds to the header associated by WinPcap and libpcap to a
|
||||
packet in a dump file. This makes straightforward sending WinPcap dump files to the network.
|
||||
*/
|
||||
struct dump_bpf_hdr{
|
||||
struct timeval ts; ///< Time stamp of the packet
|
||||
UINT caplen; ///< Length of captured portion. The captured portion can smaller than the
|
||||
///< the original packet, because it is possible (with a proper filter) to
|
||||
///< instruct the driver to capture only a portion of the packets.
|
||||
UINT len; ///< Length of the original packet (off wire).
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
struct bpf_stat;
|
||||
|
||||
#define DOSNAMEPREFIX TEXT("Packet_") ///< Prefix added to the adapters device names to create the WinPcap devices
|
||||
#define MAX_LINK_NAME_LENGTH 64 //< Maximum length of the devices symbolic links
|
||||
#define NMAX_PACKET 65535
|
||||
|
||||
/*!
|
||||
\brief Addresses of a network adapter.
|
||||
|
||||
This structure is used by the PacketGetNetInfoEx() function to return the IP addresses associated with
|
||||
an adapter.
|
||||
*/
|
||||
typedef struct npf_if_addr {
|
||||
struct sockaddr_storage IPAddress; ///< IP address.
|
||||
struct sockaddr_storage SubnetMask; ///< Netmask for that address.
|
||||
struct sockaddr_storage Broadcast; ///< Broadcast address.
|
||||
}npf_if_addr;
|
||||
|
||||
|
||||
#define ADAPTER_NAME_LENGTH 256 + 12 ///< Maximum length for the name of an adapter. The value is the same used by the IP Helper API.
|
||||
#define ADAPTER_DESC_LENGTH 128 ///< Maximum length for the description of an adapter. The value is the same used by the IP Helper API.
|
||||
#define MAX_MAC_ADDR_LENGTH 8 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
|
||||
#define MAX_NETWORK_ADDRESSES 16 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
|
||||
|
||||
|
||||
typedef struct WAN_ADAPTER_INT WAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
|
||||
typedef WAN_ADAPTER *PWAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
|
||||
|
||||
#define INFO_FLAG_NDIS_ADAPTER 0 ///< Flag for ADAPTER_INFO: this is a traditional ndis adapter
|
||||
#define INFO_FLAG_NDISWAN_ADAPTER 1 ///< Flag for ADAPTER_INFO: this is a NdisWan adapter, and it's managed by WANPACKET
|
||||
#define INFO_FLAG_DAG_CARD 2 ///< Flag for ADAPTER_INFO: this is a DAG card
|
||||
#define INFO_FLAG_DAG_FILE 6 ///< Flag for ADAPTER_INFO: this is a DAG file
|
||||
#define INFO_FLAG_DONT_EXPORT 8 ///< Flag for ADAPTER_INFO: when this flag is set, the adapter will not be listed or openend by winpcap. This allows to prevent exporting broken network adapters, like for example FireWire ones.
|
||||
#define INFO_FLAG_AIRPCAP_CARD 16 ///< Flag for ADAPTER_INFO: this is an airpcap card
|
||||
#define INFO_FLAG_NPFIM_DEVICE 32
|
||||
|
||||
/*!
|
||||
\brief Describes an opened network adapter.
|
||||
|
||||
This structure is the most important for the functioning of packet.dll, but the great part of its fields
|
||||
should be ignored by the user, since the library offers functions that avoid to cope with low-level parameters
|
||||
*/
|
||||
typedef struct _ADAPTER {
|
||||
HANDLE hFile; ///< \internal Handle to an open instance of the NPF driver.
|
||||
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; ///< \internal A string containing the name of the network adapter currently opened.
|
||||
int NumWrites; ///< \internal Number of times a packets written on this adapter will be repeated
|
||||
///< on the wire.
|
||||
HANDLE ReadEvent; ///< A notification event associated with the read calls on the adapter.
|
||||
///< It can be passed to standard Win32 functions (like WaitForSingleObject
|
||||
///< or WaitForMultipleObjects) to wait until the driver's buffer contains some
|
||||
///< data. It is particularly useful in GUI applications that need to wait
|
||||
///< concurrently on several events. In Windows NT/2000 the PacketSetMinToCopy()
|
||||
///< function can be used to define the minimum amount of data in the kernel buffer
|
||||
///< that will cause the event to be signalled.
|
||||
|
||||
UINT ReadTimeOut; ///< \internal The amount of time after which a read on the driver will be released and
|
||||
///< ReadEvent will be signaled, also if no packets were captured
|
||||
CHAR Name[ADAPTER_NAME_LENGTH];
|
||||
PWAN_ADAPTER pWanAdapter;
|
||||
UINT Flags; ///< Adapter's flags. Tell if this adapter must be treated in a different way, using the Netmon API or the dagc API.
|
||||
|
||||
#ifdef HAVE_AIRPCAP_API
|
||||
PAirpcapHandle AirpcapAd;
|
||||
#endif // HAVE_AIRPCAP_API
|
||||
|
||||
#ifdef HAVE_NPFIM_API
|
||||
void* NpfImHandle;
|
||||
#endif // HAVE_NPFIM_API
|
||||
|
||||
#ifdef HAVE_DAG_API
|
||||
dagc_t *pDagCard; ///< Pointer to the dagc API adapter descriptor for this adapter
|
||||
PCHAR DagBuffer; ///< Pointer to the buffer with the packets that is received from the DAG card
|
||||
struct timeval DagReadTimeout; ///< Read timeout. The dagc API requires a timeval structure
|
||||
unsigned DagFcsLen; ///< Length of the frame check sequence attached to any packet by the card. Obtained from the registry
|
||||
DWORD DagFastProcess; ///< True if the user requests fast capture processing on this card. Higher level applications can use this value to provide a faster but possibly unprecise capture (for example, libpcap doesn't convert the timestamps).
|
||||
#endif // HAVE_DAG_API
|
||||
} ADAPTER, *LPADAPTER;
|
||||
|
||||
/*!
|
||||
\brief Structure that contains a group of packets coming from the driver.
|
||||
|
||||
This structure defines the header associated with every packet delivered to the application.
|
||||
*/
|
||||
typedef struct _PACKET {
|
||||
HANDLE hEvent; ///< \deprecated Still present for compatibility with old applications.
|
||||
OVERLAPPED OverLapped; ///< \deprecated Still present for compatibility with old applications.
|
||||
PVOID Buffer; ///< Buffer with containing the packets. See the PacketReceivePacket() for
|
||||
///< details about the organization of the data in this buffer
|
||||
UINT Length; ///< Length of the buffer
|
||||
DWORD ulBytesReceived; ///< Number of valid bytes present in the buffer, i.e. amount of data
|
||||
///< received by the last call to PacketReceivePacket()
|
||||
BOOLEAN bIoComplete; ///< \deprecated Still present for compatibility with old applications.
|
||||
} PACKET, *LPPACKET;
|
||||
|
||||
/*!
|
||||
\brief Structure containing an OID request.
|
||||
|
||||
It is used by the PacketRequest() function to send an OID to the interface card driver.
|
||||
It can be used, for example, to retrieve the status of the error counters on the adapter, its MAC address,
|
||||
the list of the multicast groups defined on it, and so on.
|
||||
*/
|
||||
struct _PACKET_OID_DATA {
|
||||
ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
|
||||
///< for a complete list of valid codes.
|
||||
ULONG Length; ///< Length of the data field
|
||||
UCHAR Data[1]; ///< variable-length field that contains the information passed to or received
|
||||
///< from the adapter.
|
||||
};
|
||||
typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*
|
||||
BOOLEAN QueryWinPcapRegistryStringA(CHAR *SubKeyName,
|
||||
CHAR *Value,
|
||||
UINT *pValueLen,
|
||||
CHAR *DefaultVal);
|
||||
|
||||
BOOLEAN QueryWinPcapRegistryStringW(WCHAR *SubKeyName,
|
||||
WCHAR *Value,
|
||||
UINT *pValueLen,
|
||||
WCHAR *DefaultVal);
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EXPORTED FUNCTIONS
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
PCHAR PacketGetVersion();
|
||||
PCHAR PacketGetDriverVersion();
|
||||
BOOLEAN PacketSetMinToCopy(LPADAPTER AdapterObject,int nbytes);
|
||||
BOOLEAN PacketSetNumWrites(LPADAPTER AdapterObject,int nwrites);
|
||||
BOOLEAN PacketSetMode(LPADAPTER AdapterObject,int mode);
|
||||
BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout);
|
||||
BOOLEAN PacketSetBpf(LPADAPTER AdapterObject,struct bpf_program *fp);
|
||||
BOOLEAN PacketSetLoopbackBehavior(LPADAPTER AdapterObject, UINT LoopbackBehavior);
|
||||
INT PacketSetSnapLen(LPADAPTER AdapterObject,int snaplen);
|
||||
BOOLEAN PacketGetStats(LPADAPTER AdapterObject,struct bpf_stat *s);
|
||||
BOOLEAN PacketGetStatsEx(LPADAPTER AdapterObject,struct bpf_stat *s);
|
||||
BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim);
|
||||
BOOLEAN PacketGetNetType (LPADAPTER AdapterObject,NetType *type);
|
||||
LPADAPTER PacketOpenAdapter(PCHAR AdapterName);
|
||||
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET pPacket,BOOLEAN Sync);
|
||||
INT PacketSendPackets(LPADAPTER AdapterObject,PVOID PacketBuff,ULONG Size, BOOLEAN Sync);
|
||||
LPPACKET PacketAllocatePacket(void);
|
||||
VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length);
|
||||
VOID PacketFreePacket(LPPACKET lpPacket);
|
||||
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
|
||||
BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter);
|
||||
BOOLEAN PacketGetAdapterNames(PTSTR pStr,PULONG BufferSize);
|
||||
BOOLEAN PacketGetNetInfoEx(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries);
|
||||
BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);
|
||||
HANDLE PacketGetReadEvent(LPADAPTER AdapterObject);
|
||||
BOOLEAN PacketSetDumpName(LPADAPTER AdapterObject, void *name, int len);
|
||||
BOOLEAN PacketSetDumpLimits(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks);
|
||||
BOOLEAN PacketIsDumpEnded(LPADAPTER AdapterObject, BOOLEAN sync);
|
||||
BOOL PacketStopDriver();
|
||||
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
|
||||
BOOLEAN PacketStartOem(PCHAR errorString, UINT errorStringLength);
|
||||
BOOLEAN PacketStartOemEx(PCHAR errorString, UINT errorStringLength, ULONG flags);
|
||||
PAirpcapHandle PacketGetAirPcapHandle(LPADAPTER AdapterObject);
|
||||
|
||||
//
|
||||
// Used by PacketStartOemEx
|
||||
//
|
||||
#define PACKET_START_OEM_NO_NETMON 0x00000001
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__PACKET32
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __WIN32_EXTENSIONS_H__
|
||||
#define __WIN32_EXTENSIONS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Definitions */
|
||||
|
||||
/*!
|
||||
\brief A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit().
|
||||
*/
|
||||
struct pcap_send_queue
|
||||
{
|
||||
u_int maxlen; ///< Maximum size of the the queue, in bytes. This variable contains the size of the buffer field.
|
||||
u_int len; ///< Current size of the queue, in bytes.
|
||||
char *buffer; ///< Buffer containing the packets to be sent.
|
||||
};
|
||||
|
||||
typedef struct pcap_send_queue pcap_send_queue;
|
||||
|
||||
/*!
|
||||
\brief This typedef is a support for the pcap_get_airpcap_handle() function
|
||||
*/
|
||||
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
|
||||
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
|
||||
typedef struct _AirpcapHandle *PAirpcapHandle;
|
||||
#endif
|
||||
|
||||
#define BPF_MEM_EX_IMM 0xc0
|
||||
#define BPF_MEM_EX_IND 0xe0
|
||||
|
||||
/*used for ST*/
|
||||
#define BPF_MEM_EX 0xc0
|
||||
#define BPF_TME 0x08
|
||||
|
||||
#define BPF_LOOKUP 0x90
|
||||
#define BPF_EXECUTE 0xa0
|
||||
#define BPF_INIT 0xb0
|
||||
#define BPF_VALIDATE 0xc0
|
||||
#define BPF_SET_ACTIVE 0xd0
|
||||
#define BPF_RESET 0xe0
|
||||
#define BPF_SET_MEMORY 0x80
|
||||
#define BPF_GET_REGISTER_VALUE 0x70
|
||||
#define BPF_SET_REGISTER_VALUE 0x60
|
||||
#define BPF_SET_WORKING 0x50
|
||||
#define BPF_SET_ACTIVE_READ 0x40
|
||||
#define BPF_SET_AUTODELETION 0x30
|
||||
#define BPF_SEPARATION 0xff
|
||||
|
||||
/* Prototypes */
|
||||
pcap_send_queue* pcap_sendqueue_alloc(u_int memsize);
|
||||
|
||||
void pcap_sendqueue_destroy(pcap_send_queue* queue);
|
||||
|
||||
int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
|
||||
|
||||
u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync);
|
||||
|
||||
HANDLE pcap_getevent(pcap_t *p);
|
||||
|
||||
struct pcap_stat *pcap_stats_ex(pcap_t *p, int *pcap_stat_size);
|
||||
|
||||
int pcap_setuserbuffer(pcap_t *p, int size);
|
||||
|
||||
int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks);
|
||||
|
||||
int pcap_live_dump_ended(pcap_t *p, int sync);
|
||||
|
||||
int pcap_offline_filter(struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data);
|
||||
|
||||
int pcap_start_oem(char* err_str, int flags);
|
||||
|
||||
PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__WIN32_EXTENSIONS_H__
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 1999 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BITTYPES_H
|
||||
#define _BITTYPES_H
|
||||
|
||||
#ifndef HAVE_U_INT8_T
|
||||
|
||||
#if SIZEOF_CHAR == 1
|
||||
typedef unsigned char u_int8_t;
|
||||
typedef signed char int8_t;
|
||||
#elif SIZEOF_INT == 1
|
||||
typedef unsigned int u_int8_t;
|
||||
typedef signed int int8_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int8_t"
|
||||
#endif
|
||||
#define HAVE_U_INT8_T 1
|
||||
#define HAVE_INT8_T 1
|
||||
|
||||
#endif /* HAVE_U_INT8_T */
|
||||
|
||||
#ifndef HAVE_U_INT16_T
|
||||
|
||||
#if SIZEOF_SHORT == 2
|
||||
typedef unsigned short u_int16_t;
|
||||
typedef signed short int16_t;
|
||||
#elif SIZEOF_INT == 2
|
||||
typedef unsigned int u_int16_t;
|
||||
typedef signed int int16_t;
|
||||
#elif SIZEOF_CHAR == 2
|
||||
typedef unsigned char u_int16_t;
|
||||
typedef signed char int16_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int16_t"
|
||||
#endif
|
||||
#define HAVE_U_INT16_T 1
|
||||
#define HAVE_INT16_T 1
|
||||
|
||||
#endif /* HAVE_U_INT16_T */
|
||||
|
||||
#ifndef HAVE_U_INT32_T
|
||||
|
||||
#if SIZEOF_INT == 4
|
||||
typedef unsigned int u_int32_t;
|
||||
typedef signed int int32_t;
|
||||
#elif SIZEOF_LONG == 4
|
||||
typedef unsigned long u_int32_t;
|
||||
typedef signed long int32_t;
|
||||
#elif SIZEOF_SHORT == 4
|
||||
typedef unsigned short u_int32_t;
|
||||
typedef signed short int32_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int32_t"
|
||||
#endif
|
||||
#define HAVE_U_INT32_T 1
|
||||
#define HAVE_INT32_T 1
|
||||
|
||||
#endif /* HAVE_U_INT32_T */
|
||||
|
||||
#ifndef HAVE_U_INT64_T
|
||||
#if SIZEOF_LONG_LONG == 8
|
||||
typedef unsigned long long u_int64_t;
|
||||
typedef long long int64_t;
|
||||
#elif defined(_MSC_EXTENSIONS)
|
||||
typedef unsigned _int64 u_int64_t;
|
||||
typedef _int64 int64_t;
|
||||
#elif SIZEOF_INT == 8
|
||||
typedef unsigned int u_int64_t;
|
||||
#elif SIZEOF_LONG == 8
|
||||
typedef unsigned long u_int64_t;
|
||||
#elif SIZEOF_SHORT == 8
|
||||
typedef unsigned short u_int64_t;
|
||||
#else /* XXX */
|
||||
#error "there's no appropriate type for u_int64_t"
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_U_INT64_T */
|
||||
|
||||
#ifndef PRId64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRId64 "I64d"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRId64 "lld"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRId64 */
|
||||
|
||||
#ifndef PRIo64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIo64 "I64o"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIo64 "llo"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIo64 */
|
||||
|
||||
#ifndef PRIx64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIx64 "I64x"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIx64 "llx"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIx64 */
|
||||
|
||||
#ifndef PRIu64
|
||||
#ifdef _MSC_EXTENSIONS
|
||||
#define PRIu64 "I64u"
|
||||
#else /* _MSC_EXTENSIONS */
|
||||
#define PRIu64 "llu"
|
||||
#endif /* _MSC_EXTENSIONS */
|
||||
#endif /* PRIu64 */
|
||||
|
||||
#endif /* _BITTYPES_H */
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that: (1) source code distributions
|
||||
* retain the above copyright notice and this paragraph in its entirety, (2)
|
||||
* distributions including binary code include the above copyright notice and
|
||||
* this paragraph in its entirety in the documentation or other materials
|
||||
* provided with the distribution, and (3) all advertising materials mentioning
|
||||
* features or use of this software display the following acknowledgement:
|
||||
* ``This product includes software developed by the University of California,
|
||||
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
|
||||
* the University nor the names of its contributors may be used to endorse
|
||||
* or promote products derived from this software without specific prior
|
||||
* written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/Win32/Include/ip6_misc.h,v 1.5 2006-01-22 18:02:18 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#define IN_MULTICAST(a) IN_CLASSD(a)
|
||||
#endif
|
||||
|
||||
#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xf0000000) == 0xf0000000)
|
||||
|
||||
#define IN_LOOPBACKNET 127
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
/* IPv6 address */
|
||||
struct in6_addr
|
||||
{
|
||||
union
|
||||
{
|
||||
u_int8_t u6_addr8[16];
|
||||
u_int16_t u6_addr16[8];
|
||||
u_int32_t u6_addr32[4];
|
||||
} in6_u;
|
||||
#define s6_addr in6_u.u6_addr8
|
||||
#define s6_addr16 in6_u.u6_addr16
|
||||
#define s6_addr32 in6_u.u6_addr32
|
||||
#define s6_addr64 in6_u.u6_addr64
|
||||
};
|
||||
|
||||
#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
|
||||
#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
|
||||
#if (defined _MSC_VER) || (defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF))
|
||||
typedef unsigned short sa_family_t;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
|
||||
#define __SOCKADDR_COMMON(sa_prefix) \
|
||||
sa_family_t sa_prefix##family
|
||||
|
||||
/* Ditto, for IPv6. */
|
||||
struct sockaddr_in6
|
||||
{
|
||||
__SOCKADDR_COMMON (sin6_);
|
||||
u_int16_t sin6_port; /* Transport layer port # */
|
||||
u_int32_t sin6_flowinfo; /* IPv6 flow information */
|
||||
struct in6_addr sin6_addr; /* IPv6 address */
|
||||
};
|
||||
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) \
|
||||
((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \
|
||||
(((u_int32_t *) (a))[2] == htonl (0xffff)))
|
||||
|
||||
#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff)
|
||||
|
||||
#define IN6_IS_ADDR_LINKLOCAL(a) \
|
||||
((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000))
|
||||
|
||||
#define IN6_IS_ADDR_LOOPBACK(a) \
|
||||
(((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \
|
||||
((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1))
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
|
||||
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
|
||||
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
|
||||
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
|
||||
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
|
||||
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
|
||||
|
||||
#define nd_rd_type nd_rd_hdr.icmp6_type
|
||||
#define nd_rd_code nd_rd_hdr.icmp6_code
|
||||
#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
|
||||
#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
|
||||
|
||||
/*
|
||||
* IPV6 extension headers
|
||||
*/
|
||||
#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */
|
||||
#define IPPROTO_IPV6 41 /* IPv6 header. */
|
||||
#define IPPROTO_ROUTING 43 /* IPv6 routing header */
|
||||
#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
|
||||
#define IPPROTO_ESP 50 /* encapsulating security payload */
|
||||
#define IPPROTO_AH 51 /* authentication header */
|
||||
#define IPPROTO_ICMPV6 58 /* ICMPv6 */
|
||||
#define IPPROTO_NONE 59 /* IPv6 no next header */
|
||||
#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
|
||||
#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */
|
||||
|
||||
#define IPV6_RTHDR_TYPE_0 0
|
||||
|
||||
/* Option types and related macros */
|
||||
#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
|
||||
#define IP6OPT_PADN 0x01 /* 00 0 00001 */
|
||||
#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
|
||||
#define IP6OPT_JUMBO_LEN 6
|
||||
#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */
|
||||
|
||||
#define IP6OPT_RTALERT_LEN 4
|
||||
#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
|
||||
#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
|
||||
#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
|
||||
#define IP6OPT_MINLEN 2
|
||||
|
||||
#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */
|
||||
#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */
|
||||
#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */
|
||||
#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
|
||||
#define IP6OPT_EID 0x8a /* 10 0 01010 */
|
||||
|
||||
#define IP6OPT_TYPE(o) ((o) & 0xC0)
|
||||
#define IP6OPT_TYPE_SKIP 0x00
|
||||
#define IP6OPT_TYPE_DISCARD 0x40
|
||||
#define IP6OPT_TYPE_FORCEICMP 0x80
|
||||
#define IP6OPT_TYPE_ICMP 0xC0
|
||||
|
||||
#define IP6OPT_MUTABLE 0x20
|
||||
|
||||
|
||||
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
|
||||
#ifndef EAI_ADDRFAMILY
|
||||
struct addrinfo {
|
||||
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
|
||||
int ai_family; /* PF_xxx */
|
||||
int ai_socktype; /* SOCK_xxx */
|
||||
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
|
||||
size_t ai_addrlen; /* length of ai_addr */
|
||||
char *ai_canonname; /* canonical name for hostname */
|
||||
struct sockaddr *ai_addr; /* binary address */
|
||||
struct addrinfo *ai_next; /* next structure in linked list */
|
||||
};
|
||||
#endif
|
||||
#endif /* __MINGW32__ */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-bpf.h,v 1.50 2007/04/01 21:43:55 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Some applications
|
||||
* might expect to be able to include <pcap-bpf.h>.
|
||||
*/
|
||||
#include <pcap/bpf.h>
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-namedb.h,v 1.13 2006/10/04 18:13:32 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Some applications
|
||||
* might expect to be able to include <pcap-namedb.h>.
|
||||
*/
|
||||
#include <pcap/namedb.h>
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy)
|
||||
* Copyright (c) 2005 - 2009 CACE Technologies, Inc. Davis (California)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap-stdinc.h,v 1.10.2.1 2008-10-06 15:38:39 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#define SIZEOF_CHAR 1
|
||||
#define SIZEOF_SHORT 2
|
||||
#define SIZEOF_INT 4
|
||||
#ifndef _MSC_EXTENSIONS
|
||||
#define SIZEOF_LONG_LONG 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Avoids a compiler warning in case this was already defined
|
||||
* (someone defined _WINSOCKAPI_ when including 'windows.h', in order
|
||||
* to prevent it from including 'winsock.h')
|
||||
*/
|
||||
#ifdef _WINSOCKAPI_
|
||||
#undef _WINSOCKAPI_
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "bittypes.h"
|
||||
#include <time.h>
|
||||
#include <io.h>
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#include "IP6_misc.h"
|
||||
#endif
|
||||
|
||||
#define caddr_t char*
|
||||
|
||||
#if _MSC_VER < 1500
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
#define inline __inline
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <stdint.h>
|
||||
#else /*__MINGW32__*/
|
||||
/* MSVC compiler */
|
||||
#ifndef _UINTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif
|
||||
#define _UINTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef _INTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef __int64 intptr_t;
|
||||
#else
|
||||
typedef _W64 int intptr_t;
|
||||
#endif
|
||||
#define _INTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
#endif /*__MINGW32__*/
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.59 2006/10/04 18:09:22 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Note to OS vendors: do NOT get rid of this file! Many applications
|
||||
* expect to be able to include <pcap.h>, and at least some of them
|
||||
* go through contortions in their configure scripts to try to detect
|
||||
* OSes that have "helpfully" moved pcap.h to <pcap/pcap.h> without
|
||||
* leaving behind a <pcap.h> file.
|
||||
*/
|
||||
#include <pcap/pcap.h>
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Paolo Abeni (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* bluetooth data struct
|
||||
* By Paolo Abeni <paolo.abeni@email.it>
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/bluetooth.h,v 1.1 2007/09/22 02:10:17 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PCAP_BLUETOOTH_STRUCTS_H__
|
||||
#define _PCAP_BLUETOOTH_STRUCTS_H__
|
||||
|
||||
/*
|
||||
* Header prepended libpcap to each bluetooth h:4 frame.
|
||||
* fields are in network byte order
|
||||
*/
|
||||
typedef struct _pcap_bluetooth_h4_header {
|
||||
u_int32_t direction; /* if first bit is set direction is incoming */
|
||||
} pcap_bluetooth_h4_header;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,934 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)bpf.h 7.1 (Berkeley) 5/7/91
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.19.2.8 2008-09-22 20:16:01 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is libpcap's cut-down version of bpf.h; it includes only
|
||||
* the stuff needed for the code generator and the userland BPF
|
||||
* interpreter, and the libpcap APIs for setting filters, etc..
|
||||
*
|
||||
* "pcap-bpf.c" will include the native OS version, as it deals with
|
||||
* the OS's BPF implementation.
|
||||
*
|
||||
* XXX - should this all just be moved to "pcap.h"?
|
||||
*/
|
||||
|
||||
#ifndef BPF_MAJOR_VERSION
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* BSD style release date */
|
||||
#define BPF_RELEASE 199606
|
||||
|
||||
#ifdef MSDOS /* must be 32-bit */
|
||||
typedef long bpf_int32;
|
||||
typedef unsigned long bpf_u_int32;
|
||||
#else
|
||||
typedef int bpf_int32;
|
||||
typedef u_int bpf_u_int32;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Alignment macros. BPF_WORDALIGN rounds up to the next
|
||||
* even multiple of BPF_ALIGNMENT.
|
||||
*/
|
||||
#ifndef __NetBSD__
|
||||
#define BPF_ALIGNMENT sizeof(bpf_int32)
|
||||
#else
|
||||
#define BPF_ALIGNMENT sizeof(long)
|
||||
#endif
|
||||
#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
|
||||
|
||||
#define BPF_MAXBUFSIZE 0x8000
|
||||
#define BPF_MINBUFSIZE 32
|
||||
|
||||
/*
|
||||
* Structure for "pcap_compile()", "pcap_setfilter()", etc..
|
||||
*/
|
||||
struct bpf_program {
|
||||
u_int bf_len;
|
||||
struct bpf_insn *bf_insns;
|
||||
};
|
||||
|
||||
/*
|
||||
* Struct return by BIOCVERSION. This represents the version number of
|
||||
* the filter language described by the instruction encodings below.
|
||||
* bpf understands a program iff kernel_major == filter_major &&
|
||||
* kernel_minor >= filter_minor, that is, if the value returned by the
|
||||
* running kernel has the same major number and a minor number equal
|
||||
* equal to or less than the filter being downloaded. Otherwise, the
|
||||
* results are undefined, meaning an error may be returned or packets
|
||||
* may be accepted haphazardly.
|
||||
* It has nothing to do with the source code version.
|
||||
*/
|
||||
struct bpf_version {
|
||||
u_short bv_major;
|
||||
u_short bv_minor;
|
||||
};
|
||||
/* Current version number of filter architecture. */
|
||||
#define BPF_MAJOR_VERSION 1
|
||||
#define BPF_MINOR_VERSION 1
|
||||
|
||||
/*
|
||||
* Data-link level type codes.
|
||||
*
|
||||
* Do *NOT* add new values to this list without asking
|
||||
* "tcpdump-workers@lists.tcpdump.org" for a value. Otherwise, you run
|
||||
* the risk of using a value that's already being used for some other
|
||||
* purpose, and of having tools that read libpcap-format captures not
|
||||
* being able to handle captures with your new DLT_ value, with no hope
|
||||
* that they will ever be changed to do so (as that would destroy their
|
||||
* ability to read captures using that value for that other purpose).
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are the types that are the same on all platforms, and that
|
||||
* have been defined by <net/bpf.h> for ages.
|
||||
*/
|
||||
#define DLT_NULL 0 /* BSD loopback encapsulation */
|
||||
#define DLT_EN10MB 1 /* Ethernet (10Mb) */
|
||||
#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */
|
||||
#define DLT_AX25 3 /* Amateur Radio AX.25 */
|
||||
#define DLT_PRONET 4 /* Proteon ProNET Token Ring */
|
||||
#define DLT_CHAOS 5 /* Chaos */
|
||||
#define DLT_IEEE802 6 /* 802.5 Token Ring */
|
||||
#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */
|
||||
#define DLT_SLIP 8 /* Serial Line IP */
|
||||
#define DLT_PPP 9 /* Point-to-point Protocol */
|
||||
#define DLT_FDDI 10 /* FDDI */
|
||||
|
||||
/*
|
||||
* These are types that are different on some platforms, and that
|
||||
* have been defined by <net/bpf.h> for ages. We use #ifdefs to
|
||||
* detect the BSDs that define them differently from the traditional
|
||||
* libpcap <net/bpf.h>
|
||||
*
|
||||
* XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS,
|
||||
* but I don't know what the right #define is for BSD/OS.
|
||||
*/
|
||||
#define DLT_ATM_RFC1483 11 /* LLC-encapsulated ATM */
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_RAW 14 /* raw IP */
|
||||
#else
|
||||
#define DLT_RAW 12 /* raw IP */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Given that the only OS that currently generates BSD/OS SLIP or PPP
|
||||
* is, well, BSD/OS, arguably everybody should have chosen its values
|
||||
* for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they
|
||||
* didn't. So it goes.
|
||||
*/
|
||||
#if defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
#ifndef DLT_SLIP_BSDOS
|
||||
#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */
|
||||
#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */
|
||||
#endif
|
||||
#else
|
||||
#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */
|
||||
#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 17 is used for DLT_OLD_PFLOG in OpenBSD;
|
||||
* OBSOLETE: DLT_PFLOG is 117 in OpenBSD now as well. See below.
|
||||
* 18 is used for DLT_PFSYNC in OpenBSD; don't use it for anything else.
|
||||
*/
|
||||
|
||||
#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */
|
||||
|
||||
/*
|
||||
* Apparently Redback uses this for its SmartEdge 400/800. I hope
|
||||
* nobody else decided to use it, too.
|
||||
*/
|
||||
#define DLT_REDBACK_SMARTEDGE 32
|
||||
|
||||
/*
|
||||
* These values are defined by NetBSD; other platforms should refrain from
|
||||
* using them for other purposes, so that NetBSD savefiles with link
|
||||
* types of 50 or 51 can be read as this type on all platforms.
|
||||
*/
|
||||
#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */
|
||||
#define DLT_PPP_ETHER 51 /* PPP over Ethernet */
|
||||
|
||||
/*
|
||||
* The Axent Raptor firewall - now the Symantec Enterprise Firewall - uses
|
||||
* a link-layer type of 99 for the tcpdump it supplies. The link-layer
|
||||
* header has 6 bytes of unknown data, something that appears to be an
|
||||
* Ethernet type, and 36 bytes that appear to be 0 in at least one capture
|
||||
* I've seen.
|
||||
*/
|
||||
#define DLT_SYMANTEC_FIREWALL 99
|
||||
|
||||
/*
|
||||
* Values between 100 and 103 are used in capture file headers as
|
||||
* link-layer types corresponding to DLT_ types that differ
|
||||
* between platforms; don't use those values for new DLT_ new types.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This value was defined by libpcap 0.5; platforms that have defined
|
||||
* it with a different value should define it here with that value -
|
||||
* a link type of 104 in a save file will be mapped to DLT_C_HDLC,
|
||||
* whatever value that happens to be, so programs will correctly
|
||||
* handle files with that link type regardless of the value of
|
||||
* DLT_C_HDLC.
|
||||
*
|
||||
* The name DLT_C_HDLC was used by BSD/OS; we use that name for source
|
||||
* compatibility with programs written for BSD/OS.
|
||||
*
|
||||
* libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well,
|
||||
* for source compatibility with programs written for libpcap 0.5.
|
||||
*/
|
||||
#define DLT_C_HDLC 104 /* Cisco HDLC */
|
||||
#define DLT_CHDLC DLT_C_HDLC
|
||||
|
||||
#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */
|
||||
|
||||
/*
|
||||
* 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW,
|
||||
* except when it isn't. (I.e., sometimes it's just raw IP, and
|
||||
* sometimes it isn't.) We currently handle it as DLT_LINUX_SLL,
|
||||
* so that we don't have to worry about the link-layer header.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides
|
||||
* with other values.
|
||||
* DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header
|
||||
* (DLCI, etc.).
|
||||
*/
|
||||
#define DLT_FRELAY 107
|
||||
|
||||
/*
|
||||
* OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except
|
||||
* that the AF_ type in the link-layer header is in network byte order.
|
||||
*
|
||||
* DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so
|
||||
* we don't use 12 for it in OSes other than OpenBSD.
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_LOOP 12
|
||||
#else
|
||||
#define DLT_LOOP 108
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's
|
||||
* DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other
|
||||
* than OpenBSD.
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_ENC 13
|
||||
#else
|
||||
#define DLT_ENC 109
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Values between 110 and 112 are reserved for use in capture file headers
|
||||
* as link-layer types corresponding to DLT_ types that might differ
|
||||
* between platforms; don't use those values for new DLT_ types
|
||||
* other than the corresponding DLT_ types.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is for Linux cooked sockets.
|
||||
*/
|
||||
#define DLT_LINUX_SLL 113
|
||||
|
||||
/*
|
||||
* Apple LocalTalk hardware.
|
||||
*/
|
||||
#define DLT_LTALK 114
|
||||
|
||||
/*
|
||||
* Acorn Econet.
|
||||
*/
|
||||
#define DLT_ECONET 115
|
||||
|
||||
/*
|
||||
* Reserved for use with OpenBSD ipfilter.
|
||||
*/
|
||||
#define DLT_IPFILTER 116
|
||||
|
||||
/*
|
||||
* OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, but that's DLT_LANE8023
|
||||
* in SuSE 6.3, so we can't use 17 for it in capture-file headers.
|
||||
*
|
||||
* XXX: is there a conflict with DLT_PFSYNC 18 as well?
|
||||
*/
|
||||
#ifdef __OpenBSD__
|
||||
#define DLT_OLD_PFLOG 17
|
||||
#define DLT_PFSYNC 18
|
||||
#endif
|
||||
#define DLT_PFLOG 117
|
||||
|
||||
/*
|
||||
* Registered for Cisco-internal use.
|
||||
*/
|
||||
#define DLT_CISCO_IOS 118
|
||||
|
||||
/*
|
||||
* For 802.11 cards using the Prism II chips, with a link-layer
|
||||
* header including Prism monitor mode information plus an 802.11
|
||||
* header.
|
||||
*/
|
||||
#define DLT_PRISM_HEADER 119
|
||||
|
||||
/*
|
||||
* Reserved for Aironet 802.11 cards, with an Aironet link-layer header
|
||||
* (see Doug Ambrisko's FreeBSD patches).
|
||||
*/
|
||||
#define DLT_AIRONET_HEADER 120
|
||||
|
||||
/*
|
||||
* Reserved for Siemens HiPath HDLC.
|
||||
*/
|
||||
#define DLT_HHDLC 121
|
||||
|
||||
/*
|
||||
* This is for RFC 2625 IP-over-Fibre Channel.
|
||||
*
|
||||
* This is not for use with raw Fibre Channel, where the link-layer
|
||||
* header starts with a Fibre Channel frame header; it's for IP-over-FC,
|
||||
* where the link-layer header starts with an RFC 2625 Network_Header
|
||||
* field.
|
||||
*/
|
||||
#define DLT_IP_OVER_FC 122
|
||||
|
||||
/*
|
||||
* This is for Full Frontal ATM on Solaris with SunATM, with a
|
||||
* pseudo-header followed by an AALn PDU.
|
||||
*
|
||||
* There may be other forms of Full Frontal ATM on other OSes,
|
||||
* with different pseudo-headers.
|
||||
*
|
||||
* If ATM software returns a pseudo-header with VPI/VCI information
|
||||
* (and, ideally, packet type information, e.g. signalling, ILMI,
|
||||
* LANE, LLC-multiplexed traffic, etc.), it should not use
|
||||
* DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump
|
||||
* and the like don't have to infer the presence or absence of a
|
||||
* pseudo-header and the form of the pseudo-header.
|
||||
*/
|
||||
#define DLT_SUNATM 123 /* Solaris+SunATM */
|
||||
|
||||
/*
|
||||
* Reserved as per request from Kent Dahlgren <kent@praesum.com>
|
||||
* for private use.
|
||||
*/
|
||||
#define DLT_RIO 124 /* RapidIO */
|
||||
#define DLT_PCI_EXP 125 /* PCI Express */
|
||||
#define DLT_AURORA 126 /* Xilinx Aurora link layer */
|
||||
|
||||
/*
|
||||
* Header for 802.11 plus a number of bits of link-layer information
|
||||
* including radio information, used by some recent BSD drivers as
|
||||
* well as the madwifi Atheros driver for Linux.
|
||||
*/
|
||||
#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus radiotap radio header */
|
||||
|
||||
/*
|
||||
* Reserved for the TZSP encapsulation, as per request from
|
||||
* Chris Waters <chris.waters@networkchemistry.com>
|
||||
* TZSP is a generic encapsulation for any other link type,
|
||||
* which includes a means to include meta-information
|
||||
* with the packet, e.g. signal strength and channel
|
||||
* for 802.11 packets.
|
||||
*/
|
||||
#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */
|
||||
|
||||
/*
|
||||
* BSD's ARCNET headers have the source host, destination host,
|
||||
* and type at the beginning of the packet; that's what's handed
|
||||
* up to userland via BPF.
|
||||
*
|
||||
* Linux's ARCNET headers, however, have a 2-byte offset field
|
||||
* between the host IDs and the type; that's what's handed up
|
||||
* to userland via PF_PACKET sockets.
|
||||
*
|
||||
* We therefore have to have separate DLT_ values for them.
|
||||
*/
|
||||
#define DLT_ARCNET_LINUX 129 /* ARCNET */
|
||||
|
||||
/*
|
||||
* Juniper-private data link types, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_MLPPP 130
|
||||
#define DLT_JUNIPER_MLFR 131
|
||||
#define DLT_JUNIPER_ES 132
|
||||
#define DLT_JUNIPER_GGSN 133
|
||||
#define DLT_JUNIPER_MFR 134
|
||||
#define DLT_JUNIPER_ATM2 135
|
||||
#define DLT_JUNIPER_SERVICES 136
|
||||
#define DLT_JUNIPER_ATM1 137
|
||||
|
||||
/*
|
||||
* Apple IP-over-IEEE 1394, as per a request from Dieter Siegmund
|
||||
* <dieter@apple.com>. The header that's presented is an Ethernet-like
|
||||
* header:
|
||||
*
|
||||
* #define FIREWIRE_EUI64_LEN 8
|
||||
* struct firewire_header {
|
||||
* u_char firewire_dhost[FIREWIRE_EUI64_LEN];
|
||||
* u_char firewire_shost[FIREWIRE_EUI64_LEN];
|
||||
* u_short firewire_type;
|
||||
* };
|
||||
*
|
||||
* with "firewire_type" being an Ethernet type value, rather than,
|
||||
* for example, raw GASP frames being handed up.
|
||||
*/
|
||||
#define DLT_APPLE_IP_OVER_IEEE1394 138
|
||||
|
||||
/*
|
||||
* Various SS7 encapsulations, as per a request from Jeff Morriss
|
||||
* <jeff.morriss[AT]ulticom.com> and subsequent discussions.
|
||||
*/
|
||||
#define DLT_MTP2_WITH_PHDR 139 /* pseudo-header with various info, followed by MTP2 */
|
||||
#define DLT_MTP2 140 /* MTP2, without pseudo-header */
|
||||
#define DLT_MTP3 141 /* MTP3, without pseudo-header or MTP2 */
|
||||
#define DLT_SCCP 142 /* SCCP, without pseudo-header or MTP2 or MTP3 */
|
||||
|
||||
/*
|
||||
* DOCSIS MAC frames.
|
||||
*/
|
||||
#define DLT_DOCSIS 143
|
||||
|
||||
/*
|
||||
* Linux-IrDA packets. Protocol defined at http://www.irda.org.
|
||||
* Those packets include IrLAP headers and above (IrLMP...), but
|
||||
* don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy
|
||||
* framing can be handled by the hardware and depend on the bitrate.
|
||||
* This is exactly the format you would get capturing on a Linux-IrDA
|
||||
* interface (irdaX), but not on a raw serial port.
|
||||
* Note the capture is done in "Linux-cooked" mode, so each packet include
|
||||
* a fake packet header (struct sll_header). This is because IrDA packet
|
||||
* decoding is dependant on the direction of the packet (incomming or
|
||||
* outgoing).
|
||||
* When/if other platform implement IrDA capture, we may revisit the
|
||||
* issue and define a real DLT_IRDA...
|
||||
* Jean II
|
||||
*/
|
||||
#define DLT_LINUX_IRDA 144
|
||||
|
||||
/*
|
||||
* Reserved for IBM SP switch and IBM Next Federation switch.
|
||||
*/
|
||||
#define DLT_IBM_SP 145
|
||||
#define DLT_IBM_SN 146
|
||||
|
||||
/*
|
||||
* Reserved for private use. If you have some link-layer header type
|
||||
* that you want to use within your organization, with the capture files
|
||||
* using that link-layer header type not ever be sent outside your
|
||||
* organization, you can use these values.
|
||||
*
|
||||
* No libpcap release will use these for any purpose, nor will any
|
||||
* tcpdump release use them, either.
|
||||
*
|
||||
* Do *NOT* use these in capture files that you expect anybody not using
|
||||
* your private versions of capture-file-reading tools to read; in
|
||||
* particular, do *NOT* use them in products, otherwise you may find that
|
||||
* people won't be able to use tcpdump, or snort, or Ethereal, or... to
|
||||
* read capture files from your firewall/intrusion detection/traffic
|
||||
* monitoring/etc. appliance, or whatever product uses that DLT_ value,
|
||||
* and you may also find that the developers of those applications will
|
||||
* not accept patches to let them read those files.
|
||||
*
|
||||
* Also, do not use them if somebody might send you a capture using them
|
||||
* for *their* private type and tools using them for *your* private type
|
||||
* would have to read them.
|
||||
*
|
||||
* Instead, ask "tcpdump-workers@lists.tcpdump.org" for a new DLT_ value,
|
||||
* as per the comment above, and use the type you're given.
|
||||
*/
|
||||
#define DLT_USER0 147
|
||||
#define DLT_USER1 148
|
||||
#define DLT_USER2 149
|
||||
#define DLT_USER3 150
|
||||
#define DLT_USER4 151
|
||||
#define DLT_USER5 152
|
||||
#define DLT_USER6 153
|
||||
#define DLT_USER7 154
|
||||
#define DLT_USER8 155
|
||||
#define DLT_USER9 156
|
||||
#define DLT_USER10 157
|
||||
#define DLT_USER11 158
|
||||
#define DLT_USER12 159
|
||||
#define DLT_USER13 160
|
||||
#define DLT_USER14 161
|
||||
#define DLT_USER15 162
|
||||
|
||||
/*
|
||||
* For future use with 802.11 captures - defined by AbsoluteValue
|
||||
* Systems to store a number of bits of link-layer information
|
||||
* including radio information:
|
||||
*
|
||||
* http://www.shaftnet.org/~pizza/software/capturefrm.txt
|
||||
*
|
||||
* but it might be used by some non-AVS drivers now or in the
|
||||
* future.
|
||||
*/
|
||||
#define DLT_IEEE802_11_RADIO_AVS 163 /* 802.11 plus AVS radio header */
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_MONITOR 164
|
||||
|
||||
/*
|
||||
* Reserved for BACnet MS/TP.
|
||||
*/
|
||||
#define DLT_BACNET_MS_TP 165
|
||||
|
||||
/*
|
||||
* Another PPP variant as per request from Karsten Keil <kkeil@suse.de>.
|
||||
*
|
||||
* This is used in some OSes to allow a kernel socket filter to distinguish
|
||||
* between incoming and outgoing packets, on a socket intended to
|
||||
* supply pppd with outgoing packets so it can do dial-on-demand and
|
||||
* hangup-on-lack-of-demand; incoming packets are filtered out so they
|
||||
* don't cause pppd to hold the connection up (you don't want random
|
||||
* input packets such as port scans, packets from old lost connections,
|
||||
* etc. to force the connection to stay up).
|
||||
*
|
||||
* The first byte of the PPP header (0xff03) is modified to accomodate
|
||||
* the direction - 0x00 = IN, 0x01 = OUT.
|
||||
*/
|
||||
#define DLT_PPP_PPPD 166
|
||||
|
||||
/*
|
||||
* Names for backwards compatibility with older versions of some PPP
|
||||
* software; new software should use DLT_PPP_PPPD.
|
||||
*/
|
||||
#define DLT_PPP_WITH_DIRECTION DLT_PPP_PPPD
|
||||
#define DLT_LINUX_PPP_WITHDIRECTION DLT_PPP_PPPD
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
|
||||
* for passing on chassis-internal metainformation such as
|
||||
* QOS profiles, cookies, etc..
|
||||
*/
|
||||
#define DLT_JUNIPER_PPPOE 167
|
||||
#define DLT_JUNIPER_PPPOE_ATM 168
|
||||
|
||||
#define DLT_GPRS_LLC 169 /* GPRS LLC */
|
||||
#define DLT_GPF_T 170 /* GPF-T (ITU-T G.7041/Y.1303) */
|
||||
#define DLT_GPF_F 171 /* GPF-F (ITU-T G.7041/Y.1303) */
|
||||
|
||||
/*
|
||||
* Requested by Oolan Zimmer <oz@gcom.com> for use in Gcom's T1/E1 line
|
||||
* monitoring equipment.
|
||||
*/
|
||||
#define DLT_GCOM_T1E1 172
|
||||
#define DLT_GCOM_SERIAL 173
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>. The DLT_ is used
|
||||
* for internal communication to Physical Interface Cards (PIC)
|
||||
*/
|
||||
#define DLT_JUNIPER_PIC_PEER 174
|
||||
|
||||
/*
|
||||
* Link types requested by Gregor Maier <gregor@endace.com> of Endace
|
||||
* Measurement Systems. They add an ERF header (see
|
||||
* http://www.endace.com/support/EndaceRecordFormat.pdf) in front of
|
||||
* the link-layer header.
|
||||
*/
|
||||
#define DLT_ERF_ETH 175 /* Ethernet */
|
||||
#define DLT_ERF_POS 176 /* Packet-over-SONET */
|
||||
|
||||
/*
|
||||
* Requested by Daniele Orlandi <daniele@orlandi.com> for raw LAPD
|
||||
* for vISDN (http://www.orlandi.com/visdn/). Its link-layer header
|
||||
* includes additional information before the LAPD header, so it's
|
||||
* not necessarily a generic LAPD header.
|
||||
*/
|
||||
#define DLT_LINUX_LAPD 177
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ are used for prepending meta-information
|
||||
* like interface index, interface name
|
||||
* before standard Ethernet, PPP, Frelay & C-HDLC Frames
|
||||
*/
|
||||
#define DLT_JUNIPER_ETHER 178
|
||||
#define DLT_JUNIPER_PPP 179
|
||||
#define DLT_JUNIPER_FRELAY 180
|
||||
#define DLT_JUNIPER_CHDLC 181
|
||||
|
||||
/*
|
||||
* Multi Link Frame Relay (FRF.16)
|
||||
*/
|
||||
#define DLT_MFR 182
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for internal communication with a
|
||||
* voice Adapter Card (PIC)
|
||||
*/
|
||||
#define DLT_JUNIPER_VP 183
|
||||
|
||||
/*
|
||||
* Arinc 429 frames.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Every frame contains a 32bit A429 label.
|
||||
* More documentation on Arinc 429 can be found at
|
||||
* http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf
|
||||
*/
|
||||
#define DLT_A429 184
|
||||
|
||||
/*
|
||||
* Arinc 653 Interpartition Communication messages.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Please refer to the A653-1 standard for more information.
|
||||
*/
|
||||
#define DLT_A653_ICM 185
|
||||
|
||||
/*
|
||||
* USB packets, beginning with a USB setup header; requested by
|
||||
* Paolo Abeni <paolo.abeni@email.it>.
|
||||
*/
|
||||
#define DLT_USB 186
|
||||
|
||||
/*
|
||||
* Bluetooth HCI UART transport layer (part H:4); requested by
|
||||
* Paolo Abeni.
|
||||
*/
|
||||
#define DLT_BLUETOOTH_HCI_H4 187
|
||||
|
||||
/*
|
||||
* IEEE 802.16 MAC Common Part Sublayer; requested by Maria Cruz
|
||||
* <cruz_petagay@bah.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_16_MAC_CPS 188
|
||||
|
||||
/*
|
||||
* USB packets, beginning with a Linux USB header; requested by
|
||||
* Paolo Abeni <paolo.abeni@email.it>.
|
||||
*/
|
||||
#define DLT_USB_LINUX 189
|
||||
|
||||
/*
|
||||
* Controller Area Network (CAN) v. 2.0B packets.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
* Used to dump CAN packets coming from a CAN Vector board.
|
||||
* More documentation on the CAN v2.0B frames can be found at
|
||||
* http://www.can-cia.org/downloads/?269
|
||||
*/
|
||||
#define DLT_CAN20B 190
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, with address fields padded, as is done by Linux
|
||||
* drivers; requested by Juergen Schimmer.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4_LINUX 191
|
||||
|
||||
/*
|
||||
* Per Packet Information encapsulated packets.
|
||||
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
|
||||
*/
|
||||
#define DLT_PPI 192
|
||||
|
||||
/*
|
||||
* Header for 802.16 MAC Common Part Sublayer plus a radiotap radio header;
|
||||
* requested by Charles Clancy.
|
||||
*/
|
||||
#define DLT_IEEE802_16_MAC_CPS_RADIO 193
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for internal communication with a
|
||||
* integrated service module (ISM).
|
||||
*/
|
||||
#define DLT_JUNIPER_ISM 194
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
|
||||
* nothing); requested by Mikko Saarnivala <mikko.saarnivala@sensinode.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4 195
|
||||
|
||||
/*
|
||||
* Various link-layer types, with a pseudo-header, for SITA
|
||||
* (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com).
|
||||
*/
|
||||
#define DLT_SITA 196
|
||||
|
||||
/*
|
||||
* Various link-layer types, with a pseudo-header, for Endace DAG cards;
|
||||
* encapsulates Endace ERF records. Requested by Stephen Donnelly
|
||||
* <stephen@endace.com>.
|
||||
*/
|
||||
#define DLT_ERF 197
|
||||
|
||||
/*
|
||||
* Special header prepended to Ethernet packets when capturing from a
|
||||
* u10 Networks board. Requested by Phil Mulholland
|
||||
* <phil@u10networks.com>.
|
||||
*/
|
||||
#define DLT_RAIF1 198
|
||||
|
||||
/*
|
||||
* IPMB packet for IPMI, beginning with the I2C slave address, followed
|
||||
* by the netFn and LUN, etc.. Requested by Chanthy Toeung
|
||||
* <chanthy.toeung@ca.kontron.com>.
|
||||
*/
|
||||
#define DLT_IPMB 199
|
||||
|
||||
/*
|
||||
* Juniper-private data link type, as per request from
|
||||
* Hannes Gredler <hannes@juniper.net>.
|
||||
* The DLT_ is used for capturing data on a secure tunnel interface.
|
||||
*/
|
||||
#define DLT_JUNIPER_ST 200
|
||||
|
||||
/*
|
||||
* Bluetooth HCI UART transport layer (part H:4), with pseudo-header
|
||||
* that includes direction information; requested by Paolo Abeni.
|
||||
*/
|
||||
#define DLT_BLUETOOTH_HCI_H4_WITH_PHDR 201
|
||||
|
||||
/*
|
||||
* AX.25 packet with a 1-byte KISS header; see
|
||||
*
|
||||
* http://www.ax25.net/kiss.htm
|
||||
*
|
||||
* as per Richard Stearn <richard@rns-stearn.demon.co.uk>.
|
||||
*/
|
||||
#define DLT_AX25_KISS 202
|
||||
|
||||
/*
|
||||
* LAPD packets from an ISDN channel, starting with the address field,
|
||||
* with no pseudo-header.
|
||||
* Requested by Varuna De Silva <varunax@gmail.com>.
|
||||
*/
|
||||
#define DLT_LAPD 203
|
||||
|
||||
/*
|
||||
* Variants of various link-layer headers, with a one-byte direction
|
||||
* pseudo-header prepended - zero means "received by this host",
|
||||
* non-zero (any non-zero value) means "sent by this host" - as per
|
||||
* Will Barker <w.barker@zen.co.uk>.
|
||||
*/
|
||||
#define DLT_PPP_WITH_DIR 204 /* PPP - don't confuse with DLT_PPP_WITH_DIRECTION */
|
||||
#define DLT_C_HDLC_WITH_DIR 205 /* Cisco HDLC */
|
||||
#define DLT_FRELAY_WITH_DIR 206 /* Frame Relay */
|
||||
#define DLT_LAPB_WITH_DIR 207 /* LAPB */
|
||||
|
||||
/*
|
||||
* 208 is reserved for an as-yet-unspecified proprietary link-layer
|
||||
* type, as requested by Will Barker.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IPMB with a Linux-specific pseudo-header; as requested by Alexey Neyman
|
||||
* <avn@pigeonpoint.com>.
|
||||
*/
|
||||
#define DLT_IPMB_LINUX 209
|
||||
|
||||
/*
|
||||
* FlexRay automotive bus - http://www.flexray.com/ - as requested
|
||||
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_FLEXRAY 210
|
||||
|
||||
/*
|
||||
* Media Oriented Systems Transport (MOST) bus for multimedia
|
||||
* transport - http://www.mostcooperation.com/ - as requested
|
||||
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_MOST 211
|
||||
|
||||
/*
|
||||
* Local Interconnect Network (LIN) bus for vehicle networks -
|
||||
* http://www.lin-subbus.org/ - as requested by Hannes Kaelber
|
||||
* <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_LIN 212
|
||||
|
||||
/*
|
||||
* X2E-private data link type used for serial line capture,
|
||||
* as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_X2E_SERIAL 213
|
||||
|
||||
/*
|
||||
* X2E-private data link type used for the Xoraya data logger
|
||||
* family, as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
|
||||
*/
|
||||
#define DLT_X2E_XORAYA 214
|
||||
|
||||
/*
|
||||
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
|
||||
* nothing), but with the PHY-level data for non-ASK PHYs (4 octets
|
||||
* of 0 as preamble, one octet of SFD, one octet of frame length+
|
||||
* reserved bit, and then the MAC-layer data, starting with the
|
||||
* frame control field).
|
||||
*
|
||||
* Requested by Max Filippov <jcmvbkbc@gmail.com>.
|
||||
*/
|
||||
#define DLT_IEEE802_15_4_NONASK_PHY 215
|
||||
|
||||
|
||||
/*
|
||||
* DLT and savefile link type values are split into a class and
|
||||
* a member of that class. A class value of 0 indicates a regular
|
||||
* DLT_/LINKTYPE_ value.
|
||||
*/
|
||||
#define DLT_CLASS(x) ((x) & 0x03ff0000)
|
||||
|
||||
/*
|
||||
* NetBSD-specific generic "raw" link type. The class value indicates
|
||||
* that this is the generic raw type, and the lower 16 bits are the
|
||||
* address family we're dealing with. Those values are NetBSD-specific;
|
||||
* do not assume that they correspond to AF_ values for your operating
|
||||
* system.
|
||||
*/
|
||||
#define DLT_CLASS_NETBSD_RAWAF 0x02240000
|
||||
#define DLT_NETBSD_RAWAF(af) (DLT_CLASS_NETBSD_RAWAF | (af))
|
||||
#define DLT_NETBSD_RAWAF_AF(x) ((x) & 0x0000ffff)
|
||||
#define DLT_IS_NETBSD_RAWAF(x) (DLT_CLASS(x) == DLT_CLASS_NETBSD_RAWAF)
|
||||
|
||||
|
||||
/*
|
||||
* The instruction encodings.
|
||||
*/
|
||||
/* instruction classes */
|
||||
#define BPF_CLASS(code) ((code) & 0x07)
|
||||
#define BPF_LD 0x00
|
||||
#define BPF_LDX 0x01
|
||||
#define BPF_ST 0x02
|
||||
#define BPF_STX 0x03
|
||||
#define BPF_ALU 0x04
|
||||
#define BPF_JMP 0x05
|
||||
#define BPF_RET 0x06
|
||||
#define BPF_MISC 0x07
|
||||
|
||||
/* ld/ldx fields */
|
||||
#define BPF_SIZE(code) ((code) & 0x18)
|
||||
#define BPF_W 0x00
|
||||
#define BPF_H 0x08
|
||||
#define BPF_B 0x10
|
||||
#define BPF_MODE(code) ((code) & 0xe0)
|
||||
#define BPF_IMM 0x00
|
||||
#define BPF_ABS 0x20
|
||||
#define BPF_IND 0x40
|
||||
#define BPF_MEM 0x60
|
||||
#define BPF_LEN 0x80
|
||||
#define BPF_MSH 0xa0
|
||||
|
||||
/* alu/jmp fields */
|
||||
#define BPF_OP(code) ((code) & 0xf0)
|
||||
#define BPF_ADD 0x00
|
||||
#define BPF_SUB 0x10
|
||||
#define BPF_MUL 0x20
|
||||
#define BPF_DIV 0x30
|
||||
#define BPF_OR 0x40
|
||||
#define BPF_AND 0x50
|
||||
#define BPF_LSH 0x60
|
||||
#define BPF_RSH 0x70
|
||||
#define BPF_NEG 0x80
|
||||
#define BPF_JA 0x00
|
||||
#define BPF_JEQ 0x10
|
||||
#define BPF_JGT 0x20
|
||||
#define BPF_JGE 0x30
|
||||
#define BPF_JSET 0x40
|
||||
#define BPF_SRC(code) ((code) & 0x08)
|
||||
#define BPF_K 0x00
|
||||
#define BPF_X 0x08
|
||||
|
||||
/* ret - BPF_K and BPF_X also apply */
|
||||
#define BPF_RVAL(code) ((code) & 0x18)
|
||||
#define BPF_A 0x10
|
||||
|
||||
/* misc */
|
||||
#define BPF_MISCOP(code) ((code) & 0xf8)
|
||||
#define BPF_TAX 0x00
|
||||
#define BPF_TXA 0x80
|
||||
|
||||
/*
|
||||
* The instruction data structure.
|
||||
*/
|
||||
struct bpf_insn {
|
||||
u_short code;
|
||||
u_char jt;
|
||||
u_char jf;
|
||||
bpf_u_int32 k;
|
||||
};
|
||||
|
||||
/*
|
||||
* Macros for insn array initializers.
|
||||
*/
|
||||
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
|
||||
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
|
||||
|
||||
#if __STDC__ || defined(__cplusplus)
|
||||
extern int bpf_validate(const struct bpf_insn *, int);
|
||||
extern u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
|
||||
#else
|
||||
extern int bpf_validate();
|
||||
extern u_int bpf_filter();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST).
|
||||
*/
|
||||
#define BPF_MEMWORDS 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/namedb.h,v 1.1 2006/10/04 18:09:22 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_namedb_h
|
||||
#define lib_pcap_namedb_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* As returned by the pcap_next_etherent()
|
||||
* XXX this stuff doesn't belong in this interface, but this
|
||||
* library already must do name to address translation, so
|
||||
* on systems that don't have support for /etc/ethers, we
|
||||
* export these hooks since they'll
|
||||
*/
|
||||
struct pcap_etherent {
|
||||
u_char addr[6];
|
||||
char name[122];
|
||||
};
|
||||
#ifndef PCAP_ETHERS_FILE
|
||||
#define PCAP_ETHERS_FILE "/etc/ethers"
|
||||
#endif
|
||||
struct pcap_etherent *pcap_next_etherent(FILE *);
|
||||
u_char *pcap_ether_hostton(const char*);
|
||||
u_char *pcap_ether_aton(const char *);
|
||||
|
||||
bpf_u_int32 **pcap_nametoaddr(const char *);
|
||||
#ifdef INET6
|
||||
struct addrinfo *pcap_nametoaddrinfo(const char *);
|
||||
#endif
|
||||
bpf_u_int32 pcap_nametonetaddr(const char *);
|
||||
|
||||
int pcap_nametoport(const char *, int *, int *);
|
||||
int pcap_nametoportrange(const char *, int *, int *, int *);
|
||||
int pcap_nametoproto(const char *);
|
||||
int pcap_nametoeproto(const char *);
|
||||
int pcap_nametollc(const char *);
|
||||
/*
|
||||
* If a protocol is unknown, PROTO_UNDEF is returned.
|
||||
* Also, pcap_nametoport() returns the protocol along with the port number.
|
||||
* If there are ambiguous entried in /etc/services (i.e. domain
|
||||
* can be either tcp or udp) PROTO_UNDEF is returned.
|
||||
*/
|
||||
#define PROTO_UNDEF -1
|
||||
|
||||
/* XXX move these to pcap-int.h? */
|
||||
int __pcap_atodn(const char *, bpf_u_int32 *);
|
||||
int __pcap_atoin(const char *, bpf_u_int32 *);
|
||||
u_short __pcap_nametodnaddr(const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,407 @@
|
||||
/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
|
||||
/*
|
||||
* Copyright (c) 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the Computer Systems
|
||||
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.4.2.11 2008-10-06 15:38:39 gianluca Exp $ (LBL)
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_pcap_h
|
||||
#define lib_pcap_pcap_h
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <pcap-stdinc.h>
|
||||
#elif defined(MSDOS)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h> /* u_int, u_char etc. */
|
||||
#else /* UN*X */
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#endif /* WIN32/MSDOS/UN*X */
|
||||
|
||||
#ifndef PCAP_DONT_INCLUDE_PCAP_BPF_H
|
||||
#include <pcap/bpf.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
// We have to define the SOCKET here, although it has been defined in sockutils.h
|
||||
// This is to avoid the distribution of the 'sockutils.h' file around
|
||||
// (for example in the WinPcap developer's pack)
|
||||
#ifndef SOCKET
|
||||
#ifdef WIN32
|
||||
#define SOCKET unsigned int
|
||||
#else
|
||||
#define SOCKET int
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCAP_VERSION_MAJOR 2
|
||||
#define PCAP_VERSION_MINOR 4
|
||||
|
||||
#define PCAP_ERRBUF_SIZE 256
|
||||
|
||||
/*
|
||||
* Compatibility for systems that have a bpf.h that
|
||||
* predates the bpf typedefs for 64-bit support.
|
||||
*/
|
||||
#if BPF_RELEASE - 0 < 199406
|
||||
typedef int bpf_int32;
|
||||
typedef u_int bpf_u_int32;
|
||||
#endif
|
||||
|
||||
typedef struct pcap pcap_t;
|
||||
typedef struct pcap_dumper pcap_dumper_t;
|
||||
typedef struct pcap_if pcap_if_t;
|
||||
typedef struct pcap_addr pcap_addr_t;
|
||||
|
||||
/*
|
||||
* The first record in the file contains saved values for some
|
||||
* of the flags used in the printout phases of tcpdump.
|
||||
* Many fields here are 32 bit ints so compilers won't insert unwanted
|
||||
* padding; these files need to be interchangeable across architectures.
|
||||
*
|
||||
* Do not change the layout of this structure, in any way (this includes
|
||||
* changes that only affect the length of fields in this structure).
|
||||
*
|
||||
* Also, do not change the interpretation of any of the members of this
|
||||
* structure, in any way (this includes using values other than
|
||||
* LINKTYPE_ values, as defined in "savefile.c", in the "linktype"
|
||||
* field).
|
||||
*
|
||||
* Instead:
|
||||
*
|
||||
* introduce a new structure for the new format, if the layout
|
||||
* of the structure changed;
|
||||
*
|
||||
* send mail to "tcpdump-workers@lists.tcpdump.org", requesting
|
||||
* a new magic number for your new capture file format, and, when
|
||||
* you get the new magic number, put it in "savefile.c";
|
||||
*
|
||||
* use that magic number for save files with the changed file
|
||||
* header;
|
||||
*
|
||||
* make the code in "savefile.c" capable of reading files with
|
||||
* the old file header as well as files with the new file header
|
||||
* (using the magic number to determine the header format).
|
||||
*
|
||||
* Then supply the changes as a patch at
|
||||
*
|
||||
* http://sourceforge.net/projects/libpcap/
|
||||
*
|
||||
* so that future versions of libpcap and programs that use it (such as
|
||||
* tcpdump) will be able to read your new capture file format.
|
||||
*/
|
||||
struct pcap_file_header {
|
||||
bpf_u_int32 magic;
|
||||
u_short version_major;
|
||||
u_short version_minor;
|
||||
bpf_int32 thiszone; /* gmt to local correction */
|
||||
bpf_u_int32 sigfigs; /* accuracy of timestamps */
|
||||
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
|
||||
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Macros for the value returned by pcap_datalink_ext().
|
||||
*
|
||||
* If LT_FCS_LENGTH_PRESENT(x) is true, the LT_FCS_LENGTH(x) macro
|
||||
* gives the FCS length of packets in the capture.
|
||||
*/
|
||||
#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000)
|
||||
#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28)
|
||||
#define LT_FCS_DATALINK_EXT(x) ((((x) & 0xF) << 28) | 0x04000000)
|
||||
|
||||
typedef enum {
|
||||
PCAP_D_INOUT = 0,
|
||||
PCAP_D_IN,
|
||||
PCAP_D_OUT
|
||||
} pcap_direction_t;
|
||||
|
||||
/*
|
||||
* Generic per-packet information, as supplied by libpcap.
|
||||
*
|
||||
* The time stamp can and should be a "struct timeval", regardless of
|
||||
* whether your system supports 32-bit tv_sec in "struct timeval",
|
||||
* 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit
|
||||
* and 64-bit applications. The on-disk format of savefiles uses 32-bit
|
||||
* tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit
|
||||
* and 64-bit versions of libpcap, even if they're on the same platform,
|
||||
* should supply the appropriate version of "struct timeval", even if
|
||||
* that's not what the underlying packet capture mechanism supplies.
|
||||
*/
|
||||
struct pcap_pkthdr {
|
||||
struct timeval ts; /* time stamp */
|
||||
bpf_u_int32 caplen; /* length of portion present */
|
||||
bpf_u_int32 len; /* length this packet (off wire) */
|
||||
};
|
||||
|
||||
/*
|
||||
* As returned by the pcap_stats()
|
||||
*/
|
||||
struct pcap_stat {
|
||||
u_int ps_recv; /* number of packets received */
|
||||
u_int ps_drop; /* number of packets dropped */
|
||||
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
|
||||
#ifdef HAVE_REMOTE
|
||||
u_int ps_capt; /* number of packets that are received by the application; please get rid off the Win32 ifdef */
|
||||
u_int ps_sent; /* number of packets sent by the server on the network */
|
||||
u_int ps_netdrop; /* number of packets lost on the network */
|
||||
#endif /* HAVE_REMOTE */
|
||||
};
|
||||
|
||||
#ifdef MSDOS
|
||||
/*
|
||||
* As returned by the pcap_stats_ex()
|
||||
*/
|
||||
struct pcap_stat_ex {
|
||||
u_long rx_packets; /* total packets received */
|
||||
u_long tx_packets; /* total packets transmitted */
|
||||
u_long rx_bytes; /* total bytes received */
|
||||
u_long tx_bytes; /* total bytes transmitted */
|
||||
u_long rx_errors; /* bad packets received */
|
||||
u_long tx_errors; /* packet transmit problems */
|
||||
u_long rx_dropped; /* no space in Rx buffers */
|
||||
u_long tx_dropped; /* no space available for Tx */
|
||||
u_long multicast; /* multicast packets received */
|
||||
u_long collisions;
|
||||
|
||||
/* detailed rx_errors: */
|
||||
u_long rx_length_errors;
|
||||
u_long rx_over_errors; /* receiver ring buff overflow */
|
||||
u_long rx_crc_errors; /* recv'd pkt with crc error */
|
||||
u_long rx_frame_errors; /* recv'd frame alignment error */
|
||||
u_long rx_fifo_errors; /* recv'r fifo overrun */
|
||||
u_long rx_missed_errors; /* recv'r missed packet */
|
||||
|
||||
/* detailed tx_errors */
|
||||
u_long tx_aborted_errors;
|
||||
u_long tx_carrier_errors;
|
||||
u_long tx_fifo_errors;
|
||||
u_long tx_heartbeat_errors;
|
||||
u_long tx_window_errors;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Item in a list of interfaces.
|
||||
*/
|
||||
struct pcap_if {
|
||||
struct pcap_if *next;
|
||||
char *name; /* name to hand to "pcap_open_live()" */
|
||||
char *description; /* textual description of interface, or NULL */
|
||||
struct pcap_addr *addresses;
|
||||
bpf_u_int32 flags; /* PCAP_IF_ interface flags */
|
||||
};
|
||||
|
||||
#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */
|
||||
|
||||
/*
|
||||
* Representation of an interface address.
|
||||
*/
|
||||
struct pcap_addr {
|
||||
struct pcap_addr *next;
|
||||
struct sockaddr *addr; /* address */
|
||||
struct sockaddr *netmask; /* netmask for that address */
|
||||
struct sockaddr *broadaddr; /* broadcast address for that address */
|
||||
struct sockaddr *dstaddr; /* P2P destination address for that address */
|
||||
};
|
||||
|
||||
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
|
||||
const u_char *);
|
||||
|
||||
/*
|
||||
* Error codes for the pcap API.
|
||||
* These will all be negative, so you can check for the success or
|
||||
* failure of a call that returns these codes by checking for a
|
||||
* negative value.
|
||||
*/
|
||||
#define PCAP_ERROR -1 /* generic error code */
|
||||
#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */
|
||||
#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */
|
||||
#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */
|
||||
#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */
|
||||
#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */
|
||||
#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */
|
||||
#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */
|
||||
#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */
|
||||
|
||||
/*
|
||||
* Warning codes for the pcap API.
|
||||
* These will all be positive and non-zero, so they won't look like
|
||||
* errors.
|
||||
*/
|
||||
#define PCAP_WARNING 1 /* generic warning code */
|
||||
#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */
|
||||
|
||||
char *pcap_lookupdev(char *);
|
||||
int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *);
|
||||
|
||||
pcap_t *pcap_create(const char *, char *);
|
||||
int pcap_set_snaplen(pcap_t *, int);
|
||||
int pcap_set_promisc(pcap_t *, int);
|
||||
int pcap_can_set_rfmon(pcap_t *);
|
||||
int pcap_set_rfmon(pcap_t *, int);
|
||||
int pcap_set_timeout(pcap_t *, int);
|
||||
int pcap_set_buffer_size(pcap_t *, int);
|
||||
int pcap_activate(pcap_t *);
|
||||
|
||||
pcap_t *pcap_open_live(const char *, int, int, int, char *);
|
||||
pcap_t *pcap_open_dead(int, int);
|
||||
pcap_t *pcap_open_offline(const char *, char *);
|
||||
#if defined(WIN32)
|
||||
pcap_t *pcap_hopen_offline(intptr_t, char *);
|
||||
#if !defined(LIBPCAP_EXPORTS)
|
||||
#define pcap_fopen_offline(f,b) \
|
||||
pcap_hopen_offline(_get_osfhandle(_fileno(f)), b)
|
||||
#else /*LIBPCAP_EXPORTS*/
|
||||
static pcap_t *pcap_fopen_offline(FILE *, char *);
|
||||
#endif
|
||||
#else /*WIN32*/
|
||||
pcap_t *pcap_fopen_offline(FILE *, char *);
|
||||
#endif /*WIN32*/
|
||||
|
||||
void pcap_close(pcap_t *);
|
||||
int pcap_loop(pcap_t *, int, pcap_handler, u_char *);
|
||||
int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *);
|
||||
const u_char*
|
||||
pcap_next(pcap_t *, struct pcap_pkthdr *);
|
||||
int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **);
|
||||
void pcap_breakloop(pcap_t *);
|
||||
int pcap_stats(pcap_t *, struct pcap_stat *);
|
||||
int pcap_setfilter(pcap_t *, struct bpf_program *);
|
||||
int pcap_setdirection(pcap_t *, pcap_direction_t);
|
||||
int pcap_getnonblock(pcap_t *, char *);
|
||||
int pcap_setnonblock(pcap_t *, int, char *);
|
||||
int pcap_inject(pcap_t *, const void *, size_t);
|
||||
int pcap_sendpacket(pcap_t *, const u_char *, int);
|
||||
const char *pcap_statustostr(int);
|
||||
const char *pcap_strerror(int);
|
||||
char *pcap_geterr(pcap_t *);
|
||||
void pcap_perror(pcap_t *, char *);
|
||||
int pcap_compile(pcap_t *, struct bpf_program *, const char *, int,
|
||||
bpf_u_int32);
|
||||
int pcap_compile_nopcap(int, int, struct bpf_program *,
|
||||
const char *, int, bpf_u_int32);
|
||||
void pcap_freecode(struct bpf_program *);
|
||||
int pcap_offline_filter(struct bpf_program *, const struct pcap_pkthdr *,
|
||||
const u_char *);
|
||||
int pcap_datalink(pcap_t *);
|
||||
int pcap_datalink_ext(pcap_t *);
|
||||
int pcap_list_datalinks(pcap_t *, int **);
|
||||
int pcap_set_datalink(pcap_t *, int);
|
||||
void pcap_free_datalinks(int *);
|
||||
int pcap_datalink_name_to_val(const char *);
|
||||
const char *pcap_datalink_val_to_name(int);
|
||||
const char *pcap_datalink_val_to_description(int);
|
||||
int pcap_snapshot(pcap_t *);
|
||||
int pcap_is_swapped(pcap_t *);
|
||||
int pcap_major_version(pcap_t *);
|
||||
int pcap_minor_version(pcap_t *);
|
||||
|
||||
/* XXX */
|
||||
FILE *pcap_file(pcap_t *);
|
||||
int pcap_fileno(pcap_t *);
|
||||
|
||||
pcap_dumper_t *pcap_dump_open(pcap_t *, const char *);
|
||||
pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp);
|
||||
FILE *pcap_dump_file(pcap_dumper_t *);
|
||||
long pcap_dump_ftell(pcap_dumper_t *);
|
||||
int pcap_dump_flush(pcap_dumper_t *);
|
||||
void pcap_dump_close(pcap_dumper_t *);
|
||||
void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *);
|
||||
|
||||
int pcap_findalldevs(pcap_if_t **, char *);
|
||||
void pcap_freealldevs(pcap_if_t *);
|
||||
|
||||
const char *pcap_lib_version(void);
|
||||
|
||||
/* XXX this guy lives in the bpf tree */
|
||||
u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
|
||||
int bpf_validate(const struct bpf_insn *f, int len);
|
||||
char *bpf_image(const struct bpf_insn *, int);
|
||||
void bpf_dump(const struct bpf_program *, int);
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
/*
|
||||
* Win32 definitions
|
||||
*/
|
||||
|
||||
int pcap_setbuff(pcap_t *p, int dim);
|
||||
int pcap_setmode(pcap_t *p, int mode);
|
||||
int pcap_setmintocopy(pcap_t *p, int size);
|
||||
|
||||
#ifdef WPCAP
|
||||
/* Include file with the wpcap-specific extensions */
|
||||
#include <Win32-Extensions.h>
|
||||
#endif /* WPCAP */
|
||||
|
||||
#define MODE_CAPT 0
|
||||
#define MODE_STAT 1
|
||||
#define MODE_MON 2
|
||||
|
||||
#elif defined(MSDOS)
|
||||
|
||||
/*
|
||||
* MS-DOS definitions
|
||||
*/
|
||||
|
||||
int pcap_stats_ex (pcap_t *, struct pcap_stat_ex *);
|
||||
void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait);
|
||||
u_long pcap_mac_packets (void);
|
||||
|
||||
#else /* UN*X */
|
||||
|
||||
/*
|
||||
* UN*X definitions
|
||||
*/
|
||||
|
||||
int pcap_get_selectable_fd(pcap_t *);
|
||||
|
||||
#endif /* WIN32/MSDOS/UN*X */
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
/* Includes most of the public stuff that is needed for the remote capture */
|
||||
#include <remote-ext.h>
|
||||
#endif /* HAVE_REMOTE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from the Stanford/CMU enet packet filter,
|
||||
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
|
||||
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
|
||||
* Berkeley Laboratory.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/sll.h,v 1.2.2.1 2008-05-30 01:36:06 guy Exp $ (LBL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For captures on Linux cooked sockets, we construct a fake header
|
||||
* that includes:
|
||||
*
|
||||
* a 2-byte "packet type" which is one of:
|
||||
*
|
||||
* LINUX_SLL_HOST packet was sent to us
|
||||
* LINUX_SLL_BROADCAST packet was broadcast
|
||||
* LINUX_SLL_MULTICAST packet was multicast
|
||||
* LINUX_SLL_OTHERHOST packet was sent to somebody else
|
||||
* LINUX_SLL_OUTGOING packet was sent *by* us;
|
||||
*
|
||||
* a 2-byte Ethernet protocol field;
|
||||
*
|
||||
* a 2-byte link-layer type;
|
||||
*
|
||||
* a 2-byte link-layer address length;
|
||||
*
|
||||
* an 8-byte source link-layer address, whose actual length is
|
||||
* specified by the previous value.
|
||||
*
|
||||
* All fields except for the link-layer address are in network byte order.
|
||||
*
|
||||
* DO NOT change the layout of this structure, or change any of the
|
||||
* LINUX_SLL_ values below. If you must change the link-layer header
|
||||
* for a "cooked" Linux capture, introduce a new DLT_ type (ask
|
||||
* "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it
|
||||
* a value that collides with a value already being used), and use the
|
||||
* new header in captures of that type, so that programs that can
|
||||
* handle DLT_LINUX_SLL captures will continue to handle them correctly
|
||||
* without any change, and so that capture files with different headers
|
||||
* can be told apart and programs that read them can dissect the
|
||||
* packets in them.
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_sll_h
|
||||
#define lib_pcap_sll_h
|
||||
|
||||
/*
|
||||
* A DLT_LINUX_SLL fake link-layer header.
|
||||
*/
|
||||
#define SLL_HDR_LEN 16 /* total header length */
|
||||
#define SLL_ADDRLEN 8 /* length of address field */
|
||||
|
||||
struct sll_header {
|
||||
u_int16_t sll_pkttype; /* packet type */
|
||||
u_int16_t sll_hatype; /* link-layer address type */
|
||||
u_int16_t sll_halen; /* link-layer address length */
|
||||
u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */
|
||||
u_int16_t sll_protocol; /* protocol */
|
||||
};
|
||||
|
||||
/*
|
||||
* The LINUX_SLL_ values for "sll_pkttype"; these correspond to the
|
||||
* PACKET_ values on Linux, but are defined here so that they're
|
||||
* available even on systems other than Linux, and so that they
|
||||
* don't change even if the PACKET_ values change.
|
||||
*/
|
||||
#define LINUX_SLL_HOST 0
|
||||
#define LINUX_SLL_BROADCAST 1
|
||||
#define LINUX_SLL_MULTICAST 2
|
||||
#define LINUX_SLL_OTHERHOST 3
|
||||
#define LINUX_SLL_OUTGOING 4
|
||||
|
||||
/*
|
||||
* The LINUX_SLL_ values for "sll_protocol"; these correspond to the
|
||||
* ETH_P_ values on Linux, but are defined here so that they're
|
||||
* available even on systems other than Linux. We assume, for now,
|
||||
* that the ETH_P_ values won't change in Linux; if they do, then:
|
||||
*
|
||||
* if we don't translate them in "pcap-linux.c", capture files
|
||||
* won't necessarily be readable if captured on a system that
|
||||
* defines ETH_P_ values that don't match these values;
|
||||
*
|
||||
* if we do translate them in "pcap-linux.c", that makes life
|
||||
* unpleasant for the BPF code generator, as the values you test
|
||||
* for in the kernel aren't the values that you test for when
|
||||
* reading a capture file, so the fixup code run on BPF programs
|
||||
* handed to the kernel ends up having to do more work.
|
||||
*
|
||||
* Add other values here as necessary, for handling packet types that
|
||||
* might show up on non-Ethernet, non-802.x networks. (Not all the ones
|
||||
* in the Linux "if_ether.h" will, I suspect, actually show up in
|
||||
* captures.)
|
||||
*/
|
||||
#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */
|
||||
#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Paolo Abeni (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Basic USB data struct
|
||||
* By Paolo Abeni <paolo.abeni@email.it>
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/usb.h,v 1.6 2007/09/22 02:06:08 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PCAP_USB_STRUCTS_H__
|
||||
#define _PCAP_USB_STRUCTS_H__
|
||||
|
||||
/*
|
||||
* possible transfer mode
|
||||
*/
|
||||
#define URB_TRANSFER_IN 0x80
|
||||
#define URB_ISOCHRONOUS 0x0
|
||||
#define URB_INTERRUPT 0x1
|
||||
#define URB_CONTROL 0x2
|
||||
#define URB_BULK 0x3
|
||||
|
||||
/*
|
||||
* possible event type
|
||||
*/
|
||||
#define URB_SUBMIT 'S'
|
||||
#define URB_COMPLETE 'C'
|
||||
#define URB_ERROR 'E'
|
||||
|
||||
/*
|
||||
* USB setup header as defined in USB specification.
|
||||
* Appears at the front of each packet in DLT_USB captures.
|
||||
*/
|
||||
typedef struct _usb_setup {
|
||||
u_int8_t bmRequestType;
|
||||
u_int8_t bRequest;
|
||||
u_int16_t wValue;
|
||||
u_int16_t wIndex;
|
||||
u_int16_t wLength;
|
||||
} pcap_usb_setup;
|
||||
|
||||
|
||||
/*
|
||||
* Header prepended by linux kernel to each event.
|
||||
* Appears at the front of each packet in DLT_USB_LINUX captures.
|
||||
*/
|
||||
typedef struct _usb_header {
|
||||
u_int64_t id;
|
||||
u_int8_t event_type;
|
||||
u_int8_t transfer_type;
|
||||
u_int8_t endpoint_number;
|
||||
u_int8_t device_address;
|
||||
u_int16_t bus_id;
|
||||
char setup_flag;/*if !=0 the urb setup header is not present*/
|
||||
char data_flag; /*if !=0 no urb data is present*/
|
||||
int64_t ts_sec;
|
||||
int32_t ts_usec;
|
||||
int32_t status;
|
||||
u_int32_t urb_len;
|
||||
u_int32_t data_len; /* amount of urb data really present in this event*/
|
||||
pcap_usb_setup setup;
|
||||
} pcap_usb_header;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*-
|
||||
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#) $Header: /tcpdump/master/libpcap/pcap/vlan.h,v 1.1.2.2 2008-08-06 07:45:59 guy Exp $
|
||||
*/
|
||||
|
||||
#ifndef lib_pcap_vlan_h
|
||||
#define lib_pcap_vlan_h
|
||||
|
||||
struct vlan_tag {
|
||||
u_int16_t vlan_tpid; /* ETH_P_8021Q */
|
||||
u_int16_t vlan_tci; /* VLAN TCI */
|
||||
};
|
||||
|
||||
#define VLAN_TAG_LEN 4
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (c) 2002 - 2003
|
||||
* NetGroup, Politecnico di Torino (Italy)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Politecnico di Torino nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __REMOTE_EXT_H__
|
||||
#define __REMOTE_EXT_H__
|
||||
|
||||
|
||||
#ifndef HAVE_REMOTE
|
||||
#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h
|
||||
#endif
|
||||
|
||||
// Definition for Microsoft Visual Studio
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\file remote-ext.h
|
||||
|
||||
The goal of this file it to include most of the new definitions that should be
|
||||
placed into the pcap.h file.
|
||||
|
||||
It includes all new definitions (structures and functions like pcap_open().
|
||||
Some of the functions are not really a remote feature, but, right now,
|
||||
they are placed here.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// All this stuff is public
|
||||
/*! \addtogroup remote_struct
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief Defines the maximum buffer size in which address, port, interface names are kept.
|
||||
|
||||
In case the adapter name or such is larger than this value, it is truncated.
|
||||
This is not used by the user; however it must be aware that an hostname / interface
|
||||
name longer than this value will be truncated.
|
||||
*/
|
||||
#define PCAP_BUF_SIZE 1024
|
||||
|
||||
|
||||
/*! \addtogroup remote_source_ID
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a file, i.e. the user want to open a capture from a local file.
|
||||
*/
|
||||
#define PCAP_SRC_FILE 2
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a local interface, i.e. the user want to open a capture from
|
||||
a local interface. This does not involve the RPCAP protocol.
|
||||
*/
|
||||
#define PCAP_SRC_IFLOCAL 3
|
||||
/*!
|
||||
\brief Internal representation of the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This indicates a remote interface, i.e. the user want to open a capture from
|
||||
an interface on a remote host. This does involve the RPCAP protocol.
|
||||
*/
|
||||
#define PCAP_SRC_IFREMOTE 4
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*! \addtogroup remote_source_string
|
||||
|
||||
The formats allowed by the pcap_open() are the following:
|
||||
- file://path_and_filename [opens a local file]
|
||||
- rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol]
|
||||
- rpcap://host/devicename [opens the selected device available on a remote host]
|
||||
- rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP]
|
||||
- adaptername [to open a local adapter; kept for compability, but it is strongly discouraged]
|
||||
- (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged]
|
||||
|
||||
The formats allowed by the pcap_findalldevs_ex() are the following:
|
||||
- file://folder/ [lists all the files in the given folder]
|
||||
- rpcap:// [lists all local adapters]
|
||||
- rpcap://host:port/ [lists the devices available on a remote host]
|
||||
|
||||
Referring to the 'host' and 'port' paramters, they can be either numeric or literal. Since
|
||||
IPv6 is fully supported, these are the allowed formats:
|
||||
|
||||
- host (literal): e.g. host.foo.bar
|
||||
- host (numeric IPv4): e.g. 10.11.12.13
|
||||
- host (numeric IPv4, IPv6 style): e.g. [10.11.12.13]
|
||||
- host (numeric IPv6): e.g. [1:2:3::4]
|
||||
- port: can be either numeric (e.g. '80') or literal (e.g. 'http')
|
||||
|
||||
Here you find some allowed examples:
|
||||
- rpcap://host.foo.bar/devicename [everything literal, no port number]
|
||||
- rpcap://host.foo.bar:1234/devicename [everything literal, with port number]
|
||||
- rpcap://10.11.12.13/devicename [IPv4 numeric, no port number]
|
||||
- rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number]
|
||||
- rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number]
|
||||
- rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number]
|
||||
- rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number]
|
||||
- rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number]
|
||||
|
||||
\{
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\brief String that will be used to determine the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This string will be prepended to the interface name in order to create a string
|
||||
that contains all the information required to open the source.
|
||||
|
||||
This string indicates that the user wants to open a capture from a local file.
|
||||
*/
|
||||
#define PCAP_SRC_FILE_STRING "file://"
|
||||
/*!
|
||||
\brief String that will be used to determine the type of source in use (file,
|
||||
remote/local interface).
|
||||
|
||||
This string will be prepended to the interface name in order to create a string
|
||||
that contains all the information required to open the source.
|
||||
|
||||
This string indicates that the user wants to open a capture from a network interface.
|
||||
This string does not necessarily involve the use of the RPCAP protocol. If the
|
||||
interface required resides on the local host, the RPCAP protocol is not involved
|
||||
and the local functions are used.
|
||||
*/
|
||||
#define PCAP_SRC_IF_STRING "rpcap://"
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_open_flags
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief Defines if the adapter has to go in promiscuous mode.
|
||||
|
||||
It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise.
|
||||
Note that even if this parameter is false, the interface could well be in promiscuous
|
||||
mode for some other reason (for example because another capture process with
|
||||
promiscuous mode enabled is currently using that interface).
|
||||
On on Linux systems with 2.2 or later kernels (that have the "any" device), this
|
||||
flag does not work on the "any" device; if an argument of "any" is supplied,
|
||||
the 'promisc' flag is ignored.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_PROMISCUOUS 1
|
||||
|
||||
/*!
|
||||
\brief Defines if the data trasfer (in case of a remote
|
||||
capture) has to be done with UDP protocol.
|
||||
|
||||
If it is '1' if you want a UDP data connection, '0' if you want
|
||||
a TCP data connection; control connection is always TCP-based.
|
||||
A UDP connection is much lighter, but it does not guarantee that all
|
||||
the captured packets arrive to the client workstation. Moreover,
|
||||
it could be harmful in case of network congestion.
|
||||
This flag is meaningless if the source is not a remote interface.
|
||||
In that case, it is simply ignored.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_DATATX_UDP 2
|
||||
|
||||
|
||||
/*!
|
||||
\brief Defines if the remote probe will capture its own generated traffic.
|
||||
|
||||
In case the remote probe uses the same interface to capture traffic and to send
|
||||
data back to the caller, the captured traffic includes the RPCAP traffic as well.
|
||||
If this flag is turned on, the RPCAP traffic is excluded from the capture, so that
|
||||
the trace returned back to the collector is does not include this traffic.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4
|
||||
|
||||
/*!
|
||||
\brief Defines if the local adapter will capture its own generated traffic.
|
||||
|
||||
This flag tells the underlying capture driver to drop the packets that were sent by itself.
|
||||
This is usefult when building applications like bridges, that should ignore the traffic
|
||||
they just sent.
|
||||
*/
|
||||
#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8
|
||||
|
||||
/*!
|
||||
\brief This flag configures the adapter for maximum responsiveness.
|
||||
|
||||
In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before
|
||||
copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage,
|
||||
i.e. better performance, which is good for applications like sniffers. If the user sets the
|
||||
PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application
|
||||
is ready to receive them. This is suggested for real time applications (like, for example, a bridge)
|
||||
that need the best responsiveness.*/
|
||||
#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_samp_methods
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief No sampling has to be done on the current capture.
|
||||
|
||||
In this case, no sampling algorithms are applied to the current capture.
|
||||
*/
|
||||
#define PCAP_SAMP_NOSAMP 0
|
||||
|
||||
/*!
|
||||
\brief It defines that only 1 out of N packets must be returned to the user.
|
||||
|
||||
In this case, the 'value' field of the 'pcap_samp' structure indicates the
|
||||
number of packets (minus 1) that must be discarded before one packet got accepted.
|
||||
In other words, if 'value = 10', the first packet is returned to the caller, while
|
||||
the following 9 are discarded.
|
||||
*/
|
||||
#define PCAP_SAMP_1_EVERY_N 1
|
||||
|
||||
/*!
|
||||
\brief It defines that we have to return 1 packet every N milliseconds.
|
||||
|
||||
In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting
|
||||
time' in milliseconds before one packet got accepted.
|
||||
In other words, if 'value = 10', the first packet is returned to the caller; the next
|
||||
returned one will be the first packet that arrives when 10ms have elapsed.
|
||||
*/
|
||||
#define PCAP_SAMP_FIRST_AFTER_N_MS 2
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\addtogroup remote_auth_methods
|
||||
\{
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief It defines the NULL authentication.
|
||||
|
||||
This value has to be used within the 'type' member of the pcap_rmtauth structure.
|
||||
The 'NULL' authentication has to be equal to 'zero', so that old applications
|
||||
can just put every field of struct pcap_rmtauth to zero, and it does work.
|
||||
*/
|
||||
#define RPCAP_RMTAUTH_NULL 0
|
||||
/*!
|
||||
\brief It defines the username/password authentication.
|
||||
|
||||
With this type of authentication, the RPCAP protocol will use the username/
|
||||
password provided to authenticate the user on the remote machine. If the
|
||||
authentication is successful (and the user has the right to open network devices)
|
||||
the RPCAP connection will continue; otherwise it will be dropped.
|
||||
|
||||
This value has to be used within the 'type' member of the pcap_rmtauth structure.
|
||||
*/
|
||||
#define RPCAP_RMTAUTH_PWD 1
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
|
||||
\brief This structure keeps the information needed to autheticate
|
||||
the user on a remote machine.
|
||||
|
||||
The remote machine can either grant or refuse the access according
|
||||
to the information provided.
|
||||
In case the NULL authentication is required, both 'username' and
|
||||
'password' can be NULL pointers.
|
||||
|
||||
This structure is meaningless if the source is not a remote interface;
|
||||
in that case, the functions which requires such a structure can accept
|
||||
a NULL pointer as well.
|
||||
*/
|
||||
struct pcap_rmtauth
|
||||
{
|
||||
/*!
|
||||
\brief Type of the authentication required.
|
||||
|
||||
In order to provide maximum flexibility, we can support different types
|
||||
of authentication based on the value of this 'type' variable. The currently
|
||||
supported authentication methods are defined into the
|
||||
\link remote_auth_methods Remote Authentication Methods Section\endlink.
|
||||
|
||||
*/
|
||||
int type;
|
||||
/*!
|
||||
\brief Zero-terminated string containing the username that has to be
|
||||
used on the remote machine for authentication.
|
||||
|
||||
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
|
||||
and it can be NULL.
|
||||
*/
|
||||
char *username;
|
||||
/*!
|
||||
\brief Zero-terminated string containing the password that has to be
|
||||
used on the remote machine for authentication.
|
||||
|
||||
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
|
||||
and it can be NULL.
|
||||
*/
|
||||
char *password;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
\brief This structure defines the information related to sampling.
|
||||
|
||||
In case the sampling is requested, the capturing device should read
|
||||
only a subset of the packets coming from the source. The returned packets depend
|
||||
on the sampling parameters.
|
||||
|
||||
\warning The sampling process is applied <strong>after</strong> the filtering process.
|
||||
In other words, packets are filtered first, then the sampling process selects a
|
||||
subset of the 'filtered' packets and it returns them to the caller.
|
||||
*/
|
||||
struct pcap_samp
|
||||
{
|
||||
/*!
|
||||
Method used for sampling. Currently, the supported methods are listed in the
|
||||
\link remote_samp_methods Sampling Methods Section\endlink.
|
||||
*/
|
||||
int method;
|
||||
|
||||
/*!
|
||||
This value depends on the sampling method defined. For its meaning, please check
|
||||
at the \link remote_samp_methods Sampling Methods Section\endlink.
|
||||
*/
|
||||
int value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//! Maximum length of an host name (needed for the RPCAP active mode)
|
||||
#define RPCAP_HOSTLIST_SIZE 1024
|
||||
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/ // end of public documentation
|
||||
|
||||
|
||||
// Exported functions
|
||||
|
||||
|
||||
|
||||
/** \name New WinPcap functions
|
||||
|
||||
This section lists the new functions that are able to help considerably in writing
|
||||
WinPcap programs because of their easiness of use.
|
||||
*/
|
||||
//\{
|
||||
pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
|
||||
int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf);
|
||||
int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf);
|
||||
int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
|
||||
struct pcap_samp *pcap_setsampling(pcap_t *p);
|
||||
|
||||
//\}
|
||||
// End of new winpcap functions
|
||||
|
||||
|
||||
|
||||
/** \name Remote Capture functions
|
||||
*/
|
||||
//\{
|
||||
SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf);
|
||||
int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf);
|
||||
int pcap_remoteact_close(const char *host, char *errbuf);
|
||||
void pcap_remoteact_cleanup();
|
||||
//\}
|
||||
// End of remote capture functions
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
Reference in New Issue
Block a user