Addition of Original Source Code for port and folders for port to teensy
This commit is contained in:
parent
a62a39f943
commit
f503dd3f8c
197 changed files with 33941 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
BIN
OrginalSourceRepo/.DS_Store
vendored
Normal file
BIN
OrginalSourceRepo/.DS_Store
vendored
Normal file
Binary file not shown.
64
OrginalSourceRepo/OpENer-master/.clang-format
Normal file
64
OrginalSourceRepo/OpENer-master/.clang-format
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
# Based on the Google C++ Style Guide, with user-specific overrides.
|
||||||
|
BasedOnStyle: Google
|
||||||
|
|
||||||
|
# --- User Overrides from Uncrustify Config ---
|
||||||
|
|
||||||
|
# 1. Indentation
|
||||||
|
# Google Style default is 2, but we keep the explicit setting just in case.
|
||||||
|
# Uncrustify: indent_columns=2
|
||||||
|
IndentWidth: 2
|
||||||
|
|
||||||
|
# 2. Tabs
|
||||||
|
# Google Style default is Never, but explicitly set here.
|
||||||
|
# Uncrustify: indent_with_tabs=0
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
# 3. Column Width
|
||||||
|
# Uncrustify: code_width=80
|
||||||
|
ColumnLimit: 80
|
||||||
|
|
||||||
|
# 4. Brace Wrapping
|
||||||
|
# The Google Style default is already to attach braces to the line end
|
||||||
|
# (e.g., `if (x) { ... }`).
|
||||||
|
# Uncrustify: nl_class_brace=remove, nl_fdef_brace=remove
|
||||||
|
# We keep the default: BreakBeforeBraces: Attach
|
||||||
|
|
||||||
|
# 5. Assignment Alignment
|
||||||
|
# This is a key deviation from the Google default, which is usually 'None'.
|
||||||
|
# Uncrustify: indent_align_assign=true
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
|
||||||
|
# 6. Function Parameter Packing (Google default is to pack)
|
||||||
|
# Uncrustify: nl_func_def_args=add (Suggests placing each arg on a new line)
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false # Applies to declarations
|
||||||
|
|
||||||
|
# 7. Pointer Alignment
|
||||||
|
# Google Style prefers the star next to the type ('int* foo').
|
||||||
|
# Uncrustify: sp_before_ptr_star=add (e.g. 'int * a;')
|
||||||
|
# We stick to the Google default: PointerAlignment: Left (or None, which often results in Left).
|
||||||
|
PointerAlignment: Left
|
||||||
|
|
||||||
|
# 8. Namespace Indentation
|
||||||
|
# Google Style default is false, matching:
|
||||||
|
# Uncrustify: indent_namespace=false
|
||||||
|
IndentNamespace: false
|
||||||
|
|
||||||
|
# 9. Case Label Indentation
|
||||||
|
# Google Style default is to indent `case` labels.
|
||||||
|
# Uncrustify: indent_switch_case=2 (Suggests indenting case labels)
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
# 10. Operator Alignment
|
||||||
|
# Uncrustify: align_left_shift=true
|
||||||
|
AlignOperands: Align
|
||||||
|
|
||||||
|
# 11. Newlines/Block Handling
|
||||||
|
# Uncrustify: mod_full_brace_if=add, mod_full_brace_for=add, etc.
|
||||||
|
# These are generally handled by Google's requirement for braces, even for single statements.
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: Never
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
...
|
||||||
4
OrginalSourceRepo/OpENer-master/AUTHORS
Normal file
4
OrginalSourceRepo/OpENer-master/AUTHORS
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
Kurt Schweiger
|
||||||
|
Rene Smodic
|
||||||
|
Alois Zoitl
|
||||||
|
Jonathan Engdahl
|
||||||
221
OrginalSourceRepo/OpENer-master/README.md
Normal file
221
OrginalSourceRepo/OpENer-master/README.md
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
OpENer Version 2.3.0
|
||||||
|
====================
|
||||||
|
|
||||||
|
Welcome to OpENer!
|
||||||
|
------------------
|
||||||
|
|
||||||
|
OpENer is an EtherNet/IP™ stack for I/O adapter devices; supports multiple
|
||||||
|
I/O and explicit connections; includes objects and services to make EtherNet/IP™-
|
||||||
|
compliant products defined in THE ETHERNET/IP SPECIFICATION and published by
|
||||||
|
ODVA (http://www.odva.org).
|
||||||
|
|
||||||
|
Participate!
|
||||||
|
------------
|
||||||
|
Users and developers of OpENer can join the respective Google Groups in order to exchange experience, discuss the usage of OpENer, and to suggest new features and CIP objects, which would be useful for the community.
|
||||||
|
|
||||||
|
Developers mailing list: https://groups.google.com/forum/#!forum/eip-stack-group-opener-developers
|
||||||
|
|
||||||
|
Users mailing list: https://groups.google.com/forum/#!forum/eip-stack-group-opener-users
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
-------------
|
||||||
|
OpENer has been developed to be highly portable. The default version targets PCs
|
||||||
|
with a POSIX operating system and a BSD-socket network interface. To test this
|
||||||
|
version we recommend a Linux PC or Windows with Cygwin (http://www.cygwin.com)
|
||||||
|
installed. You will need to have the following installed:
|
||||||
|
|
||||||
|
* CMake
|
||||||
|
* gcc
|
||||||
|
* make
|
||||||
|
* binutils
|
||||||
|
* the development library of libcap (libcap-dev or equivalient)
|
||||||
|
|
||||||
|
for normal building. These should be installed on most Linux installations and
|
||||||
|
are part of the development packages of Cygwin.
|
||||||
|
|
||||||
|
If you want to run the unit tests you will also have to download CppUTest via
|
||||||
|
https://github.com/cpputest/cpputest
|
||||||
|
|
||||||
|
For configuring the project we recommend the use of a CMake GUI (e.g., the
|
||||||
|
cmake-gui package on Linux, or the Installer for Windows available at [CMake](https://cmake.org/))
|
||||||
|
|
||||||
|
Compile for Linux/POSIX:
|
||||||
|
----------------
|
||||||
|
1. Make sure all the needed tools are available (CMake, make, gcc, binutils)
|
||||||
|
2. Change to the <OpENer main folder>/bin/posix
|
||||||
|
3. For a standard configuration invoke ``setup_posix.sh``
|
||||||
|
1. Invoke the ``make`` command
|
||||||
|
2. Invoking OpENer:
|
||||||
|
|
||||||
|
``./src/ports/POSIX/OpENer <interface_name>``
|
||||||
|
|
||||||
|
e.g. ``./src/ports/POSIX/OpENer eth1``
|
||||||
|
|
||||||
|
OpENer also now has a real-time capable POSIX startup via the OpENer_RT option, which requires that the used kernel has the full preemptive RT patches applied and activated.
|
||||||
|
If you want to use OpENer_RT, prior to step 2, execute ``sudo setcap cap_ipc_lock,cap_sys_nice+ep ./src/ports/POSIX/OpENer
|
||||||
|
`` to grant OpENEr ``CAP_SYS_NICE``, and the ``CAP_IPC_LOCK`` capabilities, which are needed for the RT mode
|
||||||
|
|
||||||
|
OpENer can also be built and installed as a library by setting the CMake flag `-DOPENER_INSTALL_AS_LIB`. To build a shared library,
|
||||||
|
the global option `-DBUILD_SHARED_LIBS=ON` should also be set. It has only been tested under Linux/POSIX platform.
|
||||||
|
|
||||||
|
|
||||||
|
Compile for Windows XP/7/8 via Visual Studio:
|
||||||
|
---------------------------------------------
|
||||||
|
1. Invoke setup_windows.bat or configure via CMake
|
||||||
|
2. Open Visual Studio solution OpENer.sln in bin/win32
|
||||||
|
3. Compile OpENer by chosing ``Build All`` in Visual Studio
|
||||||
|
4. For invoking OpENer type from the command line:
|
||||||
|
1. Change to <OpENer main folder>\bin\win32\src\ports\WIN32\
|
||||||
|
2. Depending if you chose the ``Debug`` or ``Release`` configuration in Visual Studio, your executable will either show up in the subfolder Debug or Release
|
||||||
|
3. Invoke OpENer via
|
||||||
|
|
||||||
|
``OpENer <interface_index>``
|
||||||
|
|
||||||
|
e.g. ``OpENer 3``
|
||||||
|
|
||||||
|
In order to get the correct interface index enter the command ``route print`` in a command promt and search for the MAC address of your chosen network interface at the beginning of the output. The leftmost number is the corresponding interface index.
|
||||||
|
|
||||||
|
Compile for Windows XP/7/8/10 via Cygwin:
|
||||||
|
--------------------------------------
|
||||||
|
The POSIX setup file can be reused for Cygwin. Please note, that you cannot use RT mode and you will have to remove the code responsible for checking and getting the needed capabilities, as libcap is not available in Cygwin. The easier and more supported way to build OpENer for Windows is to either use MinGW or Visual Studio.
|
||||||
|
|
||||||
|
Compile for MinGW on Windows XP/7/8/10
|
||||||
|
-------------------------------
|
||||||
|
1. Make sure 64 bit mingw is installed. (Test with gcc --version, should show x86_64-posix-seh-rev1)
|
||||||
|
2. Make sure CMake is installed. (Test with cmake --version, should be version 3.xx)
|
||||||
|
3. Change to <opener install dir>/bin/mingw
|
||||||
|
4. Run the command `setup_mingw.bat` in a dos command line. (Not a bash shell). If tracing is desired,
|
||||||
|
use the following (where the cmake parameter must be enclosed in quotes) or change the ./source/CMakeList.txt file.
|
||||||
|
```
|
||||||
|
setup_mingw.bat "-DOpENer_TRACES:BOOL=TRUE"
|
||||||
|
```
|
||||||
|
5. Run the command "make" from the same directory (./bin/mingw)
|
||||||
|
6. The opener.exe is now found in <opener install dir>\bin\mingw\src\ports\MINGW
|
||||||
|
7. Start it like this: "opener 192.168.250.22", where the ip address is the local computer's address on the nettwork you want to use.
|
||||||
|
|
||||||
|
Directory structure:
|
||||||
|
--------------------
|
||||||
|
- bin ... The resulting binaries and make files for different ports
|
||||||
|
- doc ... Doxygen generated documentation (has to be generated for the SVN version) and Coding rules
|
||||||
|
- data ... EDS file for the default application
|
||||||
|
- source
|
||||||
|
- src ... the production source code
|
||||||
|
- cip ... the CIP layer of the stack
|
||||||
|
- cip_objects ... additional CIP objects
|
||||||
|
- enet_encap ... the Ethernet encapsulation layer
|
||||||
|
- ports ... the platform specific code
|
||||||
|
- utils ... utility functions
|
||||||
|
- tests ... the test source code
|
||||||
|
- enet_encap ... tests for Ethernet encapsulation layer
|
||||||
|
- utils ... tests for utility functions
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
--------------
|
||||||
|
The documentation of the functions of OpENer is part of the source code. The source
|
||||||
|
packages contain the generated documentation in the directory doc/api_doc. If you
|
||||||
|
use the GIT version you will need the program Doxygen for generating the HTML
|
||||||
|
documentation. You can generate the documentation by invoking doxygen from the
|
||||||
|
command line in the opener main directory.
|
||||||
|
|
||||||
|
|
||||||
|
Fuzzing
|
||||||
|
--------------
|
||||||
|
### Intro
|
||||||
|
Fuzzing is an automated testing method that directs varying input data to a program in
|
||||||
|
order to monitor output. It is a way to test for overall reliability as well as identify
|
||||||
|
potential security bugs.
|
||||||
|
|
||||||
|
The fuzzer we are using is AFL, a fuzzer that uses runtime guided techniques to create input for the tested program. From a high-level prespective AFL works as follows:
|
||||||
|
- Forks the fuzzed process
|
||||||
|
- Genereates a new test case based on a predefined input
|
||||||
|
- Feeds the fuzzed process with the test case through STDIN
|
||||||
|
- Monitors the execution and registers which paths are reachable
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Compile
|
||||||
|
To start fuzzing this project with AFL you'll need to compile it with AFL.
|
||||||
|
First make sure you have AFL installed:
|
||||||
|
```
|
||||||
|
sudo apt install build-essential
|
||||||
|
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
|
||||||
|
tar xzf afl-latest.tgz
|
||||||
|
cd afl*
|
||||||
|
make && sudo make install
|
||||||
|
echo "AFL is ready at: $(which afl-fuzz)"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, compile OpENer with AFL:
|
||||||
|
1. Change to the ``OpENer/bin/posix`` directory
|
||||||
|
2. Compile OpENer with AFL ``./setup_posix_fuzz_afl.sh``
|
||||||
|
3. Run ``make``
|
||||||
|
|
||||||
|
### Fuzz
|
||||||
|
Finally, generate some test cases and start AFL:
|
||||||
|
```
|
||||||
|
# Generate inputs
|
||||||
|
mkdir inputs
|
||||||
|
echo 630000000000000000000000000000000000000000000000 | xxd -r -p > ./inputs/enip_req_list_identity
|
||||||
|
# You can also use the inputs we prepared from OpENer/fuzz/inputs
|
||||||
|
# Finally, let's fuzz!
|
||||||
|
afl-fuzz -i inputs -o findings ./src/ports/POSIX/OpENer <interface_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reproduce a crash
|
||||||
|
Usually to reproduce a crash it's enough to retransmit the testcase using ``cat testcase | nc IP_ADDR 44818``
|
||||||
|
However, since CIP runs over the EtherNet/IP layer, it must first register a valid session. Therefore, we need to use a dedicated script:
|
||||||
|
`python fuzz/scripts/send_testcase.py IP testcase_path`
|
||||||
|
|
||||||
|
Running an OpENer "swarm":
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
1. Create a macvlan network for this purpose and tie it to the desired eth port.
|
||||||
|
Specify the IP range and use aux address to exclude the addresses used by other devices in the subnet such as the IP of the EIP scanner PC, network bridge, etc.:
|
||||||
|
docker network create -d macvlan --subnet=192.168.135.253/24 --ip-range=192.168.135.100/24 --aux-address="PC1=192.168.135.250" --aux-address="VM=192.168.135.252" --aux-address="BR=192.168.135.253" -o parent=eth2 mac_vlan_network
|
||||||
|
|
||||||
|
Check the network you created with: docker network inspect mac_vlan_network
|
||||||
|
|
||||||
|
The network will assign IP's to the docker containers and an external scanner will be able to communicate with them. To access the containers from inside the docker host, you will have to create a bridge.
|
||||||
|
|
||||||
|
2. Create a Dockerfile.
|
||||||
|
This uses Ubuntu as the base image. It will copy OpENer to the image root and install the required packages. Lastly run OpENer on eth0 of the image:
|
||||||
|
#Filename: Dockerfile
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
ADD ./bin/posix/src/ports/POSIX/OpENer /
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends libcap-dev nmap
|
||||||
|
ENTRYPOINT ["./OpENer", "eth0"]
|
||||||
|
|
||||||
|
3. Create a docker-compose.yml that will let you connect the macvlan network to the containers and easily build them and tear them down:
|
||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
dockerimagename:
|
||||||
|
network_mode: mac_vlan_network
|
||||||
|
image: dockeruser/dockerimagename
|
||||||
|
|
||||||
|
Note that to login to a running container, you have to expose a port in the dockerfile and dockercompose files and set up a network bridge.
|
||||||
|
|
||||||
|
Docker commands to start and stop multiple instances of the OpENer containers:
|
||||||
|
Start up 128 docker image instances: docker-compose up --scale dockerimagename=128 -d
|
||||||
|
Shut down all the instances: docker-compose down
|
||||||
|
|
||||||
|
Porting OpENer:
|
||||||
|
---------------
|
||||||
|
For porting OpENer to new platforms please see the porting section in the
|
||||||
|
Doxygen documentation.
|
||||||
|
|
||||||
|
Contributing to OpENer:
|
||||||
|
-----------------------
|
||||||
|
The easiest way is to fork the repository, then create a feature/bugfix branch.
|
||||||
|
After finishing your feature/bugfix create a pull request and explain your changes.
|
||||||
|
Also, please update and/or add doxygen comments to the provided code sections.
|
||||||
|
Please stick to the coding conventions, as defined in source/doc/coding_rules
|
||||||
|
The easiest way to conform to the indenting convertion is to set uncrustify as git filter in the OpENer repository, which can be done with the following to commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
git config filter.uncrustify.clean "/path/to/uncrustify/uncrustify -c uncrustify.cfg --mtime --no-backup"
|
||||||
|
|
||||||
|
git config filter.uncrustify.smudge "cat"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX:PATH="C:/Program Files (x86)/OpENer" -DOpENer_PLATFORM:STRING="MINGW" ../../source
|
||||||
2
OrginalSourceRepo/OpENer-master/bin/posix/setup_posix.sh
Executable file
2
OrginalSourceRepo/OpENer-master/bin/posix/setup_posix.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
||||||
|
cmake -DCMAKE_C_COMPILER=gcc -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="" -DBUILD_SHARED_LIBS:BOOL=OFF ../../source
|
||||||
|
|
||||||
2
OrginalSourceRepo/OpENer-master/bin/posix/setup_posix_fuzz_afl.sh
Executable file
2
OrginalSourceRepo/OpENer-master/bin/posix/setup_posix_fuzz_afl.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
||||||
|
cmake -DCMAKE_C_COMPILER=afl-clang-fast -DUSE_FUZZ_AFL=ON -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="" -DBUILD_SHARED_LIBS:BOOL=OFF ../../source
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX:PATH="C:/Program Files (x86)/OpENer" -DOpENer_PLATFORM:STRING="WIN32" ../../source
|
||||||
42
OrginalSourceRepo/OpENer-master/branding/OpENer.gpl
Normal file
42
OrginalSourceRepo/OpENer-master/branding/OpENer.gpl
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
GIMP Palette
|
||||||
|
Name: OpENer
|
||||||
|
Columns: 5
|
||||||
|
# Primary and supporting colors for OpENer branding
|
||||||
|
|
||||||
|
# Steel Blue Shades
|
||||||
|
22 58 86 Steel Blue Dark
|
||||||
|
31 78 121 Steel Blue Base
|
||||||
|
74 108 147 Steel Blue Light
|
||||||
|
123 145 176 Steel Blue Lighter
|
||||||
|
176 192 209 Steel Blue Pale
|
||||||
|
|
||||||
|
# Teal Shades
|
||||||
|
43 127 128 Teal Dark
|
||||||
|
59 175 169 Teal Base
|
||||||
|
91 190 192 Teal Light
|
||||||
|
132 209 211 Teal Lighter
|
||||||
|
179 227 226 Teal Pale
|
||||||
|
|
||||||
|
# Neutral Gray Shades
|
||||||
|
76 89 102 Neutral Gray Dark
|
||||||
|
107 124 140 Neutral Gray Base
|
||||||
|
136 147 160 Neutral Gray Light
|
||||||
|
163 172 184 Neutral Gray Lighter
|
||||||
|
192 200 208 Neutral Gray Pale
|
||||||
|
|
||||||
|
# Light Gray Shades
|
||||||
|
224 227 232 Light Gray Dark
|
||||||
|
245 247 250 Light Gray Base
|
||||||
|
250 251 252 Light Gray Light
|
||||||
|
252 253 254 Light Gray Lighter
|
||||||
|
254 254 255 Light Gray Pale
|
||||||
|
|
||||||
|
255 255 255 White
|
||||||
|
|
||||||
|
# Amber Shades
|
||||||
|
198 143 37 Amber Dark
|
||||||
|
242 182 50 Amber Base
|
||||||
|
247 199 87 Amber Light
|
||||||
|
250 216 143 Amber Lighter
|
||||||
|
253 235 194 Amber Pale
|
||||||
|
|
||||||
135
OrginalSourceRepo/OpENer-master/branding/OpENer_Icon.svg
Normal file
135
OrginalSourceRepo/OpENer-master/branding/OpENer_Icon.svg
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="90"
|
||||||
|
height="90"
|
||||||
|
viewBox="0 0 23.812501 23.8125"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="OpENer_Icon.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="5.6568543"
|
||||||
|
inkscape:cx="20.771262"
|
||||||
|
inkscape:cy="43.752232"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1168"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1">
|
||||||
|
<inkscape:grid
|
||||||
|
id="grid1"
|
||||||
|
units="mm"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
spacingx="0.26458334"
|
||||||
|
spacingy="0.26458333"
|
||||||
|
empcolor="#0099e5"
|
||||||
|
empopacity="0.30196078"
|
||||||
|
color="#0099e5"
|
||||||
|
opacity="0.14901961"
|
||||||
|
empspacing="5"
|
||||||
|
enabled="true"
|
||||||
|
visible="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect4"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.96515807,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
radius="0"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect6"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.96515807,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
radius="0"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false" />
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
id="circle20"
|
||||||
|
style="display:inline;fill:#1f4e79;fill-opacity:1;stroke:none;stroke-width:1.4552"
|
||||||
|
d="M 14.552083 3.9687528 A 10.583334 10.583333 0 0 0 3.968749 14.552086 A 10.583334 10.583333 0 0 0 6.5153636 21.437971 L 7.869803 20.770312 A 9.1281252 9.1281252 0 0 1 5.4239574 14.552086 A 9.1281252 9.1281252 0 0 1 14.552083 5.4239612 A 9.1281252 9.1281252 0 0 1 23.680208 14.552086 A 9.1281252 9.1281252 0 0 1 21.176485 20.831807 L 22.339204 21.719091 A 10.583334 10.583333 0 0 0 25.135416 14.552086 A 10.583334 10.583333 0 0 0 14.552083 3.9687528 z M 10.601419 22.781042 L 10.847916 23.892085 L 10.505818 24.331335 A 10.583334 10.583333 0 0 0 12.114505 24.850683 L 12.786299 23.507612 A 9.1281252 9.1281252 0 0 1 10.601419 22.781042 z M 18.351851 22.851322 A 9.1281252 9.1281252 0 0 1 16.317866 23.507612 L 16.98966 24.850683 A 10.583334 10.583333 0 0 0 18.784899 24.251753 L 18.351851 22.851322 z "
|
||||||
|
transform="translate(-2.6458323,-2.645836)" />
|
||||||
|
<path
|
||||||
|
id="circle1"
|
||||||
|
style="display:inline;fill:#1f4e79;fill-opacity:1;stroke-width:0.264584"
|
||||||
|
d="M 14.552083 6.6145862 A 7.937499 7.9374809 0 0 0 6.6145824 14.552086 A 7.937499 7.9374809 0 0 0 13.34182 22.396569 L 14.120584 20.839041 A 0.48257904 0.48257904 0 0 1 14.983581 20.839041 L 15.762345 22.396569 A 7.937499 7.9374809 0 0 0 22.489583 14.552086 A 7.937499 7.9374809 0 0 0 14.552083 6.6145862 z M 10.26914 12.10469 L 18.835025 12.10469 C 19.02741 12.10469 19.182291 12.259571 19.182291 12.451956 L 19.182291 13.609508 C 19.182291 13.801893 19.02741 13.956774 18.835025 13.956774 L 10.26914 13.956774 C 10.076755 13.956774 9.9218741 13.801893 9.9218741 13.609508 L 9.9218741 12.451956 C 9.9218741 12.259571 10.076755 12.10469 10.26914 12.10469 z M 10.285676 15.147399 L 12.468489 15.147399 C 12.670035 15.147399 12.832291 15.309655 12.832291 15.511201 L 12.832291 16.63568 C 12.832291 16.837226 12.670035 16.999482 12.468489 16.999482 L 10.285676 16.999482 C 10.08413 16.999482 9.9218741 16.837226 9.9218741 16.63568 L 9.9218741 15.511201 C 9.9218741 15.309655 10.08413 15.147399 10.285676 15.147399 z M 14.112316 15.147399 L 18.828307 15.147399 C 19.024277 15.147399 19.182291 15.304896 19.182291 15.500866 L 19.182291 16.645499 C 19.182291 16.841468 19.024277 16.999482 18.828307 16.999482 L 14.112316 16.999482 C 13.916346 16.999482 13.758333 16.841468 13.758333 16.645499 L 13.758333 15.500866 C 13.758333 15.304896 13.916346 15.147399 14.112316 15.147399 z "
|
||||||
|
transform="translate(-2.6458323,-2.645836)" />
|
||||||
|
<path
|
||||||
|
style="fill:#3bafa9;fill-opacity:1;stroke-width:1.32292;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none"
|
||||||
|
d="m 14.552082,21.166623 -1.984375,3.96875 h 3.96875 z"
|
||||||
|
id="path7"
|
||||||
|
transform="translate(-2.6458323,-2.645836)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#3bafa9;stroke:none;stroke-width:7.28752;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path12"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="5"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="29.463514"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="0.9422448"
|
||||||
|
sodipodi:arg2="1.5705633"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 27.498367,210.21924 -34.636437,0.008 -10.710923,-32.93871 28.016723,-20.36532 28.02621,20.35226 z"
|
||||||
|
transform="matrix(0.07081624,0,0,0.07445497,17.469728,6.8370687)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#3bafa9;stroke:none;stroke-width:6.81059;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path13"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="7"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="26.528988"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="1.1227347"
|
||||||
|
sodipodi:arg2="1.5715336"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 21.667465,210.29707 -23.0209868,-0.017 -14.3400792,-18.00911 5.139199,-22.44003 20.748557,-9.97314 20.733828,10.00373 5.106103,22.44758 z"
|
||||||
|
transform="matrix(0.0767239,0,0,0.07868376,4.8420963,5.9439512)" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.5 KiB |
198
OrginalSourceRepo/OpENer-master/branding/OpENer_Logo.svg
Normal file
198
OrginalSourceRepo/OpENer-master/branding/OpENer_Logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 122 KiB |
314
OrginalSourceRepo/OpENer-master/branding/license.txt
Normal file
314
OrginalSourceRepo/OpENer-master/branding/license.txt
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
Creative Commons Attribution-NoDerivatives 4.0 International Public
|
||||||
|
License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-NoDerivatives 4.0 International Public License ("Public
|
||||||
|
License"). To the extent this Public License may be interpreted as a
|
||||||
|
contract, You are granted the Licensed Rights in consideration of Your
|
||||||
|
acceptance of these terms and conditions, and the Licensor grants You
|
||||||
|
such rights in consideration of benefits the Licensor receives from
|
||||||
|
making the Licensed Material available under these terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
c. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
d. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
e. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
f. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
g. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
h. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
i. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
j. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part; and
|
||||||
|
|
||||||
|
b. produce and reproduce, but not Share, Adapted Material.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material, You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
For the avoidance of doubt, You do not have permission under
|
||||||
|
this Public License to Share Adapted Material.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database, provided You do not Share
|
||||||
|
Adapted Material;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material; and
|
||||||
|
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
209
OrginalSourceRepo/OpENer-master/branding/src/OpENer_Icon_src.svg
Normal file
209
OrginalSourceRepo/OpENer-master/branding/src/OpENer_Icon_src.svg
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="90"
|
||||||
|
height="90"
|
||||||
|
viewBox="0 0 23.812501 23.8125"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="OpENer_Icon_src.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="5.6568543"
|
||||||
|
inkscape:cx="34.913397"
|
||||||
|
inkscape:cy="86.708968"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1168"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer2">
|
||||||
|
<inkscape:grid
|
||||||
|
id="grid1"
|
||||||
|
units="mm"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
spacingx="0.26458334"
|
||||||
|
spacingy="0.26458333"
|
||||||
|
empcolor="#0099e5"
|
||||||
|
empopacity="0.30196078"
|
||||||
|
color="#0099e5"
|
||||||
|
opacity="0.14901961"
|
||||||
|
empspacing="5"
|
||||||
|
enabled="true"
|
||||||
|
visible="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect6"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.96515807,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
radius="0"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-2.6458323,-2.6458361)">
|
||||||
|
<ellipse
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.05833;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path22"
|
||||||
|
cx="14.552082"
|
||||||
|
cy="14.552063"
|
||||||
|
rx="10.054166"
|
||||||
|
ry="10.054142" />
|
||||||
|
<ellipse
|
||||||
|
cx="14.552082"
|
||||||
|
cy="14.552063"
|
||||||
|
fill="#1F4E79"
|
||||||
|
id="circle20"
|
||||||
|
style="display:inline;fill:none;fill-opacity:1;stroke:#1f4e79;stroke-width:1.45521;stroke-dasharray:none"
|
||||||
|
rx="9.8557281"
|
||||||
|
ry="9.8557053" />
|
||||||
|
<ellipse
|
||||||
|
cx="14.552082"
|
||||||
|
cy="14.552063"
|
||||||
|
fill="#1F4E79"
|
||||||
|
id="circle1"
|
||||||
|
style="display:inline;fill:#1f4e79;fill-opacity:1;stroke-width:0.264584"
|
||||||
|
rx="7.937499"
|
||||||
|
ry="7.9374809" />
|
||||||
|
<g
|
||||||
|
id="g5"
|
||||||
|
transform="matrix(0.26458331,0,0,0.2645827,-2.6458323,-1.785955)"
|
||||||
|
style="display:inline;stroke-width:1">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:6.00002;stroke-linejoin:round;stroke-dasharray:none"
|
||||||
|
id="rect2"
|
||||||
|
width="35"
|
||||||
|
height="7"
|
||||||
|
x="47.5"
|
||||||
|
y="52.500275"
|
||||||
|
rx="1.3125" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:4.00001;stroke-linejoin:round;stroke-dasharray:none"
|
||||||
|
id="rect3"
|
||||||
|
width="11"
|
||||||
|
height="7"
|
||||||
|
x="47.5"
|
||||||
|
y="64"
|
||||||
|
rx="1.375" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:6.00002;stroke-linejoin:round;stroke-dasharray:none"
|
||||||
|
id="rect5"
|
||||||
|
width="20.5"
|
||||||
|
height="7"
|
||||||
|
x="62"
|
||||||
|
y="64"
|
||||||
|
rx="1.3369566" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="io"
|
||||||
|
transform="translate(-2.6458323,-2.6458361)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.32292;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none"
|
||||||
|
d="m 14.12045,20.839262 -2.743368,5.486736 h 6.35 l -2.743368,-5.486736 a 0.48257904,0.48257904 0 0 0 -0.863264,0 z"
|
||||||
|
id="path2"
|
||||||
|
inkscape:path-effect="#path-effect6"
|
||||||
|
inkscape:original-d="m 14.552082,19.975998 -3.175,6.35 h 6.35 z" />
|
||||||
|
<path
|
||||||
|
style="fill:#3bafa9;fill-opacity:1;stroke-width:1.32292;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none"
|
||||||
|
d="m 14.552082,21.166623 -1.984375,3.96875 h 3.96875 z"
|
||||||
|
id="path7" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.60578;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path4"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="5"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="29.463514"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="0.9422448"
|
||||||
|
sodipodi:arg2="1.5705633"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 27.498367,210.21924 -34.636437,0.008 -10.710923,-32.93871 28.016723,-20.36532 28.02621,20.35226 z"
|
||||||
|
transform="matrix(0.09206112,0,0,0.09679147,19.899447,5.3824752)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#3bafa9;stroke:none;stroke-width:7.28752;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path12"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="5"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="29.463514"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="0.9422448"
|
||||||
|
sodipodi:arg2="1.5705633"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 27.498367,210.21924 -34.636437,0.008 -10.710923,-32.93871 28.016723,-20.36532 28.02621,20.35226 z"
|
||||||
|
transform="matrix(0.07081624,0,0,0.07445497,20.11556,9.4829048)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.23892;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path1"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="7"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="26.528988"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="1.1227347"
|
||||||
|
sodipodi:arg2="1.5715336"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 21.667465,210.29707 -23.0209868,-0.017 -14.3400792,-18.00911 5.139199,-22.44003 20.748557,-9.97314 20.733828,10.00373 5.106103,22.44758 z"
|
||||||
|
transform="matrix(0.09974107,0,0,0.1022889,7.2538384,4.2210084)" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#3bafa9;stroke:none;stroke-width:6.81059;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0"
|
||||||
|
id="path13"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="7"
|
||||||
|
sodipodi:cx="10.174595"
|
||||||
|
sodipodi:cy="186.3868"
|
||||||
|
sodipodi:r1="26.528988"
|
||||||
|
sodipodi:r2="27.402483"
|
||||||
|
sodipodi:arg1="1.1227347"
|
||||||
|
sodipodi:arg2="1.5715336"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 21.667465,210.29707 -23.0209868,-0.017 -14.3400792,-18.00911 5.139199,-22.44003 20.748557,-9.97314 20.733828,10.00373 5.106103,22.44758 z"
|
||||||
|
transform="matrix(0.0767239,0,0,0.07868376,7.4879286,8.5897873)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.3 KiB |
298
OrginalSourceRepo/OpENer-master/branding/src/OpENer_Logo_src.svg
Normal file
298
OrginalSourceRepo/OpENer-master/branding/src/OpENer_Logo_src.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 111 KiB |
156
OrginalSourceRepo/OpENer-master/data/OpENerPC.stc
Normal file
156
OrginalSourceRepo/OpENer-master/data/OpENerPC.stc
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
StcRev Data |1.25|CT17-EN|OpENerPC|1623922735| DevData |255|0|500|500|500,500|X||ENet|
|
||||||
|
[<00>]-------|OpENer PC||
|
||||||
|
General |3.27|EtherNet/IP Vol 2, Ed 1.25||2.3|Rockwell Automation/Allen-Bradley|1|65001|
|
||||||
|
DevProfile |12|Communications Adapter|,04,05,06,07,08,09,10,11,15,16,29,30,32,33,43,55,243,244,245,246,35,67,71,72,77,84,85,86,87,69,81,68,70,82,78,79,80,83,92,256,257,258,259,260,261,262|
|
||||||
|
PhysConf ||X| |X| | | | | |0|X| | |
|
||||||
|
LEDs | | | | | |
|
||||||
|
IP MAC Set | |X| |192.168.56.101| | ||08:00:27:77:CF:56| | | |
|
||||||
|
CRate Set |X| | |X| | ||
|
||||||
|
CRate Sup |X|X| |X|X|
|
||||||
|
Net Behavi | | ||| | | | | | | |
|
||||||
|
Cxn Behavi | | | |X| | | | | | ||| | |-1|1000|1000| |511| |
|
||||||
|
FO Path ||||||||||||| |
|
||||||
|
FO-IO Data | | | | | | | | | | | || | | | | | || | | |
|
||||||
|
Cfg-IO Dat ||||
|
||||||
|
Reserved ||
|
||||||
|
Safety Data| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
Reserved | |
|
||||||
|
Safety IO |||||||||||||||||||||||||||||||||||
|
||||||
|
Safety Cfg |||||||||||| | | | |||2|
|
||||||
|
Reserved | |
|
||||||
|
[<01>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X| |X| | | | | | | | | |
|
||||||
|
CSP |||||||||||||
|
||||||
|
IAG |X|X|X|X|X|X|X| | | | | | | | | | | | | | | | | |
|
||||||
|
IAS | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
IAL | =(1)| =(12)| =(65001)| =(2.3)| | | =(OpENer PC)| | | | | | | | | | | | | | | | | |
|
||||||
|
IS |X|X|X| | | |X|X| | | | |
|
||||||
|
ISP ||0,1|||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
[<02>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X|X| | | | | | | | | |
|
||||||
|
CSP ||||||||||||
|
||||||
|
IAG | | | | |
|
||||||
|
IAS | | | | |
|
||||||
|
IAL | | | | |
|
||||||
|
IS | |X| | | | | | | | | | |
|
||||||
|
ISP |||||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
[<04a>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS | | |X| | | | | | | | |
|
||||||
|
CSP ||||||||||||
|
||||||
|
IAG | | |X|X| |
|
||||||
|
IAS | | |X| | |
|
||||||
|
IAL | | | | | |
|
||||||
|
IS | |X|X| | | | | | | |
|
||||||
|
ISP |||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
OPT |100|
|
||||||
|
[<04b>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS | | |X| | | | | | | | |
|
||||||
|
CSP ||||||||||||
|
||||||
|
IAG | | |X|X| |
|
||||||
|
IAS | | |X| | |
|
||||||
|
IAL | | | | | |
|
||||||
|
IS | |X|X| | | | | | | |
|
||||||
|
ISP |||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
OPT |150,152,153|
|
||||||
|
[<04d>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS | | |X| | | | | | | | |
|
||||||
|
CSP ||||||||||||
|
||||||
|
IAG | | |X|X| |
|
||||||
|
IAS | | |X| | |
|
||||||
|
IAL | | | | | |
|
||||||
|
IS | |X|X| | | | | | | |
|
||||||
|
ISP |||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
OPT |151|
|
||||||
|
[<04c>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS | | |X| | | | | | | | |
|
||||||
|
CSP ||||||||||||
|
||||||
|
IAG | | |X|X| |
|
||||||
|
IAS | | |X| | |
|
||||||
|
IAL | | | | | |
|
||||||
|
IS | |X|X| | | | | | | |
|
||||||
|
ISP |||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
OPT |154|
|
||||||
|
[<06>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X|X| | | | | | | | |
|
||||||
|
CSP |||||||||||
|
||||||
|
IAG | | | | | | | | | | | | | | | | | | | |
|
||||||
|
IAS | | | | | | | | | | | | | | | | | | | |
|
||||||
|
IAL | | | | | | | | | | | | | | | | | | | |
|
||||||
|
IS | | |X| |X| |X|X|X|X|X| | | | | | | | |
|
||||||
|
ISP ||||||||||||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
OPT |<PossibleConnections><CIPConnection connectionNameString="demo exclusive owner connection point" ePath="200424972c962c64"><TriggerAndTransport class1="1" server="1" triggerCyclic="1"><TransportTypeExclusiveOwner transportTypeExclusiveOwner="1"/></TriggerAndTransport><Originator2TargetParameters><ConnectionParameters fixedSizeSupported="1" realTimeTransferFormat="4" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="32" minValue="32" maxValue="32"/></Size></Originator2TargetParameters><Target2OriginatorParameters><ConnectionParameters fixedSizeSupported="1" realTimeTransferFormat="0" connectionTypeMulticast="1" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="32" minValue="32" maxValue="32"/></Size></Target2OriginatorParameters></CIPConnection><CIPConnection connectionNameString="demo input only" ePath="200424972c982c64"><TriggerAndTransport class1="1" server="1" triggerCyclic="1"><TransportTypeInputOnly transportTypeInputOnly="1"/></TriggerAndTransport><Originator2TargetParameters><ConnectionParameters fixedSizeSupported="1" realTimeTransferFormat="3" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="0" minValue="0" maxValue="0"/></Size></Originator2TargetParameters><Target2OriginatorParameters><ConnectionParameters variableSizeSupported="1" realTimeTransferFormat="1" connectionTypeMulticast="1" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="32" minValue="32" maxValue="32"/></Size></Target2OriginatorParameters></CIPConnection><CIPConnection connectionNameString="demo listen only" ePath="200424972c992c64"><TriggerAndTransport class1="1" server="1" triggerCyclic="1"><TransportTypeListenOnly transportTypeListenOnly="1"/></TriggerAndTransport><Originator2TargetParameters><ConnectionParameters fixedSizeSupported="1" realTimeTransferFormat="3" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="0" minValue="0" maxValue="0"/></Size></Originator2TargetParameters><Target2OriginatorParameters><ConnectionParameters variableSizeSupported="1" realTimeTransferFormat="1" connectionTypeMulticast="1" connectionTypePoint2Point="1" priorityLow="1"/><Size><Constant constValue="32" minValue="32" maxValue="32"/></Size></Target2OriginatorParameters></CIPConnection></PossibleConnections>|
|
||||||
|
[<245>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X|X| | | | | | | | |
|
||||||
|
CSP |||||||||||
|
||||||
|
IAG |X|X|X|X|X|X|X|X| | | |X| | | | |
|
||||||
|
IAS | | |X| | | | | | | | | | | | | |
|
||||||
|
IAL | =(1,17)| =(4)| | | | | =(1..255)| | | | | =(0..3600)| | | | |
|
||||||
|
IS |X| |X|X| | | | | | | | | | |
|
||||||
|
ISP |||||||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
[<246>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X|X| | | | | | | | |
|
||||||
|
CSP |||||||||||
|
||||||
|
IAG |X|X|X| | | |X| | | |X| | | | |
|
||||||
|
IAS | | | | | | | | | | | | | | | |
|
||||||
|
IAL | =(0,10,100)| |MAC address (08:00:27:77:CF:56)| | | | | | | | | | | | |
|
||||||
|
IS |X|X| | | | | | | | | | |
|
||||||
|
ISP |||||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
[<72>]--------------------------------------------------
|
||||||
|
CAG |X|X|X| | |X|X| | |
|
||||||
|
CAS | | | | | | | | | |
|
||||||
|
CAL | | | | | | | | | |
|
||||||
|
CS |X|X| | | | | | | | |
|
||||||
|
CSP |||||||||||
|
||||||
|
IAG | | | |X|X|X|X|X|
|
||||||
|
IAS | | | |X|X|X|X|X|
|
||||||
|
IAL | =(0)| =(59)| =(47)| =(55)| =(47)| =(43)| =(31)| =(27)|
|
||||||
|
IS |X|X| | | | | | | |
|
||||||
|
ISP ||||||||||
|
||||||
|
VSA | |
|
||||||
|
OPT | | |
|
||||||
|
[--------------------------------------------------------
|
||||||
359
OrginalSourceRepo/OpENer-master/data/opener_sample_app.eds
Normal file
359
OrginalSourceRepo/OpENer-master/data/opener_sample_app.eds
Normal file
|
|
@ -0,0 +1,359 @@
|
||||||
|
$ EZ-EDS Version 3.23.1.20171205 Generated Electronic Data Sheet
|
||||||
|
|
||||||
|
[File]
|
||||||
|
DescText = "EDS file for the sample application of OpENer";
|
||||||
|
CreateDate = 11-03-2009;
|
||||||
|
CreateTime = 13:15:23;
|
||||||
|
ModDate = 02-06-2018;
|
||||||
|
ModTime = 14:05:38;
|
||||||
|
Revision = 2.3;
|
||||||
|
HomeURL = "https://github.com/EIPStackGroup/OpENer";
|
||||||
|
|
||||||
|
[Device]
|
||||||
|
VendCode = 1;
|
||||||
|
VendName = "Rockwell Automation";
|
||||||
|
ProdType = 12;
|
||||||
|
ProdTypeStr = "Communications Adapter";
|
||||||
|
ProdCode = 65001;
|
||||||
|
MajRev = 2;
|
||||||
|
MinRev = 3;
|
||||||
|
ProdName = "OpENer PC";
|
||||||
|
Catalog = "OpENer-2.x";
|
||||||
|
|
||||||
|
[Device Classification]
|
||||||
|
Class1 = EtherNetIP;
|
||||||
|
|
||||||
|
[Params]
|
||||||
|
Param1 =
|
||||||
|
0, $ reserved, shall equal 0
|
||||||
|
,, $ Link Path Size, Link Path
|
||||||
|
0x0000, $ Descriptor
|
||||||
|
0xD1, $ Data Type
|
||||||
|
1, $ Data Size in bytes
|
||||||
|
"Input Data", $ name
|
||||||
|
"", $ units
|
||||||
|
"New Help String", $ help string
|
||||||
|
,,0, $ min, max, default data values
|
||||||
|
,,,, $ mult, div, base, offset scaling
|
||||||
|
,,,, $ mult, div, base, offset links
|
||||||
|
; $ decimal places
|
||||||
|
Param2 =
|
||||||
|
0, $ reserved, shall equal 0
|
||||||
|
,, $ Link Path Size, Link Path
|
||||||
|
0x0000, $ Descriptor
|
||||||
|
0xD1, $ Data Type
|
||||||
|
1, $ Data Size in bytes
|
||||||
|
"Output Data", $ name
|
||||||
|
"", $ units
|
||||||
|
"New Help String", $ help string
|
||||||
|
,,0, $ min, max, default data values
|
||||||
|
,,,, $ mult, div, base, offset scaling
|
||||||
|
,,,, $ mult, div, base, offset links
|
||||||
|
; $ decimal places
|
||||||
|
Param3 =
|
||||||
|
0, $ reserved, shall equal 0
|
||||||
|
,, $ Link Path Size, Link Path
|
||||||
|
0x0000, $ Descriptor
|
||||||
|
0xD1, $ Data Type
|
||||||
|
1, $ Data Size in bytes
|
||||||
|
"Config Data", $ name
|
||||||
|
"", $ units
|
||||||
|
"New Help String", $ help string
|
||||||
|
,,0, $ min, max, default data values
|
||||||
|
,,,, $ mult, div, base, offset scaling
|
||||||
|
,,,, $ mult, div, base, offset links
|
||||||
|
; $ decimal places
|
||||||
|
Param4 =
|
||||||
|
0, $ reserved, shall equal 0
|
||||||
|
,, $ Link Path Size, Link Path
|
||||||
|
0x0000, $ Descriptor
|
||||||
|
0xC8, $ Data Type
|
||||||
|
4, $ Data Size in bytes
|
||||||
|
"RPI", $ name
|
||||||
|
"", $ units
|
||||||
|
"New Help String", $ help string
|
||||||
|
20000,,30000, $ min, max, default data values
|
||||||
|
,,,, $ mult, div, base, offset scaling
|
||||||
|
,,,, $ mult, div, base, offset links
|
||||||
|
; $ decimal places
|
||||||
|
|
||||||
|
[Assembly]
|
||||||
|
Object_Name = "Assembly Object";
|
||||||
|
Object_Class_Code = 0x04;
|
||||||
|
Number_Of_Static_Instances = 6;
|
||||||
|
Assem100 =
|
||||||
|
"Input Assembly",
|
||||||
|
"",
|
||||||
|
32,
|
||||||
|
0x0000,
|
||||||
|
,,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1,
|
||||||
|
8,Param1;
|
||||||
|
Assem150 =
|
||||||
|
"Output Assembly",
|
||||||
|
"",
|
||||||
|
32,
|
||||||
|
0x0001,
|
||||||
|
,,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2,
|
||||||
|
8,Param2;
|
||||||
|
Assem151 =
|
||||||
|
"Config Assembly",
|
||||||
|
"",
|
||||||
|
10,
|
||||||
|
0x0001,
|
||||||
|
,,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3,
|
||||||
|
8,Param3;
|
||||||
|
|
||||||
|
[Connection Manager]
|
||||||
|
Revision = 1;
|
||||||
|
Object_Name = "Connection Manager Object";
|
||||||
|
Object_Class_Code = 0x06;
|
||||||
|
MaxInst = 1;
|
||||||
|
Number_Of_Static_Instances = 1;
|
||||||
|
Max_Number_Of_Dynamic_Instances = 0;
|
||||||
|
Connection1 =
|
||||||
|
0x84010002, $ 0-15 = supported transport classes
|
||||||
|
$ 16 = trigger: cyclic
|
||||||
|
$ 17 = trigger: change of state
|
||||||
|
$ 18 = trigger: application
|
||||||
|
$ 19-23 = trigger: reserved
|
||||||
|
$ 24 = transport type: listen-only
|
||||||
|
$ 25 = transport type: input-only
|
||||||
|
$ 26 = transport type: exclusive-owner
|
||||||
|
$ 27 = transport type: redundant-owner
|
||||||
|
$ 28-30 = reserved
|
||||||
|
$ 31 = Client = 0 / Server = 1
|
||||||
|
0x44640405, $ 0 = O->T fixed size supported
|
||||||
|
$ 1 = O->T variable size supported
|
||||||
|
$ 2 = T->O fixed size supported
|
||||||
|
$ 3 = T->O variable size supported
|
||||||
|
$ 4-5 = O->T number of bytes per slot (obsolete)
|
||||||
|
$ 6-7 = T->O number of bytes per slot (obsolete)
|
||||||
|
$ 8-10 = O->T Real time transfer format
|
||||||
|
$ 11 = reserved
|
||||||
|
$ 12-14 = T->O Real time transfer format
|
||||||
|
$ 15 = reserved
|
||||||
|
$ 16 = O->T connection type: NULL
|
||||||
|
$ 17 = O->T connection type: MULTICAST
|
||||||
|
$ 18 = O->T connection type: POINT2POINT
|
||||||
|
$ 19 = O->T connection type: reserved
|
||||||
|
$ 20 = T->O connection type: NULL
|
||||||
|
$ 21 = T->O connection type: MULTICAST
|
||||||
|
$ 22 = T->O connection type: POINT2POINT
|
||||||
|
$ 23 = T->O connection type: reserved
|
||||||
|
$ 24 = O->T priority: LOW
|
||||||
|
$ 25 = O->T priority: HIGH
|
||||||
|
$ 26 = O->T priority: SCHEDULED
|
||||||
|
$ 27 = O->T priority: reserved
|
||||||
|
$ 28 = T->O priority: LOW
|
||||||
|
$ 29 = T->O priority: HIGH
|
||||||
|
$ 30 = T->O priority: SCHEDULED
|
||||||
|
$ 31 = T->O priority: reserved
|
||||||
|
Param4,,Assem150, $ O->T RPI, size, format
|
||||||
|
Param4,,Assem100, $ T->O RPI, size, format
|
||||||
|
,, $ config #1 size, format
|
||||||
|
,Assem151, $ config #2 size, format
|
||||||
|
"Exlusive Owner", $ Connection Name
|
||||||
|
"", $ help string
|
||||||
|
"20 04 24 97 2C 96 2C 64"; $ Path
|
||||||
|
Connection2 =
|
||||||
|
0x02010002, $ 0-15 = supported transport classes
|
||||||
|
$ 16 = trigger: cyclic
|
||||||
|
$ 17 = trigger: change of state
|
||||||
|
$ 18 = trigger: application
|
||||||
|
$ 19-23 = trigger: reserved
|
||||||
|
$ 24 = transport type: listen-only
|
||||||
|
$ 25 = transport type: input-only
|
||||||
|
$ 26 = transport type: exclusive-owner
|
||||||
|
$ 27 = transport type: redundant-owner
|
||||||
|
$ 28-30 = reserved
|
||||||
|
$ 31 = Client = 0 / Server = 1
|
||||||
|
0x44640305, $ 0 = O->T fixed size supported
|
||||||
|
$ 1 = O->T variable size supported
|
||||||
|
$ 2 = T->O fixed size supported
|
||||||
|
$ 3 = T->O variable size supported
|
||||||
|
$ 4-5 = O->T number of bytes per slot (obsolete)
|
||||||
|
$ 6-7 = T->O number of bytes per slot (obsolete)
|
||||||
|
$ 8-10 = O->T Real time transfer format
|
||||||
|
$ 11 = reserved
|
||||||
|
$ 12-14 = T->O Real time transfer format
|
||||||
|
$ 15 = reserved
|
||||||
|
$ 16 = O->T connection type: NULL
|
||||||
|
$ 17 = O->T connection type: MULTICAST
|
||||||
|
$ 18 = O->T connection type: POINT2POINT
|
||||||
|
$ 19 = O->T connection type: reserved
|
||||||
|
$ 20 = T->O connection type: NULL
|
||||||
|
$ 21 = T->O connection type: MULTICAST
|
||||||
|
$ 22 = T->O connection type: POINT2POINT
|
||||||
|
$ 23 = T->O connection type: reserved
|
||||||
|
$ 24 = O->T priority: LOW
|
||||||
|
$ 25 = O->T priority: HIGH
|
||||||
|
$ 26 = O->T priority: SCHEDULED
|
||||||
|
$ 27 = O->T priority: reserved
|
||||||
|
$ 28 = T->O priority: LOW
|
||||||
|
$ 29 = T->O priority: HIGH
|
||||||
|
$ 30 = T->O priority: SCHEDULED
|
||||||
|
$ 31 = T->O priority: reserved
|
||||||
|
Param4,0,, $ O->T RPI, size, format
|
||||||
|
Param4,32,Assem100, $ T->O RPI, size, format
|
||||||
|
,, $ config #1 size, format
|
||||||
|
0,, $ config #2 size, format
|
||||||
|
"Input Only", $ Connection Name
|
||||||
|
"", $ help string
|
||||||
|
"20 04 24 97 2C 98 2C 64"; $ Path
|
||||||
|
Connection3 =
|
||||||
|
0x01010002, $ 0-15 = supported transport classes
|
||||||
|
$ 16 = trigger: cyclic
|
||||||
|
$ 17 = trigger: change of state
|
||||||
|
$ 18 = trigger: application
|
||||||
|
$ 19-23 = trigger: reserved
|
||||||
|
$ 24 = transport type: listen-only
|
||||||
|
$ 25 = transport type: input-only
|
||||||
|
$ 26 = transport type: exclusive-owner
|
||||||
|
$ 27 = transport type: redundant-owner
|
||||||
|
$ 28-30 = reserved
|
||||||
|
$ 31 = Client = 0 / Server = 1
|
||||||
|
0x44240305, $ 0 = O->T fixed size supported
|
||||||
|
$ 1 = O->T variable size supported
|
||||||
|
$ 2 = T->O fixed size supported
|
||||||
|
$ 3 = T->O variable size supported
|
||||||
|
$ 4-5 = O->T number of bytes per slot (obsolete)
|
||||||
|
$ 6-7 = T->O number of bytes per slot (obsolete)
|
||||||
|
$ 8-10 = O->T Real time transfer format
|
||||||
|
$ 11 = reserved
|
||||||
|
$ 12-14 = T->O Real time transfer format
|
||||||
|
$ 15 = reserved
|
||||||
|
$ 16 = O->T connection type: NULL
|
||||||
|
$ 17 = O->T connection type: MULTICAST
|
||||||
|
$ 18 = O->T connection type: POINT2POINT
|
||||||
|
$ 19 = O->T connection type: reserved
|
||||||
|
$ 20 = T->O connection type: NULL
|
||||||
|
$ 21 = T->O connection type: MULTICAST
|
||||||
|
$ 22 = T->O connection type: POINT2POINT
|
||||||
|
$ 23 = T->O connection type: reserved
|
||||||
|
$ 24 = O->T priority: LOW
|
||||||
|
$ 25 = O->T priority: HIGH
|
||||||
|
$ 26 = O->T priority: SCHEDULED
|
||||||
|
$ 27 = O->T priority: reserved
|
||||||
|
$ 28 = T->O priority: LOW
|
||||||
|
$ 29 = T->O priority: HIGH
|
||||||
|
$ 30 = T->O priority: SCHEDULED
|
||||||
|
$ 31 = T->O priority: reserved
|
||||||
|
Param4,0,, $ O->T RPI, size, format
|
||||||
|
Param4,,Assem100, $ T->O RPI, size, format
|
||||||
|
,, $ config #1 size, format
|
||||||
|
,, $ config #2 size, format
|
||||||
|
"Listen Only", $ Connection Name
|
||||||
|
"", $ help string
|
||||||
|
"20 04 24 97 2C 99 2C 64"; $ Path
|
||||||
|
|
||||||
|
[Capacity]
|
||||||
|
MaxMsgConnections = 6;
|
||||||
|
MaxIOProduceConsume = 2;
|
||||||
|
MaxIOMcastProducers = 1;
|
||||||
|
MaxIOMcastConsumers = 1;
|
||||||
|
MaxConsumersPerMcast = 6;
|
||||||
|
TSpec1 = TxRx, 32, 100;
|
||||||
|
|
||||||
|
[TCP/IP Interface Class]
|
||||||
|
Revision = 4;
|
||||||
|
Object_Name = "TCP/IP Interface Object";
|
||||||
|
Object_Class_Code = 0xF5;
|
||||||
|
MaxInst = 1;
|
||||||
|
Number_Of_Static_Instances = 1;
|
||||||
|
Max_Number_Of_Dynamic_Instances = 0;
|
||||||
|
|
||||||
|
[Ethernet Link Class]
|
||||||
|
Revision = 4;
|
||||||
|
Object_Name = "Ethernet Link Object";
|
||||||
|
Object_Class_Code = 0xF6;
|
||||||
|
MaxInst = 1;
|
||||||
|
Number_Of_Static_Instances = 1;
|
||||||
|
Max_Number_Of_Dynamic_Instances = 0;
|
||||||
|
|
||||||
|
[Identity Class]
|
||||||
|
Revision = 1;
|
||||||
|
Object_Name = "Identity Object";
|
||||||
|
Object_Class_Code = 0x01;
|
||||||
|
MaxInst = 1;
|
||||||
|
Number_Of_Static_Instances = 1;
|
||||||
|
Max_Number_Of_Dynamic_Instances = 0;
|
||||||
|
|
||||||
|
[QoS Class]
|
||||||
|
Revision = 1;
|
||||||
|
Object_Name = "QoS Object";
|
||||||
|
Object_Class_Code = 0x48;
|
||||||
|
MaxInst = 1;
|
||||||
|
Number_Of_Static_Instances = 1;
|
||||||
|
Max_Number_Of_Dynamic_Instances = 0;
|
||||||
|
|
||||||
BIN
OrginalSourceRepo/OpENer-master/fuzz/imgs/fuzz.png
Normal file
BIN
OrginalSourceRepo/OpENer-master/fuzz/imgs/fuzz.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
OrginalSourceRepo/OpENer-master/fuzz/inputs/cip_req_forward_open
Normal file
BIN
OrginalSourceRepo/OpENer-master/fuzz/inputs/cip_req_forward_open
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,38 @@
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("python {} IP TESTCASE_PATH".format(sys.argv[0]))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
HOST_IP = sys.argv[1]
|
||||||
|
HOST_PORT = 44818
|
||||||
|
TESTCASE_PATH = sys.argv[2]
|
||||||
|
|
||||||
|
ENIP_SESSION_CONTEXT = b"\x92\x83J\x0b=\x9e\x0cW"
|
||||||
|
ENIP_INIT_SESSION_PACKET = b"e\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ENIP_SESSION_CONTEXT + b"\x00\x00\x00\x00\x01\x00\x00\x00"
|
||||||
|
|
||||||
|
|
||||||
|
print("[-] Connecting to {}:{}".format(HOST_IP, HOST_PORT))
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((HOST_IP, HOST_PORT))
|
||||||
|
|
||||||
|
print("[-] Init ENIP session")
|
||||||
|
s.sendall(ENIP_INIT_SESSION_PACKET)
|
||||||
|
enip_session = s.recv(1024)
|
||||||
|
session_handle = enip_session[4:8]
|
||||||
|
print("[-] Got ENIP Session Handle: {}".format(struct.unpack("<I", session_handle)[0]))
|
||||||
|
print("[-] Reading testcase from: '{}'".format(TESTCASE_PATH))
|
||||||
|
with open(TESTCASE_PATH, "rb") as f:
|
||||||
|
testcase_data = f.read()
|
||||||
|
|
||||||
|
print("[-] Patching sender context and session handle")
|
||||||
|
testcase = testcase_data[:4] # command, len
|
||||||
|
testcase += session_handle # session handle
|
||||||
|
testcase += testcase_data[8:12] # status
|
||||||
|
testcase += ENIP_SESSION_CONTEXT # session context
|
||||||
|
testcase += testcase_data[20:] # options and payload
|
||||||
|
print("[-] Sending testcase of {} bytes".format(len(testcase)))
|
||||||
|
s.send(testcase)
|
||||||
|
s.close()
|
||||||
55
OrginalSourceRepo/OpENer-master/license.txt
Normal file
55
OrginalSourceRepo/OpENer-master/license.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
SOFTWARE DISTRIBUTION LICENSE FOR THE
|
||||||
|
ETHERNET/IP(TM) COMMUNICATION STACK
|
||||||
|
(ADAPTED BSD STYLE LICENSE)
|
||||||
|
|
||||||
|
Copyright (c) 2009, Rockwell Automation, Inc. ALL RIGHTS RESERVED.
|
||||||
|
EtherNet/IP is a trademark of ODVA, Inc.
|
||||||
|
|
||||||
|
Redistribution of the Communications Stack Software for EtherNet/IP and use in
|
||||||
|
source and binary forms, with or without modification, are permitted provided
|
||||||
|
that the following conditions are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright and trademark
|
||||||
|
notices, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Neither the name of Rockwell Automation, ODVA, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission from the respective owners.
|
||||||
|
|
||||||
|
The Communications Stack Software for EtherNet/IP, or any portion thereof, with
|
||||||
|
or without modifications, may be incorporated into products for sale. However,
|
||||||
|
the software does not, by itself, convey any right to make, have made, use,
|
||||||
|
import, offer to sell, sell, lease, market, or otherwise distribute or dispose
|
||||||
|
of any products that implement this software, which products might be covered
|
||||||
|
by valid patents or copyrights of ODVA, Inc., its members or other licensors
|
||||||
|
nor does this software result in any license to use the EtherNet/IP mark owned
|
||||||
|
by ODVA. To make, have made, use, import, offer to sell, sell, lease, market,
|
||||||
|
or otherwise distribute or dispose of any products that implement this software,
|
||||||
|
and to use the EtherNet/IP mark, one must obtain the necessary license from
|
||||||
|
ODVA through its Terms of Usage Agreement for the EtherNet/IP technology,
|
||||||
|
available through the ODVA web site at www.odva.org. This license requirement
|
||||||
|
applies equally (a) to devices that completely implement ODVA's Final
|
||||||
|
Specification for EtherNet/IP (“Network Devices”), (b) to components of such
|
||||||
|
Network Devices to the extent they implement portions of the Final
|
||||||
|
Specification for EtherNet/IP, and (c) to enabling technology products, such as
|
||||||
|
any other EtherNet/IP or other network protocol stack designed for use in
|
||||||
|
Network Devices to the extent they implement portions of the Final
|
||||||
|
Specification for EtherNet/IP. Persons or entities who are not already licensed
|
||||||
|
for the EtherNet/IP technology must contact ODVA for a Terms of Usage Agreement.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
2
OrginalSourceRepo/OpENer-master/source/.gitignore
vendored
Normal file
2
OrginalSourceRepo/OpENer-master/source/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/Debug/
|
||||||
|
/build/
|
||||||
185
OrginalSourceRepo/OpENer-master/source/CMakeLists.txt
Executable file
185
OrginalSourceRepo/OpENer-master/source/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,185 @@
|
||||||
|
#######################################
|
||||||
|
# Required CMake version #
|
||||||
|
#######################################
|
||||||
|
cmake_minimum_required( VERSION 3.18 )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Project name #
|
||||||
|
#######################################
|
||||||
|
project( OpENer LANGUAGES C VERSION 2.3 )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Project version #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
if( NOT DEFINED OpENer_Device_Config_Vendor_Id )
|
||||||
|
set( OpENer_Device_Config_Vendor_Id 1 CACHE STRING "Device Vendor ID" )
|
||||||
|
endif()
|
||||||
|
if( NOT DEFINED OpENer_Device_Config_Device_Type )
|
||||||
|
set( OpENer_Device_Config_Device_Type 12 CACHE STRING "Device Type ID" )
|
||||||
|
endif()
|
||||||
|
if( NOT DEFINED OpENer_Device_Config_Product_Code )
|
||||||
|
set( OpENer_Device_Config_Product_Code 65001 CACHE STRING "Device Product Code" )
|
||||||
|
endif()
|
||||||
|
if( NOT DEFINED OpENer_Device_Config_Device_Name )
|
||||||
|
set( OpENer_Device_Config_Device_Name "OpENer PC" CACHE STRING "Device Name" )
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED OpENer_Device_Major_Version)
|
||||||
|
set(OpENer_Device_Major_Version ${PROJECT_VERSION_MAJOR} CACHE STRING "Major Version")
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED OpENer_Device_Minor_Version)
|
||||||
|
set(OpENer_Device_Minor_Version ${PROJECT_VERSION_MINOR} CACHE STRING "Minor Version")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/ports/devicedata.h.in"
|
||||||
|
"${PROJECT_BINARY_DIR}/src/ports/devicedata.h"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_path( OpENer_BUILDSUPPORT_DIR OpENer.cmake ${PROJECT_SOURCE_DIR}/buildsupport )
|
||||||
|
|
||||||
|
INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer.cmake )
|
||||||
|
|
||||||
|
option(OPENER_RANDOMIZE_CONNECTION_ID "Use randomized connection IDs also for lower 16-bits?" FALSE)
|
||||||
|
option(OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER "Shall produced data from OpENer also include a run idle header?" FALSE)
|
||||||
|
option(OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER "Will consumed data from OpENer also include a run idle header?" TRUE)
|
||||||
|
option(OPENER_INSTALL_AS_LIB "Build and install OpENer as a library" FALSE)
|
||||||
|
option(BUILD_SHARED_LIBS "Build OpENer as shared library" FALSE)
|
||||||
|
|
||||||
|
if(OPENER_RANDOMIZE_CONNECTION_ID)
|
||||||
|
add_definitions(-DOPENER_RANDOMIZE_CONNECTION_ID)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER)
|
||||||
|
add_definitions(-DOPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
|
||||||
|
add_definitions(-DOPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(OPENER_IS_DLR_DEVICE "Is OpENer built with support for a basic DLR device?" FALSE)
|
||||||
|
if (OPENER_IS_DLR_DEVICE)
|
||||||
|
add_definitions(-DOPENER_IS_DLR_DEVICE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# This buffer size will be used for any received message.
|
||||||
|
# The same buffer is used for the replied explicit message.
|
||||||
|
# There are two uses in OpENer:
|
||||||
|
# 1. Explicit messages will use this buffer to store the data generated by the request
|
||||||
|
# 2. I/O Connections will use this buffer for the produced data
|
||||||
|
set( OPENER_ETHERNET_BUFFER_SIZE "512" CACHE STRING "Number of bytes used for the Ethernet message buffer")
|
||||||
|
add_definitions(-DPC_OPENER_ETHERNET_BUFFER_SIZE=${OPENER_ETHERNET_BUFFER_SIZE} )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Platform switches #
|
||||||
|
#######################################
|
||||||
|
set( OpENer_KNOWN_PLATFORMS "POSIX" "WIN32" "MINGW")
|
||||||
|
|
||||||
|
set( OpENer_PLATFORM CACHE STRING "Platform OpENer will be built for" )
|
||||||
|
set_property(CACHE OpENer_PLATFORM PROPERTY STRINGS ${OpENer_KNOWN_PLATFORMS} )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Platform-dependent functions check #
|
||||||
|
#######################################
|
||||||
|
INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer_function_checks.cmake )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# OpENer tracer switches #
|
||||||
|
#######################################
|
||||||
|
set( OpENer_TRACES OFF CACHE BOOL "Activate OpENer traces" )
|
||||||
|
if(OpENer_TRACES)
|
||||||
|
createTraceLevelOptions()
|
||||||
|
endif(OpENer_TRACES)
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# OpENer documentation target "doc" #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
# check if Doxygen is installed
|
||||||
|
find_package(Doxygen)
|
||||||
|
if (DOXYGEN_FOUND)
|
||||||
|
# set input and output files
|
||||||
|
set(DOXYGEN_IN ${OpENer_SOURCE_DIR}/opener.doxyfile.in)
|
||||||
|
set(DOXYGEN_OUT ${OpENer_BINARY_DIR}/opener.doxyfile)
|
||||||
|
|
||||||
|
# exclude subdirectories of non active platforms depending on OpENer_PLATFORM
|
||||||
|
set(OpENer_EXCLUDE_PATTERNS "")
|
||||||
|
if (NOT (${OpENer_PLATFORM} STREQUAL "POSIX"))
|
||||||
|
set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/POSIX/*")
|
||||||
|
endif ()
|
||||||
|
if (NOT (${OpENer_PLATFORM} STREQUAL "WIN32"))
|
||||||
|
set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/WIN32/*")
|
||||||
|
endif ()
|
||||||
|
if (NOT (${OpENer_PLATFORM} STREQUAL "MINGW"))
|
||||||
|
set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/MINGW/*")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# request to configure the file
|
||||||
|
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
|
||||||
|
|
||||||
|
# note the option ALL which will build the docs always with the default target
|
||||||
|
add_custom_target( doc # ALL
|
||||||
|
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
|
||||||
|
WORKING_DIRECTORY ${OpENer_SOURCE_DIR}
|
||||||
|
COMMENT "Generating API documentation with Doxygen"
|
||||||
|
VERBATIM )
|
||||||
|
message("-- Doxygen setup done")
|
||||||
|
else (DOXYGEN_FOUND)
|
||||||
|
message("Doxygen needs to be installed to generate the Doxygen documentation")
|
||||||
|
endif (DOXYGEN_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Test switch #
|
||||||
|
#######################################
|
||||||
|
set( OpENer_TESTS OFF CACHE BOOL "Enable tests to be built" )
|
||||||
|
if( OpENer_TESTS )
|
||||||
|
enable_testing()
|
||||||
|
enable_language( CXX )
|
||||||
|
set( CPPUTEST_HOME "" CACHE PATH "Path to CppUTest directory" )
|
||||||
|
INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer_Tests.cmake )
|
||||||
|
INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/CodeCoverage.cmake )
|
||||||
|
APPEND_COVERAGE_COMPILER_FLAGS()
|
||||||
|
# The used CppUTest framework does not support parallel jobs
|
||||||
|
SETUP_TARGET_FOR_COVERAGE_LCOV(NAME ${PROJECT_NAME}_coverage EXECUTABLE OpENer_Tests EXCLUDE "tests/*" "src/ports/*/sample_application/*" "${CPPUTEST_HOME}/*")
|
||||||
|
add_test_includes()
|
||||||
|
add_definitions( -DOPENER_UNIT_TEST )
|
||||||
|
add_subdirectory( tests )
|
||||||
|
endif( OpENer_TESTS )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# OpENer C flags #
|
||||||
|
#######################################
|
||||||
|
if (OpENer_PLATFORM STREQUAL "WIN32")
|
||||||
|
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W4" )
|
||||||
|
else ()
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wconversion")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Internal cache holding the available CIP objects #
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
# Add definitions for additional CIP Objects
|
||||||
|
string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT)
|
||||||
|
if (OpENer_HAS_ADDITIONAL_OBJECT)
|
||||||
|
string(REPLACE " " ";" OpENer_ADD_CIP_OBJECTS_LIST ${OpENer_ADD_CIP_OBJECTS})
|
||||||
|
foreach (CIP_OBJECT IN LISTS OpENer_ADD_CIP_OBJECTS_LIST)
|
||||||
|
add_definitions(-D${CIP_OBJECT})
|
||||||
|
endforeach ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set( OpENer_ADD_CIP_OBJECTS "" CACHE INTERNAL STRING )
|
||||||
|
set( OpENer_ADD_CIP_OBJECTS_INCLUDES "" CACHE INTERNAL STRING )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add custom CIP objects #
|
||||||
|
#######################################
|
||||||
|
set( OpENer_CIP_OBJECTS_DIR ${PROJECT_SOURCE_DIR}/src/cip_objects )
|
||||||
|
include(${OpENer_BUILDSUPPORT_DIR}/OpENer_CIP_Object_generator.cmake)
|
||||||
|
|
||||||
|
# ######################################
|
||||||
|
# Add subdirectories #
|
||||||
|
# ######################################
|
||||||
|
add_subdirectory( src )
|
||||||
|
|
@ -0,0 +1,446 @@
|
||||||
|
# Copyright (c) 2012 - 2017, Lars Bilke
|
||||||
|
# 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 copyright holder 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 HOLDER 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.
|
||||||
|
#
|
||||||
|
# CHANGES:
|
||||||
|
#
|
||||||
|
# 2012-01-31, Lars Bilke
|
||||||
|
# - Enable Code Coverage
|
||||||
|
#
|
||||||
|
# 2013-09-17, Joakim Söderberg
|
||||||
|
# - Added support for Clang.
|
||||||
|
# - Some additional usage instructions.
|
||||||
|
#
|
||||||
|
# 2016-02-03, Lars Bilke
|
||||||
|
# - Refactored functions to use named parameters
|
||||||
|
#
|
||||||
|
# 2017-06-02, Lars Bilke
|
||||||
|
# - Merged with modified version from github.com/ufz/ogs
|
||||||
|
#
|
||||||
|
# 2019-05-06, Anatolii Kurotych
|
||||||
|
# - Remove unnecessary --coverage flag
|
||||||
|
#
|
||||||
|
# 2019-12-13, FeRD (Frank Dana)
|
||||||
|
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
|
||||||
|
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
|
||||||
|
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
|
||||||
|
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
|
||||||
|
# - Set lcov basedir with -b argument
|
||||||
|
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
|
||||||
|
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
|
||||||
|
# - Delete output dir, .info file on 'make clean'
|
||||||
|
# - Remove Python detection, since version mismatches will break gcovr
|
||||||
|
# - Minor cleanup (lowercase function names, update examples...)
|
||||||
|
#
|
||||||
|
# 2019-12-19, FeRD (Frank Dana)
|
||||||
|
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
|
||||||
|
#
|
||||||
|
# 2020-01-19, Bob Apthorpe
|
||||||
|
# - Added gfortran support
|
||||||
|
#
|
||||||
|
# 2020-02-17, FeRD (Frank Dana)
|
||||||
|
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
|
||||||
|
# in EXCLUDEs, and remove manual escaping from gcovr targets
|
||||||
|
#
|
||||||
|
# 2020-05-04, Mihchael Davis
|
||||||
|
# - Add -fprofile-abs-path to make gcno files contain absolute paths
|
||||||
|
# - Fix BASE_DIRECTORY not working when defined
|
||||||
|
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
|
||||||
|
# USAGE:
|
||||||
|
#
|
||||||
|
# 1. Copy this file into your cmake modules path.
|
||||||
|
#
|
||||||
|
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
|
||||||
|
# using a CMake option() to enable it just optionally):
|
||||||
|
# include(CodeCoverage)
|
||||||
|
#
|
||||||
|
# 3. Append necessary compiler flags:
|
||||||
|
# append_coverage_compiler_flags()
|
||||||
|
#
|
||||||
|
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
|
||||||
|
#
|
||||||
|
# 4. If you need to exclude additional directories from the report, specify them
|
||||||
|
# using full paths in the COVERAGE_EXCLUDES variable before calling
|
||||||
|
# setup_target_for_coverage_*().
|
||||||
|
# Example:
|
||||||
|
# set(COVERAGE_EXCLUDES
|
||||||
|
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
|
||||||
|
# '/path/to/my/src/dir2/*')
|
||||||
|
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
|
||||||
|
# Example:
|
||||||
|
# setup_target_for_coverage_lcov(
|
||||||
|
# NAME coverage
|
||||||
|
# EXECUTABLE testrunner
|
||||||
|
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
|
||||||
|
#
|
||||||
|
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
|
||||||
|
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
|
||||||
|
# Example:
|
||||||
|
# set(COVERAGE_EXCLUDES "dir1/*")
|
||||||
|
# setup_target_for_coverage_gcovr_html(
|
||||||
|
# NAME coverage
|
||||||
|
# EXECUTABLE testrunner
|
||||||
|
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
|
||||||
|
# EXCLUDE "dir2/*")
|
||||||
|
#
|
||||||
|
# 5. Use the functions described below to create a custom make target which
|
||||||
|
# runs your test executable and produces a code coverage report.
|
||||||
|
#
|
||||||
|
# 6. Build a Debug build:
|
||||||
|
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
|
# make
|
||||||
|
# make my_coverage_target
|
||||||
|
#
|
||||||
|
|
||||||
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
|
# Check prereqs
|
||||||
|
find_program( GCOV_PATH gcov )
|
||||||
|
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||||
|
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
|
||||||
|
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||||
|
find_program( CPPFILT_PATH NAMES c++filt )
|
||||||
|
|
||||||
|
if(NOT GCOV_PATH)
|
||||||
|
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||||
|
endif() # NOT GCOV_PATH
|
||||||
|
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||||
|
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
|
||||||
|
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||||
|
endif()
|
||||||
|
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
|
||||||
|
# Do nothing; exit conditional without error if true
|
||||||
|
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
|
||||||
|
# Do nothing; exit conditional without error if true
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
CHECK_CXX_COMPILER_FLAG(-fprofile-abs-path ProfileAbsPathAvailable)
|
||||||
|
if (ProfileAbsPathAvailable)
|
||||||
|
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage -fprofile-abs-path"
|
||||||
|
CACHE INTERNAL "")
|
||||||
|
else()
|
||||||
|
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
|
||||||
|
CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_Fortran_FLAGS_COVERAGE
|
||||||
|
${COVERAGE_COMPILER_FLAGS}
|
||||||
|
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
|
||||||
|
FORCE )
|
||||||
|
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||||
|
${COVERAGE_COMPILER_FLAGS}
|
||||||
|
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||||
|
FORCE )
|
||||||
|
set(CMAKE_C_FLAGS_COVERAGE
|
||||||
|
${COVERAGE_COMPILER_FLAGS}
|
||||||
|
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||||
|
FORCE )
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||||
|
""
|
||||||
|
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||||
|
FORCE )
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||||
|
""
|
||||||
|
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||||
|
FORCE )
|
||||||
|
mark_as_advanced(
|
||||||
|
CMAKE_Fortran_FLAGS_COVERAGE
|
||||||
|
CMAKE_CXX_FLAGS_COVERAGE
|
||||||
|
CMAKE_C_FLAGS_COVERAGE
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||||
|
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||||
|
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||||
|
link_libraries(gcov)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Defines a target for running and collection code coverage information
|
||||||
|
# Builds dependencies, runs the given executable and outputs reports.
|
||||||
|
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||||
|
# the coverage generation will not complete.
|
||||||
|
#
|
||||||
|
# setup_target_for_coverage_lcov(
|
||||||
|
# NAME testrunner_coverage # New target name
|
||||||
|
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||||
|
# DEPENDENCIES testrunner # Dependencies to build first
|
||||||
|
# BASE_DIRECTORY "../" # Base directory for report
|
||||||
|
# # (defaults to PROJECT_SOURCE_DIR)
|
||||||
|
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||||
|
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||||
|
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||||
|
# # even if c++filt is found
|
||||||
|
# )
|
||||||
|
function(setup_target_for_coverage_lcov)
|
||||||
|
|
||||||
|
set(options NO_DEMANGLE)
|
||||||
|
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||||
|
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
|
||||||
|
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(NOT LCOV_PATH)
|
||||||
|
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||||
|
endif() # NOT LCOV_PATH
|
||||||
|
|
||||||
|
if(NOT GENHTML_PATH)
|
||||||
|
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||||
|
endif() # NOT GENHTML_PATH
|
||||||
|
|
||||||
|
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||||
|
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||||
|
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||||
|
else()
|
||||||
|
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||||
|
set(LCOV_EXCLUDES "")
|
||||||
|
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||||
|
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||||
|
endif()
|
||||||
|
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
|
||||||
|
endforeach()
|
||||||
|
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
|
||||||
|
|
||||||
|
# Conditional arguments
|
||||||
|
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||||
|
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Setup target
|
||||||
|
add_custom_target(${Coverage_NAME}
|
||||||
|
|
||||||
|
# Cleanup lcov
|
||||||
|
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters
|
||||||
|
# Create baseline to make sure untouched files show up in the report
|
||||||
|
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||||
|
|
||||||
|
# Capturing lcov counters and generating report
|
||||||
|
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
|
||||||
|
# add baseline counters
|
||||||
|
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
|
||||||
|
# filter collected data to final coverage report
|
||||||
|
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
|
||||||
|
|
||||||
|
# Generate HTML output
|
||||||
|
COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info
|
||||||
|
|
||||||
|
# Set output files as GENERATED (will be removed on 'make clean')
|
||||||
|
BYPRODUCTS
|
||||||
|
${Coverage_NAME}.base
|
||||||
|
${Coverage_NAME}.capture
|
||||||
|
${Coverage_NAME}.total
|
||||||
|
${Coverage_NAME}.info
|
||||||
|
${Coverage_NAME}/index.html
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
DEPENDS ${Coverage_DEPENDENCIES}
|
||||||
|
VERBATIM # Protect arguments to commands
|
||||||
|
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show where to find the lcov info report
|
||||||
|
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||||
|
COMMAND ;
|
||||||
|
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show info where to find the report
|
||||||
|
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||||
|
COMMAND ;
|
||||||
|
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||||
|
)
|
||||||
|
|
||||||
|
endfunction() # setup_target_for_coverage_lcov
|
||||||
|
|
||||||
|
# Defines a target for running and collection code coverage information
|
||||||
|
# Builds dependencies, runs the given executable and outputs reports.
|
||||||
|
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||||
|
# the coverage generation will not complete.
|
||||||
|
#
|
||||||
|
# setup_target_for_coverage_gcovr_xml(
|
||||||
|
# NAME ctest_coverage # New target name
|
||||||
|
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||||
|
# DEPENDENCIES executable_target # Dependencies to build first
|
||||||
|
# BASE_DIRECTORY "../" # Base directory for report
|
||||||
|
# # (defaults to PROJECT_SOURCE_DIR)
|
||||||
|
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||||
|
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||||
|
# )
|
||||||
|
function(setup_target_for_coverage_gcovr_xml)
|
||||||
|
|
||||||
|
set(options NONE)
|
||||||
|
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||||
|
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||||
|
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(NOT GCOVR_PATH)
|
||||||
|
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||||
|
endif() # NOT GCOVR_PATH
|
||||||
|
|
||||||
|
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||||
|
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||||
|
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||||
|
else()
|
||||||
|
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||||
|
set(GCOVR_EXCLUDES "")
|
||||||
|
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||||
|
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||||
|
endif()
|
||||||
|
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||||
|
endforeach()
|
||||||
|
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||||
|
|
||||||
|
# Combine excludes to several -e arguments
|
||||||
|
set(GCOVR_EXCLUDE_ARGS "")
|
||||||
|
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||||
|
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||||
|
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_target(${Coverage_NAME}
|
||||||
|
# Run tests
|
||||||
|
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||||
|
|
||||||
|
# Running gcovr
|
||||||
|
COMMAND ${GCOVR_PATH} --xml
|
||||||
|
-r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS}
|
||||||
|
--object-directory=${PROJECT_BINARY_DIR}
|
||||||
|
-o ${Coverage_NAME}.xml
|
||||||
|
BYPRODUCTS ${Coverage_NAME}.xml
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
DEPENDS ${Coverage_DEPENDENCIES}
|
||||||
|
VERBATIM # Protect arguments to commands
|
||||||
|
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show info where to find the report
|
||||||
|
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||||
|
COMMAND ;
|
||||||
|
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
|
||||||
|
)
|
||||||
|
endfunction() # setup_target_for_coverage_gcovr_xml
|
||||||
|
|
||||||
|
# Defines a target for running and collection code coverage information
|
||||||
|
# Builds dependencies, runs the given executable and outputs reports.
|
||||||
|
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||||
|
# the coverage generation will not complete.
|
||||||
|
#
|
||||||
|
# setup_target_for_coverage_gcovr_html(
|
||||||
|
# NAME ctest_coverage # New target name
|
||||||
|
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||||
|
# DEPENDENCIES executable_target # Dependencies to build first
|
||||||
|
# BASE_DIRECTORY "../" # Base directory for report
|
||||||
|
# # (defaults to PROJECT_SOURCE_DIR)
|
||||||
|
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||||
|
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||||
|
# )
|
||||||
|
function(setup_target_for_coverage_gcovr_html)
|
||||||
|
|
||||||
|
set(options NONE)
|
||||||
|
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||||
|
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||||
|
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(NOT GCOVR_PATH)
|
||||||
|
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||||
|
endif() # NOT GCOVR_PATH
|
||||||
|
|
||||||
|
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||||
|
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||||
|
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||||
|
else()
|
||||||
|
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||||
|
set(GCOVR_EXCLUDES "")
|
||||||
|
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||||
|
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||||
|
endif()
|
||||||
|
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||||
|
endforeach()
|
||||||
|
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||||
|
|
||||||
|
# Combine excludes to several -e arguments
|
||||||
|
set(GCOVR_EXCLUDE_ARGS "")
|
||||||
|
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||||
|
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||||
|
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_target(${Coverage_NAME}
|
||||||
|
# Run tests
|
||||||
|
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||||
|
|
||||||
|
# Create folder
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
|
||||||
|
|
||||||
|
# Running gcovr
|
||||||
|
COMMAND ${GCOVR_PATH} --html --html-details
|
||||||
|
-r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS}
|
||||||
|
--object-directory=${PROJECT_BINARY_DIR}
|
||||||
|
-o ${Coverage_NAME}/index.html
|
||||||
|
|
||||||
|
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
DEPENDS ${Coverage_DEPENDENCIES}
|
||||||
|
VERBATIM # Protect arguments to commands
|
||||||
|
COMMENT "Running gcovr to produce HTML code coverage report."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show info where to find the report
|
||||||
|
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||||
|
COMMAND ;
|
||||||
|
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||||
|
)
|
||||||
|
|
||||||
|
endfunction() # setup_target_for_coverage_gcovr_html
|
||||||
|
|
||||||
|
function(append_coverage_compiler_flags)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||||
|
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||||
|
endfunction() # append_coverage_compiler_flags
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
macro(opener_platform_spec)
|
||||||
|
include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application )
|
||||||
|
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32 -D_WIN32_WINNT=_WIN32_WINNT_VISTA" )
|
||||||
|
endmacro(opener_platform_spec)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
FUNCTION(opener_add_definition)
|
||||||
|
FOREACH(ARG ${ARGV})
|
||||||
|
set_property(GLOBAL APPEND PROPERTY OPENER_DEFINITION ${ARG})
|
||||||
|
ENDFOREACH(ARG)
|
||||||
|
ENDFUNCTION(opener_add_definition)
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Adds platform specific include directories #
|
||||||
|
##############################################
|
||||||
|
macro(opener_platform_support ARGS)
|
||||||
|
|
||||||
|
if(OpENer_PLATFORM STREQUAL "")
|
||||||
|
message(FATAL_ERROR "No platform selected!")
|
||||||
|
endif(OpENer_PLATFORM STREQUAL "")
|
||||||
|
|
||||||
|
include( ${OpENer_BUILDSUPPORT_DIR}/${OpENer_PLATFORM}/OpENer_PLATFORM_INCLUDES.cmake)
|
||||||
|
opener_platform_spec()
|
||||||
|
endmacro(opener_platform_support ARGS)
|
||||||
|
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Adds common Include directories #
|
||||||
|
#######################################
|
||||||
|
macro(opener_common_includes)
|
||||||
|
set( SRC_DIR "${OpENer_SOURCE_DIR}/src" )
|
||||||
|
set( CIP_SRC_DIR "${SRC_DIR}/cip" )
|
||||||
|
set( ENET_ENCAP_SRC_DIR "${SRC_DIR}/enet_encap" )
|
||||||
|
set( PORTS_SRC_DIR "${SRC_DIR}/ports")
|
||||||
|
set( NVDATA_SRC_DIR "${SRC_DIR}/ports/nvdata")
|
||||||
|
set( UTILS_SRC_DIR "${SRC_DIR}/utils")
|
||||||
|
|
||||||
|
include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${CIP_CONNETION_MANAGER_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} ${UTILS_SRC_DIR} ${OpENer_CIP_OBJECTS_DIR} ${NVDATA_SRC_DIR} )
|
||||||
|
include_directories( "${PROJECT_BINARY_DIR}/src/ports" )
|
||||||
|
endmacro(opener_common_includes)
|
||||||
|
|
||||||
|
MACRO(opener_add_cip_object NAME DESCRIPTION)
|
||||||
|
set(OpENer_CIP_OBJECT_${NAME} OFF CACHE BOOL "${DESCRIPTION}")
|
||||||
|
FOREACH(dependencies ${ARGN})
|
||||||
|
if(NOT ${dependencies})
|
||||||
|
return()
|
||||||
|
endif(NOT ${dependencies})
|
||||||
|
ENDFOREACH(dependencies)
|
||||||
|
if(NOT OpENer_CIP_OBJECT_${NAME})
|
||||||
|
return()
|
||||||
|
endif(NOT OpENer_CIP_OBJECT_${NAME})
|
||||||
|
ENDMACRO(opener_add_cip_object)
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Creates options for trace level #
|
||||||
|
#######################################
|
||||||
|
macro(createTraceLevelOptions)
|
||||||
|
add_definitions( -DOPENER_WITH_TRACES )
|
||||||
|
set( TRACE_LEVEL 0 )
|
||||||
|
set( OpENer_TRACE_LEVEL_ERROR ON CACHE BOOL "Error trace level" )
|
||||||
|
set( OpENer_TRACE_LEVEL_WARNING ON CACHE BOOL "Warning trace level" )
|
||||||
|
set( OpENer_TRACE_LEVEL_STATE ON CACHE BOOL "State trace level" )
|
||||||
|
set( OpENer_TRACE_LEVEL_INFO ON CACHE BOOL "Info trace level" )
|
||||||
|
|
||||||
|
if(OpENer_TRACE_LEVEL_ERROR)
|
||||||
|
math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 1" )
|
||||||
|
endif(OpENer_TRACE_LEVEL_ERROR)
|
||||||
|
if(OpENer_TRACE_LEVEL_WARNING)
|
||||||
|
math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 2" )
|
||||||
|
endif(OpENer_TRACE_LEVEL_WARNING)
|
||||||
|
if(OpENer_TRACE_LEVEL_STATE)
|
||||||
|
math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 4" )
|
||||||
|
endif(OpENer_TRACE_LEVEL_STATE)
|
||||||
|
if(OpENer_TRACE_LEVEL_INFO)
|
||||||
|
math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 8" )
|
||||||
|
endif(OpENer_TRACE_LEVEL_INFO)
|
||||||
|
|
||||||
|
add_definitions(-DOPENER_TRACE_LEVEL=${TRACE_LEVEL})
|
||||||
|
endmacro(createTraceLevelOptions)
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
FILE(GLOB_RECURSE FILES_FOUND "${OpENer_CIP_OBJECTS_DIR}/*CMakeLists.txt")
|
||||||
|
|
||||||
|
FOREACH(FILE ${FILES_FOUND})
|
||||||
|
string(REPLACE "${OpENer_CIP_OBJECTS_DIR}" "" CIP_OBJECT_NAME ${FILE})
|
||||||
|
STRING(REGEX MATCH "^/([^/]*)/CMakeLists.txt$" CIP_OBJECT_NAME ${CIP_OBJECT_NAME})
|
||||||
|
IF(CIP_OBJECT_NAME)
|
||||||
|
string(REPLACE "/CMakeLists.txt" "" CIP_OBJECT_NAME "${CIP_OBJECT_NAME}")
|
||||||
|
string(REPLACE "/" "" CIP_OBJECT_NAME "${CIP_OBJECT_NAME}")
|
||||||
|
LIST(APPEND CIP_OBJECT_LIST ${CIP_OBJECT_NAME})
|
||||||
|
ENDIF(CIP_OBJECT_NAME)
|
||||||
|
ENDFOREACH(FILE)
|
||||||
|
|
||||||
|
SET(WRITE_FILE "")
|
||||||
|
FOREACH(CIP_OBJECT ${CIP_OBJECT_LIST})
|
||||||
|
SET(WRITE_FILE "${WRITE_FILE}SET(SOURCE_GROUP_BACKUP \"\${SOURCE_GROUP}\")\n")
|
||||||
|
SET(WRITE_FILE "${WRITE_FILE}SET(SOURCE_GROUP \"\${SOURCE_GROUP}/${CIP_OBJECT}\")\n")
|
||||||
|
SET(WRITE_FILE "${WRITE_FILE}ADD_SUBDIRECTORY(\"\${OpENer_CIP_OBJECTS_DIR}/${CIP_OBJECT}\")\n")
|
||||||
|
SET(WRITE_FILE "${WRITE_FILE}SET(SOURCE_GROUP \"\${SOURCE_GROUP_BACKUP}\")\n")
|
||||||
|
ENDFOREACH(CIP_OBJECT)
|
||||||
|
|
||||||
|
file(WRITE ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists_new.txt ${WRITE_FILE})
|
||||||
|
|
||||||
|
execute_process( COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists_new.txt ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists.txt RESULT_VARIABLE test_not_successful OUTPUT_QUIET ERROR_QUIET )
|
||||||
|
|
||||||
|
IF(test_not_successful)
|
||||||
|
file(REMOVE ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists.txt)
|
||||||
|
file(RENAME ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists_new.txt ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists.txt)
|
||||||
|
ENDIF( test_not_successful)
|
||||||
|
file(REMOVE ${CMAKE_BINARY_DIR}/cip_objects/CMakeLists_new.txt)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#######################################
|
||||||
|
# Adds test includes #
|
||||||
|
#######################################
|
||||||
|
macro( add_test_includes )
|
||||||
|
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorNewMacros.h" )
|
||||||
|
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorMallocMacros.h" )
|
||||||
|
include_directories( ${CPPUTEST_HOME}/include ${OpENer_SOURCE_DIR}/tests)
|
||||||
|
endmacro( add_test_includes )
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
########################################
|
||||||
|
# Check if functions exist on platform #
|
||||||
|
########################################
|
||||||
|
|
||||||
|
include (CheckFunctionExists)
|
||||||
|
|
||||||
|
check_function_exists( srand HAVE_SRAND )
|
||||||
|
check_function_exists( rand HAVE_RAND )
|
||||||
|
|
||||||
|
if( (NOT(HAVE_SRAND)) OR (NOT(HAVE_RAND)) )
|
||||||
|
|
||||||
|
endif( (NOT(HAVE_SRAND)) OR (NOT(HAVE_RAND)) )
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
macro(opener_platform_spec)
|
||||||
|
include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror=implicit-function-declaration -fcommon")
|
||||||
|
if(NOT CMAKE_INSTALL_LIBDIR)
|
||||||
|
set( CMAKE_INSTALL_LIBDIR "lib")
|
||||||
|
endif()
|
||||||
|
if(NOT CMAKE_INSTALL_INCLUDEDIR )
|
||||||
|
set( CMAKE_INSTALL_INCLUDEDIR "include")
|
||||||
|
endif()
|
||||||
|
add_definitions( -D_POSIX_C_SOURCE=200112L -D_GNU_SOURCE -DOPENER_POSIX)
|
||||||
|
get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||||
|
if ("CXX" IN_LIST languages)
|
||||||
|
add_definitions(-DRESTRICT=)
|
||||||
|
else()
|
||||||
|
add_definitions(-DRESTRICT=restrict)
|
||||||
|
endif()
|
||||||
|
endmacro(opener_platform_spec)
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
INCLUDE(CMakeForceCompiler)
|
||||||
|
|
||||||
|
# Embedded System - No OS
|
||||||
|
SET(CMAKE_SYSTEM_NAME Generic)
|
||||||
|
# Specifiy CPU
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR cortex-m3)
|
||||||
|
|
||||||
|
# specify the cross compiler
|
||||||
|
CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU)
|
||||||
|
CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU)
|
||||||
|
|
||||||
|
# Find the target environment prefix..
|
||||||
|
# First see where gcc is keeping libc.a
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a
|
||||||
|
OUTPUT_VARIABLE CMAKE_INSTALL_PREFIX
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
# Strip the filename off
|
||||||
|
get_filename_component(CMAKE_INSTALL_PREFIX
|
||||||
|
"${CMAKE_INSTALL_PREFIX}" PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then find the canonical path to the directory one up from there
|
||||||
|
get_filename_component(CMAKE_INSTALL_PREFIX
|
||||||
|
"${CMAKE_INSTALL_PREFIX}/.." REALPATH
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH
|
||||||
|
"Install path prefix, prepended onto install directories.")
|
||||||
|
|
||||||
|
message(STATUS "Cross-compiling with the gcc-arm-embedded toolchain")
|
||||||
|
message(STATUS "Toolchain prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${CMAKE_INSTALL_PREFIX})
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS
|
||||||
|
"${CMAKE_C_FLAGS}"
|
||||||
|
"-fno-common"
|
||||||
|
"-Wstrict-prototypes -ffunction-sections -fdata-sections"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "cortex-m3")
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS
|
||||||
|
"${CMAKE_C_FLAGS}"
|
||||||
|
"-mcpu=cortex-m3 -march=armv7-m -mthumb"
|
||||||
|
"-msoft-float"
|
||||||
|
"-ffunction-sections -fdata-sections"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||||
|
message(WARNING
|
||||||
|
"Processor not recognised in toolchain file, "
|
||||||
|
"compiler flags not configured."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# When we break up long strings in CMake we get semicolon
|
||||||
|
# separated lists, undo this here...
|
||||||
|
string(REGEX REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "")
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
macro(opener_platform_spec)
|
||||||
|
include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application )
|
||||||
|
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32" )
|
||||||
|
endmacro(opener_platform_spec)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
diff --git a/Inc/lwipopts.h b/Inc/lwipopts.h
|
||||||
|
index ef9dbfa..6a8c593 100644
|
||||||
|
--- a/Inc/lwipopts.h
|
||||||
|
+++ b/Inc/lwipopts.h
|
||||||
|
@@ -56,7 +56,9 @@ a lot of data that needs to be copied, this should be set high. */
|
||||||
|
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
|
||||||
|
timeouts. */
|
||||||
|
#define MEMP_NUM_SYS_TIMEOUT 10
|
||||||
|
-
|
||||||
|
+/* MEMP_NUM_NETCONN: the number of struct netconns.
|
||||||
|
+ (only needed if you use the sequential API, like api_lib.c) */
|
||||||
|
+#define MEMP_NUM_NETCONN 12
|
||||||
|
|
||||||
|
/* ---------- Pbuf options ---------- */
|
||||||
|
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
|
||||||
|
@@ -94,6 +96,8 @@ a lot of data that needs to be copied, this should be set high. */
|
||||||
|
/* ---------- ICMP options ---------- */
|
||||||
|
#define LWIP_ICMP 1
|
||||||
|
|
||||||
|
+/* ---------- IGMP options ---------- */
|
||||||
|
+#define LWIP_IGMP 1
|
||||||
|
|
||||||
|
/* ---------- DHCP options ---------- */
|
||||||
|
#define LWIP_DHCP 1
|
||||||
|
@@ -113,6 +117,9 @@ a lot of data that needs to be copied, this should be set high. */
|
||||||
|
*/
|
||||||
|
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||||
|
|
||||||
|
+/* ---------- Netif options ---------- */
|
||||||
|
+#define LWIP_NETIF_HOSTNAME 1
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
--------------------------------------
|
||||||
|
---------- Checksum options ----------
|
||||||
|
@@ -178,7 +185,11 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums
|
||||||
|
/**
|
||||||
|
* LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
|
||||||
|
*/
|
||||||
|
-#define LWIP_SOCKET 0
|
||||||
|
+#define LWIP_SOCKET 1
|
||||||
|
+/**
|
||||||
|
+ * SO_REUSE==1: Enable SO_REUSEADDR option.
|
||||||
|
+ */
|
||||||
|
+#define SO_REUSE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------
|
||||||
|
diff --git a/Inc/main.h b/Inc/main.h
|
||||||
|
index 36ca85f..fb7961c 100644
|
||||||
|
--- a/Inc/main.h
|
||||||
|
+++ b/Inc/main.h
|
||||||
|
@@ -31,7 +31,7 @@
|
||||||
|
|
||||||
|
/* Exported types ------------------------------------------------------------*/
|
||||||
|
/* Exported constants --------------------------------------------------------*/
|
||||||
|
-#define USE_DHCP /* enable DHCP, if disabled static address is used*/
|
||||||
|
+//#define USE_DHCP // not used, replaced by LWIP_DHCP
|
||||||
|
#define USE_LCD
|
||||||
|
|
||||||
|
/*Static IP ADDRESS*/
|
||||||
|
diff --git a/SW4STM32/syscalls.c b/SW4STM32/syscalls.c
|
||||||
|
index fa8687f..6a84edb 100644
|
||||||
|
--- a/SW4STM32/syscalls.c
|
||||||
|
+++ b/SW4STM32/syscalls.c
|
||||||
|
@@ -15,7 +15,7 @@
|
||||||
|
#include <reent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
-
|
||||||
|
+#include <stm32f7xx_hal.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define FreeRTOS
|
||||||
|
@@ -99,6 +99,15 @@ void _exit (int status)
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
+#if REDIRECT_PRINTF_TO_SWV_ITM
|
||||||
|
+__attribute__((weak)) int _write(int file, char *ptr, int len) {
|
||||||
|
+ int DataIdx;
|
||||||
|
+ for (DataIdx = 0; DataIdx < len; DataIdx++) {
|
||||||
|
+ ITM_SendChar(*ptr++);
|
||||||
|
+ }
|
||||||
|
+ return len;
|
||||||
|
+}
|
||||||
|
+#else // standard output
|
||||||
|
int _write(int file, char *ptr, int len)
|
||||||
|
{
|
||||||
|
int DataIdx;
|
||||||
|
@@ -109,6 +118,7 @@ int _write(int file, char *ptr, int len)
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
+#endif // standard output
|
||||||
|
|
||||||
|
int _close(int file)
|
||||||
|
{
|
||||||
|
diff --git a/Src/app_ethernet.c b/Src/app_ethernet.c
|
||||||
|
index e3278ac..aab6265 100644
|
||||||
|
--- a/Src/app_ethernet.c
|
||||||
|
+++ b/Src/app_ethernet.c
|
||||||
|
@@ -28,6 +28,9 @@
|
||||||
|
#include "lcd_log.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+// for OpENer
|
||||||
|
+#include "opener.h"
|
||||||
|
+
|
||||||
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
|
/* Private define ------------------------------------------------------------*/
|
||||||
|
/* Private macro -------------------------------------------------------------*/
|
||||||
|
@@ -55,6 +58,8 @@ void ethernet_link_status_updated(struct netif *netif)
|
||||||
|
uint8_t iptxt[20];
|
||||||
|
sprintf((char *)iptxt, "%s", ip4addr_ntoa(netif_ip4_addr(netif)));
|
||||||
|
LCD_UsrLog ("Static IP address: %s\n", iptxt);
|
||||||
|
+ /* Start Ethernet/IP Stack (OpENer) */
|
||||||
|
+ opener_init(netif);
|
||||||
|
#else
|
||||||
|
BSP_LED_On(LED1);
|
||||||
|
BSP_LED_Off(LED2);
|
||||||
|
@@ -124,6 +129,8 @@ void DHCP_Thread(void const * argument)
|
||||||
|
BSP_LED_On(LED1);
|
||||||
|
BSP_LED_Off(LED2);
|
||||||
|
#endif
|
||||||
|
+ /* Start Ethernet/IP Stack (OpENer) */
|
||||||
|
+ opener_init(netif);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@@ -148,6 +155,8 @@ void DHCP_Thread(void const * argument)
|
||||||
|
BSP_LED_On(LED1);
|
||||||
|
BSP_LED_Off(LED2);
|
||||||
|
#endif
|
||||||
|
+ /* Start Ethernet/IP Stack (OpENer) */
|
||||||
|
+ opener_init(netif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/Src/main.c b/Src/main.c
|
||||||
|
index c25dbd0..e3dda6c 100644
|
||||||
|
--- a/Src/main.c
|
||||||
|
+++ b/Src/main.c
|
||||||
|
@@ -71,6 +71,11 @@ int main(void)
|
||||||
|
/* Configure the system clock to 200 MHz */
|
||||||
|
SystemClock_Config();
|
||||||
|
|
||||||
|
+ /* For single step debug, e.g. timers with interrupts need to be stopped in Halt */
|
||||||
|
+ HAL_DBGMCU_EnableDBGStandbyMode();
|
||||||
|
+ HAL_DBGMCU_EnableDBGStopMode();
|
||||||
|
+ __HAL_DBGMCU_FREEZE_TIM6();
|
||||||
|
+
|
||||||
|
/* Initialize LCD and LEDs */
|
||||||
|
BSP_Config();
|
||||||
|
|
||||||
|
@@ -139,6 +144,8 @@ static void Netif_Config(void)
|
||||||
|
|
||||||
|
/* Registers the default network interface. */
|
||||||
|
netif_set_default(&gnetif);
|
||||||
|
+ /* Define the hostname, is also used by OpENer */
|
||||||
|
+ netif_set_hostname(&gnetif,"STM32");
|
||||||
|
|
||||||
|
ethernet_link_status_updated(&gnetif);
|
||||||
|
|
||||||
|
@@ -180,7 +187,7 @@ static void BSP_Config(void)
|
||||||
|
LCD_LOG_Init();
|
||||||
|
|
||||||
|
/* Show Header and Footer texts */
|
||||||
|
- LCD_LOG_SetHeader((uint8_t *)"Webserver Application Netconn API");
|
||||||
|
+ LCD_LOG_SetHeader((uint8_t *)"Webserver Application Netconn API & OpENer");
|
||||||
|
LCD_LOG_SetFooter((uint8_t *)"STM32746G-DISCO board");
|
||||||
|
|
||||||
|
LCD_UsrLog ((char *)" State: Ethernet Initialization ...\n");
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<cdtprojectproperties>
|
||||||
|
<section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
|
||||||
|
<language id="org.eclipse.cdt.core.assembly" name="s,S"/>
|
||||||
|
<language id="org.eclipse.cdt.core.gcc" name="C Source File">
|
||||||
|
<includepath>../../Inc</includepath>
|
||||||
|
<includepath>../../Src</includepath>
|
||||||
|
<includepath>../../Drivers/CMSIS/Device/ST/STM32F7xx/Include</includepath>
|
||||||
|
<includepath>../../Drivers/STM32F7xx_HAL_Driver/Inc</includepath>
|
||||||
|
<includepath>../../Drivers/BSP/STM32746G-Discovery</includepath>
|
||||||
|
<includepath>../../Drivers/BSP/Components/Common</includepath>
|
||||||
|
<includepath>../../Drivers/BSP/Components</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/LwIP/src/include</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/LwIP/system</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/FreeRTOS/Source</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/FreeRTOS/Source/include</includepath>
|
||||||
|
<includepath>../../Utilities</includepath>
|
||||||
|
<includepath>../../Utilities/Log</includepath>
|
||||||
|
<includepath>../../Drivers/CMSIS/Include</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/cip</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/enet_encap</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/ports</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/ports/STM32</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/ports/STM32/sample_application</includepath>
|
||||||
|
<includepath>../../Middlewares/Third_Party/OpENer/utils</includepath>
|
||||||
|
</language>
|
||||||
|
<language name="Object File"/>
|
||||||
|
</section>
|
||||||
|
<section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros">
|
||||||
|
<language id="org.eclipse.cdt.core.assembly" name="s,S"/>
|
||||||
|
<language id="org.eclipse.cdt.core.gcc" name="C Source File">
|
||||||
|
<macro>
|
||||||
|
<name>USE_HAL_DRIVER</name>
|
||||||
|
<value/>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>STM32F746xx</name>
|
||||||
|
<value/>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>USE_STM32746G_DISCOVERY</name>
|
||||||
|
<value/>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>RESTRICT</name>
|
||||||
|
<value>__restrict</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>STM32</name>
|
||||||
|
<value/>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>_POSIX_C_SOURCE</name>
|
||||||
|
<value>200112L</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>_GNU_SOURCE</name>
|
||||||
|
<value/>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>OPENER_TRACE_LEVEL</name>
|
||||||
|
<value>15</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER</name>
|
||||||
|
<value>1</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>OPENER_WITH_TRACES</name>
|
||||||
|
<value>1</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>PC_OPENER_ETHERNET_BUFFER_SIZE</name>
|
||||||
|
<value>512</value>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<name>REDIRECT_PRINTF_TO_SWV_ITM</name>
|
||||||
|
<value>1</value>
|
||||||
|
</macro>
|
||||||
|
</language>
|
||||||
|
<language name="Object File"/>
|
||||||
|
</section>
|
||||||
|
</cdtprojectproperties>
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,309 @@
|
||||||
|
\documentclass[final,a4paper,10pt, oneside]{article}
|
||||||
|
\usepackage[latin1]{inputenc}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
|
||||||
|
\usepackage{a4wide}
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%%%% important definitions at the beginning
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
|
\title{Coding Rules for\\ \emph{OpENer} --- Open Source EtherNet/IP$^{TM}$ Adapter Stack\\\large Version 2.0}
|
||||||
|
\author{Martin Melik Merkumians\thanks{melik-merkumians\@@acin.tuwien.ac.at}}
|
||||||
|
\date{2015-11-15}
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
% Links
|
||||||
|
\usepackage{ifpdf}
|
||||||
|
\ifpdf
|
||||||
|
\usepackage[pdftex, colorlinks, pdfstartview=FitH, plainpages=false, pdfpagelabels]{hyperref}
|
||||||
|
\pdfcompresslevel=9
|
||||||
|
\else
|
||||||
|
\usepackage[dvipdfm, colorlinks]{hyperref}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\hypersetup{colorlinks, linkcolor=black, filecolor=black, urlcolor=black, citecolor=black, pdftitle={Coding Rules for OpENer},pdfauthor={Alois Zoitl}}
|
||||||
|
|
||||||
|
% Font
|
||||||
|
\usepackage{mathpazo}
|
||||||
|
|
||||||
|
\usepackage[centerlast,small,bf]{caption} %zentriert, kleiner als fliesstext, fett (gilt nur fuer 'Abbildung x:')
|
||||||
|
|
||||||
|
% Figures
|
||||||
|
\usepackage[dvips]{graphicx}
|
||||||
|
|
||||||
|
% Listings
|
||||||
|
\usepackage{listings}
|
||||||
|
\lstset{language=C++}
|
||||||
|
\lstset{commentstyle=\textit}
|
||||||
|
\lstset{linewidth=\textwidth}
|
||||||
|
\lstset{basicstyle=\scriptsize}
|
||||||
|
|
||||||
|
\usepackage{fancyref}
|
||||||
|
\usepackage{hyperref}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
|
||||||
|
\section{Introduction}
|
||||||
|
This document describes the coding rules, which has to be used in the OpENer project. These rules are mainly the Google C++ style rules, with some extensions specific to C and the OpENer project. If something is not covered by the rules given in this document, please check the official Google C++ style guide, available at \url{http://google.github.io/styleguide/cppguide.html}. Additional code style examples can be found at \url{https://gist.github.com/davidzchen/9187878}.
|
||||||
|
|
||||||
|
As the OpENer code style aims to be as close as possible to the established Google C++ code style, please file an issue if anything in this guide contradicts the Google C++ code style.
|
||||||
|
|
||||||
|
\section{Comments}
|
||||||
|
A sufficient amount of comments has to be written. There are never too many comments, whereas invalid comments are worse than none --- thus invalid comments have to be removed from the source code. Comments have to be written in English.
|
||||||
|
|
||||||
|
|
||||||
|
Comments for function, structure, \ldots~ definitions have to follow the conventions of \emph{Doxygen} to allow the automated generation of documentation for the source code. Hereby Java-style Doxygen comments shall be used. Doxygen comments shall therefore start with slash and two starts, and use the @ symbol to indicate Doxygen keywords. For enums, variables, and strucutres inline documentation with \emph{/**<} shall be used. Autobrief behavior shall not be assumed for Doxygen comments. See the example below.
|
||||||
|
\begin{lstlisting}[frame=trbl]{}
|
||||||
|
/** @brief function, structure, enum, etc. to comment
|
||||||
|
*
|
||||||
|
* Detailed explanation, spanning multiple lines if needed.
|
||||||
|
* @param parameter1 Parameter1 description
|
||||||
|
* @return Return value description
|
||||||
|
*/
|
||||||
|
int foo(char bar) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
const int g_kFooBar = 1; /**< Global constant which needs documentation */
|
||||||
|
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Comments have to be meaningful, to describe to program and to be up to date.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Fileheaders}
|
||||||
|
Every source-file must contain a fileheader as follows:
|
||||||
|
\begin{lstlisting}[frame=trbl]{}
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* <date>: <author>, <author email> - changes
|
||||||
|
******************************************************************************/
|
||||||
|
\end{lstlisting}
|
||||||
|
Each author needs to explain his changes in the code.
|
||||||
|
\subsection{Revision History}
|
||||||
|
%To track changes in the source files every new file version must contain it's version information in the following form:
|
||||||
|
%\begin{center}
|
||||||
|
% @version: $<$date$>$/$<$author$>$: $<$description$>$
|
||||||
|
%\end{center}
|
||||||
|
%An additional example is given in the appendix \ref{subsec:FileHeader} of this document.
|
||||||
|
The revision history has to be done in a style usable by Doxygen. This means that the history is independent of the files, but all classes are documented.
|
||||||
|
|
||||||
|
\subsection{Keywords}
|
||||||
|
The following Keywords should be used in the source code to mark special comments:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{TODO:} For comments about possible or needed extensions
|
||||||
|
\item \textbf{FIXME:} To be used for comments about potential (or known) bugs
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\section{Datatypes}
|
||||||
|
\Fref{tab:datatypes} contains the definitions of important standard datatypes. This is done to ensure a machine independant defintion of the bit-width of the standard data types. For \emph{OpENer}-development these definitions are in the file: \verb|src/typedefs.h|
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\caption{Data types used in OpENer} \label{tab:datatypes}
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{lll}
|
||||||
|
defined data type & bit-width / description & used C-datatype \\
|
||||||
|
\hline
|
||||||
|
EipByte & 8 bit unsigned & uint8\_t\\
|
||||||
|
EipInt8 & 8 bit signed & int8\_t \\
|
||||||
|
EipInt16 & 16 bit signed & int16\_t \\
|
||||||
|
EipInt32 & 32 bit signed & int32\_t \\
|
||||||
|
EipInt64 & 64 bit signed & int64\_t \\
|
||||||
|
EipUint8 & 8 bit unsigned & uint8\_t \\
|
||||||
|
EipUint16 & 16 bit unsigned & uint16\_t \\
|
||||||
|
EipUint32 & 32 bit unsigned & uint32\_t \\
|
||||||
|
EipUint64 & 64 bit unsigned & uint64\_t \\
|
||||||
|
EipFloat & single precission IEEE float (32 bit) & float \\
|
||||||
|
EipDfloat & double precission IEEE float (64 bit) & double \\
|
||||||
|
EipBool8 & byte variable as boolean value & unit8\_t \\
|
||||||
|
|
||||||
|
CipOctet & unspecified type & uint8\_t \\
|
||||||
|
CipBool & byte variable as boolean value & uint8\_t \\
|
||||||
|
CipByte & 8 bit unsigned & uint8\_t \\
|
||||||
|
CipWord & 16 bit unsigned & uint16\_t \\
|
||||||
|
CipDword & 32 bit unsigned & uint32\_t \\
|
||||||
|
CipUsint & 8 bit unsigned & uint8\_t \\
|
||||||
|
CipUint & 16 bit unsigned & uint16\_t \\
|
||||||
|
CipUdint & 32 bit unsigned & uint32\_t \\
|
||||||
|
CipSint & 8 bit signed & int8\_t \\
|
||||||
|
CipInt & 16 bit signed & int16\_t \\
|
||||||
|
CipDint & 32 bit signed & int32\_t \\
|
||||||
|
CipReal & single precission IEEE float (32 bit) & float \\
|
||||||
|
CipLreal & double precission IEEE float (64 bit) & double \\
|
||||||
|
|
||||||
|
CipLint & 64 bit signed & int64\_t \\
|
||||||
|
CipUlint & 64 bit unsigned & uint64\_t \\
|
||||||
|
CipLword & 64 bit unsigned & uint64\_t
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
These data types shall only be used when the bit size is important for the correct operation of the code, whereby Eip-prefixed data types shall be used for communication functions, and Cip-prefixed data types shall be used for CIP related functions and objects. If not we advice to use the type \verb|int| or \verb|unsigned int| for most variables, as this is the most efficient data type and can lead on some platforms (e.g., ARM) even to smaller code size.
|
||||||
|
|
||||||
|
\section{Naming of Identifiers}
|
||||||
|
Every identifier has to be named in English. The first character of an identifier must not contain underscores (there are some compiler directives which start with underscores and this could lead to conflicts). Mixed case letters has to be used and the appropriate prefixes have to be inserted where necessary.
|
||||||
|
|
||||||
|
\subsection{Pre- \& Postfixes}
|
||||||
|
The following prefixes have to be applied to identifiers:\\
|
||||||
|
\begin{itemize}
|
||||||
|
\item \emph{"g\_"} shall be prefixed for global variables.
|
||||||
|
\item \emph{"\_"} shall be postfixed for member variables. These are usually CIP object variables with file-global scope.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Variables}
|
||||||
|
Variables have to be named self explanatory. The names have to be provided with the appropriate pre- or postfix and shall be all lowercase letters, and if a name consists of more than one word underscores shall be used for separating these words. The only exception are loop variables (thereby the use of i, j, k is allowed). Only one variable declaration per line is allowed. Pointer operators at the declaration have to be located in front of the variable (not after the type identifier). If possible initializations have to be done directly at the declaration.
|
||||||
|
|
||||||
|
\paragraph{Examples}
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
int i;
|
||||||
|
int local_variable;
|
||||||
|
CipBool boolean_flag_in_cip_object_;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
\subsection{Constants}
|
||||||
|
The preferred way to declare constants is to define them as\emph{const} data types, if this is not possible constants shall be defined as pre-processor statements, via \emph{\#define}.
|
||||||
|
If constants are defined as C constants the name of the constant shall start with \emph{k}, followed by the constant name in Pascal case.
|
||||||
|
If a constant is defined as a pre-processor statement the constant name shall be all upper case, separating multiple words with underscored.
|
||||||
|
Avoid the using ``magic numbers'' (e.g. \verb|if (x == 3){...}|). Instead use constants.
|
||||||
|
|
||||||
|
\paragraph{Examples}
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
static int g_global_variable;
|
||||||
|
static const int g_kAGlobalConstant = 73:
|
||||||
|
const int kAnImportantConstant = 42;
|
||||||
|
#define DO_NOT_DO_THIS_IF_IT_IS_NOT_NECESSARY bad
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
\subsection{Functions}
|
||||||
|
Functions names shall be Pascal cased, function parameters shall be named like variables. The parameter list shall adhere to the following rules:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Input parameters shall come first
|
||||||
|
\item Input parameters shall be const
|
||||||
|
\item Output parameters shall be last
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\paragraph{Examples}
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
int FooBar(const int foo, const char* const bar, double* additional_return_value)
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
\subsection{Structs}
|
||||||
|
The default case for structs shall be, that they are defined as anonymous structs, giving them a type name via the \emph{typedef} keyword. Struct names shall be pascal cased. If a struct type is needed, before a typedef alias can be created (usually inside the same struct), the struct name shall be repeated in the struct type name, following the conventions for variable names, with all lowercase and words separated with underscores.
|
||||||
|
|
||||||
|
The element names inside the struct are following the normal conventions for their types.
|
||||||
|
|
||||||
|
\paragraph{Examples}
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
typedef struct {
|
||||||
|
int foo;
|
||||||
|
char bar;
|
||||||
|
} TheDefaultCase;
|
||||||
|
|
||||||
|
typedef the_excpetion {
|
||||||
|
struct the_exection *needed_the_struct_definition_already_here;
|
||||||
|
char other_elements;
|
||||||
|
} TheException;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
\subsection{Enums}
|
||||||
|
Enums shall be defined anonymous and typedef'ed to a type name. As the values inside an enum are constant, the naming scheme of constants apply for enum members. As Enums do not define their own namespace, the enum type name shall be added between the initial \emph{k} and the constant name.
|
||||||
|
|
||||||
|
\paragraph{Examples}
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
typedef enum {
|
||||||
|
kImportantEnumConstant1 = 0,
|
||||||
|
kImportantEnumConstant2 = 1
|
||||||
|
} ImportantEnum;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
\section{Code Formatting}
|
||||||
|
In order to have consistent code formating the Google C++ coding style rules shall apply. When using Eclipse as development environment the coding format xml file is available at \url{https://github.com/google/styleguide}. By pressing \verb|<ctrl><shift>f| the formatter will format the code according to these rules.
|
||||||
|
|
||||||
|
|
||||||
|
\section{Assertions}
|
||||||
|
The \lstinline!OPENER_ASSERT(e)! macro is available for traditional
|
||||||
|
assertion checks, halting the program if the expression provided as the
|
||||||
|
argument \lstinline!e! evaluates false. This macro shall \emph{only} be used
|
||||||
|
to test conditions where the only possible cause of failure is an
|
||||||
|
unquestionable bug with this program, typically leading to undefined behavior
|
||||||
|
or a crash if execution were permitted to continue.
|
||||||
|
In other words, an assertion shall \emph{never} fail
|
||||||
|
as a result of any external input, valid or invalid,
|
||||||
|
or other similar foreseeable condition, such as a memory allocation failure.
|
||||||
|
These latter type of failures must be handled by normal code execution paths
|
||||||
|
that yield responses with appropriate error codes or possibly
|
||||||
|
terminating the program with a non-zero exit code, not an assertion failure.
|
||||||
|
|
||||||
|
The following listing of a function to set an attribute's value based
|
||||||
|
on received data is an example to help illustrate proper use of assertions.
|
||||||
|
The \lstinline!raw! and \lstinline!len! parameters
|
||||||
|
refer to the received data and length, and the \lstinline!foo! parameter
|
||||||
|
points to the target attribute; the function returns true only if the attribute
|
||||||
|
was set successfully.
|
||||||
|
|
||||||
|
\begin{quote}
|
||||||
|
\begin{lstlisting}
|
||||||
|
bool SetAttributeFoo(const void *raw, size_t len, CipDint *foo) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function should never be called with NULL pointers, regardless of
|
||||||
|
* what was received over the network, so assertions should be used to
|
||||||
|
* validate the pointer arguments.
|
||||||
|
*/
|
||||||
|
OPENER_ASSERT(NULL != raw);
|
||||||
|
OPENER_ASSERT(NULL != foo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensuring enough data was received to satisfy the target data type
|
||||||
|
* must not be done with an assertion as a malformed message containing
|
||||||
|
* insufficient data shall not halt the program.
|
||||||
|
*/
|
||||||
|
if (sizeof(CipDint) > len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipDint new_value = &(int *)raw;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here the received value is tested for conformance to acceptable values;
|
||||||
|
* assume for the sake of this example that allowable values are nonzero.
|
||||||
|
* Validating values received from external sources must not be done
|
||||||
|
* with assertions.
|
||||||
|
*/
|
||||||
|
if (0 == new_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*foo = new_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
18
OrginalSourceRepo/OpENer-master/source/doc/opener.bib
Normal file
18
OrginalSourceRepo/OpENer-master/source/doc/opener.bib
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
@book{CipVol1,
|
||||||
|
series = "The CIP Networks Library",
|
||||||
|
title = "Common {I}ndustrial {P}rotocol",
|
||||||
|
volume = "1",
|
||||||
|
edition = "3.3",
|
||||||
|
month = "November",
|
||||||
|
year = "2007"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@book{CipVol2,
|
||||||
|
series = "The CIP Networks Library",
|
||||||
|
title = "Ether{N}et/{IP} {A}daptation of {CIP}",
|
||||||
|
volume = "2",
|
||||||
|
edition = "1.4",
|
||||||
|
month = "November",
|
||||||
|
year = "2007"
|
||||||
|
}
|
||||||
2381
OrginalSourceRepo/OpENer-master/source/opener.doxyfile.in
Normal file
2381
OrginalSourceRepo/OpENer-master/source/opener.doxyfile.in
Normal file
File diff suppressed because it is too large
Load diff
1
OrginalSourceRepo/OpENer-master/source/src/.gitignore
vendored
Normal file
1
OrginalSourceRepo/OpENer-master/source/src/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
||||||
26
OrginalSourceRepo/OpENer-master/source/src/CMakeLists.txt
Normal file
26
OrginalSourceRepo/OpENer-master/source/src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#######################################
|
||||||
|
# Add subdirectories #
|
||||||
|
#######################################
|
||||||
|
add_subdirectory( enet_encap )
|
||||||
|
add_subdirectory( cip )
|
||||||
|
add_subdirectory( cip_objects )
|
||||||
|
add_subdirectory( ports )
|
||||||
|
add_subdirectory( utils )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform specific things #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
opener_platform_support( "INCLUDES" )
|
||||||
|
|
||||||
|
if( OPENER_INSTALL_AS_LIB )
|
||||||
|
install(DIRECTORY .
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILES_MATCHING PATTERN "*.h"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
30
OrginalSourceRepo/OpENer-master/source/src/cip/CMakeLists.txt
Executable file
30
OrginalSourceRepo/OpENer-master/source/src/cip/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#######################################
|
||||||
|
# CIP library #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform-specific includes #
|
||||||
|
#######################################
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
set( CIP_SRC appcontype.c cipassembly.c cipclass3connection.c cipcommon.c cipconnectionobject.c cipconnectionmanager.c cipdlr.c ciperror.h cipethernetlink.c cipidentity.c cipioconnection.c cipmessagerouter.c ciptcpipinterface.c ciptypes.h cipepath.c cipelectronickey.c cipstring.c cipstringi.c cipqos.c ciptypes.c)
|
||||||
|
|
||||||
|
add_library( CIP ${CIP_SRC} )
|
||||||
|
|
||||||
|
if( OPENER_INSTALL_AS_LIB )
|
||||||
|
target_link_libraries(CIP ENET_ENCAP)
|
||||||
|
install(TARGETS CIP
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
install(DIRECTORY ${CIP_SRC_DIR}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILES_MATCHING PATTERN "*.h"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
453
OrginalSourceRepo/OpENer-master/source/src/cip/appcontype.c
Normal file
453
OrginalSourceRepo/OpENer-master/source/src/cip/appcontype.c
Normal file
|
|
@ -0,0 +1,453 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "appcontype.h"
|
||||||
|
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "assert.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "cipepath.h"
|
||||||
|
|
||||||
|
/** @brief Exclusive Owner connection data */
|
||||||
|
typedef struct {
|
||||||
|
unsigned int output_assembly; /**< the O-to-T point for the connection */
|
||||||
|
unsigned int input_assembly; /**< the T-to-O point for the connection */
|
||||||
|
unsigned int config_assembly; /**< the config point for the connection */
|
||||||
|
CipConnectionObject connection_data; /**< the connection data, only one connection is allowed per O-to-T point*/
|
||||||
|
} ExclusiveOwnerConnection;
|
||||||
|
|
||||||
|
/** @brief Input Only connection data */
|
||||||
|
typedef struct {
|
||||||
|
unsigned int output_assembly; /**< the O-to-T point for the connection */
|
||||||
|
unsigned int input_assembly; /**< the T-to-O point for the connection */
|
||||||
|
unsigned int config_assembly; /**< the config point for the connection */
|
||||||
|
CipConnectionObject connection_data[
|
||||||
|
OPENER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH]; /*< the connection data */
|
||||||
|
} InputOnlyConnection;
|
||||||
|
|
||||||
|
/** @brief Listen Only connection data */
|
||||||
|
typedef struct {
|
||||||
|
unsigned int output_assembly; /**< the O-to-T point for the connection */
|
||||||
|
unsigned int input_assembly; /**< the T-to-O point for the connection */
|
||||||
|
unsigned int config_assembly; /**< the config point for the connection */
|
||||||
|
CipConnectionObject connection_data[
|
||||||
|
OPENER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH
|
||||||
|
]; /**< the connection data */
|
||||||
|
} ListenOnlyConnection;
|
||||||
|
|
||||||
|
ExclusiveOwnerConnection g_exlusive_owner_connections[
|
||||||
|
OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS]; /**< Exclusive Owner connections */
|
||||||
|
|
||||||
|
InputOnlyConnection g_input_only_connections[OPENER_CIP_NUM_INPUT_ONLY_CONNS]; /**< Input Only connections */
|
||||||
|
|
||||||
|
ListenOnlyConnection g_listen_only_connections[OPENER_CIP_NUM_LISTEN_ONLY_CONNS]; /**< Listen Only connections */
|
||||||
|
|
||||||
|
/** @brief Takes an ConnectionObject and searches and returns an Exclusive Owner Connection based on the ConnectionObject,
|
||||||
|
* if there is non it returns NULL
|
||||||
|
*
|
||||||
|
* @param connection_object Connection Object which will be searched for in the Exclusive Owner Connections
|
||||||
|
* @param extended_error Pointer to the extended error variable, will be written if an error occurs
|
||||||
|
* @return The corresponding Exclusive Owner Connection or NULL if there is non
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetExclusiveOwnerConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
/** @brief Takes an ConnectionObject and searches and returns an Input Only Connection based on the ConnectionObject,
|
||||||
|
* if there is non it returns NULL
|
||||||
|
*
|
||||||
|
* @param connection_object Connection Object which will be searched for in the Input Only Connections
|
||||||
|
* @param extended_error Pointer to the extended error variable, will be written if an error occurs
|
||||||
|
* @return The corresponding Exclusive Owner Connection or NULL if there is non
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetInputOnlyConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
/** @brief Takes an ConnectionObject and searches and returns an Listen Only Connection based on the ConnectionObject,
|
||||||
|
* if there is non it returns NULL
|
||||||
|
*
|
||||||
|
* @param connection_object Connection Object which will be searched for in the Listen Only Connections
|
||||||
|
* @param extended_error Pointer to the extended error variable, will be written if an error occurs
|
||||||
|
* @return The corresponding Exclusive Owner Connection or NULL if there is non
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetListenOnlyConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
void ConfigureExclusiveOwnerConnectionPoint(
|
||||||
|
const unsigned int connection_number,
|
||||||
|
const unsigned int output_assembly,
|
||||||
|
const unsigned int input_assembly,
|
||||||
|
const unsigned int config_assembly) {
|
||||||
|
if (OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS > connection_number) {
|
||||||
|
g_exlusive_owner_connections[connection_number].output_assembly =
|
||||||
|
output_assembly;
|
||||||
|
g_exlusive_owner_connections[connection_number].input_assembly =
|
||||||
|
input_assembly;
|
||||||
|
g_exlusive_owner_connections[connection_number].config_assembly =
|
||||||
|
config_assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureInputOnlyConnectionPoint(const unsigned int connection_number,
|
||||||
|
const unsigned int output_assembly,
|
||||||
|
const unsigned int input_assembly,
|
||||||
|
const unsigned int config_assembly) {
|
||||||
|
if (OPENER_CIP_NUM_INPUT_ONLY_CONNS > connection_number) {
|
||||||
|
g_input_only_connections[connection_number].output_assembly =
|
||||||
|
output_assembly;
|
||||||
|
g_input_only_connections[connection_number].input_assembly = input_assembly;
|
||||||
|
g_input_only_connections[connection_number].config_assembly =
|
||||||
|
config_assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureListenOnlyConnectionPoint(const unsigned int connection_number,
|
||||||
|
const unsigned int output_assembly,
|
||||||
|
const unsigned int input_assembly,
|
||||||
|
const unsigned int config_assembly) {
|
||||||
|
if (OPENER_CIP_NUM_LISTEN_ONLY_CONNS > connection_number) {
|
||||||
|
g_listen_only_connections[connection_number].output_assembly =
|
||||||
|
output_assembly;
|
||||||
|
g_listen_only_connections[connection_number].input_assembly =
|
||||||
|
input_assembly;
|
||||||
|
g_listen_only_connections[connection_number].config_assembly =
|
||||||
|
config_assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetIoConnectionForConnectionData(
|
||||||
|
CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error) {
|
||||||
|
|
||||||
|
*extended_error = 0;
|
||||||
|
|
||||||
|
CipConnectionObject *io_connection = GetExclusiveOwnerConnection(
|
||||||
|
connection_object,
|
||||||
|
extended_error);
|
||||||
|
if (NULL == io_connection) {
|
||||||
|
if (kConnectionManagerExtendedStatusCodeSuccess == *extended_error) {
|
||||||
|
/* we found no connection and don't have an error so try input only next */
|
||||||
|
io_connection = GetInputOnlyConnection(connection_object, extended_error);
|
||||||
|
if (NULL == io_connection) {
|
||||||
|
if (kConnectionManagerExtendedStatusCodeSuccess == *extended_error) {
|
||||||
|
/* we found no connection and don't have an error so try listen only next */
|
||||||
|
io_connection = GetListenOnlyConnection(connection_object,
|
||||||
|
extended_error);
|
||||||
|
if ( (NULL == io_connection) &&
|
||||||
|
(kCipErrorSuccess == *extended_error) ) {
|
||||||
|
/* no application connection type was found that suits the given data */
|
||||||
|
*extended_error =
|
||||||
|
kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
|
||||||
|
} else {
|
||||||
|
ConnectionObjectSetInstanceType(connection_object,
|
||||||
|
kConnectionObjectInstanceTypeIOListenOnly);
|
||||||
|
OPENER_TRACE_INFO("IO Listen only connection requested\n");
|
||||||
|
//Is listen only connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConnectionObjectSetInstanceType(connection_object,
|
||||||
|
kConnectionObjectInstanceTypeIOInputOnly);
|
||||||
|
OPENER_TRACE_INFO("IO Input only connection requested\n");
|
||||||
|
//is Input only connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConnectionObjectSetInstanceType(connection_object,
|
||||||
|
kConnectionObjectInstanceTypeIOExclusiveOwner);
|
||||||
|
OPENER_TRACE_INFO("IO Exclusive Owner connection requested\n");
|
||||||
|
//Is exclusive owner connection
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL != io_connection) {
|
||||||
|
ConnectionObjectDeepCopy(io_connection, connection_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return io_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetExclusiveOwnerConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS; ++i) {
|
||||||
|
if ( (g_exlusive_owner_connections[i].output_assembly ==
|
||||||
|
connection_object->consumed_path.instance_id)
|
||||||
|
&& (g_exlusive_owner_connections[i].input_assembly ==
|
||||||
|
connection_object->produced_path.instance_id)
|
||||||
|
&& (g_exlusive_owner_connections[i].config_assembly ==
|
||||||
|
connection_object->configuration_path.instance_id) ) {
|
||||||
|
|
||||||
|
/* check if on other connection point with the same output assembly is currently connected */
|
||||||
|
const CipConnectionObject *const exclusive_owner =
|
||||||
|
GetConnectedOutputAssembly(
|
||||||
|
connection_object->produced_path.instance_id);
|
||||||
|
if ( NULL
|
||||||
|
!= exclusive_owner ) {
|
||||||
|
if(kConnectionObjectStateEstablished ==
|
||||||
|
ConnectionObjectGetState(exclusive_owner) ) {
|
||||||
|
*extended_error =
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorOwnershipConflict;
|
||||||
|
OPENER_TRACE_INFO("Hit an Ownership conflict in appcontype.c:198\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(kConnectionObjectStateTimedOut ==
|
||||||
|
ConnectionObjectGetState(exclusive_owner)
|
||||||
|
&& ConnectionObjectEqualOriginator(connection_object,
|
||||||
|
exclusive_owner) ) {
|
||||||
|
g_exlusive_owner_connections[i].connection_data.
|
||||||
|
connection_close_function(&(g_exlusive_owner_connections[i].
|
||||||
|
connection_data) );
|
||||||
|
return &(g_exlusive_owner_connections[i].connection_data);
|
||||||
|
} else {
|
||||||
|
*extended_error =
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorOwnershipConflict;
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"Hit an Ownership conflict with timed out connection");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &(g_exlusive_owner_connections[i].connection_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetInputOnlyConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error) {
|
||||||
|
EipUint16 err = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OPENER_CIP_NUM_INPUT_ONLY_CONNS; ++i) {
|
||||||
|
if (g_input_only_connections[i].output_assembly
|
||||||
|
== connection_object->consumed_path.instance_id) { /* we have the same output assembly */
|
||||||
|
if (g_input_only_connections[i].input_assembly
|
||||||
|
!= connection_object->produced_path.instance_id) {
|
||||||
|
err = kConnectionManagerExtendedStatusCodeInvalidProducingApplicationPath;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g_input_only_connections[i].config_assembly
|
||||||
|
!= connection_object->configuration_path.instance_id) {
|
||||||
|
err = kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < OPENER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH;
|
||||||
|
++j) {
|
||||||
|
if (kConnectionObjectStateTimedOut
|
||||||
|
== ConnectionObjectGetState(&(g_input_only_connections[i].
|
||||||
|
connection_data[j]) )
|
||||||
|
&& ConnectionObjectEqualOriginator(connection_object,
|
||||||
|
&(g_input_only_connections[i].
|
||||||
|
connection_data[j]))) {
|
||||||
|
g_input_only_connections[i].connection_data[j].
|
||||||
|
connection_close_function(
|
||||||
|
&g_input_only_connections[i].connection_data[j]);
|
||||||
|
return &(g_input_only_connections[i].connection_data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < OPENER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH;
|
||||||
|
++j) {
|
||||||
|
if (kConnectionObjectStateNonExistent
|
||||||
|
== ConnectionObjectGetState(&(g_input_only_connections[i].
|
||||||
|
connection_data[j]) ) ) {
|
||||||
|
return &(g_input_only_connections[i].connection_data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = kConnectionManagerExtendedStatusCodeTargetObjectOutOfConnections;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*extended_error = err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetListenOnlyConnection(
|
||||||
|
const CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error) {
|
||||||
|
EipUint16 err = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OPENER_CIP_NUM_LISTEN_ONLY_CONNS; i++) {
|
||||||
|
if (g_listen_only_connections[i].output_assembly
|
||||||
|
== connection_object->consumed_path.instance_id) { /* we have the same output assembly */
|
||||||
|
if (g_listen_only_connections[i].input_assembly
|
||||||
|
!= connection_object->produced_path.instance_id) {
|
||||||
|
err = kConnectionManagerExtendedStatusCodeInvalidProducingApplicationPath;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g_listen_only_connections[i].config_assembly
|
||||||
|
!= connection_object->configuration_path.instance_id) {
|
||||||
|
err = kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here we look for both Point-to-Point and Multicast IO connections */
|
||||||
|
if ( NULL == GetExistingProducerIoConnection(false,
|
||||||
|
connection_object->
|
||||||
|
produced_path.instance_id)) {
|
||||||
|
err = kConnectionManagerExtendedStatusCodeNonListenOnlyConnectionNotOpened;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < OPENER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH;
|
||||||
|
++j) {
|
||||||
|
if (kConnectionObjectStateTimedOut
|
||||||
|
== ConnectionObjectGetState(&(g_listen_only_connections[i].
|
||||||
|
connection_data[j]) )
|
||||||
|
&& ConnectionObjectEqualOriginator(connection_object,
|
||||||
|
&(g_listen_only_connections[i].
|
||||||
|
connection_data[j]))) {
|
||||||
|
g_listen_only_connections[i].connection_data[j].
|
||||||
|
connection_close_function(
|
||||||
|
&g_listen_only_connections[i].connection_data[j]);
|
||||||
|
return &(g_listen_only_connections[i].connection_data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < OPENER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH;
|
||||||
|
j++) {
|
||||||
|
if (kConnectionObjectStateNonExistent
|
||||||
|
== ConnectionObjectGetState(&(g_listen_only_connections[i].
|
||||||
|
connection_data[j]) ) ) {
|
||||||
|
return &(g_listen_only_connections[i].connection_data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = kConnectionManagerExtendedStatusCodeTargetObjectOutOfConnections;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*extended_error = err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetExistingProducerIoConnection(
|
||||||
|
const bool multicast_only,
|
||||||
|
const EipUint32 input_point) {
|
||||||
|
const DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
|
||||||
|
while (NULL != node) {
|
||||||
|
CipConnectionObject *producer_io_connection = node->data;
|
||||||
|
if (ConnectionObjectIsTypeIOConnection(producer_io_connection) &&
|
||||||
|
(input_point == producer_io_connection->produced_path.instance_id) &&
|
||||||
|
(kEipInvalidSocket !=
|
||||||
|
producer_io_connection->socket[kUdpCommuncationDirectionProducing]) )
|
||||||
|
{
|
||||||
|
ConnectionObjectConnectionType cnxn_type =
|
||||||
|
ConnectionObjectGetTToOConnectionType(producer_io_connection);
|
||||||
|
/* we have a connection that produces the same input assembly,
|
||||||
|
* and manages the connection.
|
||||||
|
*/
|
||||||
|
if (kConnectionObjectConnectionTypeMulticast == cnxn_type) {
|
||||||
|
return producer_io_connection;
|
||||||
|
}
|
||||||
|
if (!multicast_only &&
|
||||||
|
kConnectionObjectConnectionTypePointToPoint == cnxn_type)
|
||||||
|
{
|
||||||
|
return producer_io_connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *GetNextNonControlMasterConnection(
|
||||||
|
const EipUint32 input_point) {
|
||||||
|
const DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
|
||||||
|
while (NULL != node) {
|
||||||
|
CipConnectionObject *next_non_control_master_connection =
|
||||||
|
node->data;
|
||||||
|
if ( true ==
|
||||||
|
ConnectionObjectIsTypeNonLOIOConnection(
|
||||||
|
next_non_control_master_connection)
|
||||||
|
&& kConnectionObjectStateEstablished ==
|
||||||
|
ConnectionObjectGetState(next_non_control_master_connection)
|
||||||
|
&& input_point ==
|
||||||
|
next_non_control_master_connection->produced_path.instance_id
|
||||||
|
&& kConnectionObjectConnectionTypeMulticast ==
|
||||||
|
ConnectionObjectGetTToOConnectionType(
|
||||||
|
next_non_control_master_connection)
|
||||||
|
&& (kEipInvalidSocket ==
|
||||||
|
next_non_control_master_connection->socket[
|
||||||
|
kUdpCommuncationDirectionProducing
|
||||||
|
]) ) {
|
||||||
|
/* we have a connection that produces the same input assembly,
|
||||||
|
* is a multicast producer and does not manage the connection.
|
||||||
|
*/
|
||||||
|
return next_non_control_master_connection;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point,
|
||||||
|
const ConnectionObjectInstanceType instance_type)
|
||||||
|
{
|
||||||
|
|
||||||
|
OPENER_TRACE_INFO("Close all instance type %d only connections\n",
|
||||||
|
instance_type);
|
||||||
|
const DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
while (NULL != node) {
|
||||||
|
CipConnectionObject *const connection = node->data;
|
||||||
|
node = node->next;
|
||||||
|
if ( (instance_type == ConnectionObjectGetInstanceType(connection) )
|
||||||
|
&& (input_point == connection->produced_path.instance_id) ) {
|
||||||
|
CipConnectionObject *connection_to_delete = connection;
|
||||||
|
CheckIoConnectionEvent(
|
||||||
|
connection_to_delete->consumed_path.instance_id,
|
||||||
|
connection_to_delete->produced_path.instance_id,
|
||||||
|
kIoConnectionEventClosed);
|
||||||
|
|
||||||
|
assert(connection_to_delete->connection_close_function != NULL);
|
||||||
|
connection_to_delete->connection_close_function(connection_to_delete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseAllConnections(void) {
|
||||||
|
const DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
while (NULL != node) {
|
||||||
|
CipConnectionObject *const connection = node->data;
|
||||||
|
assert(connection->connection_close_function != NULL);
|
||||||
|
connection->connection_close_function(connection);
|
||||||
|
node = connection_list.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionWithSameConfigPointExists(const EipUint32 config_point) {
|
||||||
|
const DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
|
||||||
|
while (NULL != node) {
|
||||||
|
const CipConnectionObject *const connection = node->data;
|
||||||
|
OPENER_ASSERT(NULL != connection);
|
||||||
|
if (config_point == connection->configuration_path.instance_id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeIoConnectionData(void) {
|
||||||
|
memset( g_exlusive_owner_connections, 0,
|
||||||
|
OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS *
|
||||||
|
sizeof(ExclusiveOwnerConnection) );
|
||||||
|
memset( g_input_only_connections, 0,
|
||||||
|
OPENER_CIP_NUM_INPUT_ONLY_CONNS * sizeof(InputOnlyConnection) );
|
||||||
|
memset( g_listen_only_connections, 0,
|
||||||
|
OPENER_CIP_NUM_LISTEN_ONLY_CONNS * sizeof(ListenOnlyConnection) );
|
||||||
|
}
|
||||||
76
OrginalSourceRepo/OpENer-master/source/src/cip/appcontype.h
Normal file
76
OrginalSourceRepo/OpENer-master/source/src/cip/appcontype.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_APPCONTYPE_H_
|
||||||
|
#define OPENER_APPCONTYPE_H_
|
||||||
|
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
|
||||||
|
void InitializeIoConnectionData(void);
|
||||||
|
|
||||||
|
/** @brief check if for the given connection data received in a forward_open request
|
||||||
|
* a suitable connection is available.
|
||||||
|
*
|
||||||
|
* If a suitable connection is found the connection data is transfered the
|
||||||
|
* application connection type is set (i.e., EConnType).
|
||||||
|
* @param connection_object connection data to be used
|
||||||
|
* @param extended_error pointer to the extended_error variable, if an error occurred this value has the according
|
||||||
|
* error code for the response
|
||||||
|
* @return
|
||||||
|
* - on success: A pointer to the connection object already containing the connection
|
||||||
|
* data given in connection_object.
|
||||||
|
* - on error: NULL
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetIoConnectionForConnectionData(
|
||||||
|
CipConnectionObject *const RESTRICT connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
/** @brief Check if there exists already an exclusive owner or listen only connection
|
||||||
|
* which produces the input assembly.
|
||||||
|
*
|
||||||
|
* @param multicast_only Look only for multi-cast connections
|
||||||
|
* @param input_point the Input point to be produced
|
||||||
|
* @return a pointer to the found connection; NULL if nothing found
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetExistingProducerIoConnection(
|
||||||
|
const bool multicast_only,
|
||||||
|
const EipUint32 input_point);
|
||||||
|
|
||||||
|
/** @brief check if there exists an producing multicast exclusive owner or
|
||||||
|
* listen only connection that should produce the same input but is not in charge
|
||||||
|
* of the connection.
|
||||||
|
*
|
||||||
|
* @param input_point the produced input
|
||||||
|
* @return if a connection could be found the pointer to this connection
|
||||||
|
* otherwise NULL.
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetNextNonControlMasterConnection(
|
||||||
|
const EipUint32 input_point);
|
||||||
|
|
||||||
|
/** @brief Close all connection producing the same input and have the same type
|
||||||
|
* (i.e., listen only or input only).
|
||||||
|
*
|
||||||
|
* @param input_point the input point
|
||||||
|
* @param instance_type the connection application type
|
||||||
|
*/
|
||||||
|
void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point,
|
||||||
|
const ConnectionObjectInstanceType instance_type);
|
||||||
|
|
||||||
|
/**@ brief close all open connections.
|
||||||
|
*
|
||||||
|
* For I/O connections the sockets will be freed. The sockets for explicit
|
||||||
|
* connections are handled by the encapsulation layer, and freed there.
|
||||||
|
*/
|
||||||
|
void CloseAllConnections(void);
|
||||||
|
|
||||||
|
/** @brief Check if there is an established connection that uses the same
|
||||||
|
* config point.
|
||||||
|
*
|
||||||
|
* @param config_point The configuration point
|
||||||
|
* @return true if connection was found, otherwise false
|
||||||
|
*/
|
||||||
|
bool ConnectionWithSameConfigPointExists(const EipUint32 config_point);
|
||||||
|
|
||||||
|
#endif /* OPENER_APPCONTYPE_H_ */
|
||||||
227
OrginalSourceRepo/OpENer-master/source/src/cip/cipassembly.c
Normal file
227
OrginalSourceRepo/OpENer-master/source/src/cip/cipassembly.c
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "cipassembly.h"
|
||||||
|
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
|
||||||
|
/** @brief Retrieve the given data according to CIP encoding from the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* Implementation of the decode function for the SetAttributeSingle CIP service for Assembly
|
||||||
|
* Objects.
|
||||||
|
* Currently only supports Attribute 3 (CIP_BYTE_ARRAY) of an Assembly
|
||||||
|
* @param data pointer to value to be written.
|
||||||
|
* @param message_router_request pointer to the request where the data should be taken from
|
||||||
|
* @param message_router_response pointer to the response where status should be set
|
||||||
|
* @return length of taken bytes
|
||||||
|
* -1 .. error
|
||||||
|
*/
|
||||||
|
int DecodeCipAssemblyAttribute3(void *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response);
|
||||||
|
|
||||||
|
static EipStatus AssemblyPreGetCallback(CipInstance *const instance,
|
||||||
|
CipAttributeStruct *const attribute,
|
||||||
|
CipByte service);
|
||||||
|
|
||||||
|
static EipStatus AssemblyPostSetCallback(CipInstance *const instance,
|
||||||
|
CipAttributeStruct *const attribute,
|
||||||
|
CipByte service);
|
||||||
|
|
||||||
|
/** @brief Constructor for the assembly object class
|
||||||
|
*
|
||||||
|
* Creates an initializes Assembly class or object instances
|
||||||
|
* @return Pointer to the created Assembly object
|
||||||
|
*/
|
||||||
|
CipClass *CreateAssemblyClass(void) {
|
||||||
|
/* create the CIP Assembly object with zero instances */
|
||||||
|
CipClass *assembly_class = CreateCipClass(kCipAssemblyClassCode, 0, /* # class attributes*/
|
||||||
|
7, /* # highest class attribute number*/
|
||||||
|
1, /* # class services*/
|
||||||
|
2, /* # instance attributes*/
|
||||||
|
4, /* # highest instance attribute number*/
|
||||||
|
2, /* # instance services*/
|
||||||
|
0, /* # instances*/
|
||||||
|
"assembly", /* name */
|
||||||
|
2, /* Revision, according to the CIP spec currently this has to be 2 */
|
||||||
|
NULL); /* # function pointer for initialization*/
|
||||||
|
if(NULL != assembly_class) {
|
||||||
|
InsertService(assembly_class,
|
||||||
|
kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
|
||||||
|
InsertService(assembly_class,
|
||||||
|
kSetAttributeSingle,
|
||||||
|
&SetAttributeSingle,
|
||||||
|
"SetAttributeSingle");
|
||||||
|
|
||||||
|
InsertGetSetCallback(assembly_class, AssemblyPreGetCallback, kPreGetFunc);
|
||||||
|
InsertGetSetCallback(assembly_class, AssemblyPostSetCallback, kPostSetFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return assembly_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief create the CIP Assembly object with zero instances
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
EipStatus CipAssemblyInitialize(void) {
|
||||||
|
return ( NULL != CreateAssemblyClass() ) ? kEipStatusOk : kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutdownAssemblies(void) {
|
||||||
|
const CipClass *const assembly_class = GetCipClass(kCipAssemblyClassCode);
|
||||||
|
|
||||||
|
if(NULL != assembly_class) {
|
||||||
|
const CipInstance *instance = assembly_class->instances;
|
||||||
|
while(NULL != instance) {
|
||||||
|
const CipAttributeStruct *const attribute = GetCipAttribute(instance, 3);
|
||||||
|
if(NULL != attribute) {
|
||||||
|
CipFree(attribute->data);
|
||||||
|
}
|
||||||
|
instance = instance->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipInstance *CreateAssemblyObject(const CipInstanceNum instance_id,
|
||||||
|
EipByte *const data,
|
||||||
|
const EipUint16 data_length) {
|
||||||
|
CipClass *assembly_class = GetCipClass(kCipAssemblyClassCode);
|
||||||
|
if(NULL == assembly_class) {
|
||||||
|
assembly_class = CreateAssemblyClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(NULL == assembly_class) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipInstance *const instance = AddCipInstance(assembly_class, instance_id); /* add instances (always succeeds (or asserts))*/
|
||||||
|
|
||||||
|
CipByteArray *const assembly_byte_array = (CipByteArray *) CipCalloc(1,
|
||||||
|
sizeof(
|
||||||
|
CipByteArray) );
|
||||||
|
if(assembly_byte_array == NULL) {
|
||||||
|
return NULL; /*TODO remove assembly instance in case of error*/
|
||||||
|
}
|
||||||
|
|
||||||
|
assembly_byte_array->length = data_length;
|
||||||
|
assembly_byte_array->data = data;
|
||||||
|
|
||||||
|
InsertAttribute(instance,
|
||||||
|
3,
|
||||||
|
kCipByteArray,
|
||||||
|
EncodeCipByteArray,
|
||||||
|
DecodeCipAssemblyAttribute3,
|
||||||
|
assembly_byte_array,
|
||||||
|
kSetAndGetAble | kPreGetFunc | kPostSetFunc);
|
||||||
|
/* Attribute 4 Number of bytes in Attribute 3 */
|
||||||
|
|
||||||
|
InsertAttribute(instance, 4, kCipUint, EncodeCipUint,
|
||||||
|
NULL, &(assembly_byte_array->length), kGetableSingle);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance,
|
||||||
|
const EipUint8 *const data,
|
||||||
|
const size_t data_length) {
|
||||||
|
/* empty path (path size = 0) need to be checked and taken care of in future */
|
||||||
|
/* copy received data to Attribute 3 */
|
||||||
|
const CipByteArray *const assembly_byte_array =
|
||||||
|
(CipByteArray *) instance->attributes->data;
|
||||||
|
if(assembly_byte_array->length != data_length) {
|
||||||
|
OPENER_TRACE_ERR("wrong amount of data arrived for assembly object\n");
|
||||||
|
return kEipStatusError; /*TODO question should we notify the application that wrong data has been received???*/
|
||||||
|
} else {
|
||||||
|
memcpy(assembly_byte_array->data, data, data_length);
|
||||||
|
/* call the application that new data arrived */
|
||||||
|
}
|
||||||
|
|
||||||
|
return AfterAssemblyDataReceived(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DecodeCipAssemblyAttribute3(void *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response)
|
||||||
|
{
|
||||||
|
CipInstance *const instance =
|
||||||
|
GetCipInstance(GetCipClass(
|
||||||
|
message_router_request->request_path.class_id),
|
||||||
|
message_router_request->request_path.instance_number);
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
OPENER_TRACE_INFO(" -> set Assembly attribute byte array\r\n");
|
||||||
|
CipByteArray *cip_byte_array = (CipByteArray *)data;
|
||||||
|
|
||||||
|
if(message_router_request->request_data_size < cip_byte_array->length) {
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"DecodeCipByteArray: not enough data received.\n");
|
||||||
|
message_router_response->general_status = kCipErrorNotEnoughData;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
if(message_router_request->request_data_size > cip_byte_array->length) {
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"DecodeCipByteArray: too much data received.\n");
|
||||||
|
message_router_response->general_status = kCipErrorTooMuchData;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data-length is correct
|
||||||
|
memcpy(cip_byte_array->data,
|
||||||
|
message_router_request->data,
|
||||||
|
cip_byte_array->length);
|
||||||
|
|
||||||
|
if(AfterAssemblyDataReceived(instance) != kEipStatusOk) {
|
||||||
|
/* punt early without updating the status... though I don't know
|
||||||
|
* how much this helps us here, as the attribute's data has already
|
||||||
|
* been overwritten.
|
||||||
|
*
|
||||||
|
* however this is the task of the application side which will
|
||||||
|
* take the data. In addition we have to inform the sender that the
|
||||||
|
* data was not ok.
|
||||||
|
*/
|
||||||
|
message_router_response->general_status = kCipErrorInvalidAttributeValue;
|
||||||
|
} else {
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
number_of_decoded_bytes = cip_byte_array->length;
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EipStatus AssemblyPreGetCallback(CipInstance *const instance,
|
||||||
|
CipAttributeStruct *const attribute,
|
||||||
|
CipByte service) {
|
||||||
|
int rc;
|
||||||
|
(void) attribute;
|
||||||
|
(void) service; /* no unused parameter warnings */
|
||||||
|
|
||||||
|
rc = BeforeAssemblyDataSend(instance);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EipStatus AssemblyPostSetCallback(CipInstance *const instance,
|
||||||
|
CipAttributeStruct *const attribute,
|
||||||
|
CipByte service) {
|
||||||
|
int rc;
|
||||||
|
(void) attribute;
|
||||||
|
(void) service; /* no unused parameter warnings */
|
||||||
|
|
||||||
|
rc = AfterAssemblyDataReceived(instance);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
61
OrginalSourceRepo/OpENer-master/source/src/cip/cipassembly.h
Normal file
61
OrginalSourceRepo/OpENer-master/source/src/cip/cipassembly.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPASSEMBLY_H_
|
||||||
|
#define OPENER_CIPASSEMBLY_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief Assembly class code */
|
||||||
|
static const CipUint kCipAssemblyClassCode = 0x04U;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Assembly object instance attribute IDs.
|
||||||
|
*
|
||||||
|
* Reference:
|
||||||
|
* \cite CipVol1, Table 5-5.4
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
kAssemblyObjectInstanceAttributeIdData = 3
|
||||||
|
} AssemblyObjectInstanceAttributeId;
|
||||||
|
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
|
||||||
|
/** @brief Setup the Assembly object
|
||||||
|
*
|
||||||
|
* Creates the Assembly Class with zero instances and sets up all services.
|
||||||
|
*
|
||||||
|
* @return Returns kEipStatusOk if assembly object was successfully created, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus CipAssemblyInitialize(void);
|
||||||
|
|
||||||
|
/** @brief clean up the data allocated in the assembly object instances
|
||||||
|
*
|
||||||
|
* Assembly object instances allocate per instance data to store attribute 3.
|
||||||
|
* This will be freed here. The assembly object data given by the application
|
||||||
|
* is not freed neither the assembly object instances. These are handled in the
|
||||||
|
* main shutdown function.
|
||||||
|
*/
|
||||||
|
void ShutdownAssemblies(void);
|
||||||
|
|
||||||
|
/** @brief notify an Assembly object that data has been received for it.
|
||||||
|
*
|
||||||
|
* The data will be copied into the assembly objects attribute 3 and
|
||||||
|
* the application will be informed with the AfterAssemblyDataReceived function.
|
||||||
|
*
|
||||||
|
* @param instance the assembly object instance for which the data was received
|
||||||
|
* @param data pointer to the data received
|
||||||
|
* @param data_length number of bytes received
|
||||||
|
* @return
|
||||||
|
* - kEipStatusOk the received data was okay
|
||||||
|
* - kEipStatusError the received data was wrong
|
||||||
|
*/
|
||||||
|
EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance,
|
||||||
|
const EipUint8 *const data,
|
||||||
|
const size_t data_length);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPASSEMBLY_H_ */
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cipclass3connection.h"
|
||||||
|
|
||||||
|
#include "encap.h"
|
||||||
|
|
||||||
|
/**** Global variables ****/
|
||||||
|
extern CipConnectionObject explicit_connection_object_pool[
|
||||||
|
OPENER_CIP_NUM_EXPLICIT_CONNS];
|
||||||
|
|
||||||
|
CipConnectionObject *GetFreeExplicitConnection(void);
|
||||||
|
|
||||||
|
void Class3ConnectionTimeoutHandler(CipConnectionObject *connection_object) {
|
||||||
|
CheckForTimedOutConnectionsAndCloseTCPConnections(connection_object,
|
||||||
|
CloseSessionBySessionHandle);
|
||||||
|
CloseConnection(connection_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**** Implementation ****/
|
||||||
|
CipError EstablishClass3Connection(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
EipUint16 *const extended_error) {
|
||||||
|
CipError cip_error = kCipErrorSuccess;
|
||||||
|
|
||||||
|
CipConnectionObject *explicit_connection = GetFreeExplicitConnection();
|
||||||
|
|
||||||
|
if (NULL == explicit_connection) {
|
||||||
|
cip_error = kCipErrorConnectionFailure;
|
||||||
|
*extended_error =
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorNoMoreConnectionsAvailable;
|
||||||
|
} else {
|
||||||
|
ConnectionObjectDeepCopy(explicit_connection, connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectGeneralConfiguration(explicit_connection);
|
||||||
|
|
||||||
|
ConnectionObjectSetInstanceType(explicit_connection,
|
||||||
|
kConnectionObjectInstanceTypeExplicitMessaging);
|
||||||
|
|
||||||
|
/* set the connection call backs */
|
||||||
|
explicit_connection->connection_close_function =
|
||||||
|
CloseConnection;
|
||||||
|
/* explicit connection have to be closed on time out*/
|
||||||
|
explicit_connection->connection_timeout_function =
|
||||||
|
Class3ConnectionTimeoutHandler;
|
||||||
|
|
||||||
|
AddNewActiveConnection(explicit_connection);
|
||||||
|
}
|
||||||
|
return cip_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Searches and returns a free explicit connection slot
|
||||||
|
*
|
||||||
|
* @return Free explicit connection slot, or NULL if no slot is free
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetFreeExplicitConnection(void) {
|
||||||
|
for (size_t i = 0; i < OPENER_CIP_NUM_EXPLICIT_CONNS; ++i) {
|
||||||
|
if (ConnectionObjectGetState(&(explicit_connection_object_pool[i]) ) ==
|
||||||
|
kConnectionObjectStateNonExistent) {
|
||||||
|
return &(explicit_connection_object_pool[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeClass3ConnectionData(void) {
|
||||||
|
memset( explicit_connection_object_pool, 0,
|
||||||
|
OPENER_CIP_NUM_EXPLICIT_CONNS * sizeof(CipConnectionObject) );
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus CipClass3ConnectionObjectStateEstablishedHandler(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
ConnectionObjectState new_state) {
|
||||||
|
switch(new_state) {
|
||||||
|
case kConnectionObjectStateNonExistent:
|
||||||
|
ConnectionObjectInitializeEmpty(connection_object);
|
||||||
|
ConnectionObjectSetState(connection_object, new_state);
|
||||||
|
return kEipStatusOk;
|
||||||
|
default: return kEipStatusError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus CipClass3ConnectionObjectStateNonExistentHandler(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
ConnectionObjectState new_state) {
|
||||||
|
switch(new_state) {
|
||||||
|
case kConnectionObjectStateEstablished:
|
||||||
|
ConnectionObjectSetState(connection_object, new_state);
|
||||||
|
return kEipStatusOk;
|
||||||
|
default: return kEipStatusError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENER_CIPCLASS3CONNECTION_H_
|
||||||
|
#define OPENER_CIPCLASS3CONNECTION_H_
|
||||||
|
|
||||||
|
/** @file cipclass3connection.h
|
||||||
|
* @brief CIP Class 3 connection
|
||||||
|
* * Explicit Connection Object State Transition Diagram
|
||||||
|
* ----------------------------------------------
|
||||||
|
* @dot
|
||||||
|
* digraph ExplicitConnectionObjectStateTransition {
|
||||||
|
* A[label="Any State"]
|
||||||
|
* N[label="Non-existent"]
|
||||||
|
* D[label="Deferred Delete"]
|
||||||
|
* E[label="Established"]
|
||||||
|
*
|
||||||
|
* A->N [label="Delete"]
|
||||||
|
* N->E [label="Open Explicit Messaging Connection Response"]
|
||||||
|
* E->N [label="Delete or inactivity time-out"]
|
||||||
|
* E->E [label="Get/Set/Apply Attribute, Reset, Message Produced/Consumed"]
|
||||||
|
* E->D [label="Inactivity time-out and deferred delete set"]
|
||||||
|
* D->N [label="Delete"]
|
||||||
|
* }
|
||||||
|
* @enddot
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
|
||||||
|
typedef EipStatus (*CipConnectionStateHandler)(CipConnectionObject *RESTRICT
|
||||||
|
const connection_object,
|
||||||
|
ConnectionObjectState new_state);
|
||||||
|
|
||||||
|
EipStatus CipClass3ConnectionObjectStateEstablishedHandler(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
ConnectionObjectState new_state);
|
||||||
|
|
||||||
|
/** @brief Check if Class3 connection is available and if yes setup all data.
|
||||||
|
*
|
||||||
|
* This function can be called after all data has been parsed from the forward open request
|
||||||
|
* @param connection_object pointer to the connection object structure holding the parsed data from the forward open request
|
||||||
|
* @param extended_error the extended error code in case an error happened
|
||||||
|
* @return general status on the establishment
|
||||||
|
* - kEipStatusOk ... on success
|
||||||
|
* - On an error the general status code to be put into the response
|
||||||
|
*/
|
||||||
|
CipError EstablishClass3Connection(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
/** @brief Initializes the explicit connections mechanism
|
||||||
|
*
|
||||||
|
* Prepares the available explicit connection slots for use at the start of the OpENer
|
||||||
|
*/
|
||||||
|
void InitializeClass3ConnectionData(void);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPCLASS3CONNECTION_H_ */
|
||||||
1623
OrginalSourceRepo/OpENer-master/source/src/cip/cipcommon.c
Normal file
1623
OrginalSourceRepo/OpENer-master/source/src/cip/cipcommon.c
Normal file
File diff suppressed because it is too large
Load diff
224
OrginalSourceRepo/OpENer-master/source/src/cip/cipcommon.h
Normal file
224
OrginalSourceRepo/OpENer-master/source/src/cip/cipcommon.h
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPCOMMON_H_
|
||||||
|
#define OPENER_CIPCOMMON_H_
|
||||||
|
|
||||||
|
/** @file cipcommon.h
|
||||||
|
* Common CIP object interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
static const EipUint16 kCipUintZero = 0; /**< Zero value for returning the UINT standard value */
|
||||||
|
|
||||||
|
/** @brief Check if requested service present in class/instance and call appropriate service.
|
||||||
|
*
|
||||||
|
* @param cip_class class receiving the message
|
||||||
|
* @param message_router_request request message
|
||||||
|
* @param message_router_response reply message
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return
|
||||||
|
* - kEipStatusOkSend ... success
|
||||||
|
* - kEipStatusOk ... no reply to send back
|
||||||
|
* - kEipStatusError ... error
|
||||||
|
*/
|
||||||
|
EipStatus NotifyClass(const CipClass *const RESTRICT cip_class,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Get largest instance_number present in class instances
|
||||||
|
*
|
||||||
|
* @param cip_class class to be considered
|
||||||
|
* @return largest instance_number in class instances
|
||||||
|
*/
|
||||||
|
CipUint GetMaxInstanceNumber(CipClass *RESTRICT const cip_class);
|
||||||
|
|
||||||
|
void GenerateGetAttributeSingleHeader(
|
||||||
|
const CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the GetAttributeSingle CIP service
|
||||||
|
*
|
||||||
|
* Check from classID which Object requests an attribute, search if object has
|
||||||
|
* the appropriate attribute implemented.
|
||||||
|
* @param instance pointer to instance.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return status >0 .. success
|
||||||
|
* -1 .. requested attribute not available
|
||||||
|
*/
|
||||||
|
EipStatus GetAttributeSingle(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
void GenerateSetAttributeSingleHeader(
|
||||||
|
const CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the SetAttributeSingle CIP service
|
||||||
|
*
|
||||||
|
* Modifies an attribute value if the requested object has
|
||||||
|
* the appropriate attribute implemented and if the attribute is settable.
|
||||||
|
*
|
||||||
|
* @param instance pointer to instance.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return status >0 .. success
|
||||||
|
* -1 .. requested attribute not set
|
||||||
|
*/
|
||||||
|
EipStatus SetAttributeSingle(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the GetAttributeAll CIP service
|
||||||
|
*
|
||||||
|
* Copy all attributes from Object into the global message buffer.
|
||||||
|
* @param instance pointer to object instance with data.
|
||||||
|
* @param message_router_request pointer to MR request.
|
||||||
|
* @param message_router_response pointer for MR response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return length of data stream >0 .. success
|
||||||
|
* 0 .. no reply to send
|
||||||
|
*/
|
||||||
|
EipStatus GetAttributeAll(CipInstance *instance,
|
||||||
|
CipMessageRouterRequest *message_router_request,
|
||||||
|
CipMessageRouterResponse *message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the GetAttributeList CIP service
|
||||||
|
*
|
||||||
|
* Copy the contents of the selected gettable attributes of the specified
|
||||||
|
* object class or instance into the global message buffer.
|
||||||
|
* @param instance pointer to object instance with data.
|
||||||
|
* @param message_router_request pointer to MR request.
|
||||||
|
* @param message_router_response pointer for MR response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return length of data stream >0 .. success
|
||||||
|
* 0 .. no reply to send
|
||||||
|
*/
|
||||||
|
EipStatus GetAttributeList(CipInstance *instance,
|
||||||
|
CipMessageRouterRequest *message_router_request,
|
||||||
|
CipMessageRouterResponse *message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the SetAttributeList CIP service
|
||||||
|
*
|
||||||
|
* Sets the values of selected attributes of the specified object class
|
||||||
|
* or instance.
|
||||||
|
* @param instance pointer to object instance with data.
|
||||||
|
* @param message_router_request pointer to MR request.
|
||||||
|
* @param message_router_response pointer to MR response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return length of data stream >0 .. success
|
||||||
|
* 0 .. no reply to send
|
||||||
|
*/
|
||||||
|
EipStatus SetAttributeList(CipInstance *instance,
|
||||||
|
CipMessageRouterRequest *message_router_request,
|
||||||
|
CipMessageRouterResponse *message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Decodes padded EPath
|
||||||
|
* @param epath EPath object to the receiving element
|
||||||
|
* @param message pointer to the message to decode
|
||||||
|
* @param[out] bytes_decoded Location to store the number of bytes consumed
|
||||||
|
* from the message buffer.
|
||||||
|
* @return @link EipStatus kEipStatusOk @endlink if successful, or
|
||||||
|
* @link EipStatus kEipStatusError @endlink if the path
|
||||||
|
* could not be parsed.
|
||||||
|
*/
|
||||||
|
EipStatus DecodePaddedEPath(CipEpath *epath,
|
||||||
|
const EipUint8 **message,
|
||||||
|
size_t *const bytes_decoded);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the CIP Create service
|
||||||
|
*
|
||||||
|
* Creates dynamically allocated object instance within the specified class.
|
||||||
|
*
|
||||||
|
* @param instance pointer to instance.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return status >0 .. success
|
||||||
|
* -1 .. requested instance not created
|
||||||
|
*/
|
||||||
|
EipStatus CipCreateService(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the CIP Delete service
|
||||||
|
*
|
||||||
|
* Deletes dynamically allocated object instance within the specified class
|
||||||
|
* and updates referred class attributes
|
||||||
|
*
|
||||||
|
* @param instance pointer to instance.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return status >0 .. success
|
||||||
|
* -1 .. requested instance not deleted
|
||||||
|
*/
|
||||||
|
EipStatus CipDeleteService(CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Generic implementation of the CIP Reset service
|
||||||
|
*
|
||||||
|
* Causes a transition to a default state or mode of
|
||||||
|
* the object instance within the specified class
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param instance pointer to instance.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @param originator_address address struct of the originator as received
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return status >0 .. success
|
||||||
|
* -1 .. requested instance not reseted
|
||||||
|
*/
|
||||||
|
EipStatus CipResetService(CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPCOMMON_H_ */
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPCONNECTIONMANAGER_H_
|
||||||
|
#define OPENER_CIPCONNECTIONMANAGER_H_
|
||||||
|
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connection Type constants of the Forward Open service request
|
||||||
|
* Indicates either a
|
||||||
|
* - Null Request
|
||||||
|
* - Point-to-point connection request (unicast)
|
||||||
|
* - Multicast connection request
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionManagerGeneralStatusSuccess = 0x00, /**< General Status - Everything is ok */
|
||||||
|
kConnectionManagerGeneralStatusExtendedStatus = 0x01, /**< Indicates that extended status is set */
|
||||||
|
kConnectionManagerGeneralStatusResourceUnavailableForUnconnectedSend = 0x02,
|
||||||
|
kConnectionManagerGeneralStatusPathSegmentErrorInUnconnectedSend = 0x04,
|
||||||
|
kConnectionManagerGeneralStatusErrorInDataSegment = 0x09,
|
||||||
|
kConnectionManagerGeneralStatusObjectStateError = 0x0C,
|
||||||
|
kConnectionManagerGeneralStatusDeviceStateError = 0x10,
|
||||||
|
kConnectionManagerGeneralStatusNotEnoughData = 0x13,
|
||||||
|
kConnectionManagerGeneralStatusTooMuchData = 0x15,
|
||||||
|
} ConnectionManagerGeneralStatus;
|
||||||
|
/** @brief Connection Manager Error codes */
|
||||||
|
typedef enum {
|
||||||
|
kConnectionManagerExtendedStatusCodeSuccess = 0x00, /**< Obsolete code, should be General Status - Everything is ok */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorConnectionInUseOrDuplicateForwardOpen
|
||||||
|
= 0x0100, /**< General Status has to be 0x01, Connection is already in use, or a duplicate Forward Open was received */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorTransportClassAndTriggerCombinationNotSupported
|
||||||
|
= 0x0103, /**< General Status has to be 0x01, A Transport class and trigger combination has been specified, which is not supported by the target application */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorOwnershipConflict = 0x0106, /**< General Status has to be 0x01, Another connection has already reserved some needed resources */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorConnectionTargetConnectionNotFound =
|
||||||
|
0x0107, /**< General Status has to be 0x01, Forward Close error message, if connection to be closed is not found at the target */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorTargetForConnectionNotConfigured =
|
||||||
|
0x0110, /**< General Status has to be 0x01, Target application not configured and connection request does not contain data segment for configuration */
|
||||||
|
kConnectionManagerExtendedStatusCodeRpiNotSupported = 0x0111,
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorRpiValuesNotAcceptable = 0x0112, /**< General Status has to be 0x01, Requested RPI parameters outside of range, needs 6 16-bit extended status words, see Vol.1 Table 3-5.33 */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorNoMoreConnectionsAvailable = 0x0113, /**< General Status has to be 0x01, No free connection slots available */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorVendorIdOrProductcodeError = 0x0114, /**< General Status has to be 0x01, The Product Code or Vendor ID in the electronic key logical segment does not match the Product Code or Vendor ID of the device, or if the compatibility bit is set and one or both are zero, or cannot be emulated. */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorDeviceTypeError = 0x0115, /**< General Status has to be 0x01, Device Type specified in the electronic key logical segment does not match the Device Type, or if the compatibility bit is set and Device Type is zero, or cannot be emulated. */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorRevisionMismatch = 0x0116, /**< General Status has to be 0x01, Major and minor revision specified in the electronic key logical segment is not a valid revision of the device, or if the compatibility bit is set and the requested Major Revision and/or Minor Revision is 0 or the device cannot emulate the specified revision. */
|
||||||
|
kConnectionManagerExtendedStatusCodeNonListenOnlyConnectionNotOpened = 0x0119, /**< General Status has to be 0x01, listen-only connection cannot be established, if no non-listen only connections are established */
|
||||||
|
kConnectionManagerExtendedStatusCodeTargetObjectOutOfConnections = 0x011A, /**< Maximum number of connections supported by the instance of the target object exceeded */
|
||||||
|
kConnectionManagerExtendedStatusCodeProductionInhibitTimerGreaterThanRpi =
|
||||||
|
0x011B, /**< The Production Inhibit Time is greater than the Target to Originator RPI */
|
||||||
|
kConnectionManagerExtendedStatusCodeTransportClassNotSupported = 0x011C, /**< The transport class requested in the Transport Type/Trigger parameter is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeProductionTriggerNotSuppoerted = 0x011D, /**< The production trigger requested in the Transport Type/Trigger parameter is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeDirectionNotSupported = 0x011E, /**< The direction requested in the Transport Type/Trigger parameter is not supported */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidOToTNetworkConnectionFixVar =
|
||||||
|
0x011F, /**< Shall be returned as the result of specifying an O->T fixed / variable flag that is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidTToONetworkConnectionFixVar =
|
||||||
|
0x0120, /**< Shall be returned as the result of specifying an T->O fixed / variable flag that is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidOToTNetworkConnectionPriority =
|
||||||
|
0x0121, /**< Shall be returned as the result of specifying an O->T priority code that is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidTToONetworkConnectionPriority =
|
||||||
|
0x0122, /**< Shall be returned as the result of specifying an T->O priority code that is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorInvalidOToTConnectionType = 0x0123, /**< Shall be returned as the result of specifying an O->T connection type that is not supported */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorInvalidTToOConnectionType = 0x0124, /**< Shall be returned as the result of specifying a T->O connection type that is not supported */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidOToTNetworkConnectionRedundantOwner
|
||||||
|
= 0x0125, /**< Shall be returned as the result of specifying an O->T Redundant Owner flag that is not supported. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidConfigurationSize = 0x0126, /**< The data segment provided in the Connection_Path parameter did not contain an acceptable number of 16-bit words for the the configuration application path requested. Two additional status words shall follow, the error code plus the max size in words */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorInvalidOToTConnectionSize = 0x0127, /**< The size of the consuming object declared in the Forward_Open request and available on the target does not match the size declared in the O->T Network Connection Parameter. Two additional status words shall follow, the error code plus the max size in words */
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorInvalidTToOConnectionSize = 0x0128, /**< The size of the consuming object declared in the Forward_Open request and available on the target does not match the size declared in the T->O Network Connection Parameter. Two additional status words shall follow, the error code plus the max size in words */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidConfigurationApplicationPath =
|
||||||
|
0x0129, /**< Configuration application path specified does not correspond to a valid configuration application path within the target application. This error could also be returned if a configuration application path was required, but not provided by a connection request. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidConsumingApplicationPath = 0x012A, /**< Consumed application path specified does not correspond to a valid consumed application path within the target application. This error could also be returned if a consumed application path was required, but not provided by a connection request. */
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidProducingApplicationPath = 0x012B, /**< Produced application path specified does not correspond to a valid produced application path within the target application. This error could also be returned if a produced application path was required, but not provided by a connection request. */
|
||||||
|
kConnectionManagerExtendedStatusCodeConfigurationSymbolDoesNotExist = 0x012C,
|
||||||
|
kConnectionManagerExtendedStatusCodeConsumingSymbolDoesNotExist = 0x012D,
|
||||||
|
kConnectionManagerExtendedStatusCodeProducingSymbolDoesNotExist = 0x012E,
|
||||||
|
kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo = 0x012F, /**< */
|
||||||
|
kConnectionManagerExtendedStatusCodeInconsistentConsumeDataFormat = 0x0130,
|
||||||
|
kConnectionManagerExtendedStatusCodeInconsistentProduceDataFormat = 0x0131,
|
||||||
|
kConnectionManagerExtendedStatusCodeNullForwardOpenNotSupported = 0x0132,
|
||||||
|
kConnectionManagerExtendedStatusCodeConnectionTimeoutMultiplierNotAcceptable
|
||||||
|
=
|
||||||
|
0x0133,
|
||||||
|
kConnectionManagerExtendedStatusCodeMismatchedTToONetworkConnectionFixVar =
|
||||||
|
0x0135,
|
||||||
|
kConnectionManagerExtendedStatusCodeMismatchedTToONetworkConnectionPriority =
|
||||||
|
0x0136,
|
||||||
|
kConnectionManagerExtendedStatusCodeMismatchedTransportClass = 0x0137,
|
||||||
|
kConnectionManagerExtendedStatusCodeMismatchedTToOProductionTrigger = 0x0138,
|
||||||
|
kConnectionManagerExtendedStatusCodeMismatchedTToOProductionInhibitTimeSegment
|
||||||
|
= 0x0139,
|
||||||
|
kConnectionManagerExtendedStatusCodeConnectionTimedOut = 0x0203,
|
||||||
|
kConnectionManagerExtendedStatusCodeUnconnectedRequestTimedOut = 0x0204,
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorParameterErrorInUnconnectedSendService
|
||||||
|
= 0x0205, /**< */
|
||||||
|
kConnectionManagerExtendedStatusCodeMessageToLargeForUnconnectedSendService =
|
||||||
|
0x0206,
|
||||||
|
kConnectionManagerExtendedStatusCodeUnconnectedAcknowledgeWithoutReply =
|
||||||
|
0x0207,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoBufferMemoryAvailable = 0x0301,
|
||||||
|
kConnectionManagerExtendedStatusCodeNetworkBandwithNotAvailableForData =
|
||||||
|
0x0302,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoConsumedConnectionIdFilterAvailable =
|
||||||
|
0x0303,
|
||||||
|
kConnectionManagerExtendedStatusCodeNotConfiguredToSendScheduledPriorityData
|
||||||
|
= 0x0304,
|
||||||
|
kConnectionManagerExtendedStatusCodeScheduleSignatureMismatch = 0x0305,
|
||||||
|
kConnectionManagerExtendedStatusCodeScheduleSignatureValidationNotPossible =
|
||||||
|
0x0306,
|
||||||
|
kConnectionManagerExtendedStatusCodePortNotAvailable = 0x0311,
|
||||||
|
kConnectionManagerExtendedStatusCodeLinkAddressNotValid = 0x0312,
|
||||||
|
kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath = 0x0315, /**< */
|
||||||
|
kConnectionManagerExtendedStatusCodeForwardCloseServiceConnectionPathMismatch
|
||||||
|
= 0x0316,
|
||||||
|
kConnectionManagerExtendedStatusCodeSchedulingNotSpecified = 0x0317,
|
||||||
|
kConnectionManagerExtendedStatusCodeLinkAddressToSelfInvalid = 0x0318,
|
||||||
|
kConnectionManagerExtendedStatusCodeSecondaryResourcesUnavailable = 0x0319,
|
||||||
|
kConnectionManagerExtendedStatusCodeRackConnectionAlreadyEstablished = 0x031A,
|
||||||
|
kConnectionManagerExtendedStatusCodeModuleConnectionAlreadyEstablished =
|
||||||
|
0x031B,
|
||||||
|
kConnectionManagerExtendedStatusCodeMiscellaneous = 0x031C,
|
||||||
|
kConnectionManagerExtendedStatusCodeRedundantConnectionMismatch = 0x031D,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoMoreUserConfigurableLinkConsumerResourcesAvailableInTheProducingModule
|
||||||
|
= 0x031E,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoUserConfigurableLinkConsumerResourcesConfiguredInTheProducingModule
|
||||||
|
= 0x031F,
|
||||||
|
kConnectionManagerExtendedStatusCodeNetworkLinkOffline = 0x0800,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoTargetApplicationDataAvailable = 0x0810,
|
||||||
|
kConnectionManagerExtendedStatusCodeNoOriginatorApplicationDataAvailable =
|
||||||
|
0x0811,
|
||||||
|
kConnectionManagerExtendedStatusCodeNodeAddressHasChangedSinceTheNetworkWasScheduled
|
||||||
|
= 0x0812,
|
||||||
|
kConnectionManagerExtendedStatusCodeNotConfiguredForOffSubnetMulticast =
|
||||||
|
0x0813,
|
||||||
|
kConnectionManagerExtendedStatusCodeInvalidProduceConsumeDataFormat = 0x0814,
|
||||||
|
kConnectionManagerExtendedStatusWrongCloser = 0xFFFF /* No a real extended error code, but needed for forward close */
|
||||||
|
} ConnectionManagerExtendedStatusCode;
|
||||||
|
|
||||||
|
/** @brief macros for comparing sequence numbers according to CIP spec vol
|
||||||
|
* 2 3-4.2 for int type variables
|
||||||
|
* @def SEQ_LEQ32(a, b) Checks if sequence number a is less or equal than b
|
||||||
|
* @def SEQ_GEQ32(a, b) Checks if sequence number a is greater or equal than
|
||||||
|
* b
|
||||||
|
* @def SEQ_GT32(a, b) Checks if sequence number a is greater than b
|
||||||
|
*/
|
||||||
|
#define SEQ_LEQ32(a, b) ( (int)( (a) - (b) ) <= 0 )
|
||||||
|
#define SEQ_GEQ32(a, b) ( (int)( (a) - (b) ) >= 0 )
|
||||||
|
#define SEQ_GT32(a, b) ( (int)( (a) - (b) ) > 0 )
|
||||||
|
|
||||||
|
/** @brief similar macros for comparing 16 bit sequence numbers
|
||||||
|
* @def SEQ_LEQ16(a, b) Checks if sequence number a is less or equal than b
|
||||||
|
* @def SEQ_GEQ16(a, b) Checks if sequence number a is greater or equal than
|
||||||
|
* b
|
||||||
|
*/
|
||||||
|
#define SEQ_LEQ16(a, b) ( (short)( (a) - (b) ) <= 0 )
|
||||||
|
#define SEQ_GEQ16(a, b) ( (short)( (a) - (b) ) >= 0 )
|
||||||
|
|
||||||
|
/** @brief Connection Manager class code */
|
||||||
|
static const CipUint kCipConnectionManagerClassCode = 0x06U;
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
|
||||||
|
/** @brief Initialize the data of the connection manager object
|
||||||
|
*
|
||||||
|
* @param unique_connection_id A unique connection id
|
||||||
|
* @return kEipStatusOk if successful, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus ConnectionManagerInit(EipUint16 unique_connection_id);
|
||||||
|
|
||||||
|
/** @brief Get a connected object dependent on requested ConnectionID.
|
||||||
|
*
|
||||||
|
* @param connection_id Connection ID of the Connection Object to get
|
||||||
|
* @return pointer to connected Object
|
||||||
|
* NULL .. connection not present in device
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetConnectedObject(const EipUint32 connection_id);
|
||||||
|
|
||||||
|
/** Get a connection object for a given output assembly.
|
||||||
|
*
|
||||||
|
* @param output_assembly_id requested output assembly of requested
|
||||||
|
* connection
|
||||||
|
* @return pointer to connected Object
|
||||||
|
* 0 .. connection not present in device
|
||||||
|
*/
|
||||||
|
CipConnectionObject *GetConnectedOutputAssembly(
|
||||||
|
const EipUint32 output_assembly_id);
|
||||||
|
|
||||||
|
/** @brief Close the given connection
|
||||||
|
*
|
||||||
|
* This function will take the data form the connection and correctly closes the
|
||||||
|
* connection (e.g., open sockets)
|
||||||
|
* @param connection_object pointer to the connection object structure to be
|
||||||
|
* closed
|
||||||
|
*/
|
||||||
|
void CloseConnection(CipConnectionObject *RESTRICT connection_object);
|
||||||
|
|
||||||
|
/* TODO: Missing documentation */
|
||||||
|
EipBool8 IsConnectedOutputAssembly(const CipInstanceNum instance_number);
|
||||||
|
|
||||||
|
/** @brief Insert the given connection object to the list of currently active
|
||||||
|
* and managed connections.
|
||||||
|
*
|
||||||
|
* By adding a connection to the active connection list the connection manager
|
||||||
|
* will perform the supervision and handle the timing (e.g., timeout,
|
||||||
|
* production inhibit, etc).
|
||||||
|
*
|
||||||
|
* @param connection_object pointer to the connection object to be added.
|
||||||
|
*/
|
||||||
|
void AddNewActiveConnection(CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
/** @brief Removes connection from the list of active connections
|
||||||
|
*
|
||||||
|
* @param connection_object Connection object to be removed from the active connection list
|
||||||
|
*/
|
||||||
|
void RemoveFromActiveConnections(CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
|
||||||
|
CipUdint GetConnectionId(void);
|
||||||
|
|
||||||
|
typedef void (*CloseSessionFunction)(const CipConnectionObject *const
|
||||||
|
connection_object);
|
||||||
|
|
||||||
|
void CheckForTimedOutConnectionsAndCloseTCPConnections(
|
||||||
|
const CipConnectionObject *const connection_object,
|
||||||
|
CloseSessionFunction CloseSessions);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPCONNECTIONMANAGER_H_ */
|
||||||
|
|
@ -0,0 +1,980 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2017, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_NON_EXISTENT 0U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_CONFIGURING 1U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_WAITING_FOR_CONNECTION_ID 2U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_ESTABLISHED 3U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_TIMEOUT 4U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_DEFERRED_DELETE 5U
|
||||||
|
#define CIP_CONNECTION_OBJECT_STATE_CLOSING 6U
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_INSTANCE_TYPE_EXPLICIT_MESSAGING 0
|
||||||
|
#define CIP_CONNECTION_OBJECT_INSTANCE_TYPE_IO 1
|
||||||
|
#define CIP_CONNECTION_OBJECT_INSTANCE_TYPE_CIP_BRIDGED 2
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CYCLIC ( \
|
||||||
|
0 << 4)
|
||||||
|
#define \
|
||||||
|
CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CHANGE_OF_STATE ( \
|
||||||
|
1 << 4)
|
||||||
|
#define \
|
||||||
|
CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_APPLICATION_OBJECT ( \
|
||||||
|
2 << 4)
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_0 0
|
||||||
|
#define CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_1 1
|
||||||
|
#define CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_2 2
|
||||||
|
#define CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_3 3
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_TRANSITION_TO_TIMED_OUT 0
|
||||||
|
#define CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_DELETE 1
|
||||||
|
#define CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_RESET 2
|
||||||
|
#define CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_DEFERRED_DELETE 3
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_CONNECTION_TYPE_NULL 0
|
||||||
|
#define CIP_CONNECTION_OBJECT_CONNECTION_TYPE_MULTICAST 1
|
||||||
|
#define CIP_CONNECTION_OBJECT_CONNECTION_TYPE_POINT_TO_POINT 2
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_PRIORITY_LOW 0
|
||||||
|
#define CIP_CONNECTION_OBJECT_PRIORITY_HIGH 1
|
||||||
|
#define CIP_CONNECTION_OBJECT_PRIORITY_SCHEDULED 2
|
||||||
|
#define CIP_CONNECTION_OBJECT_PRIORITY_URGENT 3
|
||||||
|
|
||||||
|
/** @brief Definition of the global connection list */
|
||||||
|
DoublyLinkedList connection_list;
|
||||||
|
|
||||||
|
/** @brief Array of the available explicit connections */
|
||||||
|
CipConnectionObject explicit_connection_object_pool[
|
||||||
|
OPENER_CIP_NUM_EXPLICIT_CONNS];
|
||||||
|
|
||||||
|
DoublyLinkedListNode *CipConnectionObjectListArrayAllocator() {
|
||||||
|
enum {
|
||||||
|
kNodesAmount = OPENER_CIP_NUM_EXPLICIT_CONNS +
|
||||||
|
OPENER_CIP_NUM_INPUT_ONLY_CONNS +
|
||||||
|
OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS +
|
||||||
|
OPENER_CIP_NUM_LISTEN_ONLY_CONNS
|
||||||
|
};
|
||||||
|
static DoublyLinkedListNode nodes[kNodesAmount] = { 0 };
|
||||||
|
for(size_t i = 0; i < kNodesAmount; ++i) {
|
||||||
|
if(nodes[i].previous == NULL && nodes[i].next == NULL &&
|
||||||
|
nodes[i].data == NULL) {
|
||||||
|
return &nodes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipConnectionObjectListArrayFree(DoublyLinkedListNode **node) {
|
||||||
|
|
||||||
|
if(NULL != node) {
|
||||||
|
if(NULL != *node) {
|
||||||
|
memset(*node, 0, sizeof(DoublyLinkedListNode) );
|
||||||
|
*node = NULL;
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Attempt to delete NULL pointer to node\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Attempt to provide a NULL pointer to node pointer\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private methods declaration */
|
||||||
|
uint64_t ConnectionObjectCalculateRegularInactivityWatchdogTimerValue(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetInitialInactivityWatchdogTimerValue(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
/* End private methods declaration */
|
||||||
|
|
||||||
|
void ConnectionObjectInitializeEmpty(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
memset(connection_object, 0, sizeof(*connection_object) );
|
||||||
|
ConnectionObjectSetState(connection_object,
|
||||||
|
kConnectionObjectStateNonExistent);
|
||||||
|
connection_object->socket[0] = kEipInvalidSocket;
|
||||||
|
connection_object->socket[1] = kEipInvalidSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipConnectionObject *CipConnectionObjectCreate(const CipOctet *message) {
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)message;
|
||||||
|
|
||||||
|
assert(false); /* NOT IMPLEMENTED */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectInitializeFromMessage(const CipOctet **message,
|
||||||
|
CipConnectionObject *const connection_object)
|
||||||
|
{
|
||||||
|
/* For unconnected send - can be ignored by targets, and is ignored here */
|
||||||
|
CipByte priority_timetick = GetByteFromMessage(message);
|
||||||
|
CipUsint timeout_ticks = GetUsintFromMessage(message);
|
||||||
|
(void) priority_timetick; /* Silence unused variable compiler warning */
|
||||||
|
(void) timeout_ticks;
|
||||||
|
|
||||||
|
/* O_to_T Conn ID */
|
||||||
|
ConnectionObjectSetCipConsumedConnectionID(connection_object,
|
||||||
|
GetUdintFromMessage(message) );
|
||||||
|
/* T_to_O Conn ID */
|
||||||
|
ConnectionObjectSetCipProducedConnectionID(connection_object,
|
||||||
|
GetUdintFromMessage(message) );
|
||||||
|
|
||||||
|
ConnectionObjectSetConnectionSerialNumber(connection_object,
|
||||||
|
GetUintFromMessage(message) );
|
||||||
|
ConnectionObjectSetOriginatorVendorId(connection_object,
|
||||||
|
GetUintFromMessage(message) );
|
||||||
|
ConnectionObjectSetOriginatorSerialNumber(connection_object,
|
||||||
|
GetUdintFromMessage(message) );
|
||||||
|
|
||||||
|
ConnectionObjectSetConnectionNumber(connection_object);
|
||||||
|
|
||||||
|
/* keep it to none existent till the setup is done this eases error handling and
|
||||||
|
* the state changes within the forward open request can not be detected from
|
||||||
|
* the application or from outside (reason we are single threaded)
|
||||||
|
* */
|
||||||
|
ConnectionObjectSetState(connection_object,
|
||||||
|
kConnectionObjectStateNonExistent);
|
||||||
|
connection_object->sequence_count_producing = 0; /* set the sequence count to zero */
|
||||||
|
|
||||||
|
ConnectionObjectSetConnectionTimeoutMultiplier(connection_object,
|
||||||
|
GetUsintFromMessage(message) );
|
||||||
|
|
||||||
|
(*message) += 3; /* 3 bytes reserved */
|
||||||
|
|
||||||
|
/* the requested packet interval parameter needs to be a multiple of TIMERTICK from the header file */
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"ForwardOpen: ConConnID %" PRIu32 ", ProdConnID %" PRIu32
|
||||||
|
", ConnSerNo %u\n",
|
||||||
|
connection_object->cip_consumed_connection_id,
|
||||||
|
connection_object->cip_produced_connection_id,
|
||||||
|
connection_object->connection_serial_number);
|
||||||
|
|
||||||
|
ConnectionObjectSetOToTRequestedPacketInterval(connection_object,
|
||||||
|
GetUdintFromMessage(message) );
|
||||||
|
|
||||||
|
ConnectionObjectSetInitialInactivityWatchdogTimerValue(connection_object);
|
||||||
|
|
||||||
|
if(connection_object->is_large_forward_open == true) {
|
||||||
|
ConnectionObjectSetOToTNetworkConnectionParameters(connection_object,
|
||||||
|
GetDwordFromMessage(
|
||||||
|
message) );
|
||||||
|
} else {
|
||||||
|
ConnectionObjectSetOToTNetworkConnectionParameters(connection_object,
|
||||||
|
GetWordFromMessage(
|
||||||
|
message) );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectSetTToORequestedPacketInterval(connection_object,
|
||||||
|
GetUdintFromMessage(message) );
|
||||||
|
|
||||||
|
ConnectionObjectSetExpectedPacketRate(connection_object);
|
||||||
|
|
||||||
|
if(connection_object->is_large_forward_open == true) {
|
||||||
|
ConnectionObjectSetTToONetworkConnectionParameters(connection_object,
|
||||||
|
GetDwordFromMessage(
|
||||||
|
message) );
|
||||||
|
} else {
|
||||||
|
ConnectionObjectSetTToONetworkConnectionParameters(connection_object,
|
||||||
|
GetWordFromMessage(
|
||||||
|
message) );
|
||||||
|
}
|
||||||
|
|
||||||
|
connection_object->transport_class_trigger = GetByteFromMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectState ConnectionObjectGetState(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
ConnectionObjectState new_state = kConnectionObjectStateInvalid;
|
||||||
|
switch(connection_object->state) {
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_NON_EXISTENT:
|
||||||
|
new_state = kConnectionObjectStateNonExistent;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_CONFIGURING:
|
||||||
|
new_state = kConnectionObjectStateConfiguring;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_WAITING_FOR_CONNECTION_ID:
|
||||||
|
new_state = kConnectionObjectStateWaitingForConnectionID;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_ESTABLISHED:
|
||||||
|
new_state = kConnectionObjectStateEstablished;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_TIMEOUT:
|
||||||
|
new_state = kConnectionObjectStateTimedOut;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_DEFERRED_DELETE:
|
||||||
|
new_state = kConnectionObjectStateDeferredDelete;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_STATE_CLOSING:
|
||||||
|
new_state = kConnectionObjectStateClosing;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
new_state = kConnectionObjectStateInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetState(CipConnectionObject *const connection_object,
|
||||||
|
const ConnectionObjectState state) {
|
||||||
|
switch(state) {
|
||||||
|
case kConnectionObjectStateNonExistent:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_NON_EXISTENT;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateConfiguring:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_CONFIGURING;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateWaitingForConnectionID:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_WAITING_FOR_CONNECTION_ID;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateEstablished:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_ESTABLISHED;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateTimedOut:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_TIMEOUT;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateDeferredDelete:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_DEFERRED_DELETE;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectStateClosing:
|
||||||
|
connection_object->state =
|
||||||
|
CIP_CONNECTION_OBJECT_STATE_CLOSING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_ASSERT(false);/* Never get here */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectInstanceType ConnectionObjectGetInstanceType(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->instance_type;
|
||||||
|
// switch (connection_object->instance_type) {
|
||||||
|
// case CIP_CONNECTION_OBJECT_INSTANCE_TYPE_EXPLICIT_MESSAGING:
|
||||||
|
// return kConnectionObjectInstanceTypeExplicitMessaging;
|
||||||
|
// break;
|
||||||
|
// case CIP_CONNECTION_OBJECT_INSTANCE_TYPE_IO:
|
||||||
|
// return kConnectionObjectInstanceTypeIO;
|
||||||
|
// break;
|
||||||
|
// case CIP_CONNECTION_OBJECT_INSTANCE_TYPE_CIP_BRIDGED:
|
||||||
|
// return kConnectionObjectInstanceTypeCipBridged;
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// return kConnectionObjectInstanceTypeInvalid;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetInstanceType(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const ConnectionObjectInstanceType instance_type) {
|
||||||
|
connection_object->instance_type = (CipUsint) instance_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUsint ConnectionObjectGetInstanceTypeForAttribute(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
CipUsint instance_type = kConnectionObjectInstanceTypeInvalid;
|
||||||
|
switch(connection_object->instance_type) {
|
||||||
|
case kConnectionObjectInstanceTypeExplicitMessaging:
|
||||||
|
instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_EXPLICIT_MESSAGING;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectInstanceTypeIO:
|
||||||
|
case kConnectionObjectInstanceTypeIOExclusiveOwner:
|
||||||
|
case kConnectionObjectInstanceTypeIOInputOnly:
|
||||||
|
case kConnectionObjectInstanceTypeIOListenOnly:
|
||||||
|
instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_IO;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectInstanceTypeCipBridged:
|
||||||
|
instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_CIP_BRIDGED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_ASSERT(false);/* This is a fault case */
|
||||||
|
instance_type = kConnectionObjectInstanceTypeInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return instance_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTypeNonLOIOConnection(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
switch(connection_object->instance_type) {
|
||||||
|
case kConnectionObjectInstanceTypeIO:
|
||||||
|
case kConnectionObjectInstanceTypeIOExclusiveOwner:
|
||||||
|
case kConnectionObjectInstanceTypeIOInputOnly:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTypeIOConnection(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
switch(connection_object->instance_type) {
|
||||||
|
case kConnectionObjectInstanceTypeIO:
|
||||||
|
case kConnectionObjectInstanceTypeIOExclusiveOwner:
|
||||||
|
case kConnectionObjectInstanceTypeIOInputOnly:
|
||||||
|
case kConnectionObjectInstanceTypeIOListenOnly:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerDirection
|
||||||
|
ConnectionObjectGetTransportClassTriggerDirection(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
const CipByte TransportClassTriggerDirectionMask = 0x80;
|
||||||
|
return
|
||||||
|
(connection_object->transport_class_trigger &
|
||||||
|
TransportClassTriggerDirectionMask) == TransportClassTriggerDirectionMask ?
|
||||||
|
kConnectionObjectTransportClassTriggerDirectionServer :
|
||||||
|
kConnectionObjectTransportClassTriggerDirectionClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerProductionTrigger
|
||||||
|
ConnectionObjectGetTransportClassTriggerProductionTrigger(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
const CipByte kTransportClassTriggerProductionTriggerMask = 0x70;
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerProductionTrigger production_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerInvalid;
|
||||||
|
switch( (connection_object->transport_class_trigger) &
|
||||||
|
kTransportClassTriggerProductionTriggerMask ) {
|
||||||
|
case
|
||||||
|
CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CYCLIC:
|
||||||
|
production_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerCyclic;
|
||||||
|
break;
|
||||||
|
case
|
||||||
|
CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CHANGE_OF_STATE
|
||||||
|
:
|
||||||
|
production_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerChangeOfState;
|
||||||
|
break;
|
||||||
|
case
|
||||||
|
CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_APPLICATION_OBJECT
|
||||||
|
:
|
||||||
|
production_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerApplicationObject;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
production_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return production_trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerTransportClass
|
||||||
|
ConnectionObjectGetTransportClassTriggerTransportClass(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
const CipByte kTransportClassTriggerTransportClassMask = 0x0F;
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerTransportClass transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClassInvalid;
|
||||||
|
switch( (connection_object->transport_class_trigger) &
|
||||||
|
kTransportClassTriggerTransportClassMask ) {
|
||||||
|
case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_0:
|
||||||
|
transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass0;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_1:
|
||||||
|
transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass1;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_2:
|
||||||
|
transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass2;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_3:
|
||||||
|
transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
transport_class_trigger =
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClassInvalid;
|
||||||
|
}
|
||||||
|
return transport_class_trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProducedConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->produced_connection_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetProducedConnectionSize(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint produced_connection_size) {
|
||||||
|
connection_object->produced_connection_size = produced_connection_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConsumedConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->consumed_connection_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetConsumedConnectionSize(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint consumed_connection_size) {
|
||||||
|
connection_object->consumed_connection_size = consumed_connection_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetExpectedPacketRate(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->expected_packet_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetRequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
CipUdint remainder_to_resolution =
|
||||||
|
(connection_object->t_to_o_requested_packet_interval) %
|
||||||
|
(kOpenerTimerTickInMilliSeconds * 1000);
|
||||||
|
if(0 == remainder_to_resolution) { /* Value can be represented in multiples of the timer resolution */
|
||||||
|
return (CipUint) (connection_object->t_to_o_requested_packet_interval /
|
||||||
|
1000);
|
||||||
|
} else {
|
||||||
|
return (CipUint) (connection_object->t_to_o_requested_packet_interval /
|
||||||
|
1000 - remainder_to_resolution / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetExpectedPacketRate(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
CipUdint remainder_to_resolution =
|
||||||
|
(connection_object->t_to_o_requested_packet_interval) %
|
||||||
|
(kOpenerTimerTickInMilliSeconds * 1000);
|
||||||
|
if(0 == remainder_to_resolution) { /* Value can be represented in multiples of the timer resolution */
|
||||||
|
connection_object->expected_packet_rate =
|
||||||
|
connection_object->t_to_o_requested_packet_interval / 1000;
|
||||||
|
} else {
|
||||||
|
connection_object->expected_packet_rate =
|
||||||
|
connection_object->t_to_o_requested_packet_interval / 1000
|
||||||
|
+ ( (CipUdint)
|
||||||
|
kOpenerTimerTickInMilliSeconds - remainder_to_resolution / 1000 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetCipProducedConnectionID(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->cip_produced_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetCipProducedConnectionID(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUdint cip_produced_connection_id) {
|
||||||
|
connection_object->cip_produced_connection_id = cip_produced_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetCipConsumedConnectionID(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->cip_consumed_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetCipConsumedConnectionID(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUdint cip_consumed_connection_id) {
|
||||||
|
connection_object->cip_consumed_connection_id = cip_consumed_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectWatchdogTimeoutAction ConnectionObjectGetWatchdogTimeoutAction(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
ConnectionObjectWatchdogTimeoutAction timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionInvalid;
|
||||||
|
switch(connection_object->watchdog_timeout_action) {
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_TRANSITION_TO_TIMED_OUT:
|
||||||
|
timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionTransitionToTimedOut;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_DELETE:
|
||||||
|
timeout_action = kConnectionObjectWatchdogTimeoutActionAutoDelete;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_RESET:
|
||||||
|
timeout_action = kConnectionObjectWatchdogTimeoutActionAutoReset;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_DEFERRED_DELETE:
|
||||||
|
timeout_action = kConnectionObjectWatchdogTimeoutActionDeferredDelete;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
timeout_action = kConnectionObjectWatchdogTimeoutActionInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return timeout_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetWatchdogTimeoutAction(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUsint watchdog_timeout_action) {
|
||||||
|
switch(watchdog_timeout_action) {
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_TRANSITION_TO_TIMED_OUT:
|
||||||
|
connection_object->watchdog_timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionTransitionToTimedOut;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_DELETE:
|
||||||
|
connection_object->watchdog_timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionAutoDelete;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_RESET:
|
||||||
|
connection_object->watchdog_timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionAutoReset;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_DEFERRED_DELETE:
|
||||||
|
connection_object->watchdog_timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionDeferredDelete;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
connection_object->watchdog_timeout_action =
|
||||||
|
kConnectionObjectWatchdogTimeoutActionInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProducedConnectionPathLength(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->produced_connection_path_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetProducedConnectionPathLength(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint produced_connection_path_length) {
|
||||||
|
connection_object->produced_connection_path_length =
|
||||||
|
produced_connection_path_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConsumedConnectionPathLength(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->consumed_connection_path_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetConsumedConnectionPathLength(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint consumed_connection_path_length) {
|
||||||
|
connection_object->consumed_connection_path_length =
|
||||||
|
consumed_connection_path_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProductionInhibitTime(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->production_inhibit_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetProductionInhibitTime(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint production_inhibit_time) {
|
||||||
|
connection_object->production_inhibit_time = production_inhibit_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*setup the preconsumption timer: max(ConnectionTimeoutMultiplier * ExpectedPacketRate, 10s) */
|
||||||
|
void ConnectionObjectSetInitialInactivityWatchdogTimerValue(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
const uint64_t kMinimumInitialTimeoutValue = 10000;
|
||||||
|
const uint64_t calculated_timeout_value =
|
||||||
|
ConnectionObjectCalculateRegularInactivityWatchdogTimerValue(
|
||||||
|
connection_object);
|
||||||
|
connection_object->inactivity_watchdog_timer =
|
||||||
|
(calculated_timeout_value >
|
||||||
|
kMinimumInitialTimeoutValue) ? calculated_timeout_value :
|
||||||
|
kMinimumInitialTimeoutValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectResetInactivityWatchdogTimerValue(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
connection_object->inactivity_watchdog_timer =
|
||||||
|
ConnectionObjectCalculateRegularInactivityWatchdogTimerValue(
|
||||||
|
connection_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectResetLastPackageInactivityTimerValue(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
connection_object->last_package_watchdog_timer =
|
||||||
|
ConnectionObjectCalculateRegularInactivityWatchdogTimerValue(
|
||||||
|
connection_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ConnectionObjectCalculateRegularInactivityWatchdogTimerValue(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ( ( (uint64_t)(connection_object->o_to_t_requested_packet_interval) /
|
||||||
|
(uint64_t) 1000 ) <<
|
||||||
|
(2 + connection_object->connection_timeout_multiplier) );
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConnectionSerialNumber(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->connection_serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionSerialNumber(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUint connection_serial_number) {
|
||||||
|
connection_object->connection_serial_number = connection_serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetOriginatorVendorId(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->originator_vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetOriginatorVendorId(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUint vendor_id) {
|
||||||
|
connection_object->originator_vendor_id = vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetOriginatorSerialNumber(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->originator_serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetOriginatorSerialNumber(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
CipUdint originator_serial_number) {
|
||||||
|
connection_object->originator_serial_number = originator_serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetConnectionlNumber(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->connection_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionNumber(
|
||||||
|
CipConnectionObject *connection_object) {
|
||||||
|
connection_object->connection_number = GenerateRandomConnectionNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint GenerateRandomConnectionNumber(void) {
|
||||||
|
CipUint rand_num = (CipUint)rand(); //TODO: update to random.c functions
|
||||||
|
|
||||||
|
//search for existing connection_numbers
|
||||||
|
DoublyLinkedListNode *iterator = connection_list.first;
|
||||||
|
CipConnectionObject *search_connection_object = NULL;
|
||||||
|
|
||||||
|
while (NULL != iterator) {
|
||||||
|
search_connection_object = iterator->data;
|
||||||
|
|
||||||
|
if ((search_connection_object->connection_number == rand_num)) {
|
||||||
|
|
||||||
|
rand_num = GenerateRandomConnectionNumber();
|
||||||
|
}
|
||||||
|
iterator = iterator->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUsint ConnectionObjectGetConnectionTimeoutMultiplier(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->connection_timeout_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionTimeoutMultiplier(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUsint connection_timeout_multiplier) {
|
||||||
|
connection_object->connection_timeout_multiplier =
|
||||||
|
connection_timeout_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetOToTRequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->o_to_t_requested_packet_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetOToTRequestedPacketInterval(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUdint requested_packet_interval) {
|
||||||
|
connection_object->o_to_t_requested_packet_interval =
|
||||||
|
requested_packet_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetTToORequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return connection_object->t_to_o_requested_packet_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetTToORequestedPacketInterval(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUdint requested_packet_interval) {
|
||||||
|
connection_object->t_to_o_requested_packet_interval =
|
||||||
|
requested_packet_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetTToONetworkConnectionParameters(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipDword connection_parameters) {
|
||||||
|
connection_object->t_to_o_network_connection_parameters =
|
||||||
|
connection_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectSetOToTNetworkConnectionParameters(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipDword connection_parameters) {
|
||||||
|
connection_object->o_to_t_network_connection_parameters =
|
||||||
|
connection_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectIsRedundantOwner(const CipDword connection_parameters,
|
||||||
|
const CipBool is_lfo) {
|
||||||
|
if(is_lfo) {
|
||||||
|
return (connection_parameters & (1 << 31) );
|
||||||
|
} else {
|
||||||
|
return (connection_parameters & (1 << 15) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectIsOToTRedundantOwner(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectIsRedundantOwner(
|
||||||
|
connection_object->o_to_t_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTToORedundantOwner(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectIsRedundantOwner(
|
||||||
|
connection_object->t_to_o_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionType ConnectionObjectGetConnectionType(
|
||||||
|
const CipDword connection_parameters,
|
||||||
|
const CipBool is_lfo) {
|
||||||
|
|
||||||
|
CipUsint connection_type;
|
||||||
|
if(is_lfo) {
|
||||||
|
connection_type = (connection_parameters & (3 << 29) ) >> 29;
|
||||||
|
} else {
|
||||||
|
connection_type = (connection_parameters & (3 << 13) ) >> 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(connection_type) {
|
||||||
|
case CIP_CONNECTION_OBJECT_CONNECTION_TYPE_NULL:
|
||||||
|
return kConnectionObjectConnectionTypeNull;
|
||||||
|
case CIP_CONNECTION_OBJECT_CONNECTION_TYPE_MULTICAST:
|
||||||
|
return kConnectionObjectConnectionTypeMulticast;
|
||||||
|
case CIP_CONNECTION_OBJECT_CONNECTION_TYPE_POINT_TO_POINT:
|
||||||
|
return kConnectionObjectConnectionTypePointToPoint;
|
||||||
|
default:
|
||||||
|
return kConnectionObjectConnectionTypeInvalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionType ConnectionObjectGetOToTConnectionType(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionType(
|
||||||
|
connection_object->o_to_t_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionType ConnectionObjectGetTToOConnectionType(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionType(
|
||||||
|
connection_object->t_to_o_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectPriority ConnectionObjectGetPriority(
|
||||||
|
const CipDword connection_parameters,
|
||||||
|
const CipBool is_lfo) {
|
||||||
|
|
||||||
|
CipUsint priority;
|
||||||
|
if(is_lfo) {
|
||||||
|
priority = (connection_parameters & (3 << 26) ) >> 26;
|
||||||
|
} else {
|
||||||
|
priority = (connection_parameters & (3 << 10) ) >> 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectPriority result;
|
||||||
|
switch(priority) {
|
||||||
|
case CIP_CONNECTION_OBJECT_PRIORITY_LOW:
|
||||||
|
result = kConnectionObjectPriorityLow;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_PRIORITY_HIGH:
|
||||||
|
result = kConnectionObjectPriorityHigh;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_PRIORITY_SCHEDULED:
|
||||||
|
result = kConnectionObjectPriorityScheduled;
|
||||||
|
break;
|
||||||
|
case CIP_CONNECTION_OBJECT_PRIORITY_URGENT:
|
||||||
|
result = kConnectionObjectPriorityUrgent;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_ASSERT(false);/* Not possible to get here! */
|
||||||
|
result = kConnectionObjectPriorityLow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectPriority ConnectionObjectGetOToTPriority(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetPriority(
|
||||||
|
connection_object->o_to_t_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectPriority ConnectionObjectGetTToOPriority(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetPriority(
|
||||||
|
connection_object->t_to_o_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionSizeType ConnectionObjectGetConnectionSizeType(
|
||||||
|
const CipDword connection_parameters,
|
||||||
|
const CipBool is_lfo) {
|
||||||
|
|
||||||
|
bool connection_size_type;
|
||||||
|
if(is_lfo) {
|
||||||
|
connection_size_type = (connection_parameters & (1 << 25) );
|
||||||
|
} else {
|
||||||
|
connection_size_type = (connection_parameters & (1 << 9) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if(connection_size_type) {
|
||||||
|
return kConnectionObjectConnectionSizeTypeVariable;
|
||||||
|
} else {
|
||||||
|
return kConnectionObjectConnectionSizeTypeFixed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionSizeType ConnectionObjectGetOToTConnectionSizeType(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionSizeType(
|
||||||
|
connection_object->o_to_t_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectConnectionSizeType ConnectionObjectGetTToOConnectionSizeType(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionSizeType(
|
||||||
|
connection_object->t_to_o_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ConnectionObjectGetConnectionSize(const CipDword connection_parameters,
|
||||||
|
const CipBool is_lfo) {
|
||||||
|
const CipDword kConnectionSizeMask = 0x000001FF;
|
||||||
|
const CipDword kConnectionSizeMaskLFO = 0x0000FFFF;
|
||||||
|
|
||||||
|
CipDword mask = kConnectionSizeMask;
|
||||||
|
if(is_lfo) {
|
||||||
|
mask = kConnectionSizeMaskLFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection_parameters & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ConnectionObjectGetOToTConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionSize(
|
||||||
|
connection_object->o_to_t_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ConnectionObjectGetTToOConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object) {
|
||||||
|
return ConnectionObjectGetConnectionSize(
|
||||||
|
connection_object->t_to_o_network_connection_parameters,
|
||||||
|
connection_object->is_large_forward_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectDeepCopy(
|
||||||
|
CipConnectionObject *RESTRICT destination,
|
||||||
|
const CipConnectionObject *RESTRICT const source
|
||||||
|
) {
|
||||||
|
memcpy( destination, source, sizeof(CipConnectionObject) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectResetSequenceCounts(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
connection_object->eip_level_sequence_count_producing = 0;
|
||||||
|
connection_object->sequence_count_producing = 0;
|
||||||
|
connection_object->eip_level_sequence_count_consuming = 0;
|
||||||
|
connection_object->eip_first_level_sequence_count_received = false;
|
||||||
|
connection_object->sequence_count_consuming = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectResetProductionInhibitTimer(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
connection_object->production_inhibit_timer =
|
||||||
|
connection_object->production_inhibit_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionObjectGeneralConfiguration(
|
||||||
|
CipConnectionObject *const connection_object) {
|
||||||
|
|
||||||
|
connection_object->socket[0] = kEipInvalidSocket;
|
||||||
|
connection_object->socket[1] = kEipInvalidSocket;
|
||||||
|
|
||||||
|
if(kConnectionObjectConnectionTypePointToPoint ==
|
||||||
|
ConnectionObjectGetOToTConnectionType(connection_object) ) {
|
||||||
|
/* if we have a point to point connection for the O to T direction
|
||||||
|
* the target shall choose the connection ID.
|
||||||
|
*/
|
||||||
|
ConnectionObjectSetCipConsumedConnectionID(connection_object,
|
||||||
|
GetConnectionId() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kConnectionObjectConnectionTypeMulticast ==
|
||||||
|
ConnectionObjectGetTToOConnectionType(connection_object) ) {
|
||||||
|
/* if we have a multi-cast connection for the T to O direction the
|
||||||
|
* target shall choose the connection ID.
|
||||||
|
*/
|
||||||
|
ConnectionObjectSetCipProducedConnectionID(connection_object,
|
||||||
|
GetConnectionId() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionObjectResetSequenceCounts(connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectSetWatchdogTimeoutAction(connection_object,
|
||||||
|
kConnectionObjectWatchdogTimeoutActionInvalid); /* Correct value not know at this point */
|
||||||
|
|
||||||
|
ConnectionObjectResetProductionInhibitTimer(connection_object);
|
||||||
|
|
||||||
|
connection_object->transmission_trigger_timer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionObjectEqualOriginator(const CipConnectionObject *const object1,
|
||||||
|
const CipConnectionObject *const object2) {
|
||||||
|
if( (object1->originator_vendor_id == object2->originator_vendor_id) &&
|
||||||
|
(object1->originator_serial_number ==
|
||||||
|
object2->originator_serial_number) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EqualConnectionTriad(const CipConnectionObject *const object1,
|
||||||
|
const CipConnectionObject *const object2) {
|
||||||
|
if( (object1->connection_serial_number ==
|
||||||
|
object2->connection_serial_number) &&
|
||||||
|
(object1->originator_vendor_id == object2->originator_vendor_id)
|
||||||
|
&& (object1->originator_serial_number ==
|
||||||
|
object2->originator_serial_number) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CipConnectionObjectOriginatorHasSameIP(
|
||||||
|
const CipConnectionObject *const connection_object,
|
||||||
|
const struct sockaddr *const originator_address) {
|
||||||
|
return ( (struct sockaddr_in *) originator_address )->sin_addr.s_addr ==
|
||||||
|
connection_object->originator_address.sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,464 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2017, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SRC_CIP_CIPCONNECTIONOBJECT_H_
|
||||||
|
#define SRC_CIP_CIPCONNECTIONOBJECT_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "doublylinkedlist.h"
|
||||||
|
#include "cipelectronickey.h"
|
||||||
|
#include "cipepath.h"
|
||||||
|
|
||||||
|
#define CIP_CONNECTION_OBJECT_CODE 0x05
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectStateNonExistent = 0, /**< Connection is non existent */
|
||||||
|
kConnectionObjectStateConfiguring, /**< Waiting for both to be configured and to apply the configuration */
|
||||||
|
kConnectionObjectStateWaitingForConnectionID, /**< Only used for device net */
|
||||||
|
kConnectionObjectStateEstablished, /**< Connection is established */
|
||||||
|
kConnectionObjectStateTimedOut, /**< Connection timed out - inactivity or watchdog timer expired */
|
||||||
|
kConnectionObjectStateDeferredDelete, /**< Only used for device net */
|
||||||
|
kConnectionObjectStateClosing, /**< For CIP bridged connections - have to wait for a successful forward close */
|
||||||
|
kConnectionObjectStateInvalid /**< An invalid state, shall never occur! */
|
||||||
|
} ConnectionObjectState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectInstanceTypeInvalid = (CipUsint)(~0), /**< Invalid instance type - shall never occur! */
|
||||||
|
kConnectionObjectInstanceTypeExplicitMessaging = 0, /**< Connection is an explicit messaging connection */
|
||||||
|
kConnectionObjectInstanceTypeIO, /**< Connection is an I/O connection */
|
||||||
|
kConnectionObjectInstanceTypeIOExclusiveOwner, /**< Also I/O connection, only for easy differentiation */
|
||||||
|
kConnectionObjectInstanceTypeIOInputOnly, /**< Also I/O connection, only for easy differentiation */
|
||||||
|
kConnectionObjectInstanceTypeIOListenOnly, /**< Also I/O connection, only for easy differentiation */
|
||||||
|
kConnectionObjectInstanceTypeCipBridged /**< Connection is a bridged connection */
|
||||||
|
} ConnectionObjectInstanceType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectTransportClassTriggerDirectionClient = 0, /**< Endpoint provides client behavior */
|
||||||
|
kConnectionObjectTransportClassTriggerDirectionServer /**< Endpoint provides server behavior - production trigger bits are to be ignored */
|
||||||
|
} ConnectionObjectTransportClassTriggerDirection;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerInvalid = -1, /**< Invalid Production trigger - shall never occur! */
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerCyclic = 0, /**< Transmission Trigger Timer trigger data production */
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerChangeOfState, /**< Production is trigger when change-of-state is detected by the Application Object */
|
||||||
|
kConnectionObjectTransportClassTriggerProductionTriggerApplicationObject /**< The Application Object decided when production is triggered */
|
||||||
|
} ConnectionObjectTransportClassTriggerProductionTrigger;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClassInvalid = -1, /**< Invalid Transport Class - shall never occur! */
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass0 = 0, /**< Class 0 producing or consuming connection, based on Direction */
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass1, /**< Class 1 producing or consuming connection, based on Direction */
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass2, /**< Class 2 producing and consuming connection, Client starts producing */
|
||||||
|
kConnectionObjectTransportClassTriggerTransportClass3 /**< Class 3 producing and consuming connection, Client starts producing */
|
||||||
|
/* Higher transport classes not supported */
|
||||||
|
} ConnectionObjectTransportClassTriggerTransportClass;
|
||||||
|
|
||||||
|
/** @brief Possible values for the watch dog time out action of a connection
|
||||||
|
*
|
||||||
|
* Only positive values allowed
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectWatchdogTimeoutActionTransitionToTimedOut = 0, /**< Default for I/O connections, invalid for Explicit Messaging */
|
||||||
|
kConnectionObjectWatchdogTimeoutActionAutoDelete, /**< Default for explicit connections */
|
||||||
|
kConnectionObjectWatchdogTimeoutActionAutoReset, /**< Invalid for explicit connections */
|
||||||
|
kConnectionObjectWatchdogTimeoutActionDeferredDelete, /**< Only for Device Net, invalid for I/O connections */
|
||||||
|
kConnectionObjectWatchdogTimeoutActionInvalid /**< Invalid Watchdog Timeout Action - shall never occur! */
|
||||||
|
} ConnectionObjectWatchdogTimeoutAction;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectConnectionTypeNull = 0,
|
||||||
|
kConnectionObjectConnectionTypeMulticast,
|
||||||
|
kConnectionObjectConnectionTypePointToPoint,
|
||||||
|
kConnectionObjectConnectionTypeInvalid
|
||||||
|
} ConnectionObjectConnectionType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectPriorityLow = 0,
|
||||||
|
kConnectionObjectPriorityHigh,
|
||||||
|
kConnectionObjectPriorityScheduled,
|
||||||
|
kConnectionObjectPriorityUrgent,
|
||||||
|
kConnectionObjectPriorityExplicit
|
||||||
|
} ConnectionObjectPriority;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectConnectionSizeTypeFixed,
|
||||||
|
kConnectionObjectConnectionSizeTypeVariable
|
||||||
|
} ConnectionObjectConnectionSizeType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kConnectionObjectSocketTypeProducing = 0,
|
||||||
|
kConnectionObjectSocketTypeConsuming = 1
|
||||||
|
} ConnectionObjectSocketType;
|
||||||
|
|
||||||
|
typedef struct cip_connection_object CipConnectionObject;
|
||||||
|
|
||||||
|
typedef EipStatus (*CipConnectionStateHandler)(CipConnectionObject *RESTRICT
|
||||||
|
const connection_object,
|
||||||
|
ConnectionObjectState new_state);
|
||||||
|
|
||||||
|
struct cip_connection_object {
|
||||||
|
CipUsint state; /*< Attribute 1 */
|
||||||
|
CipUsint instance_type; /*< Attribute 2 */
|
||||||
|
CipByte transport_class_trigger; /*< Attribute 3 */
|
||||||
|
/* Attribute 4-6 only for device net*/
|
||||||
|
CipUint produced_connection_size; /*< Attribute 7 - Limits produced connection size - for explicit messaging 0xFFFF means no predefined limit */
|
||||||
|
CipUint consumed_connection_size; /*< Attribute 8 - Limits produced connection size - for explicit messaging 0xFFFF means no predefined limit */
|
||||||
|
CipUint expected_packet_rate; /*< Attribute 9 - Resolution in Milliseconds */
|
||||||
|
CipUdint cip_produced_connection_id; /*< Attribute 10 */
|
||||||
|
CipUdint cip_consumed_connection_id; /*< Attribute 11 */
|
||||||
|
CipUsint watchdog_timeout_action; /*< Attribute 12 */
|
||||||
|
CipUint produced_connection_path_length; /*< Attribute 13 */
|
||||||
|
CipOctet *produced_connection_path; /*< Attribute 14 */
|
||||||
|
CipConnectionPathEpath produced_path;
|
||||||
|
CipUint consumed_connection_path_length; /*< Attribute 15 */
|
||||||
|
CipOctet *consumed_connection_path; /*< Attribute 16 */
|
||||||
|
CipConnectionPathEpath consumed_path;
|
||||||
|
CipUint production_inhibit_time; /*< Attribute 17 */
|
||||||
|
CipUsint connection_timeout_multiplier; /*< Attribute 18 */
|
||||||
|
/* Attribute 19 not supported as Connection Bind service not supported */
|
||||||
|
|
||||||
|
/* End of CIP attributes */
|
||||||
|
/* Start of needed non-object variables */
|
||||||
|
CipElectronicKey electronic_key; //TODO: Check if really needed
|
||||||
|
|
||||||
|
CipConnectionPathEpath configuration_path;
|
||||||
|
|
||||||
|
CipInstance *producing_instance;
|
||||||
|
CipInstance *consuming_instance;
|
||||||
|
|
||||||
|
CipUint requested_produced_connection_size;
|
||||||
|
CipUint requested_consumed_connection_size;
|
||||||
|
|
||||||
|
uint64_t transmission_trigger_timer;
|
||||||
|
uint64_t inactivity_watchdog_timer;
|
||||||
|
uint64_t last_package_watchdog_timer;
|
||||||
|
uint64_t production_inhibit_timer;
|
||||||
|
|
||||||
|
CipUint connection_serial_number;
|
||||||
|
CipUint originator_vendor_id;
|
||||||
|
CipUdint originator_serial_number;
|
||||||
|
|
||||||
|
CipUint connection_number;
|
||||||
|
|
||||||
|
CipUdint o_to_t_requested_packet_interval;
|
||||||
|
CipDword o_to_t_network_connection_parameters;
|
||||||
|
|
||||||
|
CipUdint t_to_o_requested_packet_interval;
|
||||||
|
CipDword t_to_o_network_connection_parameters;
|
||||||
|
|
||||||
|
CipUint sequence_count_producing; /**< sequence Count for Class 1 Producing
|
||||||
|
Connections */
|
||||||
|
CipUint sequence_count_consuming; /**< sequence Count for Class 1 Producing
|
||||||
|
Connections */
|
||||||
|
|
||||||
|
EipUint32 eip_level_sequence_count_producing; /**< the EIP level sequence Count
|
||||||
|
for Class 0/1
|
||||||
|
Producing Connections may have a
|
||||||
|
different
|
||||||
|
value than SequenceCountProducing */
|
||||||
|
EipUint32 eip_level_sequence_count_consuming; /**< the EIP level sequence Count
|
||||||
|
for Class 0/1
|
||||||
|
Producing Connections may have a
|
||||||
|
different
|
||||||
|
value than SequenceCountProducing */
|
||||||
|
CipBool eip_first_level_sequence_count_received; /**< False if eip_level_sequence_count_consuming
|
||||||
|
hasn't been initialized with a sequence
|
||||||
|
count yet, true otherwise */
|
||||||
|
CipInt correct_originator_to_target_size;
|
||||||
|
CipInt correct_target_to_originator_size;
|
||||||
|
|
||||||
|
/* Sockets for consuming and producing connection */
|
||||||
|
int socket[2];
|
||||||
|
|
||||||
|
struct sockaddr_in remote_address; /* socket address for produce */
|
||||||
|
struct sockaddr_in originator_address; /* the address of the originator that
|
||||||
|
established the connection. needed
|
||||||
|
for scanning if the right packet is
|
||||||
|
arriving */
|
||||||
|
|
||||||
|
CipSessionHandle associated_encapsulation_session; /* The session handle ID via which the forward open was sent */
|
||||||
|
|
||||||
|
/* pointers to connection handling functions */
|
||||||
|
CipConnectionStateHandler current_state_handler;
|
||||||
|
|
||||||
|
ConnectionCloseFunction connection_close_function;
|
||||||
|
ConnectionTimeoutFunction connection_timeout_function;
|
||||||
|
ConnectionSendDataFunction connection_send_data_function;
|
||||||
|
ConnectionReceiveDataFunction connection_receive_data_function;
|
||||||
|
|
||||||
|
ENIPMessage last_reply_sent;
|
||||||
|
CipBool is_large_forward_open;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Extern declaration of the global connection list */
|
||||||
|
extern DoublyLinkedList connection_list;
|
||||||
|
|
||||||
|
DoublyLinkedListNode *CipConnectionObjectListArrayAllocator(
|
||||||
|
);
|
||||||
|
void CipConnectionObjectListArrayFree(DoublyLinkedListNode **node);
|
||||||
|
|
||||||
|
/** @brief Array allocator
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipConnectionObject *CipConnectionObjectCreate(const CipOctet *message);
|
||||||
|
|
||||||
|
/** @brief Array deallocator
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void CipConnectionObjectDelete(CipConnectionObject **connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectInitializeEmpty(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectInitializeFromMessage(
|
||||||
|
const CipOctet **message,
|
||||||
|
CipConnectionObject *const
|
||||||
|
connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectState ConnectionObjectGetState(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetState(CipConnectionObject *const connection_object,
|
||||||
|
const ConnectionObjectState state);
|
||||||
|
|
||||||
|
ConnectionObjectInstanceType ConnectionObjectGetInstanceType(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetInstanceType(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const ConnectionObjectInstanceType instance_type);
|
||||||
|
|
||||||
|
CipUsint ConnectionObjectGetInstanceTypeForAttribute(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerDirection
|
||||||
|
ConnectionObjectGetTransportClassTriggerDirection(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerProductionTrigger
|
||||||
|
ConnectionObjectGetTransportClassTriggerProductionTrigger(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectTransportClassTriggerTransportClass
|
||||||
|
ConnectionObjectGetTransportClassTriggerTransportClass(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProducedConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetProducedConnectionSize(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint
|
||||||
|
produced_connection_size);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConsumedConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetConsumedConnectionSize(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint
|
||||||
|
consumed_connection_size);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetExpectedPacketRate(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetRequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the expected packet rate according to the rules of the CIP specification
|
||||||
|
*
|
||||||
|
* As this function sets the expected packet rate according to the rules of the CIP specification, it is not always
|
||||||
|
* the exact value entered, but rounded up to the next serviceable increment, relative to the timer resolution
|
||||||
|
*/
|
||||||
|
void ConnectionObjectSetExpectedPacketRate(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetCipProducedConnectionID(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetCipProducedConnectionID(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUdint
|
||||||
|
cip_produced_connection_id);
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetCipConsumedConnectionID(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetCipConsumedConnectionID(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUdint
|
||||||
|
cip_consumed_connection_id);
|
||||||
|
|
||||||
|
ConnectionObjectWatchdogTimeoutAction ConnectionObjectGetWatchdogTimeoutAction(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetWatchdogTimeoutAction(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUsint
|
||||||
|
watchdog_timeout_action);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProducedConnectionPathLength(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetProducedConnectionPathLength(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint
|
||||||
|
produced_connection_path_length);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConsumedConnectionPathLength(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetConsumedConnectionPathLength(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint
|
||||||
|
consumed_connection_path_length);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetProductionInhibitTime(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetProductionInhibitTime(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUint
|
||||||
|
production_inhibit_time);
|
||||||
|
|
||||||
|
CipUsint ConnectionObjectGetConnectionTimeoutMultiplier(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionTimeoutMultiplier(
|
||||||
|
CipConnectionObject *const connection_object,
|
||||||
|
const CipUsint
|
||||||
|
connection_timeout_multiplier);
|
||||||
|
|
||||||
|
void ConnectionObjectResetInactivityWatchdogTimerValue(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectResetLastPackageInactivityTimerValue(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetConnectionSerialNumber(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionSerialNumber(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUint connection_serial_number);
|
||||||
|
|
||||||
|
CipUint ConnectionObjectGetOriginatorVendorId(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetOriginatorVendorId(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUint vendor_id);
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetOriginatorSerialNumber(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetOriginatorSerialNumber(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
CipUdint originator_serial_number);
|
||||||
|
|
||||||
|
void ConnectionObjectGetConnectionNumber(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUint connection_number);
|
||||||
|
|
||||||
|
void ConnectionObjectSetConnectionNumber(
|
||||||
|
CipConnectionObject *connection_object);
|
||||||
|
|
||||||
|
CipUint GenerateRandomConnectionNumber(void);
|
||||||
|
|
||||||
|
CipUdint ConnectionObjectGetOToTRequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetOToTRequestedPacketInterval(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUdint requested_packet_interval);
|
||||||
|
|
||||||
|
bool ConnectionObjectIsOToTRedundantOwner(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectConnectionType ConnectionObjectGetOToTConnectionType(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectPriority ConnectionObjectGetOToTPriority(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectConnectionSizeType ConnectionObjectGetOToTConnectionSizeType(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
size_t ConnectionObjectGetOToTConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
/* T to O */
|
||||||
|
CipUdint ConnectionObjectGetTToORequestedPacketInterval(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void ConnectionObjectSetTToORequestedPacketInterval(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipUdint requested_packet_interval);
|
||||||
|
|
||||||
|
void ConnectionObjectSetTToONetworkConnectionParameters(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipDword connection_parameters);
|
||||||
|
|
||||||
|
void ConnectionObjectSetOToTNetworkConnectionParameters(
|
||||||
|
CipConnectionObject *connection_object,
|
||||||
|
const CipDword connection_parameters);
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTToORedundantOwner(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectConnectionType ConnectionObjectGetTToOConnectionType(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectPriority ConnectionObjectGetTToOPriority(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
ConnectionObjectConnectionSizeType ConnectionObjectGetTToOConnectionSizeType(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
size_t ConnectionObjectGetTToOConnectionSize(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
/** @brief Copy the given connection data from source to destination
|
||||||
|
*
|
||||||
|
* @param destination Destination of the copy operation
|
||||||
|
* @param source Source of the copy operation
|
||||||
|
*/
|
||||||
|
void ConnectionObjectDeepCopy(CipConnectionObject *RESTRICT destination,
|
||||||
|
const CipConnectionObject *RESTRICT const source);
|
||||||
|
|
||||||
|
void ConnectionObjectResetProductionInhibitTimer(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
/** @brief Generate the ConnectionIDs and set the general configuration
|
||||||
|
* parameter in the given connection object.
|
||||||
|
*
|
||||||
|
* @param connection_object pointer to the connection object that should be set
|
||||||
|
* up.
|
||||||
|
*/
|
||||||
|
void ConnectionObjectGeneralConfiguration(
|
||||||
|
CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTypeNonLOIOConnection(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
bool ConnectionObjectIsTypeIOConnection(
|
||||||
|
const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
bool ConnectionObjectEqualOriginator(const CipConnectionObject *const object1,
|
||||||
|
const CipConnectionObject *const object2);
|
||||||
|
|
||||||
|
bool EqualConnectionTriad(const CipConnectionObject *const object1,
|
||||||
|
const CipConnectionObject *const object2);
|
||||||
|
|
||||||
|
bool CipConnectionObjectOriginatorHasSameIP(
|
||||||
|
const CipConnectionObject *const connection_object,
|
||||||
|
const struct sockaddr *const originator_address);
|
||||||
|
|
||||||
|
#endif /* SRC_CIP_CIPCONNECTIONOBJECT_H_ */
|
||||||
232
OrginalSourceRepo/OpENer-master/source/src/cip/cipdlr.c
Normal file
232
OrginalSourceRepo/OpENer-master/source/src/cip/cipdlr.c
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
/** @file
|
||||||
|
* @brief Implements the DLR object
|
||||||
|
* @author Stefan Maetje <stefan.maetje@esd.eu>
|
||||||
|
*
|
||||||
|
* CIP DLR object
|
||||||
|
* ==============
|
||||||
|
*
|
||||||
|
* This module implements the DLR object for a non supervisor and non gateway
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* Implemented Attributes
|
||||||
|
* ----------------------
|
||||||
|
* - Attribute 1: Network Topology
|
||||||
|
* - Attribute 2: Network Status
|
||||||
|
* - Attribute 10: Active Supervisor Address
|
||||||
|
* - Attribute 12: Capability Flags
|
||||||
|
*
|
||||||
|
* Non-implemented attributes
|
||||||
|
* --------------------------
|
||||||
|
* The attributes 3, 4, 5, 6, 7, 8, 9 and 11 are only required for devices
|
||||||
|
* that are capable of functioning as a ring supervisor. These attributes
|
||||||
|
* shall not be implemented by non-supervisor devices.
|
||||||
|
*
|
||||||
|
* The attributes 13, 14, 15 and 16 are only required for devices that are
|
||||||
|
* capable of functioning as a redundant gateway. These attributes shall
|
||||||
|
* not be implemented by non-redundant gateway devices.
|
||||||
|
*
|
||||||
|
* None of the attributes 17, 18 and 19 is required and implemented.
|
||||||
|
* Because of this the Object Revision stays on level 3 (see @ref
|
||||||
|
* DLR_CLASS_REVISION).
|
||||||
|
*
|
||||||
|
* Implemented Services
|
||||||
|
* --------------------
|
||||||
|
* - GetAttributesAll
|
||||||
|
* - GetAttributeSingle
|
||||||
|
*/
|
||||||
|
/* ********************************************************************
|
||||||
|
* include files
|
||||||
|
*/
|
||||||
|
#include "cipdlr.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* defines
|
||||||
|
*/
|
||||||
|
/** The implemented class revision is still 3 because the attributes
|
||||||
|
* mandatory for revision 4 are NOT implemented. */
|
||||||
|
#define DLR_CLASS_REVISION 3
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* Type declarations
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* module local variables
|
||||||
|
*/
|
||||||
|
/* Define variables with default values to be used for the
|
||||||
|
* GetAttributeAll response for not implemented attributes. */
|
||||||
|
static const CipUsint s_0xFF_default = 0xFFU;
|
||||||
|
static const CipUint s_0xFFFF_default = 0xFFFFU;
|
||||||
|
|
||||||
|
static const CipUsint s_0x00_default = 0x00U;
|
||||||
|
static const CipUint s_0x0000_default = 0x0000U;
|
||||||
|
static const CipUdint s_0x00000000_default = 0x00000000U;
|
||||||
|
|
||||||
|
static const CipNodeAddress s_zero_node = {
|
||||||
|
.device_ip = 0,
|
||||||
|
.device_mac = {
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* global public variables
|
||||||
|
*/
|
||||||
|
CipDlrObject g_dlr; /**< definition of DLR object instance 1 data */
|
||||||
|
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* local functions
|
||||||
|
*/
|
||||||
|
static void EncodeCipRingSupervisorConfig(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
const size_t kRingSupStructSize = 12u;
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
|
||||||
|
kRingSupStructSize,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EncodeCipNodeAddress(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
CipNodeAddress *node_address = (CipNodeAddress *)data;
|
||||||
|
EncodeCipUdint(&node_address->device_ip, outgoing_message);
|
||||||
|
EncodeCipEthernetLinkPhyisicalAddress(&node_address->device_mac,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* public functions
|
||||||
|
*/
|
||||||
|
EipStatus CipDlrInit(void) {
|
||||||
|
CipClass *dlr_class = NULL;
|
||||||
|
|
||||||
|
dlr_class = CreateCipClass(kCipDlrClassCode,
|
||||||
|
0, /* # class attributes */
|
||||||
|
7, /* # highest class attribute number */
|
||||||
|
2, /* # class services */
|
||||||
|
11,/* # instance attributes */
|
||||||
|
12,/* # of highest instance attribute */
|
||||||
|
2, /* # instance services */
|
||||||
|
1, /* # instances */
|
||||||
|
"DLR", /* object class name */
|
||||||
|
DLR_CLASS_REVISION, /* # class revision */
|
||||||
|
NULL /* function pointer for initialization */
|
||||||
|
);
|
||||||
|
|
||||||
|
if (NULL == dlr_class) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add services to the class */
|
||||||
|
InsertService(dlr_class, kGetAttributeSingle,
|
||||||
|
GetAttributeSingle, "GetAttributeSingle");
|
||||||
|
InsertService(dlr_class, kGetAttributeAll,
|
||||||
|
GetAttributeAll, "GetAttributeAll");
|
||||||
|
|
||||||
|
/* Bind attributes to the instance */
|
||||||
|
CipInstance *dlr_instance = GetCipInstance(dlr_class, 1u);
|
||||||
|
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
1,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_dlr.network_topology,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
2,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_dlr.network_status,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
3,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_0xFF_default,
|
||||||
|
kGetableAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
4,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipRingSupervisorConfig,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_0x00000000_default,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
5,
|
||||||
|
kCipUint,
|
||||||
|
EncodeCipUint,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_0x0000_default,
|
||||||
|
kGetableAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
6,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipNodeAddress,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_zero_node,
|
||||||
|
kGetableAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
7,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipNodeAddress,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_zero_node,
|
||||||
|
kGetableAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
8,
|
||||||
|
kCipUint,
|
||||||
|
EncodeCipUint,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_0xFFFF_default,
|
||||||
|
kGetableAll);
|
||||||
|
/* Attribute #9 is not implemented and also NOT part of the GetAttributesAll
|
||||||
|
* response. Therefore it is not added here! */
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
10,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipNodeAddress,
|
||||||
|
NULL,
|
||||||
|
&g_dlr.active_supervisor_address,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
11,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
(void *)&s_0x00_default,
|
||||||
|
kGetableAll);
|
||||||
|
InsertAttribute(dlr_instance,
|
||||||
|
12,
|
||||||
|
kCipDword,
|
||||||
|
EncodeCipDword,
|
||||||
|
NULL,
|
||||||
|
&g_dlr.capability_flags,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
|
||||||
|
/* Set attributes to initial values */
|
||||||
|
/* Assume beacon based DLR device. Also all Revision 3 and higher devices
|
||||||
|
* are required to support the Flush_Tables and Learning_Update frames
|
||||||
|
* (see Vol. 2 Section 5-6.2 Revision History of the DLR object).*/
|
||||||
|
g_dlr.capability_flags = (kDlrCapBeaconBased | kDlrCapFlushTableFrame);
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
84
OrginalSourceRepo/OpENer-master/source/src/cip/cipdlr.h
Normal file
84
OrginalSourceRepo/OpENer-master/source/src/cip/cipdlr.h
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* Copyright (c) 2019, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Declare public interface of the DLR object
|
||||||
|
*
|
||||||
|
* @author Stefan Maetje <stefan.maetje@esd.eu>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENER_CIPDLR_H_
|
||||||
|
#define OPENER_CIPDLR_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief DLR object class code */
|
||||||
|
static const CipUint kCipDlrClassCode = 0x47U;
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* Type declarations
|
||||||
|
*/
|
||||||
|
/** @brief Provide bit masks for the Capability Flags attribute (#12)
|
||||||
|
*
|
||||||
|
* The @ref kDlrCapAnnounceBased and @ref kDlrCapBeaconBased capability
|
||||||
|
* flags are mutually exclusive but one of it must be set.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/** Device is an announce based ring node. */
|
||||||
|
kDlrCapAnnounceBased = 0x01,
|
||||||
|
/** Device is a beacon based ring node. */
|
||||||
|
kDlrCapBeaconBased = 0x02,
|
||||||
|
/** The device is capable of providing the supervisor function. */
|
||||||
|
kDlrCapSupervisor = 0x20,
|
||||||
|
/** The device is capable of providing the redundant gateway function. */
|
||||||
|
kDlrCapRedundantGateway = 0x40,
|
||||||
|
/** The device is capable of supporting the Flush_Tables frame. */
|
||||||
|
kDlrCapFlushTableFrame = 0x80,
|
||||||
|
} CipDlrCapabilityFlags;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Node address information for a DLR node
|
||||||
|
*
|
||||||
|
* This is the node address information that uniquely identifies a
|
||||||
|
* participant of the DLR protocol.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUdint device_ip; /**< IP address of a participating DLR node */
|
||||||
|
CipUsint device_mac[6]; /**< MAC address of a participating DLR node */
|
||||||
|
} CipNodeAddress;
|
||||||
|
|
||||||
|
/** @brief Type declaration for the DLR object
|
||||||
|
*
|
||||||
|
* This is the type declaration for the DLR object. It contains only the
|
||||||
|
* attributes needed for a non supervisor and non redundant gateway
|
||||||
|
* ring participant.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUsint network_topology; /**< Attribute #1: */
|
||||||
|
CipUsint network_status; /**< Attribute #2: */
|
||||||
|
CipNodeAddress active_supervisor_address; /**< Attribute #10: */
|
||||||
|
CipDword capability_flags; /**< Attribute #12: */
|
||||||
|
} CipDlrObject;
|
||||||
|
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* global public variables
|
||||||
|
*/
|
||||||
|
extern CipDlrObject g_dlr; /**< declaration of DLR object instance 1 data */
|
||||||
|
|
||||||
|
|
||||||
|
/* ********************************************************************
|
||||||
|
* public functions
|
||||||
|
*/
|
||||||
|
/** @brief Initializing the data structures of the DLR object
|
||||||
|
*
|
||||||
|
* @return kEipStatusOk on success, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus CipDlrInit(void);
|
||||||
|
|
||||||
|
#endif /* of OPENER_CIPDLR_H_ */
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2016, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "cipelectronickey.h"
|
||||||
|
|
||||||
|
void ElectronicKeySetKeyFormat(CipElectronicKey *const electronic_key,
|
||||||
|
const CipUsint key_format) {
|
||||||
|
electronic_key->key_format = key_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ElectronicKeyGetKeyFormat(const CipElectronicKey *const electronic_key)
|
||||||
|
{
|
||||||
|
return electronic_key->key_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeySetKeyData(CipElectronicKey *const electronic_key,
|
||||||
|
void *key_data) {
|
||||||
|
electronic_key->key_data = key_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ElectronicKeyGetKeyData(const CipElectronicKey *const electronic_key) {
|
||||||
|
return electronic_key->key_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct electronic_key_format_4 {
|
||||||
|
CipUint vendor_id;
|
||||||
|
CipUint device_type;
|
||||||
|
CipUint product_code;
|
||||||
|
CipByte major_revision_compatibility;
|
||||||
|
CipUsint minor_revision;
|
||||||
|
} ElectronicKeyFormat4;
|
||||||
|
|
||||||
|
const size_t kElectronicKeyFormat4Size = sizeof(ElectronicKeyFormat4);
|
||||||
|
|
||||||
|
ElectronicKeyFormat4 *ElectronicKeyFormat4New() {
|
||||||
|
return (ElectronicKeyFormat4 *)calloc( 1, sizeof(ElectronicKeyFormat4) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4Delete(ElectronicKeyFormat4 **electronic_key) {
|
||||||
|
free(*electronic_key);
|
||||||
|
*electronic_key = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4SetVendorId(ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint vendor_id) {
|
||||||
|
electronic_key->vendor_id = vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ElectronicKeyFormat4GetVendorId(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
return electronic_key->vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4SetDeviceType(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint device_type) {
|
||||||
|
electronic_key->device_type = device_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ElectronicKeyFormat4GetDeviceType(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
return electronic_key->device_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4SetProductCode(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint product_code) {
|
||||||
|
electronic_key->product_code = product_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ElectronicKeyFormat4GetProductCode(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
return electronic_key->product_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4SetMajorRevisionCompatibility(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipByte major_revision_compatibility) {
|
||||||
|
electronic_key->major_revision_compatibility = major_revision_compatibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipByte ElectronicKeyFormat4GetMajorRevision(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
const CipByte kMajorRevisionMask = 0x7F;
|
||||||
|
return (electronic_key->major_revision_compatibility & kMajorRevisionMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ElectronicKeyFormat4GetMajorRevisionCompatibility(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
const CipByte kCompatibilityMask = 0x80;
|
||||||
|
if( kCompatibilityMask ==
|
||||||
|
(electronic_key->major_revision_compatibility & kCompatibilityMask) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronicKeyFormat4SetMinorRevision(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUsint minor_revision) {
|
||||||
|
electronic_key->minor_revision = minor_revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUsint ElectronicKeyFormat4GetMinorRevision(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key) {
|
||||||
|
return electronic_key->minor_revision;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2016, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SRC_CIP_CIPELECTRONICKEY_H_
|
||||||
|
#define SRC_CIP_CIPELECTRONICKEY_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
|
||||||
|
/** @brief CIP Electronic Key Segment struct
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUsint key_format; /**< Key Format 0-3 reserved, 4 = see Key Format Table,
|
||||||
|
5-255 = Reserved */
|
||||||
|
void *key_data; /**< Depends on key format used, usually Key Format 4 as
|
||||||
|
specified in CIP Specification, Volume 1*/
|
||||||
|
} CipElectronicKey;
|
||||||
|
|
||||||
|
void ElectronicKeySetSegmentType(CipElectronicKey *const electronic_key,
|
||||||
|
const CipUsint segment_type);
|
||||||
|
|
||||||
|
CipUsint ElectronicKeyGetSegmentType(
|
||||||
|
const CipElectronicKey *const electronic_key);
|
||||||
|
|
||||||
|
void ElectronicKeySetKeyFormat(CipElectronicKey *const electronic_key,
|
||||||
|
const CipUsint key_format);
|
||||||
|
|
||||||
|
CipUint ElectronicKeyGetKeyFormat(const CipElectronicKey *const electronic_key);
|
||||||
|
|
||||||
|
void ElectronicKeySetKeyData(CipElectronicKey *const electronic_key,
|
||||||
|
void *key_data);
|
||||||
|
|
||||||
|
void *ElectronicKeyGetKeyData(const CipElectronicKey *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Declaration of the electronic key format 4 data struct for the class
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct electronic_key_format_4 ElectronicKeyFormat4;
|
||||||
|
|
||||||
|
extern const size_t kElectronicKeyFormat4Size;
|
||||||
|
|
||||||
|
/** @brief Constructor for the electroic key format 4 class
|
||||||
|
*
|
||||||
|
* @return A new unset electronic key
|
||||||
|
*/
|
||||||
|
ElectronicKeyFormat4 *ElectronicKeyFormat4New(
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @brief Destructor for the electroic key format 4 class
|
||||||
|
*
|
||||||
|
* Safe destructor/free, nulls the pointer after freeing it
|
||||||
|
* @param electronic_key A format 4 electronic key
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4Delete(ElectronicKeyFormat4 **electronic_key);
|
||||||
|
|
||||||
|
/** @brief Sets vendor ID in the electronic key
|
||||||
|
* @param electronic_key The electronic key to be set - will be modified
|
||||||
|
* @param vendor_id The vendor ID to be set into the electronic key
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4SetVendorId(ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint vendor_id);
|
||||||
|
|
||||||
|
/** @brief Gets the vendor ID form the electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The format 4 electronic key from which the vendor ID will be extracted
|
||||||
|
* @return The vendor ID
|
||||||
|
*/
|
||||||
|
CipUint ElectronicKeyFormat4GetVendorId(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Sets the device type in the electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key A format 4 electronic key
|
||||||
|
* @param device_type The device type which shall be inserted into the electronic key
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4SetDeviceType(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint device_type);
|
||||||
|
|
||||||
|
/** @brief Gets the device type from a format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The format 4 electronic key from which the device type will be extracted
|
||||||
|
* @return The device type
|
||||||
|
*/
|
||||||
|
CipUint ElectronicKeyFormat4GetDeviceType(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Set product code in the electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The electronic key to be modified
|
||||||
|
* @param product_code The product code to be inserted
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4SetProductCode(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUint product_code);
|
||||||
|
|
||||||
|
/** @brief Gets the product code from an format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The format 4 electronic key to be read
|
||||||
|
* @return The product code
|
||||||
|
*/
|
||||||
|
CipUint ElectronicKeyFormat4GetProductCode(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Sets the major revision byte including the compatibility flag
|
||||||
|
*
|
||||||
|
* @param major_revision_compatibility The major revision byte including the compatibility flag
|
||||||
|
* @param electronic_key The electronic key to be modified
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4SetMajorRevisionCompatibility(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipByte major_revision_compatibility);
|
||||||
|
|
||||||
|
/** @brief Gets the major revision from an format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key An format 4 electronic key
|
||||||
|
* @return The device's major revision
|
||||||
|
*/
|
||||||
|
CipByte ElectronicKeyFormat4GetMajorRevision(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Gets the Compatibility flag from the format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The format 4 electronic key to be read
|
||||||
|
* @return True if compatibility bit is set, false if otherwise
|
||||||
|
*/
|
||||||
|
bool ElectronicKeyFormat4GetMajorRevisionCompatibility(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
/** @brief Sets the devices minor revision in an format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param minor_revision The minor revision to be set in the electronic key
|
||||||
|
* @param electronic_key The electronic key to be modified
|
||||||
|
*/
|
||||||
|
void ElectronicKeyFormat4SetMinorRevision(
|
||||||
|
ElectronicKeyFormat4 *const electronic_key,
|
||||||
|
const CipUsint minor_revision);
|
||||||
|
|
||||||
|
/** @brief Gets the minor revision from an format 4 electronic key
|
||||||
|
*
|
||||||
|
* @param electronic_key The format 4 electronic key to be read
|
||||||
|
* @return The device's minor revision
|
||||||
|
*/
|
||||||
|
CipUsint ElectronicKeyFormat4GetMinorRevision(
|
||||||
|
const ElectronicKeyFormat4 *const electronic_key);
|
||||||
|
|
||||||
|
#endif /* SRC_CIP_CIPELECTRONICKEY_H_ */
|
||||||
696
OrginalSourceRepo/OpENer-master/source/src/cip/cipepath.c
Normal file
696
OrginalSourceRepo/OpENer-master/source/src/cip/cipepath.c
Normal file
|
|
@ -0,0 +1,696 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "cipepath.h"
|
||||||
|
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "cipelectronickey.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
const unsigned int kPortSegmentExtendedPort = 15; /**< Reserved port segment port value, indicating the use of the extended port field */
|
||||||
|
|
||||||
|
/*** Path Segment ***/
|
||||||
|
SegmentType GetPathSegmentType(const CipOctet *const cip_path) {
|
||||||
|
const unsigned int kSegmentTypeMask = 0xE0;
|
||||||
|
const unsigned int segment_type = *cip_path & kSegmentTypeMask;
|
||||||
|
SegmentType result = kSegmentTypeInvalid;
|
||||||
|
switch(segment_type) {
|
||||||
|
case SEGMENT_TYPE_PORT_SEGMENT:
|
||||||
|
result = kSegmentTypePortSegment;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_LOGICAL_SEGMENT:
|
||||||
|
result = kSegmentTypeLogicalSegment;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_NETWORK_SEGMENT:
|
||||||
|
result = kSegmentTypeNetworkSegment;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_SYMBOLIC_SEGMENT:
|
||||||
|
result = kSegmentTypeSymbolicSegment;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_DATA_SEGMENT:
|
||||||
|
result = kSegmentTypeDataSegment;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_DATA_TYPE_CONSTRUCTED:
|
||||||
|
result = kSegmentTypeDataTypeConstructed;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_DATA_TYPE_ELEMENTARTY:
|
||||||
|
result = kSegmentTypeDataTypeElementary;
|
||||||
|
break;
|
||||||
|
case SEGMENT_TYPE_SEGMENT_RESERVED:
|
||||||
|
result = kSegmentTypeReserved;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Invalid Segment type in the message! We should never come here!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPathSegmentType(SegmentType segment_type,
|
||||||
|
unsigned char *const cip_path) {
|
||||||
|
switch(segment_type) {
|
||||||
|
case kSegmentTypePortSegment:
|
||||||
|
*cip_path = SEGMENT_TYPE_PORT_SEGMENT;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeLogicalSegment:
|
||||||
|
*cip_path = SEGMENT_TYPE_LOGICAL_SEGMENT;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeNetworkSegment:
|
||||||
|
*cip_path = SEGMENT_TYPE_NETWORK_SEGMENT;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeSymbolicSegment:
|
||||||
|
*cip_path = SEGMENT_TYPE_SYMBOLIC_SEGMENT;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeDataSegment:
|
||||||
|
*cip_path = SEGMENT_TYPE_DATA_SEGMENT;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeDataTypeConstructed:
|
||||||
|
*cip_path = SEGMENT_TYPE_DATA_TYPE_CONSTRUCTED;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeDataTypeElementary:
|
||||||
|
*cip_path = SEGMENT_TYPE_DATA_TYPE_ELEMENTARTY;
|
||||||
|
break;
|
||||||
|
case kSegmentTypeReserved:
|
||||||
|
*cip_path = SEGMENT_TYPE_SEGMENT_RESERVED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Invalid Segment type chosen! We should never come here!\n");
|
||||||
|
OPENER_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Port Segment ***/
|
||||||
|
bool GetPathPortSegmentExtendedLinkAddressSizeBit(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
const unsigned int kExtendedLinkAddressSizeMask = 0x10;
|
||||||
|
if(kExtendedLinkAddressSizeMask ==
|
||||||
|
(*cip_path & kExtendedLinkAddressSizeMask) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int GetPathPortSegmentPortIdentifier(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
const unsigned int kPortIdentifierMask = 0x0F;
|
||||||
|
unsigned int port_identifier = *cip_path & kPortIdentifierMask;
|
||||||
|
|
||||||
|
OPENER_ASSERT(kSegmentTypePortSegment == GetPathSegmentType(cip_path) );
|
||||||
|
/* Use of reserved port identifier 0 */
|
||||||
|
OPENER_ASSERT(0 != port_identifier);
|
||||||
|
return port_identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPathPortSegmentPortIdentifier(const unsigned int port_identifier,
|
||||||
|
unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(
|
||||||
|
port_identifier < 16,
|
||||||
|
"Port identifier too large for standard port identifier field\n"); */
|
||||||
|
OPENER_ASSERT(port_identifier < 16);
|
||||||
|
(*cip_path) |= port_identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int GetPathPortSegmentLinkAddressSize(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(false == GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path),
|
||||||
|
"Call to non existent extended link address size\n"); */
|
||||||
|
OPENER_ASSERT(true ==
|
||||||
|
GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) );
|
||||||
|
return *(cip_path + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int GetPathPortSegmentExtendedPortNumber(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(kPortSegmentExtendedPort == GetPathPortSegmentPortIdentifier(cip_path),
|
||||||
|
"There is no extended port available!\n");*/
|
||||||
|
OPENER_ASSERT(kPortSegmentExtendedPort ==
|
||||||
|
GetPathPortSegmentPortIdentifier(cip_path) );
|
||||||
|
const unsigned int kExtendedPortSegmentPosition =
|
||||||
|
GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) == true ? 2 : 1;
|
||||||
|
return cip_path[kExtendedPortSegmentPosition] +
|
||||||
|
(cip_path[kExtendedPortSegmentPosition + 1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPathPortSegmentExtendedPortIdentifier(
|
||||||
|
const unsigned int extended_port_identifier,
|
||||||
|
CipOctet *const cip_path) {
|
||||||
|
SetPathPortSegmentPortIdentifier(kPortSegmentExtendedPort, cip_path);
|
||||||
|
const unsigned int kExtendedPortSegmentPosition =
|
||||||
|
GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) == true ? 2 : 1;
|
||||||
|
cip_path[kExtendedPortSegmentPosition] =
|
||||||
|
(char) (extended_port_identifier & 0x00FF);
|
||||||
|
cip_path[kExtendedPortSegmentPosition +
|
||||||
|
1] = (char) ( (extended_port_identifier & 0xFF00) >> 8 );
|
||||||
|
}
|
||||||
|
/*** Port Segment ***/
|
||||||
|
|
||||||
|
/*** Logical Segment ***/
|
||||||
|
|
||||||
|
LogicalSegmentLogicalType GetPathLogicalSegmentLogicalType(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
|
||||||
|
const unsigned int kLogicalTypeMask = 0x1C;
|
||||||
|
const unsigned int logical_type = (*cip_path) & kLogicalTypeMask;
|
||||||
|
LogicalSegmentLogicalType result = kLogicalSegmentLogicalTypeInvalid;
|
||||||
|
switch(logical_type) {
|
||||||
|
case LOGICAL_SEGMENT_TYPE_CLASS_ID:
|
||||||
|
result = kLogicalSegmentLogicalTypeClassId;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_INSTANCE_ID:
|
||||||
|
result = kLogicalSegmentLogicalTypeInstanceId;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_MEMBER_ID:
|
||||||
|
result = kLogicalSegmentLogicalTypeMemberId;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_CONNECTION_POINT:
|
||||||
|
result = kLogicalSegmentLogicalTypeConnectionPoint;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_ATTRIBUTE_ID:
|
||||||
|
result = kLogicalSegmentLogicalTypeAttributeId;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_SPECIAL:
|
||||||
|
result = kLogicalSegmentLogicalTypeSpecial;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_SERVICE_ID:
|
||||||
|
result = kLogicalSegmentLogicalTypeServiceId;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_TYPE_EXTENDED_LOGICAL:
|
||||||
|
result = kLogicalSegmentLogicalTypeExtendedLogical;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: Invalid input!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPathLogicalSegmentLogicalType(LogicalSegmentLogicalType logical_type,
|
||||||
|
CipOctet *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
|
||||||
|
switch(logical_type) {
|
||||||
|
case kLogicalSegmentLogicalTypeClassId:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_CLASS_ID;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeInstanceId:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_INSTANCE_ID;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeMemberId:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_MEMBER_ID;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeConnectionPoint:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_CONNECTION_POINT;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeAttributeId:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_ATTRIBUTE_ID;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeSpecial:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_SPECIAL;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeServiceId:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_SERVICE_ID;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalTypeExtendedLogical:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_TYPE_EXTENDED_LOGICAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: It is not possible to reach this point!\n");
|
||||||
|
OPENER_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicalSegmentLogicalFormat GetPathLogicalSegmentLogicalFormat(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
|
||||||
|
const unsigned int kLogicalFormatMask = 0x03;
|
||||||
|
const unsigned int logical_format = (*cip_path) & kLogicalFormatMask;
|
||||||
|
LogicalSegmentLogicalFormat result = kLogicalSegmentLogicalFormatInvalid;
|
||||||
|
switch(logical_format) {
|
||||||
|
case LOGICAL_SEGMENT_FORMAT_EIGHT_BIT:
|
||||||
|
result = kLogicalSegmentLogicalFormatEightBit;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_FORMAT_SIXTEEN_BIT:
|
||||||
|
result = kLogicalSegmentLogicalFormatSixteenBit;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_FORMAT_THIRTY_TWO_BIT:
|
||||||
|
result = kLogicalSegmentLogicalFormatThirtyTwoBit;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: Invalid logical type detected!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPathLogicalSegmentLogicalFormat(LogicalSegmentLogicalFormat format,
|
||||||
|
CipOctet *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeLogicalSegment ==
|
||||||
|
GetPathSegmentType( (const CipOctet * )cip_path ) );
|
||||||
|
switch(format) {
|
||||||
|
case kLogicalSegmentLogicalFormatEightBit:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_FORMAT_EIGHT_BIT;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatSixteenBit:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_FORMAT_SIXTEEN_BIT;
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatThirtyTwoBit:
|
||||||
|
(*cip_path) |= LOGICAL_SEGMENT_FORMAT_THIRTY_TWO_BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: Invalid logical type detected!\n");
|
||||||
|
OPENER_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipDword CipEpathGetLogicalValue(const EipUint8 **message) {
|
||||||
|
LogicalSegmentLogicalFormat logical_format =
|
||||||
|
GetPathLogicalSegmentLogicalFormat(*message);
|
||||||
|
CipDword data = kLogicalSegmentLogicalFormatInvalid;
|
||||||
|
(*message) += 1; /* Move to logical value */
|
||||||
|
switch(logical_format) {
|
||||||
|
case kLogicalSegmentLogicalFormatEightBit:
|
||||||
|
data = GetByteFromMessage(message);
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatSixteenBit:
|
||||||
|
(*message) += 1; /* Pad byte needs to be skipped */
|
||||||
|
data = GetWordFromMessage(message);
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatThirtyTwoBit:
|
||||||
|
(*message) += 1; /* Pad byte needs to be skipped */
|
||||||
|
data = GetDwordFromMessage(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: Invalid logical value detected!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipEpathSetLogicalValue(const CipDword logical_value,
|
||||||
|
const LogicalSegmentLogicalFormat logical_format,
|
||||||
|
CipMessageRouterResponse *const message) {
|
||||||
|
switch(logical_format) {
|
||||||
|
case kLogicalSegmentLogicalFormatEightBit:
|
||||||
|
OPENER_ASSERT( (logical_value <= UINT8_MAX) ); /* Sanity check before casting to a smaller integer. */
|
||||||
|
AddSintToMessage( (EipUint8)logical_value, &message->message );
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatSixteenBit:
|
||||||
|
MoveMessageNOctets(1, &message->message); /* Needed for padding */
|
||||||
|
OPENER_ASSERT( (logical_value <= UINT16_MAX) ); /* Sanity check before casting to a smaller integer. */
|
||||||
|
AddIntToMessage( (EipUint16)logical_value, &message->message );
|
||||||
|
break;
|
||||||
|
case kLogicalSegmentLogicalFormatThirtyTwoBit:
|
||||||
|
MoveMessageNOctets(1, &message->message); /* Needed for padding */
|
||||||
|
AddDintToMessage(logical_value, &message->message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_ASSERT(false); /* This should never happen! */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicalSegmentExtendedLogicalType GetPathLogicalSegmentExtendedLogicalType(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(LOGICAL_SEGMENT_TYPE_EXTENDED_kLogicalSegmentLogicalTypeExtendedLogicalMessageValue == GetPathLogicalSegmentLogicalType(cip_path),
|
||||||
|
"Trying to extract non-existent extended logical type") */
|
||||||
|
OPENER_ASSERT(kLogicalSegmentLogicalTypeExtendedLogical == GetPathLogicalSegmentLogicalType(
|
||||||
|
cip_path) );
|
||||||
|
const unsigned int extended_logical_type = *(cip_path + 1);
|
||||||
|
LogicalSegmentExtendedLogicalType result =
|
||||||
|
kLogicalSegmentExtendedLogicalTypeInvalid;
|
||||||
|
switch(extended_logical_type) {
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_ARRAY_INDEX:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeArrayIndex;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_INDIRECT_ARRAY_INDEX:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeIndirectArrayIndex;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_BIT_INDEX:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeBitIndex;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_INDIRECT_BIT_INDEX:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeIndirectBitIndex;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_STRUCTURE_MEMBER_NUMBER:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeStructureMemberNumber;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_STRUCTURE_MEMBER_HANDLE:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeStructureMemberHandle;
|
||||||
|
break;
|
||||||
|
case LOGICAL_SEGMENT_EXTENDED_TYPE_RESERVED:
|
||||||
|
result = kLogicalSegmentExtendedLogicalTypeReserved;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Logical segment/logical type: Invalid extended type detected!\n");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicalSegmentSpecialTypeLogicalFormat
|
||||||
|
GetPathLogicalSegmentSpecialTypeLogicalType(const unsigned char *const cip_path)
|
||||||
|
{
|
||||||
|
/* OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path), "Not a logical segment!\n") */
|
||||||
|
OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
|
||||||
|
OPENER_ASSERT(kLogicalSegmentLogicalTypeSpecial == GetPathLogicalSegmentLogicalType(
|
||||||
|
cip_path) );
|
||||||
|
const unsigned int kLogicalFormatMask = 0x03;
|
||||||
|
const unsigned int logical_format = (*cip_path) & kLogicalFormatMask;
|
||||||
|
|
||||||
|
LogicalSegmentSpecialTypeLogicalFormat result =
|
||||||
|
kLogicalSegmentSpecialTypeLogicalFormatReserved;
|
||||||
|
switch(logical_format) {
|
||||||
|
case LOGICAL_SEGMENT_SPECIAL_TYPE_FORMAT_ELECTRONIC_KEY:
|
||||||
|
result = kLogicalSegmentSpecialTypeLogicalFormatElectronicKey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kLogicalSegmentSpecialTypeLogicalFormatReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElectronicKeySegmentFormat GetPathLogicalSegmentElectronicKeyFormat(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(kLogicalSegmentSpecialTypeLogicalFormatElectronicKey ==
|
||||||
|
GetPathLogicalSegmentSpecialTypeLogicalType(cip_path), "Not an electronic key!\n") */
|
||||||
|
OPENER_ASSERT(kLogicalSegmentSpecialTypeLogicalFormatElectronicKey == GetPathLogicalSegmentSpecialTypeLogicalType(
|
||||||
|
cip_path) );
|
||||||
|
ElectronicKeySegmentFormat result = kElectronicKeySegmentFormatReserved;
|
||||||
|
switch(*(cip_path + 1) ) {
|
||||||
|
case ELECTRONIC_KEY_SEGMENT_KEY_FORMAT_4:
|
||||||
|
result = kElectronicKeySegmentFormatKeyFormat4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kElectronicKeySegmentFormatReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetElectronicKeyFormat4FromMessage(const CipOctet **const message,
|
||||||
|
ElectronicKeyFormat4 *key) {
|
||||||
|
OPENER_ASSERT(kElectronicKeySegmentFormatKeyFormat4 == GetPathLogicalSegmentElectronicKeyFormat(
|
||||||
|
*message) );
|
||||||
|
|
||||||
|
(*message) += 2;
|
||||||
|
ElectronicKeyFormat4SetVendorId(key, GetUintFromMessage(message) );
|
||||||
|
ElectronicKeyFormat4SetDeviceType(key, GetUintFromMessage(message) );
|
||||||
|
ElectronicKeyFormat4SetProductCode(key, GetUintFromMessage(message) );
|
||||||
|
ElectronicKeyFormat4SetMajorRevisionCompatibility(key,
|
||||||
|
GetByteFromMessage(message) );
|
||||||
|
ElectronicKeyFormat4SetMinorRevision(key, GetUsintFromMessage(message) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Logical Segment ***/
|
||||||
|
|
||||||
|
/*** Network Segment ***/
|
||||||
|
|
||||||
|
/** @brief Return the Network Segment subtype
|
||||||
|
*
|
||||||
|
* @param cip_path Pointer to the start of the EPath message
|
||||||
|
* @return The Network Segment subtype of the EPath
|
||||||
|
*/
|
||||||
|
NetworkSegmentSubtype GetPathNetworkSegmentSubtype(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
|
||||||
|
const unsigned int kSubtypeMask = 0x1F;
|
||||||
|
const unsigned int subtype = (*cip_path) & kSubtypeMask;
|
||||||
|
NetworkSegmentSubtype result = kNetworkSegmentSubtypeReserved;
|
||||||
|
switch(subtype) {
|
||||||
|
case NETWORK_SEGMENT_SCHEDULE:
|
||||||
|
result = kNetworkSegmentSubtypeScheduleSegment;
|
||||||
|
break;
|
||||||
|
case NETWORK_SEGMENT_FIXED_TAG:
|
||||||
|
result = kNetworkSegmentSubtypeFixedTagSegment;
|
||||||
|
break;
|
||||||
|
case NETWORK_SEGMENT_PRODUCTION_INHIBIT_TIME_IN_MILLISECONDS:
|
||||||
|
result = kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds;
|
||||||
|
break;
|
||||||
|
case NETWORK_SEGMENT_SAFETY:
|
||||||
|
result = kNetworkSegmentSubtypeSafetySegment;
|
||||||
|
break;
|
||||||
|
case NETWORK_SEGMENT_PRODUCTION_INHIBIT_TIME_IN_MICROSECONDS:
|
||||||
|
result = kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds;
|
||||||
|
break;
|
||||||
|
case NETWORK_SEGMENT_EXTENDED_NETWORK:
|
||||||
|
result = kNetworkSegmentSubtypeExtendedNetworkSegment;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kNetworkSegmentSubtypeReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the Production Inhibit Time in milliseconds from an EPath
|
||||||
|
*
|
||||||
|
* @param cip_path Pointer to the start of the EPath message
|
||||||
|
* @return the Production Inhibit Time in milliseconds ranging from 0 to 255
|
||||||
|
*/
|
||||||
|
CipUsint GetPathNetworkSegmentProductionInhibitTimeInMilliseconds(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n")
|
||||||
|
OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds == GetPathNetworkSegmentSubtype(cip_path),
|
||||||
|
"Not a Production Inhibit Time milliseconds segment!\n") */
|
||||||
|
OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
|
||||||
|
OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds == GetPathNetworkSegmentSubtype(
|
||||||
|
cip_path) );
|
||||||
|
return *(cip_path + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the Production Inhibit Time in microseconds from an EPath
|
||||||
|
*
|
||||||
|
* @param cip_path Pointer to the start of the EPath message
|
||||||
|
* @return the Production Inhibit Time in microseconds ranging from 0 to 4294967295
|
||||||
|
*/
|
||||||
|
CipUdint GetPathNetworkSegmentProductionInhibitTimeInMicroseconds(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n")
|
||||||
|
OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds == GetPathNetworkSegmentSubtype(cip_path),
|
||||||
|
"Not a Production Inhibit Time microseconds segment!\n")
|
||||||
|
OPENER_ASSERT(2 == *(cip_path + 1), "Data Words length is incorrect! See CIP Spec Vol.1 C-1.4.3.3.2\n") */
|
||||||
|
|
||||||
|
OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
|
||||||
|
OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds == GetPathNetworkSegmentSubtype(
|
||||||
|
cip_path) );
|
||||||
|
OPENER_ASSERT(2 == *(cip_path + 1) );
|
||||||
|
|
||||||
|
const unsigned char *message_runner = cip_path + 2;
|
||||||
|
return GetUdintFromMessage(&message_runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Network Segment ***/
|
||||||
|
|
||||||
|
/*** Symbolic Segment ***/
|
||||||
|
|
||||||
|
SymbolicSegmentFormat GetPathSymbolicSegmentFormat(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
const unsigned int kSymbolicSegmentFormatMask = 0x1F;
|
||||||
|
if(SYMBOLIC_SEGMENT_FORMAT_EXTENDED_STRING ==
|
||||||
|
(*cip_path & kSymbolicSegmentFormatMask) ) {
|
||||||
|
return kSymbolicSegmentFormatExtendedString;
|
||||||
|
}
|
||||||
|
return kSymbolicSegmentFormatASCII;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int GetPathSymbolicSegmentASCIIFormatLength(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
const unsigned int kSymbolicSegmentASCIIFormatLength = 0x1F;
|
||||||
|
const unsigned int length = *cip_path & kSymbolicSegmentASCIIFormatLength;
|
||||||
|
OPENER_ASSERT(0 != length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolicSegmentExtendedFormat GetPathSymbolicSegmentNumericType(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
const unsigned int kSymbolicSegmentExtendedFormatNumericTypeMask = 0x1F;
|
||||||
|
const unsigned int numeric_subtype = *(cip_path + 1) &
|
||||||
|
kSymbolicSegmentExtendedFormatNumericTypeMask;
|
||||||
|
SymbolicSegmentExtendedFormat result = kSymbolicSegmentExtendedFormatReserved;
|
||||||
|
switch(numeric_subtype) {
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_USINT_TYPE:
|
||||||
|
result = kSymbolicSegmentExtendedFormatNumericSymbolUSINT;
|
||||||
|
break;
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_UINT_TYPE:
|
||||||
|
result = kSymbolicSegmentExtendedFormatNumericSymbolUINT;
|
||||||
|
break;
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_UDINT_TYPE:
|
||||||
|
result = kSymbolicSegmentExtendedFormatNumericSymbolUDINT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kSymbolicSegmentExtendedFormatReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolicSegmentExtendedFormat GetPathSymbolicSegmentExtendedFormat(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
OPENER_ASSERT(kSegmentTypeSymbolicSegment == GetPathSegmentType(cip_path) );
|
||||||
|
OPENER_ASSERT(kSymbolicSegmentFormatExtendedString == GetPathSymbolicSegmentFormat(
|
||||||
|
cip_path) );
|
||||||
|
const unsigned int kSymbolicSegmentExtendedFormatMask = 0xE0;
|
||||||
|
const unsigned int extended_type = *(cip_path + 1) &
|
||||||
|
kSymbolicSegmentExtendedFormatMask;
|
||||||
|
SymbolicSegmentExtendedFormat result = kSymbolicSegmentExtendedFormatReserved;
|
||||||
|
switch(extended_type) {
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_DOUBLE_CHAR:
|
||||||
|
result = kSymbolicSegmentExtendedFormatDoubleByteChars;
|
||||||
|
break;
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_TRIPLE_CHAR:
|
||||||
|
result = kSymbolicSegmentExtendedFormatTripleByteChars;
|
||||||
|
break;
|
||||||
|
case SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC:
|
||||||
|
result = GetPathSymbolicSegmentNumericType(cip_path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kSymbolicSegmentExtendedFormatReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Symbolic Segment ***/
|
||||||
|
|
||||||
|
/*** Data Segment ***/
|
||||||
|
|
||||||
|
DataSegmentSubtype GetPathDataSegmentSubtype(const unsigned char *const cip_path)
|
||||||
|
{
|
||||||
|
const unsigned int kDataSegmentSubtypeMask = 0x1F;
|
||||||
|
const unsigned int data_subtype = (*cip_path) & kDataSegmentSubtypeMask;
|
||||||
|
|
||||||
|
DataSegmentSubtype result = kDataSegmentSubtypeReserved;
|
||||||
|
switch(data_subtype) {
|
||||||
|
case DATA_SEGMENT_SUBTYPE_SIMPLE_DATA:
|
||||||
|
result = kDataSegmentSubtypeSimpleData;
|
||||||
|
break;
|
||||||
|
case DATA_SEGMENT_SUBTYPE_ANSI_EXTENDED_SYMBOL:
|
||||||
|
result = kDataSegmentSubtypeANSIExtendedSymbol;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = kDataSegmentSubtypeReserved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Returns the amount of 16-bit data words in the Simple Data EPath
|
||||||
|
*
|
||||||
|
* @param cip_path Pointer to the start of the EPath message
|
||||||
|
* @return The amount of 16-bit words of data in the EPath
|
||||||
|
*/
|
||||||
|
CipUsint GetPathDataSegmentSimpleDataWordLength(
|
||||||
|
const unsigned char *const cip_path) {
|
||||||
|
/* OPENER_ASSERT(kSegmentTypeDataSegment == GetPathSegmentType(cip_path),"Not a data segment!\n");
|
||||||
|
OPENER_ASSERT(kDataSegmentSubtypeSimpleData == GetPathDataSegmentSubtype(cip_path), "Not a simple data segment!\n") */
|
||||||
|
OPENER_ASSERT(kSegmentTypeDataSegment == GetPathSegmentType(cip_path) );
|
||||||
|
OPENER_ASSERT(kDataSegmentSubtypeSimpleData ==
|
||||||
|
GetPathDataSegmentSubtype(cip_path) );
|
||||||
|
|
||||||
|
const unsigned char *message_runner = cip_path + 1;
|
||||||
|
return GetUsintFromMessage(&message_runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** End Data Segment ***/
|
||||||
|
|
||||||
|
/* Special purpose functions */
|
||||||
|
|
||||||
|
LogicalSegmentLogicalFormat CipEpathGetNeededLogicalFormatForValue(
|
||||||
|
CipDword value) {
|
||||||
|
LogicalSegmentLogicalFormat logical_format =
|
||||||
|
kLogicalSegmentLogicalFormatEightBit;
|
||||||
|
if(0xFF < value) {
|
||||||
|
logical_format = kLogicalSegmentLogicalFormatSixteenBit;
|
||||||
|
}
|
||||||
|
if(0xFFFF < value) {
|
||||||
|
logical_format = kLogicalSegmentLogicalFormatThirtyTwoBit;
|
||||||
|
}
|
||||||
|
return logical_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
////TODO: Does not match the actual interface anymore, check how to fix
|
||||||
|
//size_t CipEpathEncodeConnectionEpath(
|
||||||
|
// const CipConnectionPathEpath *const connection_epath,
|
||||||
|
// CipOctet **encoded_path) {
|
||||||
|
//
|
||||||
|
// size_t encoded_path_length = 0;
|
||||||
|
// {
|
||||||
|
// SetPathSegmentType(kSegmentTypeLogicalSegment, *encoded_path);
|
||||||
|
// SetPathLogicalSegmentLogicalType(kLogicalSegmentLogicalTypeClassId,
|
||||||
|
// *encoded_path);
|
||||||
|
// LogicalSegmentLogicalFormat logical_value =
|
||||||
|
// CipEpathGetNeededLogicalFormatForValue(connection_epath->class_id);
|
||||||
|
// SetPathLogicalSegmentLogicalFormat(logical_value, *encoded_path);
|
||||||
|
// encoded_path_length += 1;
|
||||||
|
// MoveMessageNOctets(1, (ENIPMessage * const) *encoded_path);
|
||||||
|
// CipEpathSetLogicalValue(connection_epath->class_id,
|
||||||
|
// logical_value,
|
||||||
|
// *encoded_path);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// SetPathSegmentType(kSegmentTypeLogicalSegment, *encoded_path);
|
||||||
|
// SetPathLogicalSegmentLogicalType(kLogicalSegmentLogicalTypeClassId,
|
||||||
|
// *encoded_path);
|
||||||
|
// LogicalSegmentLogicalFormat logical_value =
|
||||||
|
// CipEpathGetNeededLogicalFormatForValue(connection_epath->instance_id);
|
||||||
|
// SetPathLogicalSegmentLogicalFormat(logical_value, *encoded_path);
|
||||||
|
// encoded_path_length += 1;
|
||||||
|
// MoveMessageNOctets(1, (const CipOctet **) encoded_path);
|
||||||
|
// CipEpathSetLogicalValue(connection_epath->instance_id,
|
||||||
|
// logical_value,
|
||||||
|
// encoded_path);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if(0 != connection_epath->attribute_id_or_connection_point) {
|
||||||
|
// SetPathSegmentType(kSegmentTypeLogicalSegment, *encoded_path);
|
||||||
|
// SetPathLogicalSegmentLogicalType(kLogicalSegmentLogicalTypeClassId,
|
||||||
|
// *encoded_path);
|
||||||
|
// LogicalSegmentLogicalFormat logical_value =
|
||||||
|
// CipEpathGetNeededLogicalFormatForValue(
|
||||||
|
// connection_epath->attribute_id_or_connection_point);
|
||||||
|
// SetPathLogicalSegmentLogicalFormat(logical_value, *encoded_path);
|
||||||
|
// encoded_path_length += 1;
|
||||||
|
// MoveMessageNOctets(1, (const CipOctet **) encoded_path);
|
||||||
|
// CipEpathSetLogicalValue(connection_epath->attribute_id_or_connection_point,
|
||||||
|
// logical_value,
|
||||||
|
// encoded_path);
|
||||||
|
// }
|
||||||
|
// return encoded_path_length += 1;
|
||||||
|
//}
|
||||||
|
|
||||||
|
bool CipEpathEqual(const CipOctet *const path1,
|
||||||
|
const CipUint path1_length,
|
||||||
|
const CipOctet *const path2,
|
||||||
|
const CipUint path2_length) {
|
||||||
|
if(path1_length != path2_length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < path1_length; ++i) {
|
||||||
|
if(path1[i] != path2[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
392
OrginalSourceRepo/OpENer-master/source/src/cip/cipepath.h
Normal file
392
OrginalSourceRepo/OpENer-master/source/src/cip/cipepath.h
Normal file
|
|
@ -0,0 +1,392 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2016, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SRC_CIP_CIPEPATH_H_
|
||||||
|
#define SRC_CIP_CIPEPATH_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "ciptypes.h"
|
||||||
|
#include "cipelectronickey.h"
|
||||||
|
|
||||||
|
#define SEGMENT_TYPE_PORT_SEGMENT 0x00 /**< Message value of the Port segment */
|
||||||
|
#define SEGMENT_TYPE_LOGICAL_SEGMENT 0x20 /**< Message value of the Logical segment */
|
||||||
|
#define SEGMENT_TYPE_NETWORK_SEGMENT 0x40 /**< Message value of the Network segment */
|
||||||
|
#define SEGMENT_TYPE_SYMBOLIC_SEGMENT 0x60 /**< Message value of the Symbolic segment */
|
||||||
|
#define SEGMENT_TYPE_DATA_SEGMENT 0x80 /**< Message value of the Data segment */
|
||||||
|
#define SEGMENT_TYPE_DATA_TYPE_CONSTRUCTED 0xA0 /**< Message value of the Data type constructed */
|
||||||
|
#define SEGMENT_TYPE_DATA_TYPE_ELEMENTARTY 0xC0 /**< Message value of the Data type elementary */
|
||||||
|
#define SEGMENT_TYPE_SEGMENT_RESERVED 0xE0 /**< Reserved value */
|
||||||
|
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_CLASS_ID 0x00 /**< Message value of the logical segment/logical type Class ID */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_INSTANCE_ID 0x04 /**< Message value of the logical segment/logical type Instance ID */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_MEMBER_ID 0x08 /**< Message value of the logical segment/logical type Member ID */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_CONNECTION_POINT 0x0C /**< Message value of the logical segment/logical type Connection Point */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_ATTRIBUTE_ID 0x10 /**< Message value of the logical segment/logical type Attribute ID */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_SPECIAL 0x14 /**< Message value of the logical segment/logical type Special */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_SERVICE_ID 0x18 /**< Message value of the logical segment/logical type Service ID */
|
||||||
|
#define LOGICAL_SEGMENT_TYPE_EXTENDED_LOGICAL 0x1C /**< Message value of the logical segment/logical type Extended Logical */
|
||||||
|
|
||||||
|
#define LOGICAL_SEGMENT_FORMAT_EIGHT_BIT 0x00 /**< Message value indicating an 8 bit value */
|
||||||
|
#define LOGICAL_SEGMENT_FORMAT_SIXTEEN_BIT 0x01 /**< Message value indicating an 16 bit value */
|
||||||
|
#define LOGICAL_SEGMENT_FORMAT_THIRTY_TWO_BIT 0x02 /**< Message value indicating an 32 bit value */
|
||||||
|
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_RESERVED 0x00 /**< Message value indicating an reserved/unused Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_ARRAY_INDEX 0x01 /**< Message value indicating the Array Index Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_INDIRECT_ARRAY_INDEX 0x02 /**< Message value indicating the Indirect Array Index Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_BIT_INDEX 0x03 /**< Message value indicating the Bit Index Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_INDIRECT_BIT_INDEX 0x04 /**< Message value indicating the Indirect Bit Index Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_STRUCTURE_MEMBER_NUMBER 0x05 /**< Message value indicating the Structured Member Number Extended Logical Segment type */
|
||||||
|
#define LOGICAL_SEGMENT_EXTENDED_TYPE_STRUCTURE_MEMBER_HANDLE 0x06 /**< Message value indicating the Structured Member Handler Extended Logical Segment type */
|
||||||
|
|
||||||
|
#define LOGICAL_SEGMENT_SPECIAL_TYPE_FORMAT_ELECTRONIC_KEY 0x00 /**< Message value indicating an electronic key */
|
||||||
|
#define ELECTRONIC_KEY_SEGMENT_KEY_FORMAT_4 0x04
|
||||||
|
|
||||||
|
#define NETWORK_SEGMENT_SCHEDULE 0x01 /**< Message value indicating a network segment schedule message */
|
||||||
|
#define NETWORK_SEGMENT_FIXED_TAG 0x02 /**< Message value indicating a network segment fixed tag message */
|
||||||
|
#define NETWORK_SEGMENT_PRODUCTION_INHIBIT_TIME_IN_MILLISECONDS 0x03 /**< Message value indicating a network segment PIT in milliseconds message */
|
||||||
|
#define NETWORK_SEGMENT_SAFETY 0x04 /**< Message value indicating a network segment safety message */
|
||||||
|
#define NETWORK_SEGMENT_PRODUCTION_INHIBIT_TIME_IN_MICROSECONDS 0x10 /**< Message value indicating a network segment PIT in microseconds message */
|
||||||
|
#define NETWORK_SEGMENT_EXTENDED_NETWORK 0x1F /**< Message indicating a network message extended network message */
|
||||||
|
|
||||||
|
#define SYMBOLIC_SEGMENT_FORMAT_EXTENDED_STRING 0x00
|
||||||
|
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_DOUBLE_CHAR 0x20
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_TRIPLE_CHAR 0x40
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC 0xC0
|
||||||
|
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_USINT_TYPE 0x06
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_UINT_TYPE 0x07
|
||||||
|
#define SYMBOLIC_SEGMENT_EXTENDED_FORMAT_NUMERIC_UDINT_TYPE 0x08
|
||||||
|
|
||||||
|
#define DATA_SEGMENT_SUBTYPE_SIMPLE_DATA 0x00
|
||||||
|
#define DATA_SEGMENT_SUBTYPE_ANSI_EXTENDED_SYMBOL 0x11
|
||||||
|
|
||||||
|
/** @brief Segment type Enum
|
||||||
|
*
|
||||||
|
* Bits 7-5 in the Segment Type/Format byte
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum segment_type {
|
||||||
|
/* Segments */
|
||||||
|
kSegmentTypePortSegment, /**< Port segment */
|
||||||
|
kSegmentTypeLogicalSegment, /**< Logical segment */
|
||||||
|
kSegmentTypeNetworkSegment, /**< Network segment */
|
||||||
|
kSegmentTypeSymbolicSegment, /**< Symbolic segment */
|
||||||
|
kSegmentTypeDataSegment, /**< Data segment */
|
||||||
|
kSegmentTypeDataTypeConstructed, /**< Data type constructed */
|
||||||
|
kSegmentTypeDataTypeElementary, /**< Data type elementary */
|
||||||
|
kSegmentTypeReserved, /**< Reserved segment type */
|
||||||
|
kSegmentTypeInvalid /**< Invalid segment type */
|
||||||
|
} SegmentType;
|
||||||
|
|
||||||
|
/** @brief Port Segment flags */
|
||||||
|
typedef enum port_segment_type {
|
||||||
|
kPortSegmentFlagExtendedLinkAddressSize = 0x10 /**< Extended Link Address Size flag, Port segment */
|
||||||
|
} PortSegmentFlag;
|
||||||
|
|
||||||
|
/** @brief Enum containing values which kind of logical segment is encoded */
|
||||||
|
typedef enum logical_segment_type {
|
||||||
|
kLogicalSegmentLogicalTypeClassId, /**< Class ID */
|
||||||
|
kLogicalSegmentLogicalTypeInstanceId, /**< Instance ID */
|
||||||
|
kLogicalSegmentLogicalTypeMemberId, /**< Member ID */
|
||||||
|
kLogicalSegmentLogicalTypeConnectionPoint, /**< Connection Point */
|
||||||
|
kLogicalSegmentLogicalTypeAttributeId, /**< Attribute ID */
|
||||||
|
kLogicalSegmentLogicalTypeSpecial, /**< Special */
|
||||||
|
kLogicalSegmentLogicalTypeServiceId, /**< Service ID */
|
||||||
|
kLogicalSegmentLogicalTypeExtendedLogical, /**< Extended Logical */
|
||||||
|
kLogicalSegmentLogicalTypeInvalid /**< Invalid segment type */
|
||||||
|
} LogicalSegmentLogicalType;
|
||||||
|
|
||||||
|
typedef enum logical_segment_extended_logical_type {
|
||||||
|
kLogicalSegmentExtendedLogicalTypeReserved,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeArrayIndex,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeIndirectArrayIndex,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeBitIndex,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeIndirectBitIndex,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeStructureMemberNumber,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeStructureMemberHandle,
|
||||||
|
kLogicalSegmentExtendedLogicalTypeInvalid
|
||||||
|
} LogicalSegmentExtendedLogicalType;
|
||||||
|
|
||||||
|
/** @brief Enum containing values how long the encoded value will be (8, 16, or
|
||||||
|
* 32 bit) */
|
||||||
|
typedef enum logical_segment_logical_format {
|
||||||
|
kLogicalSegmentLogicalFormatEightBit,
|
||||||
|
kLogicalSegmentLogicalFormatSixteenBit,
|
||||||
|
kLogicalSegmentLogicalFormatThirtyTwoBit,
|
||||||
|
kLogicalSegmentLogicalFormatInvalid
|
||||||
|
} LogicalSegmentLogicalFormat;
|
||||||
|
|
||||||
|
typedef enum logical_segment_special_type_logical_format {
|
||||||
|
kLogicalSegmentSpecialTypeLogicalFormatReserved,
|
||||||
|
kLogicalSegmentSpecialTypeLogicalFormatElectronicKey
|
||||||
|
} LogicalSegmentSpecialTypeLogicalFormat;
|
||||||
|
|
||||||
|
/** @brief Electronic key formats
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum electronic_key_segment_format {
|
||||||
|
kElectronicKeySegmentFormatReserved, /**< Reserved */
|
||||||
|
kElectronicKeySegmentFormatKeyFormat4 /**< Electronic key format 4 key */
|
||||||
|
} ElectronicKeySegmentFormat;
|
||||||
|
|
||||||
|
/** @brief All types of network segment types for the use in all code
|
||||||
|
*
|
||||||
|
* Enum constants for the different network segment subtypes to decouple code from the actual needed message values
|
||||||
|
*/
|
||||||
|
typedef enum network_segment_subtype {
|
||||||
|
kNetworkSegmentSubtypeReserved, /**< Reserverd */
|
||||||
|
kNetworkSegmentSubtypeScheduleSegment, /**< Schedule segment */
|
||||||
|
kNetworkSegmentSubtypeFixedTagSegment, /**< Fixed tag segment */
|
||||||
|
kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds, /**< Production Inhibit Time in milliseconds segment */
|
||||||
|
kNetworkSegmentSubtypeSafetySegment, /**< Safety segment */
|
||||||
|
kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds, /**< Production Inhibit Time in microseconds segment */
|
||||||
|
kNetworkSegmentSubtypeExtendedNetworkSegment /**< Extended network segment */
|
||||||
|
} NetworkSegmentSubtype;
|
||||||
|
|
||||||
|
/** @brief Data segment sub types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum data_segment_subtype {
|
||||||
|
kDataSegmentSubtypeReserved, /**< Reserved */
|
||||||
|
kDataSegmentSubtypeSimpleData, /**< Simple Data segment */
|
||||||
|
kDataSegmentSubtypeANSIExtendedSymbol /**< ANSI extended symbol segment */
|
||||||
|
} DataSegmentSubtype;
|
||||||
|
|
||||||
|
/** @brief Symbolic segment formats
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum symbolic_segment_format {
|
||||||
|
kSymbolicSegmentFormatASCII, /**< ASCII format */
|
||||||
|
kSymbolicSegmentFormatExtendedString /**< Extended String format */
|
||||||
|
} SymbolicSegmentFormat;
|
||||||
|
|
||||||
|
/** @brief Extended symbolic symbol formats
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum symbolic_segment_extended_format {
|
||||||
|
kSymbolicSegmentExtendedFormatDoubleByteChars, /**< Double byte character encoding */
|
||||||
|
kSymbolicSegmentExtendedFormatTripleByteChars, /**< Triple byte character encoding */
|
||||||
|
kSymbolicSegmentExtendedFormatNumericSymbolUSINT, /**< Numeric USINT symbol */
|
||||||
|
kSymbolicSegmentExtendedFormatNumericSymbolUINT, /**< Numeric UINT symbol */
|
||||||
|
kSymbolicSegmentExtendedFormatNumericSymbolUDINT, /**< Numeric UDINT symbol */
|
||||||
|
kSymbolicSegmentExtendedFormatReserved /**< Reserved */
|
||||||
|
} SymbolicSegmentExtendedFormat;
|
||||||
|
|
||||||
|
/* Start - Often used types of EPaths */
|
||||||
|
typedef struct connection_path_epath {
|
||||||
|
CipDword class_id; /**< here in accordance with Vol. 1 C-1.4.2 */
|
||||||
|
CipInstanceNum instance_id;
|
||||||
|
CipDword attribute_id_or_connection_point;
|
||||||
|
} CipConnectionPathEpath;
|
||||||
|
/* End - Often used types of EPaths */
|
||||||
|
|
||||||
|
/** @brief Gets the basic segment type of a CIP EPath
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The basic segment type
|
||||||
|
*/
|
||||||
|
SegmentType GetPathSegmentType(const CipOctet *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Sets the basic segment type of an CIP EPath to be sent
|
||||||
|
*
|
||||||
|
* @param segment_type The segment type
|
||||||
|
* @param cip_path A message buffer - Will be written on!
|
||||||
|
*/
|
||||||
|
void SetPathSegmentType(SegmentType segment_type,
|
||||||
|
unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* Port Segment functions
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
/** @brief Only to be used on Port Segments. Returns if the Port Segment has the extended link address size bit set
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return True if extended link addres size bit set, false otherwise
|
||||||
|
*/
|
||||||
|
bool GetPathPortSegmentExtendedLinkAddressSizeBit(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Only to be used on Port Segments. Returns the Port Identifier
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Port Identifier
|
||||||
|
*/
|
||||||
|
unsigned int GetPathPortSegmentPortIdentifier(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Sets the Port Identifier form an Port Segment EPath to be sent.
|
||||||
|
*
|
||||||
|
* @param port_identifier The port identifier
|
||||||
|
* @param cip_path A message buffer - Will be written on!
|
||||||
|
*/
|
||||||
|
void SetPathPortSegmentPortIdentifier(const unsigned int port_identifier,
|
||||||
|
unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Only to be used on Port Segments. Gets the Link Address Size
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Link Address Size
|
||||||
|
*/
|
||||||
|
unsigned int GetPathPortSegmentLinkAddressSize(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Only to be used on Port Segments with Extended Port Number. Gets the Extended Port Number
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Link Address Size
|
||||||
|
*/
|
||||||
|
unsigned int GetPathPortSegmentExtendedPortNumber(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Sets the Extended Port Identifier in a EPath Port Segment message
|
||||||
|
*
|
||||||
|
* @param extended_port_identifier The extended port identifier to be encoded into the message
|
||||||
|
* @param cip_path The start for the EPatch message
|
||||||
|
*/
|
||||||
|
void SetPathPortSegmentExtendedPortIdentifier(
|
||||||
|
const unsigned int extended_port_identifier,
|
||||||
|
CipOctet *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Logical Type of an EPath Logical Segment message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The logical type of the logical segment
|
||||||
|
*/
|
||||||
|
LogicalSegmentLogicalType GetPathLogicalSegmentLogicalType(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
void SetPathLogicalSegmentLogicalType(LogicalSegmentLogicalType logical_type,
|
||||||
|
CipOctet *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Logical Format of a Logical Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The logical format of the logical format
|
||||||
|
*/
|
||||||
|
LogicalSegmentLogicalFormat GetPathLogicalSegmentLogicalFormat(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
void SetPathLogicalSegmentLogicalFormat(LogicalSegmentLogicalFormat format,
|
||||||
|
CipOctet *const cip_path);
|
||||||
|
|
||||||
|
CipDword CipEpathGetLogicalValue(const EipUint8 **message);
|
||||||
|
|
||||||
|
void CipEpathSetLogicalValue(const CipDword logical_value,
|
||||||
|
const LogicalSegmentLogicalFormat logical_format,
|
||||||
|
CipMessageRouterResponse *const message);
|
||||||
|
|
||||||
|
/** @brief Gets the Extended Logical Type of a Logical Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The extended logical type of the logical segment
|
||||||
|
*/
|
||||||
|
LogicalSegmentExtendedLogicalType GetPathLogicalSegmentExtendedLogicalType(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Special Type Logical Type of a Logical Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Special Type Logical Format subtype of a Logical Segment EPath message
|
||||||
|
*/
|
||||||
|
LogicalSegmentSpecialTypeLogicalFormat
|
||||||
|
GetPathLogicalSegmentSpecialTypeLogicalType(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Electronic Key format of a Logical Segment Special Type EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Electronic Key Format used in the EPath
|
||||||
|
*/
|
||||||
|
ElectronicKeySegmentFormat GetPathLogicalSegmentElectronicKeyFormat(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the data for an Electronic Key of format 4 from the EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @param key Writes the data on the user provided data electronic key struct
|
||||||
|
*/
|
||||||
|
void GetElectronicKeyFormat4FromMessage(const CipOctet **const cip_path,
|
||||||
|
ElectronicKeyFormat4 *key);
|
||||||
|
|
||||||
|
/** @brief Gets the Network Segment Subtype of a EPatch Network Segement EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return Network Segment subtype
|
||||||
|
*/
|
||||||
|
NetworkSegmentSubtype GetPathNetworkSegmentSubtype(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Production Inhibit Time in Milliseconds
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The production Inhibit Time in Milliseconds
|
||||||
|
*/
|
||||||
|
CipUsint GetPathNetworkSegmentProductionInhibitTimeInMilliseconds(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Production Inhibit Time in Microseconds
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The production Inhibit Time in Microseconds
|
||||||
|
*/
|
||||||
|
CipUdint GetPathNetworkSegmentProductionInhibitTimeInMicroseconds(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Data Segment subtype of a Data Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Data Segment subtype
|
||||||
|
*/
|
||||||
|
DataSegmentSubtype GetPathDataSegmentSubtype(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the data word length of a Simple Data segment
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The length in words of the Simple Data segment
|
||||||
|
*/
|
||||||
|
CipUsint GetPathDataSegmentSimpleDataWordLength(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Symbolic Segment Format of the Symbolic Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Symbolic Segment Format
|
||||||
|
*/
|
||||||
|
SymbolicSegmentFormat GetPathSymbolicSegmentFormat(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Numeric subtype of a Symbolic Segment Extended Format EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return The Numeric Extended Format subtype
|
||||||
|
*/
|
||||||
|
SymbolicSegmentExtendedFormat GetPathSymbolicSegmentNumericType(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/** @brief Gets the Extended Format subtype of a Symbolic Segment EPath message
|
||||||
|
*
|
||||||
|
* @param cip_path The start of the EPath message
|
||||||
|
* @return Symbolic Segment Extended Format
|
||||||
|
*/
|
||||||
|
SymbolicSegmentExtendedFormat GetPathSymbolicSegmentExtendedFormat(
|
||||||
|
const unsigned char *const cip_path);
|
||||||
|
|
||||||
|
/* Special purpose encoding and decoding functions */
|
||||||
|
//TODO currently not fully implemented
|
||||||
|
//size_t CipEpathEncodeConnectionEpath(
|
||||||
|
// const CipConnectionPathEpath *const connection_epath,
|
||||||
|
// CipOctet **encoded_path);
|
||||||
|
|
||||||
|
bool CipEpathEqual(const CipOctet *const path1,
|
||||||
|
const CipUint path1_length,
|
||||||
|
const CipOctet *const path2,
|
||||||
|
const CipUint path2_length);
|
||||||
|
|
||||||
|
#endif /* SRC_CIP_CIPEPATH_H_ */
|
||||||
59
OrginalSourceRepo/OpENer-master/source/src/cip/ciperror.h
Normal file
59
OrginalSourceRepo/OpENer-master/source/src/cip/ciperror.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPERROR_H_
|
||||||
|
#define OPENER_CIPERROR_H_
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kCipErrorSuccess = 0x00, /**< Service was successfully performed by the object specified. */
|
||||||
|
kCipErrorConnectionFailure = 0x01, /**< A connection related service failed along the connection path. */
|
||||||
|
kCipErrorResourceUnavailable = 0x02, /**< Resources needed for the object to perform the requested service were unavailable */
|
||||||
|
kCipErrorInvalidParameterValue = 0x03, /**< See Status Code 0x20, which is the preferred value to use for this condition. */
|
||||||
|
kCipErrorPathSegmentError = 0x04, /**< The path segment identifier or the segment syntax was not understood by the processing node. Path processing shall stop when a path segment error is encountered. */
|
||||||
|
kCipErrorPathDestinationUnknown = 0x05, /**< The path is referencing an object class, instance or structure element that is not known or is not contained in the processing node. Path processing shall stop when a path destination unknown error is encountered. */
|
||||||
|
kCipErrorPartialTransfer = 0x06, /**< Only part of the expected data was transferred. */
|
||||||
|
kCipErrorConnectionLost = 0x07, /**< The messaging connection was lost. */
|
||||||
|
kCipErrorServiceNotSupported = 0x08, /**< The requested service was not implemented or was not defined for this Object Class/Instance. */
|
||||||
|
kCipErrorInvalidAttributeValue = 0x09, /**< Invalid attribute data detected */
|
||||||
|
kCipErrorAttributeListError = 0x0A, /**< An attribute in the Get_Attribute_List or Set_Attribute_List response has a non-zero status. */
|
||||||
|
kCipErrorAlreadyInRequestedMode = 0x0B, /**< The object is already in the mode/state being requested by the service */
|
||||||
|
kCipErrorObjectStateConflict = 0x0C, /**< The object cannot perform the requested service in its current mode/state */
|
||||||
|
kCipErrorObjectAlreadyExists = 0x0D, /**< The requested instance of object to be created already exists.*/
|
||||||
|
kCipErrorAttributeNotSetable = 0x0E, /**< A request to modify a non-modifiable attribute was received. */
|
||||||
|
kCipErrorPrivilegeViolation = 0x0F, /**< A permission/privilege check failed */
|
||||||
|
kCipErrorDeviceStateConflict = 0x10, /**< The device's current mode/state prohibits the execution of the requested service. */
|
||||||
|
kCipErrorReplyDataTooLarge = 0x11, /**< The data to be transmitted in the response buffer is larger than the allocated response buffer */
|
||||||
|
kCipErrorFragmentationOfAPrimitiveValue = 0x12, /**< The service specified an operation that is going to fragment a primitive data value, i.e. half a REAL data type. */
|
||||||
|
kCipErrorNotEnoughData = 0x13, /**< The service did not supply enough data to perform the specified operation. */
|
||||||
|
kCipErrorAttributeNotSupported = 0x14, /**< The attribute specified in the request is not supported */
|
||||||
|
kCipErrorTooMuchData = 0x15, /**< The service supplied more data than was expected */
|
||||||
|
kCipErrorObjectDoesNotExist = 0x16, /**< The object specified does not exist in the device. */
|
||||||
|
kCipErrorServiceFragmentationSequenceNotInProgress = 0x17, /**< The fragmentation sequence for this service is not currently active for this data. */
|
||||||
|
kCipErrorNoStoredAttributeData = 0x18, /**< The attribute data of this object was not saved prior to the requested service. */
|
||||||
|
kCipErrorStoreOperationFailure = 0x19, /**< The attribute data of this object was not saved due to a failure during the attempt. */
|
||||||
|
kCipErrorRoutingFailureRequestPacketTooLarge = 0x1A, /**< The service request packet was too large for transmission on a network in the path to the destination. The routing device was forced to abort the service. */
|
||||||
|
kCipErrorRoutingFailureResponsePacketTooLarge = 0x1B, /**< The service response packet was too large for transmission on a network in the path from the destination. The routing device was forced to abort the service. */
|
||||||
|
kCipErrorMissingAttributeListEntry = 0x1C, /**< The service did not supply an attribute in a list of attributes that was needed by the service to perform the requested behavior. */
|
||||||
|
kCipErrorInvalidAttributeValueList = 0x1D, /**< The service is returning the list of attributes supplied with status information for those attributes that were invalid. */
|
||||||
|
kCipErrorEmbeddedServiceError = 0x1E, /**< An embedded service resulted in an error. */
|
||||||
|
kCipErrorVendorSpecificError = 0x1F, /**< A vendor specific error has been encountered. The Additional Code Field of the Error Response defines the particular error encountered. Use of this General Error Code should only be performed when none of the Error Codes presented in this table or within an Object Class definition accurately reflect the error. */
|
||||||
|
kCipErrorInvalidParameter = 0x20, /**< A parameter associated with the request was invalid. This code is used when a parameter does not meet the requirements of this specification and/or the requirements defined in an Application Object Specification. */
|
||||||
|
kCipErrorWriteonceValueOrMediumAlreadyWritten = 0x21, /**< An attempt was made to write to a write-once medium (e.g. WORM drive, PROM) that has already been written, or to modify a value that cannot be changed once established. */
|
||||||
|
kCipErrorInvalidReplyReceived = 0x22, /**< An invalid reply is received (e.g. reply service code does not match the request service code, or reply message is shorter than the minimum expected reply size). This status code can serve for other causes of invalid replies. */
|
||||||
|
/* 23-24 Reserved by CIP for future extensions */
|
||||||
|
kCipErrorKeyFailureInPath = 0x25, /**< The Key Segment that was included as the first segment in the path does not match the destination module. The object specific status shall indicate which part of the key check failed. */
|
||||||
|
kCipErrorPathSizeInvalid = 0x26, /**< The size of the path which was sent with the Service Request is either not large enough to allow the Request to be routed to an object or too much routing data was included. */
|
||||||
|
kCipErrorUnexpectedAttributeInList = 0x27, /**< An attempt was made to set an attribute that is not able to be set at this time. */
|
||||||
|
kCipErrorInvalidMemberId = 0x28, /**< The Member ID specified in the request does not exist in the specified Class/Instance/Attribute */
|
||||||
|
kCipErrorMemberNotSetable = 0x29, /**< A request to modify a non-modifiable member was received */
|
||||||
|
kCipErrorGroup2OnlyServerGeneralFailure = 0x2A, /**< This error code may only be reported by DeviceNet group 2 only servers with 4K or less code space and only in place of Service not supported, Attribute not supported and Attribute not setable. */
|
||||||
|
kCipErrorUnknownModbusError = 0x2B, /**< A CIP to Modbus translator received an unknown Modbus Exception Code. */
|
||||||
|
kCipErrorAttributeNotGettable = 0x2C, /**< A request to read a non-readable attribute was received. */
|
||||||
|
kCipErrorInstanceNotDeletable = 0x2D, /**< The requested object instance cannot be deleted. */
|
||||||
|
kCipErrorServiceNotSupportedForSpecifiedPath = 0x2E, /**< The object supports the service, but not for the designated application path (e.g. attribute). NOTE: Not to be used when a more specific General Status Code applies,
|
||||||
|
e.g. 0x0E (Attribute not settable) or 0x29 (Member not settable).*/
|
||||||
|
/* 2F - CF Reserved by CIP for future extensions D0 - FF Reserved for Object Class and service errors*/
|
||||||
|
} CipError;
|
||||||
|
#endif /* OPENER_CIPERROR_H_ */
|
||||||
620
OrginalSourceRepo/OpENer-master/source/src/cip/cipethernetlink.c
Normal file
620
OrginalSourceRepo/OpENer-master/source/src/cip/cipethernetlink.c
Normal file
|
|
@ -0,0 +1,620 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
/** @file
|
||||||
|
* @brief Implement the CIP Ethernet Link Object
|
||||||
|
*
|
||||||
|
* CIP Ethernet Link Object
|
||||||
|
* ========================
|
||||||
|
*
|
||||||
|
* Implemented Attributes
|
||||||
|
* ----------------------
|
||||||
|
* Conditional attributes are indented and marked with the condition it
|
||||||
|
* depends on like "(0 != OPENER_ETHLINK_CNTRS_ENABLE)"
|
||||||
|
*
|
||||||
|
* - Attribute 1: Interface Speed
|
||||||
|
* - Attribute 2: Interface Flags
|
||||||
|
* - Attribute 3: Physical Address (Ethernet MAC)
|
||||||
|
* - Attribute 4: Interface Counters (32-bit) (0 != OPENER_ETHLINK_CNTRS_ENABLE)
|
||||||
|
* - Attribute 5: Media Counters (32-bit) (0 != OPENER_ETHLINK_CNTRS_ENABLE)
|
||||||
|
* - Attribute 6: Interface Control (0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE)
|
||||||
|
* - Attribute 7: Interface Type
|
||||||
|
* See Vol. 2 Section 6-3.4 regarding an example for a device with internal
|
||||||
|
* switch port where the implementation of this attribute is recommended.
|
||||||
|
* - Attribute 10: Interface Label (0 != OPENER_ETHLINK_LABEL_ENABLE)
|
||||||
|
* If the define OPENER_ETHLINK_LABEL_ENABLE is set then this attribute
|
||||||
|
* has a string content ("PORT 1" by default on instance 1) otherwise it
|
||||||
|
* is empty.
|
||||||
|
* - Attribute 11: Interface Capabilities
|
||||||
|
*
|
||||||
|
* Implemented Services
|
||||||
|
* --------------------
|
||||||
|
* Conditional services are indented and marked with the condition it
|
||||||
|
* depends on like "(0 != OPENER_ETHLINK_CNTRS_ENABLE)"
|
||||||
|
*
|
||||||
|
* - GetAttributesAll
|
||||||
|
* - GetAttributeSingle
|
||||||
|
* - GetAndClearAttribute (0 != OPENER_ETHLINK_CNTRS_ENABLE)
|
||||||
|
* This service should only implemented for the attributes 4, 5, 12,
|
||||||
|
* 13 and 15.
|
||||||
|
* - SetAttributeSingle (0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE)
|
||||||
|
* This service should only be implemented if attribute 6 is enabled.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cipethernetlink.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
|
||||||
|
#if OPENER_ETHLINK_INSTANCE_CNT > 1
|
||||||
|
/* If we have more than 1 Ethernet Link instance then the interface label
|
||||||
|
* attribute is mandatory. We need then OPENER_ETHLINK_LABEL_ENABLE. */
|
||||||
|
#define OPENER_ETHLINK_LABEL_ENABLE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OPENER_ETHLINK_LABEL_ENABLE
|
||||||
|
#define OPENER_ETHLINK_LABEL_ENABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
|
||||||
|
#define IFACE_LABEL_ACCESS_MODE kGetableSingleAndAll
|
||||||
|
#define IFACE_LABEL "PORT 1"
|
||||||
|
#define IFACE_LABEL_1 "PORT 2"
|
||||||
|
#define IFACE_LABEL_2 "PORT internal"
|
||||||
|
#else
|
||||||
|
#define IFACE_LABEL_ACCESS_MODE kGetableAll
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
#define IFACE_CTRL_ACCESS_MODE (kSetAndGetAble | kNvDataFunc)
|
||||||
|
#else
|
||||||
|
#define IFACE_CTRL_ACCESS_MODE kGetableAll
|
||||||
|
#endif
|
||||||
|
/** @brief Type definition of one entry in the speed / duplex array
|
||||||
|
*/
|
||||||
|
typedef struct speed_duplex_array_entry {
|
||||||
|
CipUint interface_speed; /**< the interface speed in Mbit/s */
|
||||||
|
CipUsint interface_duplex_mode; /**< the interface's duplex mode: 0 = half duplex, 1 = full duplex, 2-255 = reserved */
|
||||||
|
} CipEthernetLinkSpeedDuplexArrayEntry;
|
||||||
|
|
||||||
|
|
||||||
|
/* forward declaration of functions to encode certain attribute objects */
|
||||||
|
static void EncodeCipEthernetLinkInterfaceCounters(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkMediaCounters(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkInterfaceControl(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkInterfaceCaps(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
/* forward declaration for the GetAndClear service handler function */
|
||||||
|
EipStatus GetAndClearEthernetLink(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
#endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
|
||||||
|
/** @brief Modify the attribute values for Attribute6: Interface Control
|
||||||
|
*
|
||||||
|
* @param CipEthernetLinkInterfaceControl pointer to attribute data.
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @return length of taken bytes
|
||||||
|
* -1 .. error
|
||||||
|
*/
|
||||||
|
int DecodeCipEthernetLinkInterfaceControl(
|
||||||
|
CipEthernetLinkInterfaceControl *const data,
|
||||||
|
const CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief This is the internal table of possible speed / duplex combinations
|
||||||
|
*
|
||||||
|
* This table contains all possible speed / duplex combinations of today.
|
||||||
|
* Which entries of this table are transmitted during the GetService
|
||||||
|
* is controlled by the
|
||||||
|
* CipEthernetLinkMetaInterfaceCapability::speed_duplex_selector bit mask.
|
||||||
|
* Therefore you need to keep this array in sync with the bit masks of
|
||||||
|
* CipEthLinkSpeedDpxSelect.
|
||||||
|
*/
|
||||||
|
static const CipEthernetLinkSpeedDuplexArrayEntry speed_duplex_table[] =
|
||||||
|
{
|
||||||
|
{ /* Index 0: 10Mbit/s half duplex*/
|
||||||
|
.interface_speed = 10,
|
||||||
|
.interface_duplex_mode = 0
|
||||||
|
},
|
||||||
|
{ /* Index 1: 10Mbit/s full duplex*/
|
||||||
|
.interface_speed = 10,
|
||||||
|
.interface_duplex_mode = 1
|
||||||
|
},
|
||||||
|
{ /* Index 2: 100Mbit/s half duplex*/
|
||||||
|
.interface_speed = 100,
|
||||||
|
.interface_duplex_mode = 0
|
||||||
|
},
|
||||||
|
{ /* Index 3: 100Mbit/s full duplex*/
|
||||||
|
.interface_speed = 100,
|
||||||
|
.interface_duplex_mode = 1
|
||||||
|
},
|
||||||
|
{ /* Index 4: 1000Mbit/s half duplex*/
|
||||||
|
.interface_speed = 1000,
|
||||||
|
.interface_duplex_mode = 0
|
||||||
|
},
|
||||||
|
{ /* Index 5: 1000Mbit/s full duplex*/
|
||||||
|
.interface_speed = 1000,
|
||||||
|
.interface_duplex_mode = 1
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
|
||||||
|
static const CipShortString iface_label_table[OPENER_ETHLINK_INSTANCE_CNT] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.length = sizeof IFACE_LABEL - 1,
|
||||||
|
.string = (EipByte *)IFACE_LABEL
|
||||||
|
},
|
||||||
|
#if OPENER_ETHLINK_INSTANCE_CNT > 1
|
||||||
|
{
|
||||||
|
.length = sizeof IFACE_LABEL_1 - 1,
|
||||||
|
.string = (EipByte *)IFACE_LABEL_1
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
#if OPENER_ETHLINK_INSTANCE_CNT > 2
|
||||||
|
{
|
||||||
|
.length = sizeof IFACE_LABEL_2 - 1,
|
||||||
|
.string = (EipByte *)IFACE_LABEL_2
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#endif /* defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE */
|
||||||
|
|
||||||
|
/* Two dummy variables to provide fill data for the GetAttributeAll service. */
|
||||||
|
static CipUsint dummy_attribute_usint = 0;
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
#else
|
||||||
|
static CipUdint dummy_attribute_udint = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
#else
|
||||||
|
/* Constant dummy data for attribute #6 */
|
||||||
|
static CipEthernetLinkInterfaceControl s_interface_control =
|
||||||
|
{
|
||||||
|
.control_bits = 0,
|
||||||
|
.forced_interface_speed = 0,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Definition of the Ethernet Link object instance(s) */
|
||||||
|
CipEthernetLinkObject g_ethernet_link[OPENER_ETHLINK_INSTANCE_CNT];
|
||||||
|
|
||||||
|
EipStatus CipEthernetLinkInit(void) {
|
||||||
|
CipClass *ethernet_link_class = CreateCipClass(kCipEthernetLinkClassCode,
|
||||||
|
0,
|
||||||
|
/* # class attributes*/
|
||||||
|
7,
|
||||||
|
/* # highest class attribute number*/
|
||||||
|
2,
|
||||||
|
/* # class services*/
|
||||||
|
11,
|
||||||
|
/* # instance attributes*/
|
||||||
|
11,
|
||||||
|
/* # highest instance attribute number*/
|
||||||
|
/* # instance services follow */
|
||||||
|
2 + OPENER_ETHLINK_CNTRS_ENABLE + OPENER_ETHLINK_IFACE_CTRL_ENABLE,
|
||||||
|
OPENER_ETHLINK_INSTANCE_CNT,
|
||||||
|
/* # instances*/
|
||||||
|
"Ethernet Link",
|
||||||
|
/* # class name */
|
||||||
|
4,
|
||||||
|
/* # class revision*/
|
||||||
|
NULL); /* # function pointer for initialization*/
|
||||||
|
|
||||||
|
/* set attributes to initial values */
|
||||||
|
for (size_t idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
|
||||||
|
g_ethernet_link[idx].interface_speed = 100;
|
||||||
|
/* successful speed and duplex neg, full duplex active link.
|
||||||
|
* TODO: in future it should be checked if link is active */
|
||||||
|
g_ethernet_link[idx].interface_flags = 0xF;
|
||||||
|
|
||||||
|
g_ethernet_link[idx].interface_type = kEthLinkIfTypeTwistedPair;
|
||||||
|
if (2 == idx) {
|
||||||
|
g_ethernet_link[idx].interface_type = kEthLinkIfTypeInternal;
|
||||||
|
}
|
||||||
|
#if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
|
||||||
|
g_ethernet_link[idx].interface_label = iface_label_table[idx];
|
||||||
|
#endif
|
||||||
|
g_ethernet_link[idx].interface_caps.capability_bits = kEthLinkCapAutoNeg;
|
||||||
|
g_ethernet_link[idx].interface_caps.speed_duplex_selector =
|
||||||
|
kEthLinkSpeedDpx_100_FD;
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
g_ethernet_link[idx].interface_control.control_bits =
|
||||||
|
kEthLinkIfCntrlAutonegotiate;
|
||||||
|
g_ethernet_link[idx].interface_control.forced_interface_speed = 0U;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ethernet_link_class != NULL) {
|
||||||
|
/* add services to the class */
|
||||||
|
InsertService(ethernet_link_class, kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
InsertService(ethernet_link_class, kGetAttributeAll, &GetAttributeAll,
|
||||||
|
"GetAttributeAll");
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
InsertService(ethernet_link_class, kEthLinkGetAndClear,
|
||||||
|
&GetAndClearEthernetLink, "GetAndClear");
|
||||||
|
#endif
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
InsertService(ethernet_link_class, kSetAttributeSingle,
|
||||||
|
&SetAttributeSingle,
|
||||||
|
"SetAttributeSingle");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* bind attributes to the instance */
|
||||||
|
for (CipInstanceNum idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
|
||||||
|
CipInstance *ethernet_link_instance =
|
||||||
|
GetCipInstance( ethernet_link_class, (CipInstanceNum)(idx + 1) );
|
||||||
|
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
1,
|
||||||
|
kCipUdint,
|
||||||
|
EncodeCipUdint,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_speed,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
2,
|
||||||
|
kCipDword,
|
||||||
|
EncodeCipDword,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_flags,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
3,
|
||||||
|
kCip6Usint,
|
||||||
|
EncodeCipEthernetLinkPhyisicalAddress,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].physical_address,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
4,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_cntrs,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
5,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].media_cntrs,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
#else
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
4,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkInterfaceCounters,
|
||||||
|
NULL,
|
||||||
|
&dummy_attribute_udint,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
5,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkMediaCounters,
|
||||||
|
NULL,
|
||||||
|
&dummy_attribute_udint,
|
||||||
|
kGetableAllDummy);
|
||||||
|
#endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
if (2 == idx) {
|
||||||
|
/* Interface control of internal switch port is never settable. */
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
6,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkInterfaceControl,
|
||||||
|
DecodeCipEthernetLinkInterfaceControl,
|
||||||
|
&g_ethernet_link[idx].interface_control,
|
||||||
|
IFACE_CTRL_ACCESS_MODE & ~kSetable);
|
||||||
|
} else {
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
6,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkInterfaceControl,
|
||||||
|
DecodeCipEthernetLinkInterfaceControl,
|
||||||
|
&g_ethernet_link[idx].interface_control,
|
||||||
|
IFACE_CTRL_ACCESS_MODE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
6,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkInterfaceControl,
|
||||||
|
NULL,
|
||||||
|
&s_interface_control,
|
||||||
|
kGetableAll);
|
||||||
|
#endif
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
7,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_type,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
8,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&dummy_attribute_usint,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
9,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&dummy_attribute_usint, kGetableAllDummy);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
10,
|
||||||
|
kCipShortString,
|
||||||
|
EncodeCipShortString,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_label,
|
||||||
|
IFACE_LABEL_ACCESS_MODE);
|
||||||
|
InsertAttribute(ethernet_link_instance,
|
||||||
|
11,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipEthernetLinkInterfaceCaps,
|
||||||
|
NULL,
|
||||||
|
&g_ethernet_link[idx].interface_caps,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipEthernetLinkSetMac(EipUint8 *p_physical_address) {
|
||||||
|
for (size_t idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
|
||||||
|
memcpy(g_ethernet_link[idx].physical_address,
|
||||||
|
p_physical_address,
|
||||||
|
sizeof(g_ethernet_link[0].physical_address)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkInterfaceCounters(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
for (size_t i = 0; i < 11; i++) {
|
||||||
|
/* Encode real values using the access through the array of the
|
||||||
|
* interface_cntrs union. */
|
||||||
|
EncodeData(kCipUdint,
|
||||||
|
g_ethernet_link[instance_id - 1].interface_cntrs.cntr32 + i,
|
||||||
|
message_router_response);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Encode the default counter value of 0 */
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
|
||||||
|
11 * sizeof(CipUdint),
|
||||||
|
outgoing_message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkMediaCounters(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
for (size_t i = 0; i < 12; i++) {
|
||||||
|
/* Encode real values using the access through the array of the
|
||||||
|
* media_cntrs union. */
|
||||||
|
EncodeData(kCipUdint,
|
||||||
|
g_ethernet_link[instance_id - 1].media_cntrs.cntr32 + i,
|
||||||
|
message_router_response);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Encode the default counter value of 0 */
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
|
||||||
|
12 * sizeof(CipUdint),
|
||||||
|
outgoing_message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EncodeCipEthernetLinkInterfaceControl(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
const CipEthernetLinkInterfaceControl *const interface_control =
|
||||||
|
data;
|
||||||
|
#else
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
CipEthernetLinkInterfaceControl *interface_control = &s_interface_control;
|
||||||
|
#endif
|
||||||
|
EncodeCipWord(&interface_control->control_bits, outgoing_message);
|
||||||
|
EncodeCipUint(&interface_control->forced_interface_speed, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NELEMENTS(x) ( (sizeof(x) / sizeof(x[0]) ) )
|
||||||
|
static void EncodeCipEthernetLinkInterfaceCaps(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
const CipEthernetLinkMetaInterfaceCapability *const interface_caps = data;
|
||||||
|
EncodeCipDword(&interface_caps->capability_bits, outgoing_message);
|
||||||
|
uint16_t selected = interface_caps->speed_duplex_selector;
|
||||||
|
CipUsint count = 0;
|
||||||
|
while(selected) { /* count # of bits set */
|
||||||
|
selected &= selected - 1U; /* clear the least significant bit set */
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
EncodeCipUsint(&count, outgoing_message);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
|
||||||
|
if (interface_caps->speed_duplex_selector &
|
||||||
|
(1U << i) ) {
|
||||||
|
EncodeCipUint(&speed_duplex_table[i].interface_speed, outgoing_message);
|
||||||
|
EncodeCipUsint(&speed_duplex_table[i].interface_duplex_mode,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
EipStatus GetAndClearEthernetLink(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response,
|
||||||
|
const struct sockaddr *originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session) {
|
||||||
|
|
||||||
|
CipAttributeStruct *attribute = GetCipAttribute(
|
||||||
|
instance, message_router_request->request_path.attribute_number);
|
||||||
|
|
||||||
|
message_router_response->data_length = 0;
|
||||||
|
message_router_response->reply_service = (0x80
|
||||||
|
| message_router_request->service);
|
||||||
|
message_router_response->general_status = kCipErrorAttributeNotSupported;
|
||||||
|
message_router_response->size_of_additional_status = 0;
|
||||||
|
|
||||||
|
EipUint16 attribute_number = message_router_request->request_path
|
||||||
|
.attribute_number;
|
||||||
|
|
||||||
|
if ( (NULL != attribute) && (NULL != attribute->data) ) {
|
||||||
|
OPENER_TRACE_INFO("GetAndClear attribute %" PRIu16 "\n", attribute_number);
|
||||||
|
|
||||||
|
/* The PreGetCallback is not executed here.
|
||||||
|
* The GetAttributeSingle{EthernetLink}() will do it on our behalf. */
|
||||||
|
switch (attribute_number) {
|
||||||
|
case 4: /* fall through */
|
||||||
|
case 5:
|
||||||
|
GetAttributeSingleEthernetLink(
|
||||||
|
instance, message_router_request,
|
||||||
|
message_router_response,
|
||||||
|
originator_address,
|
||||||
|
encapsulation_session);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorServiceNotSupportedForSpecifiedPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clearing the counters must be done in the PostGetCallback because it
|
||||||
|
* is often hardware specific and can't be handled in generic code. */
|
||||||
|
/* The PostGetCallback is not executed here. The
|
||||||
|
* GetAttributeSingle{EthernetLink}() should have done it on our
|
||||||
|
* behalf already.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
#endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
static bool IsIfaceControlAllowed(CipInstanceNum instance_id,
|
||||||
|
CipEthernetLinkInterfaceControl const *iface_cntrl)
|
||||||
|
{
|
||||||
|
const CipUsint duplex_mode =
|
||||||
|
(iface_cntrl->control_bits & kEthLinkIfCntrlForceDuplexFD) ? 1 : 0;
|
||||||
|
for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
|
||||||
|
if (g_ethernet_link[instance_id - 1].interface_caps.speed_duplex_selector &
|
||||||
|
(1U << i) ) {
|
||||||
|
if (duplex_mode == speed_duplex_table[i].interface_duplex_mode &&
|
||||||
|
iface_cntrl->forced_interface_speed ==
|
||||||
|
speed_duplex_table[i].interface_speed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DecodeCipEthernetLinkInterfaceControl(
|
||||||
|
CipEthernetLinkInterfaceControl *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
CipInstance *const instance = GetCipInstance(
|
||||||
|
GetCipClass(message_router_request->request_path.class_id),
|
||||||
|
message_router_request->request_path.instance_number);
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
CipEthernetLinkInterfaceControl if_cntrl;
|
||||||
|
|
||||||
|
DecodeCipWord(&if_cntrl.control_bits, message_router_request,
|
||||||
|
message_router_response);
|
||||||
|
DecodeCipUint(&if_cntrl.forced_interface_speed, message_router_request,
|
||||||
|
message_router_response);
|
||||||
|
|
||||||
|
if (if_cntrl.control_bits > kEthLinkIfCntrlMaxValid) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate))
|
||||||
|
&& ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlForceDuplexFD))
|
||||||
|
|| (0 != if_cntrl.forced_interface_speed))) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorObjectStateConflict;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
} else {
|
||||||
|
if (0 == (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate)) {
|
||||||
|
/* Need to check if a supported mode is forced. */
|
||||||
|
if (!IsIfaceControlAllowed(instance->instance_number,
|
||||||
|
&if_cntrl)) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*data = if_cntrl; //write data to attribute
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
number_of_decoded_bytes = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
196
OrginalSourceRepo/OpENer-master/source/src/cip/cipethernetlink.h
Normal file
196
OrginalSourceRepo/OpENer-master/source/src/cip/cipethernetlink.h
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPETHERNETLINK_H_
|
||||||
|
#define OPENER_CIPETHERNETLINK_H_
|
||||||
|
/** @file
|
||||||
|
* @brief Declare public interface of the CIP Ethernet Link Object
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief This Ethernet Link class code as #define is still needed for a static
|
||||||
|
* initialization. */
|
||||||
|
#define CIP_ETHERNETLINK_CLASS_CODE 0xF6U
|
||||||
|
/** @brief Ethernet Link class code */
|
||||||
|
static const CipUint kCipEthernetLinkClassCode = CIP_ETHERNETLINK_CLASS_CODE;
|
||||||
|
|
||||||
|
/* public type definitions */
|
||||||
|
|
||||||
|
/** @brief Provide values for the Interface Flags (attribute #2) */
|
||||||
|
typedef enum {
|
||||||
|
/** Set this bit if your device needs a reset to take over new settings made via
|
||||||
|
* attribute #6. It is duplicates the meaning of kEthLinkCapManualReset */
|
||||||
|
kEthLinkFlagsManualReset = 0x20,
|
||||||
|
} CipEthLinkIfaceFlags;
|
||||||
|
|
||||||
|
/** @brief Provide values for the Interface Control (attribute #6) Control bits member */
|
||||||
|
typedef enum {
|
||||||
|
/** Set this bit to enable Auto-negotiation of ethernet link parameters. */
|
||||||
|
kEthLinkIfCntrlAutonegotiate = 0x01,
|
||||||
|
/** When Auto-negotiation is disabled set this bit to force Full-Duplex mode else
|
||||||
|
* Half-Duplex mode is forced. */
|
||||||
|
kEthLinkIfCntrlForceDuplexFD = 0x02,
|
||||||
|
/** For convenience declare the sum of valid bits as the maximum allowed value. */
|
||||||
|
kEthLinkIfCntrlMaxValid = kEthLinkIfCntrlAutonegotiate +
|
||||||
|
kEthLinkIfCntrlForceDuplexFD,
|
||||||
|
} CipEthLinkIfaceControl;
|
||||||
|
|
||||||
|
/** @brief Provide values for the Interface Type (attribute #7) */
|
||||||
|
typedef enum {
|
||||||
|
/** Unknown interface type */
|
||||||
|
kEthLinkIfTypeUnknown = 0x00,
|
||||||
|
/** Internal (switch) port */
|
||||||
|
kEthLinkIfTypeInternal = 0x01,
|
||||||
|
/** Twisted pair (e.g., 10Base-T, 100Base-TX, 1000Base-T, etc.) */
|
||||||
|
kEthLinkIfTypeTwistedPair = 0x02,
|
||||||
|
/** Optical fiber (e.g., 100Base-FX) */
|
||||||
|
kEthLinkIfTypeOptical = 0x03,
|
||||||
|
} CipEthLinkIfaceType;
|
||||||
|
|
||||||
|
/** @brief Provide bit masks for the Interface Capability (#11) attribute's Capability Bits */
|
||||||
|
typedef enum {
|
||||||
|
/** Interface needs reset to activate attribute #6 */
|
||||||
|
kEthLinkCapManualReset = 0x01,
|
||||||
|
/** Interface supports link auto-negotiation */
|
||||||
|
kEthLinkCapAutoNeg = 0x02,
|
||||||
|
/** Interface supports link auto-crossover */
|
||||||
|
kEthLinkCapAutoMDX = 0x04,
|
||||||
|
/** Interface supports setting of Interface Control attribute(#6) */
|
||||||
|
kEthLinkCapManualSpeed = 0x08,
|
||||||
|
} CipEthLinkCapabilityBits;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Provide bit masks to select available speed / duplex combinations
|
||||||
|
*
|
||||||
|
* Keep the bit index of these bit masks in sync with the array index of the
|
||||||
|
* matching speed / duplex structure in the internal @p speed_duplex_table
|
||||||
|
* of cipethernetlink.c
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
kEthLinkSpeedDpx_10_HD = 0x01,
|
||||||
|
kEthLinkSpeedDpx_10_FD = 0x02,
|
||||||
|
kEthLinkSpeedDpx_100_HD = 0x04,
|
||||||
|
kEthLinkSpeedDpx_100_FD = 0x08,
|
||||||
|
kEthLinkSpeedDpx_1000_HD = 0x10,
|
||||||
|
kEthLinkSpeedDpx_1000_FD = 0x20,
|
||||||
|
} CipEthLinkSpeedDpxSelect;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Type definition to describe the Interface Capability
|
||||||
|
*
|
||||||
|
* This structure is not a direct representation of the Interface Capability
|
||||||
|
* attribute (#11) but replaces the needed array of speed / duplex list entries
|
||||||
|
* by @ref speed_duplex_selector to create the needed array on the fly.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/** Capability flags of CipEthLinkCapabilityBits group */
|
||||||
|
CipDword capability_bits;
|
||||||
|
/** Speed / duplex selector bit map of CipEthLinkSpeedDpxSelect */
|
||||||
|
uint16_t speed_duplex_selector;
|
||||||
|
} CipEthernetLinkMetaInterfaceCapability;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
/** @brief Type definition of the Interface Counters attribute #4
|
||||||
|
*
|
||||||
|
* This union holds the 32-bit Interface Counters of the Ethernet Link object.
|
||||||
|
* This is attribute is becomes required if the HC Interface Counters attribute or
|
||||||
|
* Media Counters attribute is implemented, otherwise highly recommended.
|
||||||
|
* That means for DLR capable devices this attribute is required because the
|
||||||
|
* Media Counters attribute is required for DLR capable devices.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
CipUdint cntr32[11];
|
||||||
|
struct {
|
||||||
|
CipUdint in_octets;
|
||||||
|
CipUdint in_ucast;
|
||||||
|
CipUdint in_nucast;
|
||||||
|
CipUdint in_discards;
|
||||||
|
CipUdint in_errors;
|
||||||
|
CipUdint in_unknown_protos;
|
||||||
|
CipUdint out_octets;
|
||||||
|
CipUdint out_ucast;
|
||||||
|
CipUdint out_nucast;
|
||||||
|
CipUdint out_discards;
|
||||||
|
CipUdint out_errors;
|
||||||
|
} ul;
|
||||||
|
} CipEthernetLinkInterfaceCounters;
|
||||||
|
|
||||||
|
/** @brief Type definition of the Media Counters attribute #5
|
||||||
|
*
|
||||||
|
* This union holds the 32-bit Media Counters of the Ethernet Link object.
|
||||||
|
* This attribute becomes required if the devices supports DLR or if the
|
||||||
|
* HC Media Counters attribute is implemented, otherwise highly recommended.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
CipUdint cntr32[12];
|
||||||
|
struct {
|
||||||
|
CipUdint align_errs;
|
||||||
|
CipUdint fcs_errs;
|
||||||
|
CipUdint single_coll;
|
||||||
|
CipUdint multi_coll;
|
||||||
|
CipUdint sqe_test_errs;
|
||||||
|
CipUdint def_trans;
|
||||||
|
CipUdint late_coll;
|
||||||
|
CipUdint exc_coll;
|
||||||
|
CipUdint mac_tx_errs;
|
||||||
|
CipUdint crs_errs;
|
||||||
|
CipUdint frame_too_long;
|
||||||
|
CipUdint mac_rx_errs;
|
||||||
|
} ul;
|
||||||
|
} CipEthernetLinkMediaCounters;
|
||||||
|
#endif /* ... && OPENER_ETHLINK_CNTRS_ENABLE != 0 */
|
||||||
|
|
||||||
|
/** @brief Type definition of the Interface Control attribute (#6)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipWord control_bits;
|
||||||
|
CipUint forced_interface_speed;
|
||||||
|
} CipEthernetLinkInterfaceControl;
|
||||||
|
|
||||||
|
/** @brief Data of an CIP Ethernet Link object */
|
||||||
|
typedef struct {
|
||||||
|
EipUint32 interface_speed; /**< Attribute #1: 10/100/1000 Mbit/sec */
|
||||||
|
EipUint32 interface_flags; /**< Attribute #2: Interface flags as defined in the CIP specification */
|
||||||
|
EipUint8 physical_address[6]; /**< Attribute #3: MAC address of the Ethernet link */
|
||||||
|
#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
|
||||||
|
CipEthernetLinkInterfaceCounters interface_cntrs; /**< Attribute #4: Interface counters 32-bit wide */
|
||||||
|
CipEthernetLinkMediaCounters media_cntrs; /**< Attribute #5: Media counters 32-bit wide */
|
||||||
|
#endif
|
||||||
|
#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
|
||||||
|
0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
|
||||||
|
CipEthernetLinkInterfaceControl interface_control; /** Attribute #6: control link properties */
|
||||||
|
#endif
|
||||||
|
CipUsint interface_type; /**< Attribute #7: Type of interface */
|
||||||
|
CipShortString interface_label; /**< Attribute #10: Interface label */
|
||||||
|
CipEthernetLinkMetaInterfaceCapability interface_caps; /**< Attribute #11: Interface capabilities */
|
||||||
|
} CipEthernetLinkObject;
|
||||||
|
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
/** @brief Initialize the Ethernet Link Objects data
|
||||||
|
*
|
||||||
|
* @return kEipStatusOk if initialization was successful, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus CipEthernetLinkInit(void);
|
||||||
|
|
||||||
|
/** @brief Initialize the Ethernet MAC of the Ethernet Link object instances
|
||||||
|
*
|
||||||
|
* @param p_physical_address pointer to 6 bytes of MAC address
|
||||||
|
*
|
||||||
|
* This function sets the MAC address of all involved Ethernet Link objects.
|
||||||
|
*/
|
||||||
|
void CipEthernetLinkSetMac(EipUint8 *p_physical_address);
|
||||||
|
|
||||||
|
|
||||||
|
/* global object instance(s) */
|
||||||
|
|
||||||
|
extern CipEthernetLinkObject g_ethernet_link[];
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPETHERNETLINK_H_*/
|
||||||
297
OrginalSourceRepo/OpENer-master/source/src/cip/cipidentity.c
Normal file
297
OrginalSourceRepo/OpENer-master/source/src/cip/cipidentity.c
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file cipidentity.c
|
||||||
|
*
|
||||||
|
* CIP Identity Object
|
||||||
|
* ===================
|
||||||
|
*
|
||||||
|
* Implemented Attributes
|
||||||
|
* ----------------------
|
||||||
|
* - Attribute 1: VendorID
|
||||||
|
* - Attribute 2: Device Type
|
||||||
|
* - Attribute 3: Product Code
|
||||||
|
* - Attribute 4: Revision
|
||||||
|
* - Attribute 5: Status
|
||||||
|
* - Attribute 6: Serial Number
|
||||||
|
* - Attribute 7: Product Name
|
||||||
|
* - Attribute 8: State
|
||||||
|
*
|
||||||
|
* Implemented Services
|
||||||
|
* --------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cipidentity.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipstring.h"
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
#include "ciperror.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
/** @brief The device's configuration data for the Identity Object */
|
||||||
|
#include "devicedata.h"
|
||||||
|
|
||||||
|
/** @brief Definition of the global Identity Object */
|
||||||
|
CipIdentityObject g_identity = { .vendor_id = OPENER_DEVICE_VENDOR_ID, /* Attribute 1: Vendor ID */
|
||||||
|
.device_type = OPENER_DEVICE_TYPE, /* Attribute 2: Device Type */
|
||||||
|
.product_code = OPENER_DEVICE_PRODUCT_CODE, /* Attribute 3: Product Code */
|
||||||
|
.revision = { /* Attribute 4: Revision / CipUsint Major, CipUsint Minor */
|
||||||
|
OPENER_DEVICE_MAJOR_REVISION,
|
||||||
|
OPENER_DEVICE_MINOR_REVISION
|
||||||
|
}, .status = 0, /* Attribute 5: Status */
|
||||||
|
.ext_status = kSelftestingUnknown, /* Attribute 5: Extended Device Status field */
|
||||||
|
.serial_number = 0, /* Attribute 6: Serial Number */
|
||||||
|
/* Attribute 7: Product Name, set by CipIdentityInit() */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceRevision(EipUint8 major, EipUint8 minor) {
|
||||||
|
g_identity.revision.major_revision = major;
|
||||||
|
g_identity.revision.minor_revision = minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceSerialNumber(const EipUint32 serial_number) {
|
||||||
|
g_identity.serial_number = serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceType(const EipUint16 type) {
|
||||||
|
g_identity.device_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceProductCode(const EipUint16 code) {
|
||||||
|
g_identity.product_code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceStatus(const CipWord status) {
|
||||||
|
g_identity.status = status;
|
||||||
|
g_identity.ext_status = status & kExtStatusMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceVendorId(CipUint vendor_id) {
|
||||||
|
g_identity.vendor_id = vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
CipUint GetDeviceVendorId(void) {
|
||||||
|
return g_identity.vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
void SetDeviceProductName(const char *product_name) {
|
||||||
|
if (!product_name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetCipShortStringByCstr(&g_identity.product_name, product_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Doxygen comment is with the function's prototype in opener_api.h. */
|
||||||
|
CipShortString *GetDeviceProductName(void) {
|
||||||
|
return &g_identity.product_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void MergeStatusAndExtStatus(void) {
|
||||||
|
CipWord status_flags = g_identity.status & (~kExtStatusMask);
|
||||||
|
CipWord ext_status = g_identity.ext_status & kExtStatusMask;
|
||||||
|
|
||||||
|
/* Any major fault will override the current extended status with kMajorFault.
|
||||||
|
See comment on Major Fault at Vol. 1, Table 5A-2.4. */
|
||||||
|
if(0 !=
|
||||||
|
(status_flags & (kMajorRecoverableFault | kMajorUnrecoverableFault) ) ) {
|
||||||
|
ext_status = kMajorFault;
|
||||||
|
}
|
||||||
|
g_identity.status = status_flags | ext_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Set status flags of the device's Status word
|
||||||
|
*
|
||||||
|
* @param status_flags flags to set in the Status word
|
||||||
|
*
|
||||||
|
* This function sets status flags of the device's Status word and combines
|
||||||
|
* the flag values with the internal ext_status member into a new Status
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
void CipIdentitySetStatusFlags(const CipWord status_flags) {
|
||||||
|
g_identity.status |= status_flags & (~kExtStatusMask);
|
||||||
|
MergeStatusAndExtStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Clear status flags of the device's Status word
|
||||||
|
*
|
||||||
|
* @param status_flags flags to clear in the Status word
|
||||||
|
*
|
||||||
|
* This function clears status flags of the device's Status word and combines
|
||||||
|
* the flag values with the internal ext_status member into a new Status
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
void CipIdentityClearStatusFlags(const CipWord status_flags) {
|
||||||
|
g_identity.status &= ~(status_flags & (~kExtStatusMask) );
|
||||||
|
MergeStatusAndExtStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Set the device's Extended Device Status field in the Status word
|
||||||
|
*
|
||||||
|
* @param extended_status Extended Device Status field
|
||||||
|
*
|
||||||
|
* This function sets the internal ext_status member of the Identity object
|
||||||
|
* and combines its value depending on the other Status flags into a new
|
||||||
|
* Status value.
|
||||||
|
*/
|
||||||
|
void CipIdentitySetExtendedDeviceStatus(
|
||||||
|
CipIdentityExtendedStatus extended_status) {
|
||||||
|
OPENER_TRACE_INFO("Setting extended status: %x\n", extended_status);
|
||||||
|
g_identity.ext_status = extended_status & kExtStatusMask;
|
||||||
|
MergeStatusAndExtStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Identity Object PreResetCallback
|
||||||
|
*
|
||||||
|
* Used for common Reset service
|
||||||
|
*
|
||||||
|
* @returns Currently always kEipOkSend is returned
|
||||||
|
*/
|
||||||
|
EipStatus IdentityObjectPreResetCallback(
|
||||||
|
CipInstance *RESTRICT const instance,
|
||||||
|
const CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response
|
||||||
|
) {
|
||||||
|
(void) instance;
|
||||||
|
|
||||||
|
EipStatus eip_status = kEipStatusOkSend;
|
||||||
|
|
||||||
|
if(message_router_request->request_data_size > 1) {
|
||||||
|
message_router_response->general_status = kCipErrorTooMuchData;
|
||||||
|
} else {
|
||||||
|
CipOctet reset_type = 0; /* The default type if type parameter was omitted. */
|
||||||
|
if(message_router_request->request_data_size == 1) {
|
||||||
|
reset_type = message_router_request->data[0];
|
||||||
|
}
|
||||||
|
switch(reset_type) {
|
||||||
|
case 0: /* Reset type 0 -> Emulate power cycle */
|
||||||
|
if(kEipStatusError == ResetDevice() ) {
|
||||||
|
message_router_response->general_status = kCipErrorInvalidParameter;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* Reset type 1 -> Return to factory defaults & power cycle*/
|
||||||
|
if(kEipStatusError == ResetDeviceToInitialConfiguration() ) {
|
||||||
|
message_router_response->general_status = kCipErrorInvalidParameter;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* case 2: Not supported Reset type 2 ->
|
||||||
|
Return to factory defaults except communications parameters & power cycle*/
|
||||||
|
|
||||||
|
default:
|
||||||
|
message_router_response->general_status = kCipErrorInvalidParameter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eip_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeCipIdentity(CipClass *class) {
|
||||||
|
|
||||||
|
CipClass *meta_class = class->class_instance.cip_class;
|
||||||
|
|
||||||
|
InsertAttribute( (CipInstance *) class, 1, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &class->revision, kGetableSingleAndAll ); /* revision */
|
||||||
|
InsertAttribute( (CipInstance *) class, 2, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &class->number_of_instances, kGetableSingleAndAll ); /* largest instance number */
|
||||||
|
InsertAttribute( (CipInstance *) class, 3, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &class->number_of_instances, kGetableSingle ); /* number of instances currently existing*/
|
||||||
|
InsertAttribute( (CipInstance *) class, 4, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &kCipUintZero, kNotSetOrGetable ); /* optional attribute list - default = 0 */
|
||||||
|
InsertAttribute( (CipInstance *) class, 5, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &kCipUintZero, kNotSetOrGetable ); /* optional service list - default = 0 */
|
||||||
|
InsertAttribute( (CipInstance *) class, 6, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &meta_class->highest_attribute_number,
|
||||||
|
kGetableSingleAndAll ); /* max class attribute number*/
|
||||||
|
InsertAttribute( (CipInstance *) class, 7, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &class->highest_attribute_number,
|
||||||
|
kGetableSingleAndAll ); /* max instance attribute number*/
|
||||||
|
|
||||||
|
InsertService(meta_class,
|
||||||
|
kGetAttributeAll,
|
||||||
|
&GetAttributeAll,
|
||||||
|
"GetAttributeAll"); /* bind instance services to the metaclass*/
|
||||||
|
InsertService(meta_class,
|
||||||
|
kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
|
||||||
|
// add Callback function pointers
|
||||||
|
class->PreResetCallback = &IdentityObjectPreResetCallback;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeRevision(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
CipRevision *revision = (CipRevision *) data;
|
||||||
|
AddSintToMessage(revision->major_revision, outgoing_message);
|
||||||
|
AddSintToMessage(revision->minor_revision, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus CipIdentityInit() {
|
||||||
|
|
||||||
|
CipClass *class = CreateCipClass(kCipIdentityClassCode, 0, /* # of non-default class attributes */
|
||||||
|
7, /* # highest class attribute number*/
|
||||||
|
2, /* # of class services*/
|
||||||
|
7, /* # of instance attributes*/
|
||||||
|
7, /* # highest instance attribute number*/
|
||||||
|
5, /* # of instance services*/
|
||||||
|
1, /* # of instances*/
|
||||||
|
"identity", /* # class name (for debug)*/
|
||||||
|
1, /* # class revision*/ //TODO: change revision to 2 - check
|
||||||
|
&InitializeCipIdentity); /* # function pointer for initialization*/
|
||||||
|
|
||||||
|
if(class == 0) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_identity.product_name.length == 0)
|
||||||
|
SetDeviceProductName(OPENER_DEVICE_NAME);
|
||||||
|
|
||||||
|
CipInstance *instance = GetCipInstance(class, 1);
|
||||||
|
InsertAttribute(instance, 1, kCipUint, EncodeCipUint,
|
||||||
|
NULL, &g_identity.vendor_id, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 2, kCipUint, EncodeCipUint,
|
||||||
|
NULL, &g_identity.device_type, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 3, kCipUint, EncodeCipUint,
|
||||||
|
NULL, &g_identity.product_code, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 4, kCipUsintUsint, EncodeRevision,
|
||||||
|
NULL, &g_identity.revision, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 5, kCipWord, EncodeCipWord,
|
||||||
|
NULL, &g_identity.status, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 6, kCipUdint, EncodeCipUdint,
|
||||||
|
NULL, &g_identity.serial_number, kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance, 7, kCipShortString, EncodeCipShortString,
|
||||||
|
NULL, &g_identity.product_name, kGetableSingleAndAll);
|
||||||
|
|
||||||
|
InsertService(class,
|
||||||
|
kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
InsertService(class, kGetAttributeAll, &GetAttributeAll, "GetAttributeAll");
|
||||||
|
InsertService(class, kReset, &CipResetService, "Reset");
|
||||||
|
InsertService(class, kGetAttributeList, &GetAttributeList,
|
||||||
|
"GetAttributeList");
|
||||||
|
InsertService(class, kSetAttributeList, &SetAttributeList,
|
||||||
|
"SetAttributeList");
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
89
OrginalSourceRepo/OpENer-master/source/src/cip/cipidentity.h
Normal file
89
OrginalSourceRepo/OpENer-master/source/src/cip/cipidentity.h
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPIDENTITY_H_
|
||||||
|
#define OPENER_CIPIDENTITY_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief Identity class code */
|
||||||
|
static const CipUint kCipIdentityClassCode = 0x01U;
|
||||||
|
|
||||||
|
/** @brief Status of the CIP Identity object */
|
||||||
|
typedef enum {
|
||||||
|
kOwned = 0x0001, /**< Indicates that the device has an owner */
|
||||||
|
kConfigured = 0x0004, /**< Indicates that the device is configured to do
|
||||||
|
something different, than the out-of-the-box default. */
|
||||||
|
kMinorRecoverableFault = 0x0100, /**< Indicates that the device detected a
|
||||||
|
fault with itself, which was thought to be recoverable. The device did not
|
||||||
|
switch to a faulted state. */
|
||||||
|
kMinorUncoverableFault = 0x0200, /**< Indicates that the device detected a
|
||||||
|
fault with itself, which was thought to be recoverable. The device did not
|
||||||
|
switch to a faulted state. */
|
||||||
|
kMajorRecoverableFault = 0x0400, /**< Indicates that the device detected a
|
||||||
|
fault with itself,which was thought to be recoverable. The device changed
|
||||||
|
to the "Major Recoverable Fault" state */
|
||||||
|
kMajorUnrecoverableFault = 0x0800 /**< Indicates that the device detected a
|
||||||
|
fault with itself,which was thought to be recoverable. The device changed
|
||||||
|
to the "Major Unrecoverable Fault" state */
|
||||||
|
} CipIdentityStatus;
|
||||||
|
|
||||||
|
/** @brief Constants for the extended status field in the Status word */
|
||||||
|
typedef enum {
|
||||||
|
kSelftestingUnknown = 0x0000U,
|
||||||
|
kFirmwareUpdateInProgress = 0x0010U,
|
||||||
|
kStatusAtLeastOneFaultedIoConnection = 0x0020U,
|
||||||
|
kNoIoConnectionsEstablished = 0x0030U,
|
||||||
|
kNonVolatileConfigurationBad = 0x0040U,
|
||||||
|
kMajorFault = 0x0050U,
|
||||||
|
kAtLeastOneIoConnectionInRunMode = 0x0060U,
|
||||||
|
kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070U,
|
||||||
|
kExtStatusMask = 0x00F0U
|
||||||
|
} CipIdentityExtendedStatus;
|
||||||
|
|
||||||
|
/** @brief Constants for the state member of the Identity object. */
|
||||||
|
typedef enum {
|
||||||
|
kStateNonExistent = 0U,
|
||||||
|
kStateSelfTesting = 1U,
|
||||||
|
kStateStandby = 2U,
|
||||||
|
kStateOperational = 3U,
|
||||||
|
kStateMajorRecoverableFault = 4U,
|
||||||
|
kStateMajorUnrecoverableFault = 5U,
|
||||||
|
kStateDefault = 255U
|
||||||
|
} CipIdentityState;
|
||||||
|
|
||||||
|
/** @brief Declaration of the Identity object's structure type
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUint vendor_id; /**< Attribute 1: Vendor ID */
|
||||||
|
CipUint device_type; /**< Attribute 2: Device Type */
|
||||||
|
CipUint product_code; /**< Attribute 3: Product Code */
|
||||||
|
CipRevision revision; /**< Attribute 4: Revision / CipUsint Major, CipUsint Minor */
|
||||||
|
CipWord status; /**< Attribute 5: Status */
|
||||||
|
CipWord ext_status; /**< Attribute 5: last set extended status, needed for Status handling */
|
||||||
|
CipUdint serial_number; /**< Attribute 6: Serial Number, has to be set prior to OpENer's network initialization */
|
||||||
|
CipShortString product_name; /**< Attribute 7: Product Name */
|
||||||
|
CipUsint state; /** Attribute 8: state, this member could control the Module Status LED blink pattern */
|
||||||
|
} CipIdentityObject;
|
||||||
|
|
||||||
|
|
||||||
|
/* global public variables */
|
||||||
|
extern CipIdentityObject g_identity;
|
||||||
|
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
/** @brief CIP Identity object constructor
|
||||||
|
*
|
||||||
|
* @returns kEipStatusError if the class could not be created, otherwise kEipStatusOk
|
||||||
|
*/
|
||||||
|
EipStatus CipIdentityInit(void);
|
||||||
|
|
||||||
|
void CipIdentitySetStatusFlags(const CipWord status_flags);
|
||||||
|
void CipIdentityClearStatusFlags(const CipWord status_flags);
|
||||||
|
void CipIdentitySetExtendedDeviceStatus(
|
||||||
|
CipIdentityExtendedStatus extended_status);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPIDENTITY_H_ */
|
||||||
1042
OrginalSourceRepo/OpENer-master/source/src/cip/cipioconnection.c
Normal file
1042
OrginalSourceRepo/OpENer-master/source/src/cip/cipioconnection.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file cipioconnection.h
|
||||||
|
* CIP I/O Connection implementation
|
||||||
|
* =================================
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* I/O Connection Object State Transition Diagram
|
||||||
|
* ----------------------------------------------
|
||||||
|
* @dot
|
||||||
|
* digraph IOConnectionObjectStateTransition {
|
||||||
|
* A[label="Any State"]
|
||||||
|
* N[label="Non-existent"]
|
||||||
|
* C[label="Configuring"]
|
||||||
|
* E[label="Established"]
|
||||||
|
* W[label="Waiting for Connection ID"]
|
||||||
|
* T[label="Timed Out"]
|
||||||
|
*
|
||||||
|
* A->N [label="Delete"]
|
||||||
|
* N->C [label="Create"]
|
||||||
|
* C->C [label="Get/Set/Apply Attribute"]
|
||||||
|
* C->W [label="Apply Attribute"]
|
||||||
|
* W->W [label="Get/Set Attribute"]
|
||||||
|
* C->E [label="Apply Attribute"]
|
||||||
|
* E->E [label="Get/Set/Apply Attribute, Reset, Message Produced/Consumed"]
|
||||||
|
* W->E [label="Apply Attribute"]
|
||||||
|
* E->T [label="Inactivity/Watchdog"]
|
||||||
|
* T->E [label="Reset"]
|
||||||
|
* T->N [label="Delete"]
|
||||||
|
* }
|
||||||
|
* @enddot
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENER_CIPIOCONNECTION_H_
|
||||||
|
#define OPENER_CIPIOCONNECTION_H_
|
||||||
|
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
|
||||||
|
/** @brief Setup all data in order to establish an IO connection
|
||||||
|
*
|
||||||
|
* This function can be called after all data has been parsed from the forward open request
|
||||||
|
* @param connection_object pointer to the connection object structure holding the parsed data from the forward open request
|
||||||
|
* @param extended_error the extended error code in case an error happened
|
||||||
|
* @return general status on the establishment
|
||||||
|
* - EIP_OK ... on success
|
||||||
|
* - On an error the general status code to be put into the response
|
||||||
|
*/
|
||||||
|
CipError EstablishIoConnection(
|
||||||
|
CipConnectionObject *RESTRICT const connection_object,
|
||||||
|
EipUint16 *const extended_error);
|
||||||
|
|
||||||
|
/** @brief Take the data given in the connection object structure and open the necessary communication channels
|
||||||
|
*
|
||||||
|
* This function will use the g_stCPFDataItem!
|
||||||
|
* @param connection_object pointer to the connection object data
|
||||||
|
* @return general status on the open process
|
||||||
|
* - EIP_OK ... on success
|
||||||
|
* - On an error the general status code to be put into the response
|
||||||
|
*/
|
||||||
|
CipError OpenCommunicationChannels(CipConnectionObject *connection_object);
|
||||||
|
|
||||||
|
/** @brief close the communication channels of the given connection and remove it
|
||||||
|
* from the active connections list.
|
||||||
|
*
|
||||||
|
* @param connection_object pointer to the connection object data
|
||||||
|
*/
|
||||||
|
void CloseCommunicationChannelsAndRemoveFromActiveConnectionsList(
|
||||||
|
CipConnectionObject *connection_object);
|
||||||
|
|
||||||
|
extern EipUint8 *g_config_data_buffer;
|
||||||
|
extern unsigned int g_config_data_length;
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPIOCONNECTION_H_ */
|
||||||
|
|
@ -0,0 +1,315 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "ciperror.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "enipmessage.h"
|
||||||
|
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
|
||||||
|
CipMessageRouterRequest g_message_router_request;
|
||||||
|
|
||||||
|
/** @brief A class registry list node
|
||||||
|
*
|
||||||
|
* A linked list of this object is the registry of classes known to the message router
|
||||||
|
* for small devices with very limited memory it could make sense to change this list into an
|
||||||
|
* array with a given max size for removing the need for having to dynamically allocate
|
||||||
|
* memory. The size of the array could be a parameter in the platform config file.
|
||||||
|
*/
|
||||||
|
typedef struct cip_message_router_object {
|
||||||
|
struct cip_message_router_object *next; /**< link */
|
||||||
|
CipClass *cip_class; /**< object */
|
||||||
|
} CipMessageRouterObject;
|
||||||
|
|
||||||
|
/** @brief Pointer to first registered object in MessageRouter*/
|
||||||
|
CipMessageRouterObject *g_first_object = NULL;
|
||||||
|
|
||||||
|
/** @brief Register a CIP Class to the message router
|
||||||
|
* @param cip_class Pointer to a class object to be registered.
|
||||||
|
* @return kEipStatusOk on success
|
||||||
|
* kEipStatusError on no memory available to register more objects
|
||||||
|
*/
|
||||||
|
EipStatus RegisterCipClass(CipClass *cip_class);
|
||||||
|
|
||||||
|
/** @brief Create Message Router Request structure out of the received data.
|
||||||
|
*
|
||||||
|
* Parses the UCMM header consisting of: service, IOI size, IOI, data into a request structure
|
||||||
|
* @param data pointer to the message data received
|
||||||
|
* @param data_length number of bytes in the message
|
||||||
|
* @param message_router_request pointer to structure of MRRequest data item.
|
||||||
|
* @return kEipStatusOk on success. otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
CipError CreateMessageRouterRequestStructure(const EipUint8 *data,
|
||||||
|
EipInt16 data_length,
|
||||||
|
CipMessageRouterRequest *message_router_request);
|
||||||
|
|
||||||
|
void InitializeCipMessageRouterClass(CipClass *cip_class) {
|
||||||
|
|
||||||
|
CipClass *meta_class = cip_class->class_instance.cip_class;
|
||||||
|
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 1, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &cip_class->revision, kGetableSingleAndAll ); /* revision */
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 2, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &cip_class->number_of_instances, kGetableSingle ); /* largest instance number */
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 3, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &cip_class->number_of_instances, kGetableSingle ); /* number of instances currently existing*/
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 4, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &kCipUintZero, kGetableAll ); /* optional attribute list - default = 0 */
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 5, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &kCipUintZero, kGetableAll ); /* optional service list - default = 0 */
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 6, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &meta_class->highest_attribute_number,
|
||||||
|
kGetableSingleAndAll ); /* max class attribute number*/
|
||||||
|
InsertAttribute( (CipInstance *) cip_class, 7, kCipUint, EncodeCipUint, NULL,
|
||||||
|
(void *) &cip_class->highest_attribute_number,
|
||||||
|
kGetableSingleAndAll ); /* max instance attribute number*/
|
||||||
|
|
||||||
|
InsertService(meta_class,
|
||||||
|
kGetAttributeAll,
|
||||||
|
&GetAttributeAll,
|
||||||
|
"GetAttributeAll"); /* bind instance services to the metaclass*/
|
||||||
|
InsertService(meta_class,
|
||||||
|
kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus CipMessageRouterInit() {
|
||||||
|
|
||||||
|
CipClass *message_router = CreateCipClass(kCipMessageRouterClassCode, /* class code */
|
||||||
|
7, /* # of class attributes */
|
||||||
|
7, /* # highest class attribute number */
|
||||||
|
2, /* # of class services */
|
||||||
|
0, /* # of instance attributes */
|
||||||
|
0, /* # highest instance attribute number */
|
||||||
|
1, /* # of instance services */
|
||||||
|
1, /* # of instances */
|
||||||
|
"message router", /* class name */
|
||||||
|
1, /* # class revision*/
|
||||||
|
InitializeCipMessageRouterClass); /* # function pointer for initialization*/
|
||||||
|
if(NULL == message_router) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
InsertService(message_router,
|
||||||
|
kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
|
||||||
|
/* reserved for future use -> set to zero */
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Get the registered MessageRouter object corresponding to ClassID.
|
||||||
|
* given a class ID, return a pointer to the registration node for that object
|
||||||
|
*
|
||||||
|
* @param class_id Class code to be searched for.
|
||||||
|
* @return Pointer to registered message router object
|
||||||
|
* NULL .. Class not registered
|
||||||
|
*/
|
||||||
|
CipMessageRouterObject *GetRegisteredObject(EipUint32 class_id) {
|
||||||
|
CipMessageRouterObject *object = g_first_object; /* get pointer to head of class registration list */
|
||||||
|
|
||||||
|
while(NULL != object) /* for each entry in list*/
|
||||||
|
{
|
||||||
|
OPENER_ASSERT(NULL != object->cip_class);
|
||||||
|
if(object->cip_class->class_code == class_id) {
|
||||||
|
return object; /* return registration node if it matches class ID*/
|
||||||
|
}
|
||||||
|
object = object->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipClass *GetCipClass(const CipUdint class_code) {
|
||||||
|
CipMessageRouterObject *message_router_object =
|
||||||
|
GetRegisteredObject(class_code);
|
||||||
|
|
||||||
|
if(message_router_object) {
|
||||||
|
return message_router_object->cip_class;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipInstance *GetCipInstance(const CipClass *RESTRICT const cip_class,
|
||||||
|
const CipInstanceNum instance_number) {
|
||||||
|
|
||||||
|
if(instance_number == 0) {
|
||||||
|
return (CipInstance *) cip_class; /* if the instance number is zero, return the class object itself*/
|
||||||
|
|
||||||
|
}
|
||||||
|
/* pointer to linked list of instances from the class object*/
|
||||||
|
for(CipInstance *instance = cip_class->instances; instance;
|
||||||
|
instance = instance->next) /* follow the list*/
|
||||||
|
{
|
||||||
|
if(instance->instance_number == instance_number) {
|
||||||
|
return instance; /* if the number matches, return the instance*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus RegisterCipClass(CipClass *cip_class) {
|
||||||
|
CipMessageRouterObject **message_router_object = &g_first_object;
|
||||||
|
|
||||||
|
while(*message_router_object) {
|
||||||
|
message_router_object = &(*message_router_object)->next; /* follow the list until p points to an empty link (list end)*/
|
||||||
|
|
||||||
|
}
|
||||||
|
*message_router_object =
|
||||||
|
(CipMessageRouterObject *) CipCalloc(1, sizeof(CipMessageRouterObject) ); /* create a new node at the end of the list*/
|
||||||
|
if(*message_router_object == 0) {
|
||||||
|
return kEipStatusError; /* check for memory error*/
|
||||||
|
|
||||||
|
}
|
||||||
|
(*message_router_object)->cip_class = cip_class; /* fill in the new node*/
|
||||||
|
(*message_router_object)->next = NULL;
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus NotifyMessageRouter(EipUint8 *data,
|
||||||
|
int data_length,
|
||||||
|
CipMessageRouterResponse *message_router_response,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session) {
|
||||||
|
EipStatus eip_status = kEipStatusOkSend;
|
||||||
|
CipError status = kCipErrorSuccess;
|
||||||
|
|
||||||
|
OPENER_TRACE_INFO("NotifyMessageRouter: routing unconnected message\n");
|
||||||
|
if(kCipErrorSuccess !=
|
||||||
|
(status =
|
||||||
|
CreateMessageRouterRequestStructure(data, data_length,
|
||||||
|
&g_message_router_request) ) ) { /* error from create MR structure*/
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"NotifyMessageRouter: error from createMRRequeststructure\n");
|
||||||
|
message_router_response->general_status = status;
|
||||||
|
message_router_response->size_of_additional_status = 0;
|
||||||
|
message_router_response->reserved = 0;
|
||||||
|
message_router_response->reply_service =
|
||||||
|
(0x80 | g_message_router_request.service);
|
||||||
|
} else {
|
||||||
|
/* forward request to appropriate Object if it is registered*/
|
||||||
|
CipMessageRouterObject *registered_object = GetRegisteredObject(
|
||||||
|
g_message_router_request.request_path.class_id);
|
||||||
|
if(registered_object == 0) {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"NotifyMessageRouter: sending CIP_ERROR_OBJECT_DOES_NOT_EXIST reply, class id 0x%x is not registered\n",
|
||||||
|
(unsigned ) g_message_router_request.request_path.class_id);
|
||||||
|
message_router_response->general_status = kCipErrorPathDestinationUnknown; /*according to the test tool this should be the correct error flag instead of CIP_ERROR_OBJECT_DOES_NOT_EXIST;*/
|
||||||
|
message_router_response->size_of_additional_status = 0;
|
||||||
|
message_router_response->reserved = 0;
|
||||||
|
message_router_response->reply_service =
|
||||||
|
(0x80 | g_message_router_request.service);
|
||||||
|
} else {
|
||||||
|
/* call notify function from Object with ClassID (gMRRequest.RequestPath.ClassID)
|
||||||
|
object will or will not make an reply into gMRResponse*/
|
||||||
|
message_router_response->reserved = 0;
|
||||||
|
OPENER_ASSERT(NULL != registered_object->cip_class); OPENER_TRACE_INFO(
|
||||||
|
"NotifyMessageRouter: calling notify function of class '%s'\n",
|
||||||
|
registered_object->cip_class->class_name);
|
||||||
|
eip_status = NotifyClass(registered_object->cip_class,
|
||||||
|
&g_message_router_request,
|
||||||
|
message_router_response,
|
||||||
|
originator_address,
|
||||||
|
encapsulation_session);
|
||||||
|
|
||||||
|
#ifdef OPENER_TRACE_ENABLED
|
||||||
|
if (eip_status == kEipStatusError) {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyMR: notify function of class '%s' returned an error\n",
|
||||||
|
registered_object->cip_class->class_name);
|
||||||
|
} else if (eip_status == kEipStatusOk) {
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"notifyMR: notify function of class '%s' returned no reply\n",
|
||||||
|
registered_object->cip_class->class_name);
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"notifyMR: notify function of class '%s' returned a reply\n",
|
||||||
|
registered_object->cip_class->class_name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eip_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipError CreateMessageRouterRequestStructure(const EipUint8 *data,
|
||||||
|
EipInt16 data_length,
|
||||||
|
CipMessageRouterRequest *message_router_request)
|
||||||
|
{
|
||||||
|
|
||||||
|
message_router_request->service = *data;
|
||||||
|
data++;
|
||||||
|
data_length--;
|
||||||
|
|
||||||
|
size_t number_of_decoded_bytes;
|
||||||
|
const EipStatus path_result =
|
||||||
|
DecodePaddedEPath(&(message_router_request->request_path),
|
||||||
|
&data,
|
||||||
|
&number_of_decoded_bytes);
|
||||||
|
if(path_result != kEipStatusOk) {
|
||||||
|
return kCipErrorPathSegmentError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(number_of_decoded_bytes > data_length) {
|
||||||
|
return kCipErrorPathSizeInvalid;
|
||||||
|
} else {
|
||||||
|
message_router_request->data = data;
|
||||||
|
message_router_request->request_data_size = data_length -
|
||||||
|
number_of_decoded_bytes;
|
||||||
|
return kCipErrorSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteAllClasses(void) {
|
||||||
|
CipMessageRouterObject *message_router_object = g_first_object; /* get pointer to head of class registration list */
|
||||||
|
CipMessageRouterObject *message_router_object_to_delete = NULL;
|
||||||
|
CipInstance *instance = NULL;
|
||||||
|
CipInstance *instance_to_delete = NULL;
|
||||||
|
|
||||||
|
while(NULL != message_router_object) {
|
||||||
|
message_router_object_to_delete = message_router_object;
|
||||||
|
message_router_object = message_router_object->next;
|
||||||
|
|
||||||
|
instance = message_router_object_to_delete->cip_class->instances;
|
||||||
|
while(NULL != instance) {
|
||||||
|
instance_to_delete = instance;
|
||||||
|
instance = instance->next;
|
||||||
|
if(message_router_object_to_delete->cip_class->number_of_attributes) /* if the class has instance attributes */
|
||||||
|
{ /* then free storage for the attribute array */
|
||||||
|
CipFree(instance_to_delete->attributes);
|
||||||
|
}
|
||||||
|
CipFree(instance_to_delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free meta class data*/
|
||||||
|
CipClass *meta_class =
|
||||||
|
message_router_object_to_delete->cip_class->class_instance.cip_class;
|
||||||
|
CipFree(meta_class->class_name);
|
||||||
|
CipFree(meta_class->services);
|
||||||
|
CipFree(meta_class->get_single_bit_mask);
|
||||||
|
CipFree(meta_class->set_bit_mask);
|
||||||
|
CipFree(meta_class->get_all_bit_mask);
|
||||||
|
CipFree(meta_class);
|
||||||
|
|
||||||
|
/* free class data*/
|
||||||
|
CipClass *cip_class = message_router_object_to_delete->cip_class;
|
||||||
|
CipFree(cip_class->class_name);
|
||||||
|
CipFree(cip_class->get_single_bit_mask);
|
||||||
|
CipFree(cip_class->set_bit_mask);
|
||||||
|
CipFree(cip_class->get_all_bit_mask);
|
||||||
|
CipFree(cip_class->class_instance.attributes);
|
||||||
|
CipFree(cip_class->services);
|
||||||
|
CipFree(cip_class);
|
||||||
|
/* free message router object */
|
||||||
|
CipFree(message_router_object_to_delete);
|
||||||
|
}
|
||||||
|
g_first_object = NULL;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPMESSAGEROUTER_H_
|
||||||
|
#define OPENER_CIPMESSAGEROUTER_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief Message Router class code */
|
||||||
|
static const CipUint kCipMessageRouterClassCode = 0x02U;
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
|
||||||
|
/** @brief Initialize the data structures of the message router
|
||||||
|
* @return kEipStatusOk if class was initialized, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus CipMessageRouterInit(void);
|
||||||
|
|
||||||
|
/** @brief Free all data allocated by the classes created in the CIP stack
|
||||||
|
*/
|
||||||
|
void DeleteAllClasses(void);
|
||||||
|
|
||||||
|
/** @brief Notify the MessageRouter that an explicit message (connected or unconnected)
|
||||||
|
* has been received. This function will be called from the encapsulation layer.
|
||||||
|
* The CPF structure is already parsed an can be accessed via the global variable:
|
||||||
|
* g_stCPFDataItem.
|
||||||
|
* @param data pointer to the data buffer of the message directly at the beginning of the CIP part.
|
||||||
|
* @param data_length number of bytes in the data buffer
|
||||||
|
* @param originator_address The address of the originator as received
|
||||||
|
* @param encapsulation_session The associated encapsulation session of the explicit message
|
||||||
|
* @return kEipStatusError on fault
|
||||||
|
* kEipStatusOk on success
|
||||||
|
*/
|
||||||
|
EipStatus NotifyMessageRouter(EipUint8 *data,
|
||||||
|
int data_length,
|
||||||
|
CipMessageRouterResponse *message_router_response,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/*! Register a class at the message router.
|
||||||
|
* In order that the message router can deliver
|
||||||
|
* explicit messages each class has to register.
|
||||||
|
* Will be automatically done when invoking create
|
||||||
|
* createCIPClass.
|
||||||
|
* @param cip_class CIP class to be registered
|
||||||
|
* @return kEipStatusOk on success
|
||||||
|
*/
|
||||||
|
EipStatus RegisterCipClass(CipClass *cip_class);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPMESSAGEROUTER_H_ */
|
||||||
230
OrginalSourceRepo/OpENer-master/source/src/cip/cipqos.c
Normal file
230
OrginalSourceRepo/OpENer-master/source/src/cip/cipqos.c
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009/, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "cipqos.h"
|
||||||
|
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
#include "ciperror.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "cipethernetlink.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
#define DEFAULT_DSCP_EVENT 59U
|
||||||
|
#define DEFAULT_DSCP_GENERAL 47U
|
||||||
|
#define DEFAULT_DSCP_URGENT 55U
|
||||||
|
#define DEFAULT_DSCP_SCHEDULED 47U
|
||||||
|
#define DEFAULT_DSCP_HIGH 43U
|
||||||
|
#define DEFAULT_DSCP_LOW 31U
|
||||||
|
#define DEFAULT_DSCP_EXPLICIT 27U
|
||||||
|
|
||||||
|
/** @brief The QoS object
|
||||||
|
*
|
||||||
|
* The global instance of the QoS object
|
||||||
|
*/
|
||||||
|
CipQosObject g_qos = {
|
||||||
|
.q_frames_enable = false,
|
||||||
|
.dscp.event = DEFAULT_DSCP_EVENT,
|
||||||
|
.dscp.general = DEFAULT_DSCP_GENERAL,
|
||||||
|
.dscp.urgent = DEFAULT_DSCP_URGENT,
|
||||||
|
.dscp.scheduled = DEFAULT_DSCP_SCHEDULED,
|
||||||
|
.dscp.high = DEFAULT_DSCP_HIGH,
|
||||||
|
.dscp.low = DEFAULT_DSCP_LOW,
|
||||||
|
.dscp.explicit_msg = DEFAULT_DSCP_EXPLICIT
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Active set of DSCP data inherits its data from the QoS object on boot-up
|
||||||
|
*
|
||||||
|
* The QoS DSCP values can be changed from the EIP network but the changes should come
|
||||||
|
* into effect only after a restart. Values are initialized with the default values.
|
||||||
|
* Changes are activated via the Identity Reset function
|
||||||
|
*/
|
||||||
|
static CipQosDscpValues s_active_dscp = {
|
||||||
|
.event = DEFAULT_DSCP_EVENT,
|
||||||
|
.general = DEFAULT_DSCP_GENERAL,
|
||||||
|
.urgent = DEFAULT_DSCP_URGENT,
|
||||||
|
.scheduled = DEFAULT_DSCP_SCHEDULED,
|
||||||
|
.high = DEFAULT_DSCP_HIGH,
|
||||||
|
.low = DEFAULT_DSCP_LOW,
|
||||||
|
.explicit_msg = DEFAULT_DSCP_EXPLICIT
|
||||||
|
};
|
||||||
|
|
||||||
|
/************** Functions ****************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/**@brief Retrieve the given data according to CIP encoding from the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* Implementation of the decode function for the SetAttributeSingle CIP service for QoS
|
||||||
|
* Objects.
|
||||||
|
* @param data pointer to value to be written.
|
||||||
|
* @param message_router_request pointer to the request where the data should be taken from
|
||||||
|
* @param message_router_response pointer to the response where status should be set
|
||||||
|
* @return length of taken bytes
|
||||||
|
* -1 .. error
|
||||||
|
*/
|
||||||
|
int DecodeCipQoSAttribute(void *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
const EipUint8 **const cip_message = &(message_router_request->data);
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
if (NULL != cip_message) {
|
||||||
|
|
||||||
|
CipUsint attribute_value_received = GetUsintFromMessage(cip_message);
|
||||||
|
if (attribute_value_received < 64U) {
|
||||||
|
|
||||||
|
*(CipUsint *)data = attribute_value_received; //write value to attribute
|
||||||
|
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
number_of_decoded_bytes = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
message_router_response->general_status = kCipErrorNotEnoughData;
|
||||||
|
OPENER_TRACE_INFO("CIP QoS not enough data\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority) {
|
||||||
|
|
||||||
|
CipUsint priority_value;
|
||||||
|
switch (priority) {
|
||||||
|
case kConnectionObjectPriorityLow:
|
||||||
|
priority_value = s_active_dscp.low;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectPriorityHigh:
|
||||||
|
priority_value = s_active_dscp.high;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectPriorityScheduled:
|
||||||
|
priority_value = s_active_dscp.scheduled;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectPriorityUrgent:
|
||||||
|
priority_value = s_active_dscp.urgent;
|
||||||
|
break;
|
||||||
|
case kConnectionObjectPriorityExplicit: /* fall through */
|
||||||
|
default:
|
||||||
|
priority_value = s_active_dscp.explicit_msg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return priority_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus CipQoSInit() {
|
||||||
|
|
||||||
|
CipClass *qos_class = NULL;
|
||||||
|
|
||||||
|
if( ( qos_class = CreateCipClass(kCipQoSClassCode,
|
||||||
|
7, /* # class attributes */
|
||||||
|
7, /* # highest class attribute number */
|
||||||
|
2, /* # class services */
|
||||||
|
8, /* # instance attributes */
|
||||||
|
8, /* # highest instance attribute number */
|
||||||
|
2, /* # instance services */
|
||||||
|
1, /* # instances */
|
||||||
|
"Quality of Service",
|
||||||
|
1, /* # class revision */
|
||||||
|
NULL /* # function pointer for initialization */
|
||||||
|
) ) == 0 ) {
|
||||||
|
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipInstance *instance = GetCipInstance(qos_class, 1); /* bind attributes to the instance #1 that was created above */
|
||||||
|
|
||||||
|
InsertAttribute(instance,
|
||||||
|
1,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
(void *) &g_qos.q_frames_enable,
|
||||||
|
kNotSetOrGetable);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
2,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
(void *) &g_qos.dscp.event,
|
||||||
|
kNotSetOrGetable);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
3,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
(void *) &g_qos.dscp.general,
|
||||||
|
kNotSetOrGetable);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
4,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
DecodeCipQoSAttribute,
|
||||||
|
(void *) &g_qos.dscp.urgent,
|
||||||
|
kGetableSingle | kSetable | kNvDataFunc);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
5,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
DecodeCipQoSAttribute,
|
||||||
|
(void *) &g_qos.dscp.scheduled,
|
||||||
|
kGetableSingle | kSetable | kNvDataFunc);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
6,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
DecodeCipQoSAttribute,
|
||||||
|
(void *) &g_qos.dscp.high,
|
||||||
|
kGetableSingle | kSetable | kNvDataFunc);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
7,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
DecodeCipQoSAttribute,
|
||||||
|
(void *) &g_qos.dscp.low,
|
||||||
|
kGetableSingle | kSetable | kNvDataFunc);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
8,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
DecodeCipQoSAttribute,
|
||||||
|
(void *) &g_qos.dscp.explicit_msg,
|
||||||
|
kGetableSingle | kSetable | kNvDataFunc);
|
||||||
|
|
||||||
|
InsertService(qos_class, kGetAttributeSingle, &GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
InsertService(qos_class, kSetAttributeSingle, &SetAttributeSingle,
|
||||||
|
"SetAttributeSingle");
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipQosUpdateUsedSetQosValues(void) {
|
||||||
|
s_active_dscp = g_qos.dscp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipQosResetAttributesToDefaultValues(void) {
|
||||||
|
static const CipQosDscpValues kDefaultValues = {
|
||||||
|
.event = DEFAULT_DSCP_EVENT,
|
||||||
|
.general = DEFAULT_DSCP_GENERAL,
|
||||||
|
.urgent = DEFAULT_DSCP_URGENT,
|
||||||
|
.scheduled = DEFAULT_DSCP_SCHEDULED,
|
||||||
|
.high = DEFAULT_DSCP_HIGH,
|
||||||
|
.low = DEFAULT_DSCP_LOW,
|
||||||
|
.explicit_msg = DEFAULT_DSCP_EXPLICIT
|
||||||
|
};
|
||||||
|
g_qos.q_frames_enable = false;
|
||||||
|
g_qos.dscp = kDefaultValues;
|
||||||
|
}
|
||||||
63
OrginalSourceRepo/OpENer-master/source/src/cip/cipqos.h
Normal file
63
OrginalSourceRepo/OpENer-master/source/src/cip/cipqos.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENER_CIPQOS_H_
|
||||||
|
#define OPENER_CIPQOS_H_
|
||||||
|
|
||||||
|
/** @file cipqos.h
|
||||||
|
* @brief Public interface of the QoS Object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
|
||||||
|
/** @brief QoS Object class code */
|
||||||
|
static const CipUint kCipQoSClassCode = 0x48U;
|
||||||
|
|
||||||
|
/* public types */
|
||||||
|
|
||||||
|
/** This type represents the group of DSCP values of the QoS object. */
|
||||||
|
typedef struct cip_qos_dscp_values {
|
||||||
|
CipUsint event; /**< Attr. #2: DSCP value for event messages */
|
||||||
|
CipUsint general; /**< Attr. #3: DSCP value for general messages */
|
||||||
|
CipUsint urgent; /**< Attr. #4: DSCP value for CIP transport class 0/1 Urgent priority messages */
|
||||||
|
CipUsint scheduled; /**< Attr. #5: DSCP value for CIP transport class 0/1 Scheduled priority messages */
|
||||||
|
CipUsint high; /**< Attr. #6: DSCP value for CIP transport class 0/1 High priority messages */
|
||||||
|
CipUsint low; /**< Attr. #7: DSCP value for CIP transport class 0/1 low priority messages */
|
||||||
|
CipUsint explicit_msg; /**< Attr. #8: DSCP value for CIP explicit messages (transport class 2/3 and UCMM)
|
||||||
|
and all other EtherNet/IP encapsulation messages */
|
||||||
|
} CipQosDscpValues;
|
||||||
|
|
||||||
|
/** This type represents the QoS object */
|
||||||
|
typedef struct {
|
||||||
|
CipUsint q_frames_enable; /**< Attr. #1: Enables or disable sending 802.1Q frames on CIP and IEEE 1588 messages */
|
||||||
|
CipQosDscpValues dscp; /**< Attributes #2 ... #8 of DSCP values - beware! must not be the used set */
|
||||||
|
} CipQosObject;
|
||||||
|
|
||||||
|
|
||||||
|
/* public data */
|
||||||
|
extern CipQosObject g_qos;
|
||||||
|
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
/** @brief Provide the matching DSCP value for a given connection object priority level
|
||||||
|
*/
|
||||||
|
CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority);
|
||||||
|
|
||||||
|
/** @brief Create and initialize the QoS object
|
||||||
|
*/
|
||||||
|
EipStatus CipQoSInit(void);
|
||||||
|
|
||||||
|
/** @brief Updates the currently used set of DSCP priority values
|
||||||
|
*/
|
||||||
|
void CipQosUpdateUsedSetQosValues(void);
|
||||||
|
|
||||||
|
/** @brief Reset attribute values to default. Does not update currently used set */
|
||||||
|
void CipQosResetAttributesToDefaultValues(void);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPQOS_H_*/
|
||||||
286
OrginalSourceRepo/OpENer-master/source/src/cip/cipstring.c
Normal file
286
OrginalSourceRepo/OpENer-master/source/src/cip/cipstring.c
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Implements functions to operate on CIP string types
|
||||||
|
*
|
||||||
|
* Some functions to create CIP string types from C strings or data buffers.
|
||||||
|
*/
|
||||||
|
#include "cipstring.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
|
||||||
|
CipStringN *ClearCipStringN(CipStringN *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
if(NULL != cip_string->string) {
|
||||||
|
CipFree(cip_string->string);
|
||||||
|
}
|
||||||
|
cip_string->string = NULL;
|
||||||
|
cip_string->length = 0;
|
||||||
|
cip_string->size = 0;
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
return cip_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeCipStringN(CipStringN *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
ClearCipStringN(cip_string);
|
||||||
|
CipFree(cip_string);
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Sets length and data for a CipStringN based on an octet stream, symbol length, and symbol width
|
||||||
|
*
|
||||||
|
* @param cip_string The string to be set
|
||||||
|
* @param str_len Amount of CipStringN symbols
|
||||||
|
* @param size Width of the CipStringN symbols
|
||||||
|
* @param data The octet stream
|
||||||
|
*
|
||||||
|
* @return The CipStringN address
|
||||||
|
*/
|
||||||
|
CipStringN *SetCipStringNByData(CipStringN *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
CipUint size,
|
||||||
|
const CipOctet *const data) {
|
||||||
|
CipStringN *result = cip_string;
|
||||||
|
|
||||||
|
(void) ClearCipStringN(cip_string);
|
||||||
|
|
||||||
|
if(0 != str_len) {
|
||||||
|
/* No trailing '\0' character! */
|
||||||
|
cip_string->length = str_len;
|
||||||
|
cip_string->size = size;
|
||||||
|
cip_string->string = CipCalloc(cip_string->length,
|
||||||
|
cip_string->size * sizeof(CipOctet) );
|
||||||
|
if(NULL == cip_string->string) {
|
||||||
|
result = NULL;
|
||||||
|
cip_string->length = 0;
|
||||||
|
cip_string->size = 0;
|
||||||
|
} else {
|
||||||
|
memcpy(cip_string->string,
|
||||||
|
data,
|
||||||
|
cip_string->length * cip_string->size * sizeof(CipOctet) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipStringN *SetCipStringNByCstr(CipStringN *const cip_string,
|
||||||
|
const char *const string,
|
||||||
|
const CipUint symbol_size) {
|
||||||
|
if(0 != strlen(string) % symbol_size) {
|
||||||
|
OPENER_TRACE_ERR("Not enough octets for %d width StringN\n", symbol_size);
|
||||||
|
return cip_string;
|
||||||
|
}
|
||||||
|
/* We expect here, that the length of the string is the total length in Octets */
|
||||||
|
return SetCipStringNByData(cip_string,
|
||||||
|
(CipUint) strlen(string) / symbol_size,
|
||||||
|
symbol_size,
|
||||||
|
(const CipOctet *) string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeCipString2(CipString2 *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
ClearCipString2(cip_string);
|
||||||
|
CipFree(cip_string);
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Frees a CipString2 structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString2 structure to be freed
|
||||||
|
*
|
||||||
|
* @return Freed CipString2 structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipString2 *ClearCipString2(CipString2 *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
if(NULL != cip_string->string) {
|
||||||
|
CipFree(cip_string->string);
|
||||||
|
cip_string->string = NULL;
|
||||||
|
cip_string->length = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
return cip_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipString2 *SetCipString2ByData(CipString2 *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
const CipOctet *const data) {
|
||||||
|
CipString2 *result = cip_string;
|
||||||
|
|
||||||
|
(void) ClearCipString2(cip_string);
|
||||||
|
|
||||||
|
if(0 != str_len) {
|
||||||
|
/* No trailing '\0' character! */
|
||||||
|
cip_string->string = CipCalloc(str_len, 2 * sizeof(CipOctet) );
|
||||||
|
if(NULL == cip_string->string) {
|
||||||
|
result = NULL;
|
||||||
|
} else {
|
||||||
|
cip_string->length = str_len;
|
||||||
|
memcpy(cip_string->string, data, str_len * 2 * sizeof(CipOctet) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipString2 *SetCipString2ByCstr(CipString2 *const cip_string,
|
||||||
|
const char *const string) {
|
||||||
|
return SetCipString2ByData(cip_string, (CipUint) strlen(string) / 2,
|
||||||
|
(const CipOctet *) string);
|
||||||
|
}
|
||||||
|
|
||||||
|
CipString *ClearCipString(CipString *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
if(NULL != cip_string->string) {
|
||||||
|
CipFree(cip_string->string);
|
||||||
|
cip_string->string = NULL;
|
||||||
|
cip_string->length = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString!\n");
|
||||||
|
}
|
||||||
|
return cip_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeCipString(CipString *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
ClearCipString(cip_string);
|
||||||
|
CipFree(cip_string);
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipString *SetCipStringByData(CipString *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
const CipOctet *const data) {
|
||||||
|
CipString *result = cip_string;
|
||||||
|
|
||||||
|
(void) ClearCipString(cip_string);
|
||||||
|
|
||||||
|
if(0 != str_len) {
|
||||||
|
/* No trailing '\0' character. */
|
||||||
|
cip_string->string = CipCalloc(str_len, sizeof(CipOctet) );
|
||||||
|
if(NULL == cip_string->string) {
|
||||||
|
result = NULL;
|
||||||
|
} else {
|
||||||
|
cip_string->length = str_len;
|
||||||
|
memcpy(cip_string->string, data, str_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipString *SetCipStringByCstr(CipString *const cip_string,
|
||||||
|
const char *const string) {
|
||||||
|
return SetCipStringByData(cip_string, (CipUint) strlen(string),
|
||||||
|
(const CipOctet *) string);
|
||||||
|
}
|
||||||
|
|
||||||
|
CipShortString *ClearCipShortString(CipShortString *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
if(NULL != cip_string->string) {
|
||||||
|
CipFree(cip_string->string);
|
||||||
|
cip_string->string = NULL;
|
||||||
|
cip_string->length = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipShortString!\n");
|
||||||
|
}
|
||||||
|
return cip_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Frees a CipShortString structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipShortString structure to be freed
|
||||||
|
*
|
||||||
|
* @return Freed CipShortString structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void FreeCipShortString(CipShortString *const cip_string) {
|
||||||
|
if(NULL != cip_string) {
|
||||||
|
ClearCipShortString(cip_string);
|
||||||
|
CipFree(cip_string);
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Trying to free NULL CipString2!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Sets length and data for a CipShortString based on an octet stream and symbol length
|
||||||
|
*
|
||||||
|
* @param cip_string The CipShortString to be set
|
||||||
|
* @param str_len Amount of CipShortString symbols
|
||||||
|
* @param data The octet stream
|
||||||
|
*
|
||||||
|
* @return The CipShortString address
|
||||||
|
*/
|
||||||
|
CipShortString *SetCipShortStringByData(CipShortString *const cip_string,
|
||||||
|
const CipUsint str_len,
|
||||||
|
const CipOctet *const data) {
|
||||||
|
CipShortString *result = cip_string;
|
||||||
|
|
||||||
|
(void) ClearCipShortString(cip_string);
|
||||||
|
|
||||||
|
if(0 != str_len) {
|
||||||
|
/* No trailing '\0' character. */
|
||||||
|
cip_string->string = CipCalloc(str_len, sizeof(CipOctet) );
|
||||||
|
if(NULL == cip_string->string) {
|
||||||
|
result = NULL;
|
||||||
|
} else {
|
||||||
|
cip_string->length = str_len;
|
||||||
|
memcpy(cip_string->string, data, str_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Copies the content of C-string to a CipShortString under the expectation, that each C-String element is a CipShortString octet
|
||||||
|
*
|
||||||
|
* @param cip_string Target CipShortString
|
||||||
|
* @param string Source C-string
|
||||||
|
*
|
||||||
|
* @return Target CipShortString
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipShortString *SetCipShortStringByCstr(CipShortString *const cip_string,
|
||||||
|
const char *const string) {
|
||||||
|
return SetCipShortStringByData(cip_string, (CipUsint) strlen(string),
|
||||||
|
(const CipOctet *) string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensures buf is NUL terminated, provided initial validation is successful */
|
||||||
|
int GetCstrFromCipShortString(CipShortString *const string, char *buf, size_t len) {
|
||||||
|
size_t num;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (!string || !buf || len < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
num = (size_t)string->length;
|
||||||
|
if (len <= num) {
|
||||||
|
rc = (int)(num - len - 1);
|
||||||
|
num = len - 1;
|
||||||
|
}
|
||||||
|
len = num;
|
||||||
|
|
||||||
|
memcpy(buf, string->string, num);
|
||||||
|
buf[len] = 0;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
183
OrginalSourceRepo/OpENer-master/source/src/cip/cipstring.h
Normal file
183
OrginalSourceRepo/OpENer-master/source/src/cip/cipstring.h
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Declare functions to operate on CIP string types
|
||||||
|
*
|
||||||
|
* Some functions to create CIP string types from C strings or data buffers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENER_CIPSTRING_H_
|
||||||
|
#define OPENER_CIPSTRING_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
CipStringN *SetCipStringNByData(CipStringN *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
CipUint size,
|
||||||
|
const CipOctet *const data);
|
||||||
|
|
||||||
|
/** @brief Copies the content of C-string to a CipStringN under the expectation, that each C-String element is a StringN octet
|
||||||
|
*
|
||||||
|
* @param cip_string Target CipStringN
|
||||||
|
* @param string Source C-string
|
||||||
|
* @param symbol_size Size of a symbol in CipStringN instance
|
||||||
|
*
|
||||||
|
* @return Target CipStringN
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipStringN *SetCipStringNByCstr(CipStringN *const cip_string,
|
||||||
|
const char *const string,
|
||||||
|
const CipUint symbol_size);
|
||||||
|
|
||||||
|
/** @brief Clears the internal structure of a CipStringN
|
||||||
|
*
|
||||||
|
* @param cip_string The CipStringN to be freed
|
||||||
|
*
|
||||||
|
* @return Returns the address of the cleared CipStringN
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipStringN *ClearCipStringN(CipStringN *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Frees the memory of a CipStringN
|
||||||
|
*
|
||||||
|
* @param cip_string The CipStringN to be freed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void FreeCipStringN(CipStringN *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Sets length and data for a CipString2 based on an octet stream and symbol length
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString2 to be set
|
||||||
|
* @param str_len Amount of CipString2 symbols
|
||||||
|
* @param data The octet stream
|
||||||
|
*
|
||||||
|
* @return The CipString2 address
|
||||||
|
*/
|
||||||
|
CipString2 *SetCipString2ByData(CipString2 *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
const CipOctet *const data);
|
||||||
|
|
||||||
|
/** @brief Copies the content of C-string to a CipString2 under the expectation, that each C-String element is a CipString2 octet
|
||||||
|
*
|
||||||
|
* @param cip_string Target CipString2
|
||||||
|
* @param string Source C-string
|
||||||
|
*
|
||||||
|
* @return Target CipString2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipString2 *SetCipString2ByCstr(CipString2 *const cip_string,
|
||||||
|
const char *const string);
|
||||||
|
|
||||||
|
/** @brief Clears the internal structure of a CipString2
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString2 to be cleared
|
||||||
|
*
|
||||||
|
* @return Returns the address of the cleared CipString2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipString2 *ClearCipString2(CipString2 *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Frees a CipString2 structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString2 structure to be freed
|
||||||
|
*
|
||||||
|
* @return Freed CipString2 structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void FreeCipString2(CipString2 *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Clears the internal CipString structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString structure to be cleared
|
||||||
|
*
|
||||||
|
* @return Cleared CipString structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipString *ClearCipString(CipString *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Frees a CipString structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipString structure to be freed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void FreeCipString(CipString *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Sets length and data for a CipString based on an octet stream and symbol length
|
||||||
|
*
|
||||||
|
* @param cip_string The string to be set
|
||||||
|
* @param str_len Amount of CipString symbols
|
||||||
|
* @param data The octet stream
|
||||||
|
*
|
||||||
|
* @return The CipString address
|
||||||
|
*/
|
||||||
|
CipString *SetCipStringByData(CipString *const cip_string,
|
||||||
|
CipUint str_len,
|
||||||
|
const CipOctet *const data);
|
||||||
|
|
||||||
|
/** @brief Copies the content of C-string to a CipString under the expectation, that each C-String element is a CipString octet
|
||||||
|
*
|
||||||
|
* @param cip_string Target CipString
|
||||||
|
* @param string Source C-string
|
||||||
|
*
|
||||||
|
* @return Target CipString
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipString *SetCipStringByCstr(CipString *const cip_string,
|
||||||
|
const char *const string);
|
||||||
|
|
||||||
|
/** @brief Clears the internal CipShortString structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipShortString structure to be cleared
|
||||||
|
*
|
||||||
|
* @return Cleared CipShortString structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipShortString *ClearCipShortString(CipShortString *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Frees a CipShortString structure
|
||||||
|
*
|
||||||
|
* @param cip_string The CipShortString structure to be freed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void FreeCipShortString(CipShortString *const cip_string);
|
||||||
|
|
||||||
|
/** @brief Sets length and data for a CipShortString based on an octet stream and symbol length
|
||||||
|
*
|
||||||
|
* @param cip_string The string to be set
|
||||||
|
* @param str_len Amount of CipString symbols
|
||||||
|
* @param data The octet stream
|
||||||
|
*
|
||||||
|
* @return The CipShortString address
|
||||||
|
*/
|
||||||
|
CipShortString *SetCipShortStringByData(CipShortString *const cip_string,
|
||||||
|
const CipUsint str_len,
|
||||||
|
const CipOctet *const data);
|
||||||
|
|
||||||
|
/** @brief Copies the content of C-string to a CipShortString under the expectation, that each C-String element is a CipShortString octet
|
||||||
|
*
|
||||||
|
* @param cip_string Target CipShortString
|
||||||
|
* @param string Source C-string
|
||||||
|
*
|
||||||
|
* @return Target CipShortString
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CipShortString *SetCipShortStringByCstr(CipShortString *const cip_string,
|
||||||
|
const char *const string);
|
||||||
|
|
||||||
|
/** @brief Returns a NUL terminated C-string from a CipShortString
|
||||||
|
*
|
||||||
|
* @param string The CipShortString to extract from
|
||||||
|
* @param buf Pointer to a buffer to store the contents in
|
||||||
|
* @param len Length of buffer
|
||||||
|
*
|
||||||
|
* @return POSIX OK(0) if the complete string fit in @param buf, otherwise non-zero.
|
||||||
|
*/
|
||||||
|
int GetCstrFromCipShortString(CipShortString *const string, char *buf,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
#endif /* of OPENER_CIPSTRING_H_ */
|
||||||
331
OrginalSourceRepo/OpENer-master/source/src/cip/cipstringi.c
Normal file
331
OrginalSourceRepo/OpENer-master/source/src/cip/cipstringi.c
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cipstringi.h"
|
||||||
|
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipstring.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
|
||||||
|
void CipStringIDelete(CipStringI *const string) {
|
||||||
|
for(size_t i = 0; i < string->number_of_strings; ++i) {
|
||||||
|
string->array_of_string_i_structs[i].language_char_1 = '\0';
|
||||||
|
string->array_of_string_i_structs[i].language_char_2 = '\0';
|
||||||
|
string->array_of_string_i_structs[i].language_char_3 = '\0';
|
||||||
|
string->array_of_string_i_structs[i].character_set = '\0';
|
||||||
|
switch(string->array_of_string_i_structs[i].char_string_struct) {
|
||||||
|
case kCipShortString:
|
||||||
|
ClearCipShortString(
|
||||||
|
(CipShortString *) &string->array_of_string_i_structs[i].string );
|
||||||
|
break;
|
||||||
|
case kCipString:
|
||||||
|
ClearCipString(
|
||||||
|
(CipString *) &string->array_of_string_i_structs[i].string );
|
||||||
|
break;
|
||||||
|
case kCipString2:
|
||||||
|
ClearCipString2(
|
||||||
|
(CipString2 *) &string->array_of_string_i_structs[i].string );
|
||||||
|
break;
|
||||||
|
case kCipStringN:
|
||||||
|
ClearCipStringN(
|
||||||
|
(CipStringN *) &string->array_of_string_i_structs[i].string );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
string->array_of_string_i_structs[i].char_string_struct = 0x00;
|
||||||
|
}
|
||||||
|
string->number_of_strings = 0;
|
||||||
|
CipFree(string->array_of_string_i_structs);
|
||||||
|
string->array_of_string_i_structs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CipStringIsAnyStringEmpty(const CipStringI *const string) {
|
||||||
|
for(size_t i = 0; i < string->number_of_strings; ++i) {
|
||||||
|
size_t length = 0;
|
||||||
|
void *pointer_to_string = string->array_of_string_i_structs[i].string;
|
||||||
|
switch(string->array_of_string_i_structs[i].char_string_struct) {
|
||||||
|
case kCipShortString:
|
||||||
|
length = ( (CipShortString *) pointer_to_string )->length;
|
||||||
|
break;
|
||||||
|
case kCipString:
|
||||||
|
length = ( (CipString *) pointer_to_string )->length;
|
||||||
|
break;
|
||||||
|
case kCipString2:
|
||||||
|
length = ( (CipString2 *) pointer_to_string )->length;
|
||||||
|
break;
|
||||||
|
case kCipStringN:
|
||||||
|
length = ( (CipStringN *) pointer_to_string )->length;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
if(0 == length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *CipStringICreateStringStructure(CipStringIStruct *const to) {
|
||||||
|
switch(to->char_string_struct) {
|
||||||
|
case kCipShortString:
|
||||||
|
return to->string = CipCalloc(1, sizeof(CipShortString) );
|
||||||
|
case kCipString:
|
||||||
|
return to->string = CipCalloc(1, sizeof(CipString) );
|
||||||
|
case kCipString2:
|
||||||
|
return to->string = CipCalloc(1, sizeof(CipString2) );
|
||||||
|
case kCipStringN:
|
||||||
|
return to->string = CipCalloc(1, sizeof(CipStringN) );
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipStringIDeepCopyInternalString(CipStringIStruct *const to,
|
||||||
|
const CipStringIStruct *const from) {
|
||||||
|
switch(to->char_string_struct) {
|
||||||
|
case kCipShortString: {
|
||||||
|
CipShortString *toString = (CipShortString *) to->string;
|
||||||
|
CipShortString *fromString = (CipShortString *) from->string;
|
||||||
|
toString->length = fromString->length;
|
||||||
|
toString->string = CipCalloc(toString->length, sizeof(CipOctet) );
|
||||||
|
memcpy(toString->string,
|
||||||
|
fromString->string,
|
||||||
|
sizeof(CipOctet) * toString->length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString: {
|
||||||
|
CipString *toString = (CipString *) to->string;
|
||||||
|
CipString *fromString = (CipString *) from->string;
|
||||||
|
toString->length = fromString->length;
|
||||||
|
toString->string = CipCalloc(toString->length, sizeof(CipOctet) );
|
||||||
|
memcpy(toString->string,
|
||||||
|
fromString->string,
|
||||||
|
sizeof(CipOctet) * toString->length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString2: {
|
||||||
|
CipString2 *toString = (CipString2 *) to->string;
|
||||||
|
CipString2 *fromString = (CipString2 *) from->string;
|
||||||
|
toString->length = fromString->length;
|
||||||
|
toString->string = CipCalloc(toString->length, 2 * sizeof(CipOctet) );
|
||||||
|
memcpy(toString->string,
|
||||||
|
fromString->string,
|
||||||
|
2 * sizeof(CipOctet) * toString->length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipStringN: {
|
||||||
|
CipStringN *toString = (CipStringN *) to->string;
|
||||||
|
CipStringN *fromString = (CipStringN *) from->string;
|
||||||
|
toString->length = fromString->length;
|
||||||
|
toString->size = fromString->size;
|
||||||
|
toString->string =
|
||||||
|
CipCalloc(toString->length, toString->size * sizeof(CipOctet) );
|
||||||
|
memcpy(toString->string, fromString->string,
|
||||||
|
toString->size * sizeof(CipOctet) * toString->length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipStringICopy(CipStringI *const to,
|
||||||
|
const CipStringI *const from) {
|
||||||
|
to->number_of_strings = from->number_of_strings;
|
||||||
|
to->array_of_string_i_structs =
|
||||||
|
CipCalloc(to->number_of_strings, sizeof(CipStringIStruct) );
|
||||||
|
for(size_t i = 0; i < to->number_of_strings; ++i) {
|
||||||
|
CipStringIStruct *const toStruct = to->array_of_string_i_structs + i;
|
||||||
|
CipStringIStruct *const fromStruct = from->array_of_string_i_structs + i;
|
||||||
|
toStruct->language_char_1 = fromStruct->language_char_1;
|
||||||
|
toStruct->language_char_2 = fromStruct->language_char_2;
|
||||||
|
toStruct->language_char_3 = fromStruct->language_char_3;
|
||||||
|
toStruct->char_string_struct = fromStruct->char_string_struct;
|
||||||
|
toStruct->character_set = fromStruct->character_set;
|
||||||
|
CipStringICreateStringStructure(toStruct);
|
||||||
|
CipStringIDeepCopyInternalString(toStruct, fromStruct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipStringIDecodeFromMessage(CipStringI *data_to,
|
||||||
|
CipMessageRouterRequest *const message_router_request)
|
||||||
|
{
|
||||||
|
|
||||||
|
CipStringI *target_stringI = data_to;
|
||||||
|
|
||||||
|
target_stringI->number_of_strings = GetUsintFromMessage(
|
||||||
|
&message_router_request->data);
|
||||||
|
|
||||||
|
target_stringI->array_of_string_i_structs = CipCalloc(
|
||||||
|
target_stringI->number_of_strings, sizeof(CipStringIStruct) );
|
||||||
|
|
||||||
|
for (size_t i = 0; i < target_stringI->number_of_strings; ++i) {
|
||||||
|
|
||||||
|
target_stringI->array_of_string_i_structs[i].language_char_1 =
|
||||||
|
GetUsintFromMessage(&message_router_request->data);
|
||||||
|
target_stringI->array_of_string_i_structs[i].language_char_2 =
|
||||||
|
GetUsintFromMessage(&message_router_request->data);
|
||||||
|
target_stringI->array_of_string_i_structs[i].language_char_3 =
|
||||||
|
GetUsintFromMessage(&message_router_request->data);
|
||||||
|
target_stringI->array_of_string_i_structs[i].char_string_struct =
|
||||||
|
GetUsintFromMessage(&message_router_request->data);
|
||||||
|
target_stringI->array_of_string_i_structs[i].character_set =
|
||||||
|
GetUintFromMessage(&message_router_request->data);
|
||||||
|
|
||||||
|
switch (target_stringI->array_of_string_i_structs[i].char_string_struct) {
|
||||||
|
case kCipShortString: {
|
||||||
|
target_stringI->array_of_string_i_structs[i].string = CipCalloc(1,
|
||||||
|
sizeof(
|
||||||
|
CipShortString) );
|
||||||
|
CipShortString *short_string =
|
||||||
|
(CipShortString *) (target_stringI->array_of_string_i_structs[i].
|
||||||
|
string);
|
||||||
|
CipUsint length = GetUsintFromMessage(
|
||||||
|
&message_router_request->data);
|
||||||
|
SetCipShortStringByData(short_string, length,
|
||||||
|
message_router_request->data);
|
||||||
|
message_router_request->data += length;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString: {
|
||||||
|
target_stringI->array_of_string_i_structs[i].string = CipCalloc(1,
|
||||||
|
sizeof(
|
||||||
|
CipString) );
|
||||||
|
CipString *const string =
|
||||||
|
(CipString *const ) target_stringI->array_of_string_i_structs[i].
|
||||||
|
string;
|
||||||
|
CipUint length = GetUintFromMessage(&message_router_request->data);
|
||||||
|
SetCipStringByData(string, length, message_router_request->data);
|
||||||
|
message_router_request->data += length;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString2: {
|
||||||
|
target_stringI->array_of_string_i_structs[i].string = CipCalloc(1,
|
||||||
|
sizeof(
|
||||||
|
CipString2) );
|
||||||
|
CipString2 *const string =
|
||||||
|
(CipString2 *const ) target_stringI->array_of_string_i_structs[i].
|
||||||
|
string;
|
||||||
|
CipUint length = GetUintFromMessage(&message_router_request->data);
|
||||||
|
SetCipString2ByData(string, length, message_router_request->data);
|
||||||
|
message_router_request->data += length * 2 * sizeof(CipOctet);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipStringN: {
|
||||||
|
CipUint size = GetUintFromMessage(&message_router_request->data);
|
||||||
|
CipUint length = GetUintFromMessage(&message_router_request->data);
|
||||||
|
|
||||||
|
target_stringI->array_of_string_i_structs[i].string = CipCalloc(1,
|
||||||
|
sizeof(
|
||||||
|
CipStringN) );
|
||||||
|
CipStringN *const string =
|
||||||
|
(CipStringN *const ) target_stringI->array_of_string_i_structs[i].
|
||||||
|
string;
|
||||||
|
SetCipStringNByData(string, length, size,
|
||||||
|
message_router_request->data);
|
||||||
|
message_router_request->data += length * size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
} //end for
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CipStringICompare(const CipStringI *const stringI_1,
|
||||||
|
const CipStringI *const stringI_2) {
|
||||||
|
|
||||||
|
/*loop through struct 1 strings*/
|
||||||
|
for (size_t i = 0; i < stringI_1->number_of_strings; ++i) {
|
||||||
|
// String 1
|
||||||
|
void *string_1 = stringI_1->array_of_string_i_structs[i].string;
|
||||||
|
void *string_1_data = NULL;
|
||||||
|
CipUint len_1 = 0; //size of string-struct in bytes
|
||||||
|
|
||||||
|
switch (stringI_1->array_of_string_i_structs[i].char_string_struct) {
|
||||||
|
case kCipShortString: {
|
||||||
|
len_1 = ((CipShortString *)string_1)->length;
|
||||||
|
string_1_data = ((CipShortString *)string_1)->string;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString: {
|
||||||
|
len_1 = ((CipString *)string_1)->length;
|
||||||
|
string_1_data = ((CipString *)string_1)->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case kCipString2: {
|
||||||
|
len_1 = ((CipString2 *)string_1)->length * 2;
|
||||||
|
string_1_data = ((CipString2 *)string_1)->string;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipStringN: {
|
||||||
|
CipUint length = ((CipStringN *)string_1)->length;
|
||||||
|
CipUint size = ((CipStringN *)string_1)->size; //bytes per symbol
|
||||||
|
len_1 = length * size;
|
||||||
|
string_1_data = ((CipStringN *)string_1)->string;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*loop through struct 2 strings*/
|
||||||
|
for (size_t j = 0; j < stringI_2->number_of_strings; ++j) {
|
||||||
|
// String 2
|
||||||
|
void *string_2 = stringI_2->array_of_string_i_structs[j].string;
|
||||||
|
void *string_2_data = NULL;
|
||||||
|
CipUint len_2 = 0; //size of string-struct in bytes
|
||||||
|
|
||||||
|
switch (stringI_2->array_of_string_i_structs[j].char_string_struct) {
|
||||||
|
case kCipShortString: {
|
||||||
|
len_2 = ((CipShortString *)string_2)->length;
|
||||||
|
string_2_data = ((CipShortString *)string_2)->string;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipString: {
|
||||||
|
len_2 = ((CipString *)string_2)->length;
|
||||||
|
string_2_data = ((CipString *)string_2)->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case kCipString2: {
|
||||||
|
len_2 = ((CipString2 *)string_2)->length * 2;
|
||||||
|
string_2_data = ((CipString2 *)string_2)->string;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCipStringN: {
|
||||||
|
CipUint length = ((CipStringN *)string_2)->length;
|
||||||
|
CipUint size = ((CipStringN *)string_2)->size; //bytes per symbol
|
||||||
|
len_2 = length * size;
|
||||||
|
string_2_data = ((CipStringN *)string_2)->string;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("CIP File: No valid String type received!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*compare strings*/ //TODO: compare works only for same data types
|
||||||
|
if (len_1 == len_2 && string_1_data != NULL && string_2_data != NULL) {
|
||||||
|
if (0 == memcmp(string_1_data, string_2_data, len_1) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //end for 1
|
||||||
|
} //end for 2
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
20
OrginalSourceRepo/OpENer-master/source/src/cip/cipstringi.h
Normal file
20
OrginalSourceRepo/OpENer-master/source/src/cip/cipstringi.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
void CipStringIDelete(CipStringI *const string);
|
||||||
|
|
||||||
|
bool CipStringIsAnyStringEmpty(const CipStringI *const string);
|
||||||
|
|
||||||
|
void CipStringICopy(CipStringI *const to,
|
||||||
|
const CipStringI *const from);
|
||||||
|
|
||||||
|
void CipStringIDecodeFromMessage(CipStringI *data_to,
|
||||||
|
CipMessageRouterRequest *const message_router_request);
|
||||||
|
|
||||||
|
bool CipStringICompare(const CipStringI *const stringI_1,
|
||||||
|
const CipStringI *const stringI_2);
|
||||||
|
|
@ -0,0 +1,725 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include "ciptcpipinterface.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
#include "ciperror.h"
|
||||||
|
#include "cipstring.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "cipethernetlink.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "cipassembly.h"
|
||||||
|
|
||||||
|
/* Define constants to initialize the config_capability attribute (#2). These
|
||||||
|
* are needed as defines because we use them for static initialization. */
|
||||||
|
#define CFG_CAPS_BOOTP_CLIENT 0x01U /**< Device has BOOTP client */
|
||||||
|
#define CFG_CAPS_DNS_CLIENT 0x02U /**< Device has DNS client */
|
||||||
|
#define CFG_CAPS_DHCP_CLIENT 0x04U /**< Device has DHCP client */
|
||||||
|
#define CFG_CAPS_CFG_SETTABLE 0x10U /**< Interface configuration can be set */
|
||||||
|
#define CFG_CAPS_CFG_CHG_NEEDS_RESET 0x40U /**< Interface configuration change needs RESET */
|
||||||
|
#define CFG_CAPS_ACD_CAPABLE 0x80U /**< Device supports ACD */
|
||||||
|
|
||||||
|
/* OPENER_TCPIP_IFACE_CFG_SETTABLE controls if the interface configuration is fully settable.
|
||||||
|
* Prepare additional defines needed here:
|
||||||
|
* - IFACE_CFG_SET_MODE is used to initialize the set mode of the affected attributes (3, 5, 6).
|
||||||
|
* - CFG_CAPS is the matching initial value for .config_capability
|
||||||
|
*/
|
||||||
|
#if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
|
||||||
|
0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
|
||||||
|
#define IFACE_CFG_SET_MODE kSetable
|
||||||
|
#define CFG_CAPS (CFG_CAPS_DHCP_CLIENT | CFG_CAPS_CFG_SETTABLE | \
|
||||||
|
CFG_CAPS_CFG_CHG_NEEDS_RESET)
|
||||||
|
#else
|
||||||
|
#define IFACE_CFG_SET_MODE kNotSetOrGetable
|
||||||
|
#define CFG_CAPS (CFG_CAPS_DHCP_CLIENT)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** definition of TCP/IP object instance 1 data */
|
||||||
|
CipTcpIpObject g_tcpip =
|
||||||
|
{
|
||||||
|
.status = 0x01, /* attribute #1 TCP status with 1 we indicate that we got a valid configuration from DHCP, BOOTP or NV data */
|
||||||
|
.config_capability = CFG_CAPS, /* attribute #2 config_capability */
|
||||||
|
.config_control = 0x02, /* attribute #3 config_control: 0x02 means that the device shall obtain its interface configuration values via DHCP. */
|
||||||
|
#if 2 != OPENER_ETHLINK_INSTANCE_CNT
|
||||||
|
/* For the details where the physical_link_object path should point to, depending on the #
|
||||||
|
* of Ethernet Link objects refer to Vol. 2, Section 5-4.3.2.4 "Physical Link Object". */
|
||||||
|
.physical_link_object = { /* attribute #4 physical link object */
|
||||||
|
2, /* PathSize in 16 Bit chunks */
|
||||||
|
CIP_ETHERNETLINK_CLASS_CODE, /* Class Code */
|
||||||
|
OPENER_ETHLINK_INSTANCE_CNT, /* Instance # */
|
||||||
|
0 /* Attribute # (not used as this is the EPATH to the EthernetLink object)*/
|
||||||
|
},
|
||||||
|
#else
|
||||||
|
.physical_link_object = { /* attribute #4 physical link object */
|
||||||
|
0, /* PathSize in 16 Bit chunks */
|
||||||
|
0, /* Class Code */
|
||||||
|
0, /* Instance # */
|
||||||
|
0 /* Attribute # */
|
||||||
|
},
|
||||||
|
#endif /* #if OPENER_ETHLINK_INSTANCE_CNT != 2 */
|
||||||
|
.interface_configuration = { /* attribute #5 interface_configuration */
|
||||||
|
0, /* IP address */
|
||||||
|
0, /* NetworkMask */
|
||||||
|
0, /* Gateway */
|
||||||
|
0, /* NameServer */
|
||||||
|
0, /* NameServer2 */
|
||||||
|
{ /* DomainName */
|
||||||
|
0, NULL,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.hostname = { /* attribute #6 hostname */
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
.mcast_ttl_value = 1, /* attribute #8 mcast TTL value */
|
||||||
|
.mcast_config = { /* attribute #9 multicast configuration */
|
||||||
|
0, /* use the default allocation algorithm */
|
||||||
|
0, /* reserved */
|
||||||
|
1, /* we currently use only one multicast address */
|
||||||
|
0 /* the multicast address will be allocated on IP address configuration */
|
||||||
|
},
|
||||||
|
.select_acd = false,
|
||||||
|
.encapsulation_inactivity_timeout = 120 /* attribute #13 encapsulation_inactivity_timeout, use a default value of 120 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/************** Static Functions *********************************/
|
||||||
|
|
||||||
|
#if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
|
||||||
|
0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
|
||||||
|
/** Check for pb being an alphanumerical character
|
||||||
|
*
|
||||||
|
* Is slow but avoids issues with the locale if we're NOT in the 'C' locale.
|
||||||
|
*/
|
||||||
|
static bool isalnum_c(const EipByte byte) {
|
||||||
|
return
|
||||||
|
('a' <= byte && byte <= 'z') ||
|
||||||
|
('A' <= byte && byte <= 'Z') ||
|
||||||
|
('0' <= byte && byte <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check passed string to conform to the rules for host name labels
|
||||||
|
*
|
||||||
|
* @param label pointer to the label string to check
|
||||||
|
* @return true if label is valid
|
||||||
|
*
|
||||||
|
* A host name label is a string of length 1 to 63 characters with
|
||||||
|
* the characters of the string conforming to this rules:
|
||||||
|
* - 1st character: [A-Za-z0-9]
|
||||||
|
* - next character: [-A-Za-z0-9]
|
||||||
|
* - last character: [A-Za-z0-9]
|
||||||
|
* The minimum length of 1 is checked but not the maximum length
|
||||||
|
* that has already been enforced on data reception.
|
||||||
|
*/
|
||||||
|
static bool IsValidNameLabel(const EipByte *label) {
|
||||||
|
if (!isalnum_c(*label) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++label;
|
||||||
|
while ('\0' != *label && (isalnum_c(*label) || '-' == *label) ) {
|
||||||
|
++label;
|
||||||
|
}
|
||||||
|
return ('\0' == *label && '-' != label[-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if domain is a valid domain
|
||||||
|
*
|
||||||
|
* @param p_domain pointer to domain string to check
|
||||||
|
* @return true if domain is valid
|
||||||
|
*
|
||||||
|
* We check here for domain names that are part of a valid host name.
|
||||||
|
* - Do not allow leading or trailing dots.
|
||||||
|
* - Also a single '.' (the root domain) is not allowed.
|
||||||
|
* - A complete numeric domain is accepted even if it should not.
|
||||||
|
* - IDN domain names are not supported. Any IDN domain names must
|
||||||
|
* be converted to punycode (see https://www.punycoder.com/) by
|
||||||
|
* the user in advance.
|
||||||
|
*/
|
||||||
|
static bool IsValidDomain(EipByte *domain) {
|
||||||
|
bool status = true;
|
||||||
|
|
||||||
|
OPENER_TRACE_INFO("Enter '%s'->", domain);
|
||||||
|
if ('.' == *domain) { /* Forbid leading dot */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EipByte *dot = (EipByte *)strchr( (char *)domain, '.' );
|
||||||
|
if (dot) {
|
||||||
|
bool rc;
|
||||||
|
|
||||||
|
*dot = '\0';
|
||||||
|
status &= rc = IsValidNameLabel(domain);
|
||||||
|
OPENER_TRACE_INFO("Checked %d '%s'\n", rc, domain);
|
||||||
|
if ('\0' != dot[1]) {
|
||||||
|
status &= IsValidDomain(dot + 1);
|
||||||
|
}
|
||||||
|
else { /* Forbid trailing dot */
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
*dot = '.';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status = IsValidNameLabel(domain);
|
||||||
|
OPENER_TRACE_INFO("Check end %d '%s'\n", status, domain);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Check if an IP address is a valid network mask
|
||||||
|
*
|
||||||
|
* @param netmask network mask in network byte order
|
||||||
|
* @return valid status
|
||||||
|
*
|
||||||
|
* Check if it is a valid network mask pattern. The pattern 0xffffffff and
|
||||||
|
* 0x00000000 are considered as invalid.
|
||||||
|
*/
|
||||||
|
static bool IsValidNetmask(in_addr_t netmask) {
|
||||||
|
in_addr_t v = ntohl(netmask);
|
||||||
|
|
||||||
|
v = ~v; /* Create the host mask */
|
||||||
|
++v; /* This must be a power of 2 then */
|
||||||
|
bool valid = v && !(v & (v - 1) ); /* Check if it is a power of 2 */
|
||||||
|
|
||||||
|
return valid && (INADDR_BROADCAST != netmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if an IP address is in one of the network classes A, B or C
|
||||||
|
*
|
||||||
|
* @param ip_addr IP address in network byte order
|
||||||
|
* @return status
|
||||||
|
*
|
||||||
|
* Check if the IP address belongs to the network classes A, B or C.
|
||||||
|
*/
|
||||||
|
static bool IsInClassAbc(in_addr_t ip_addr) {
|
||||||
|
in_addr_t ip = ntohl(ip_addr);
|
||||||
|
return IN_CLASSA(ip) || IN_CLASSB(ip) || IN_CLASSC(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if an IP address is on the loopback network
|
||||||
|
*
|
||||||
|
* @param ip_addr IP address in network byte order
|
||||||
|
* @return status
|
||||||
|
*
|
||||||
|
* Check if the IP address belongs to the loopback network
|
||||||
|
* 127.0.0.0 - 127.255.255.255.
|
||||||
|
*/
|
||||||
|
static bool IsOnLoopbackNetwork(in_addr_t ip_addr) {
|
||||||
|
in_addr_t ip = ntohl(ip_addr);
|
||||||
|
return (ip & IN_CLASSA_NET) == (INADDR_LOOPBACK & IN_CLASSA_NET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if an IP address is either the network or the broadcast address
|
||||||
|
*
|
||||||
|
* @param ip_addr IP address in network byte order
|
||||||
|
* @param net_mask network mask in network byte order
|
||||||
|
* @return status
|
||||||
|
*
|
||||||
|
* Check if an IP address is either the network or the broadcast address.
|
||||||
|
* In this case it is not a valid IP address for a host.
|
||||||
|
* This check is endian agnostic.
|
||||||
|
*/
|
||||||
|
static bool IsNetworkOrBroadcastIp(in_addr_t ip_addr,
|
||||||
|
in_addr_t net_mask) {
|
||||||
|
return ( (ip_addr & net_mask) == ip_addr ) || /* is network address */
|
||||||
|
( (ip_addr | ~net_mask) == ip_addr ); /* is broadcast address */
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check the Interface configuration being valid according to EIP specification
|
||||||
|
*
|
||||||
|
* In Vol. 2 the "Table 5-4.3 Instance Attributes" provides some information
|
||||||
|
* which checks should be carried out on the Interface configuration's IP
|
||||||
|
* addresses. Also there are some hints in the
|
||||||
|
* Figure 5-4.1 "Diagram Showing the Behavior of the TCP/IP Object".
|
||||||
|
*
|
||||||
|
* The following checks may carried out on the IP addresses:
|
||||||
|
* - N0: IP is not 0 aka. INADDR_ANY
|
||||||
|
* - MASK: IP is a valid network mask
|
||||||
|
* - ABC: IP is in class A, B or C
|
||||||
|
* - NLCL: IP is not localhost aka. INADDR_LOOPBACK
|
||||||
|
* - NB: IP is neither network or broadcast address (using network_mask)
|
||||||
|
*
|
||||||
|
* This is the table which checks are applied to what IP:
|
||||||
|
* N0 | MASK | ABC | NLCL | NB | IP address
|
||||||
|
* + | - | + | + | + | ip_address
|
||||||
|
* - | + | - | - | - | network_mask
|
||||||
|
* - | - | + | + | + | gateway
|
||||||
|
* - | - | + | - | - | name_server / name_server_2
|
||||||
|
* A configured gateway must be reachable according to the network mask.
|
||||||
|
*/
|
||||||
|
static bool IsValidNetworkConfig(const CipTcpIpInterfaceConfiguration *if_cfg) {
|
||||||
|
if (INADDR_ANY == ntohl(if_cfg->ip_address) ) { /* N0 */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (INADDR_ANY != ntohl(if_cfg->network_mask) && /* MASK */
|
||||||
|
!IsValidNetmask(if_cfg->network_mask) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!IsInClassAbc(if_cfg->ip_address) || /* ABC */
|
||||||
|
!IsInClassAbc(if_cfg->gateway) ||
|
||||||
|
!IsInClassAbc(if_cfg->name_server) ||
|
||||||
|
!IsInClassAbc(if_cfg->name_server_2) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (IsOnLoopbackNetwork(if_cfg->ip_address) || /* NLCL */
|
||||||
|
IsOnLoopbackNetwork(if_cfg->gateway) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* Check NB */
|
||||||
|
if (IsNetworkOrBroadcastIp(if_cfg->ip_address, if_cfg->network_mask) ||
|
||||||
|
(INADDR_ANY != ntohl(if_cfg->gateway) &&
|
||||||
|
IsNetworkOrBroadcastIp(if_cfg->gateway, if_cfg->network_mask) ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (INADDR_ANY != ntohl(if_cfg->gateway) &&
|
||||||
|
INADDR_ANY != ntohl(if_cfg->network_mask) ) {
|
||||||
|
/* gateway is configured. Check if it is reachable. */
|
||||||
|
if ( (if_cfg->network_mask & if_cfg->ip_address) !=
|
||||||
|
(if_cfg->network_mask & if_cfg->gateway) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsIOConnectionActive(void) {
|
||||||
|
DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
|
||||||
|
while (NULL != node) {
|
||||||
|
CipConnectionObject *connection = node->data;
|
||||||
|
if (ConnectionObjectIsTypeIOConnection(connection) &&
|
||||||
|
kConnectionObjectStateTimedOut !=
|
||||||
|
ConnectionObjectGetState(connection) ) {
|
||||||
|
/* An IO connection is present but is only considered active
|
||||||
|
* if it is NOT in timeout state. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
|
||||||
|
|
||||||
|
|
||||||
|
static CipUsint dummy_data_field = 0; /**< dummy data fiel to provide non-null data pointers for attributes without data fields */
|
||||||
|
|
||||||
|
/************** Functions ****************************************/
|
||||||
|
|
||||||
|
void EncodeCipTcpIpInterfaceConfiguration(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
CipTcpIpInterfaceConfiguration *
|
||||||
|
tcp_ip_network_interface_configuration =
|
||||||
|
(CipTcpIpInterfaceConfiguration *) data;
|
||||||
|
AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->ip_address),
|
||||||
|
outgoing_message);
|
||||||
|
AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->network_mask),
|
||||||
|
outgoing_message);
|
||||||
|
AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->gateway),
|
||||||
|
outgoing_message);
|
||||||
|
AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->name_server),
|
||||||
|
outgoing_message);
|
||||||
|
AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->name_server_2),
|
||||||
|
outgoing_message);
|
||||||
|
EncodeCipString(&(tcp_ip_network_interface_configuration->domain_name),
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeCipTcpIpMulticastConfiguration(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
EncodeCipUsint(&(g_tcpip.mcast_config.alloc_control), outgoing_message);
|
||||||
|
EncodeCipUsint(&(g_tcpip.mcast_config.reserved_shall_be_zero),
|
||||||
|
outgoing_message);
|
||||||
|
EncodeCipUint(&(g_tcpip.mcast_config.number_of_allocated_multicast_addresses),
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
CipUdint multicast_address = ntohl(
|
||||||
|
g_tcpip.mcast_config.starting_multicast_address);
|
||||||
|
|
||||||
|
EncodeCipUdint(&multicast_address, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeSafetyNetworkNumber(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0, 6, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeCipLastConflictDetected(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* Suppress unused parameter compiler warning. */
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
const size_t kAttribute11Size = sizeof(CipUsint) + 6 * sizeof(CipUsint) + 28 *
|
||||||
|
sizeof(CipUsint);
|
||||||
|
OPENER_ASSERT(kAttribute11Size == 35);
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
|
||||||
|
kAttribute11Size,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int DecodeTcpIpInterfaceConfigurationControl( /* Attribute 3 */
|
||||||
|
void *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
CipDword configuration_control_received = GetDintFromMessage(
|
||||||
|
&(message_router_request->data));
|
||||||
|
if ((configuration_control_received & kTcpipCfgCtrlMethodMask) >= 0x03
|
||||||
|
|| (configuration_control_received & ~kTcpipCfgCtrlMethodMask)) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Set reserved bits to zero on reception. */
|
||||||
|
configuration_control_received &= (kTcpipCfgCtrlMethodMask
|
||||||
|
| kTcpipCfgCtrlDnsEnable);
|
||||||
|
|
||||||
|
*(CipDword *)data = configuration_control_received;
|
||||||
|
number_of_decoded_bytes = 4;
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
|
||||||
|
0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
|
||||||
|
|
||||||
|
int DecodeCipTcpIpInterfaceConfiguration( /* Attribute 5 */
|
||||||
|
CipTcpIpInterfaceConfiguration *const data, //kCipUdintUdintUdintUdintUdintString
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
CipTcpIpInterfaceConfiguration if_cfg;
|
||||||
|
CipUdint tmp_ip;
|
||||||
|
|
||||||
|
if (IsIOConnectionActive()) {
|
||||||
|
message_router_response->general_status = kCipErrorDeviceStateConflict;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
if (kTcpipCfgCtrlStaticIp
|
||||||
|
!= (g_tcpip.config_control & kTcpipCfgCtrlMethodMask)) {
|
||||||
|
message_router_response->general_status = kCipErrorObjectStateConflict;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
memset(&if_cfg, 0, sizeof if_cfg);
|
||||||
|
tmp_ip = GetUdintFromMessage(&(message_router_request->data));
|
||||||
|
if_cfg.ip_address = htonl(tmp_ip);
|
||||||
|
tmp_ip = GetUdintFromMessage(&(message_router_request->data));
|
||||||
|
if_cfg.network_mask = htonl(tmp_ip);
|
||||||
|
tmp_ip = GetUdintFromMessage(&(message_router_request->data));
|
||||||
|
if_cfg.gateway = htonl(tmp_ip);
|
||||||
|
tmp_ip = GetUdintFromMessage(&(message_router_request->data));
|
||||||
|
if_cfg.name_server = htonl(tmp_ip);
|
||||||
|
tmp_ip = GetUdintFromMessage(&(message_router_request->data));
|
||||||
|
if_cfg.name_server_2 = htonl(tmp_ip);
|
||||||
|
|
||||||
|
CipUint domain_name_length = GetUintFromMessage(
|
||||||
|
&(message_router_request->data));
|
||||||
|
if (domain_name_length > 48) { /* see Vol. 2, Table 5-4.3 Instance Attributes */
|
||||||
|
message_router_response->general_status = kCipErrorTooMuchData;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
SetCipStringByData(&if_cfg.domain_name, domain_name_length,
|
||||||
|
message_router_request->data);
|
||||||
|
domain_name_length = (domain_name_length + 1) & (~0x0001u); /* Align for possible pad byte */
|
||||||
|
OPENER_TRACE_INFO("Domain: ds %hu '%s'\n",
|
||||||
|
domain_name_length,
|
||||||
|
if_cfg.domain_name.string);
|
||||||
|
|
||||||
|
if (!IsValidNetworkConfig(&if_cfg)
|
||||||
|
|| (domain_name_length > 0
|
||||||
|
&& !IsValidDomain(if_cfg.domain_name.string))) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = if_cfg; //write data to attribute
|
||||||
|
number_of_decoded_bytes = 20 + domain_name_length;
|
||||||
|
|
||||||
|
/* Tell that this configuration change becomes active after a reset */
|
||||||
|
g_tcpip.status |= kTcpipStatusIfaceCfgPend;
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int DecodeCipTcpIpInterfaceHostName( /* Attribute 6 */
|
||||||
|
CipString *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
CipString tmp_host_name = {
|
||||||
|
.length = 0u,
|
||||||
|
.string = NULL
|
||||||
|
};
|
||||||
|
CipUint host_name_length =
|
||||||
|
GetUintFromMessage(&(message_router_request->data) );
|
||||||
|
if (host_name_length > 64) { /* see RFC 1123 on more details */
|
||||||
|
message_router_response->general_status = kCipErrorTooMuchData;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
SetCipStringByData(&tmp_host_name,
|
||||||
|
host_name_length,
|
||||||
|
message_router_request->data);
|
||||||
|
host_name_length = (host_name_length + 1) & (~0x0001u); /* Align for possible pad byte */
|
||||||
|
OPENER_TRACE_INFO("Host Name: ds %hu '%s'\n",
|
||||||
|
host_name_length,
|
||||||
|
tmp_host_name.string);
|
||||||
|
|
||||||
|
if (!IsValidNameLabel(tmp_host_name.string) ) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = tmp_host_name; //write data to attribute
|
||||||
|
|
||||||
|
/* Tell that this configuration change becomes active after a reset */
|
||||||
|
g_tcpip.status |= kTcpipStatusIfaceCfgPend;
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
|
||||||
|
|
||||||
|
int DecodeCipTcpIpInterfaceEncapsulationInactivityTimeout( /* Attribute 13 */
|
||||||
|
void *const data,
|
||||||
|
CipMessageRouterRequest *const message_router_request,
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
|
||||||
|
int number_of_decoded_bytes = -1;
|
||||||
|
|
||||||
|
CipUint inactivity_timeout_received = GetUintFromMessage(
|
||||||
|
&(message_router_request->data));
|
||||||
|
|
||||||
|
if (inactivity_timeout_received > 3600) {
|
||||||
|
message_router_response->general_status =
|
||||||
|
kCipErrorInvalidAttributeValue;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
*(CipUint *)data = inactivity_timeout_received;
|
||||||
|
message_router_response->general_status = kCipErrorSuccess;
|
||||||
|
number_of_decoded_bytes = 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_of_decoded_bytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EipStatus CipTcpIpInterfaceInit() {
|
||||||
|
CipClass *tcp_ip_class = NULL;
|
||||||
|
|
||||||
|
if ( ( tcp_ip_class = CreateCipClass(kCipTcpIpInterfaceClassCode, /* class code */
|
||||||
|
0, /* # class attributes */
|
||||||
|
7, /* # highest class attribute number */
|
||||||
|
2, /* # class services */
|
||||||
|
13, /* # instance attributes */
|
||||||
|
13, /* # highest instance attribute number */
|
||||||
|
3, /* # instance services */
|
||||||
|
1, /* # instances */
|
||||||
|
"TCP/IP interface", 4, /* # class revision */
|
||||||
|
NULL /* # function pointer for initialization */
|
||||||
|
) ) == 0 ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipInstance *instance = GetCipInstance(tcp_ip_class, 1); /* bind attributes to the instance #1 that was created above */
|
||||||
|
|
||||||
|
InsertAttribute(instance,
|
||||||
|
1,
|
||||||
|
kCipDword,
|
||||||
|
EncodeCipDword,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.status,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
2,
|
||||||
|
kCipDword,
|
||||||
|
EncodeCipDword,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.config_capability,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
3,
|
||||||
|
kCipDword,
|
||||||
|
EncodeCipDword,
|
||||||
|
DecodeTcpIpInterfaceConfigurationControl,
|
||||||
|
&g_tcpip.config_control,
|
||||||
|
kSetAndGetAble | kNvDataFunc | IFACE_CFG_SET_MODE );
|
||||||
|
InsertAttribute(instance,
|
||||||
|
4,
|
||||||
|
kCipEpath,
|
||||||
|
EncodeCipEPath,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.physical_link_object,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
|
||||||
|
#if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
|
||||||
|
0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
|
||||||
|
InsertAttribute(instance,
|
||||||
|
5,
|
||||||
|
kCipUdintUdintUdintUdintUdintString,
|
||||||
|
EncodeCipTcpIpInterfaceConfiguration,
|
||||||
|
DecodeCipTcpIpInterfaceConfiguration,
|
||||||
|
&g_tcpip.interface_configuration,
|
||||||
|
kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
6,
|
||||||
|
kCipString,
|
||||||
|
EncodeCipString,
|
||||||
|
DecodeCipTcpIpInterfaceHostName,
|
||||||
|
&g_tcpip.hostname,
|
||||||
|
kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
|
||||||
|
|
||||||
|
#else
|
||||||
|
InsertAttribute(instance,
|
||||||
|
5,
|
||||||
|
kCipUdintUdintUdintUdintUdintString,
|
||||||
|
EncodeCipTcpIpInterfaceConfiguration,
|
||||||
|
NULL, //not settable
|
||||||
|
&g_tcpip.interface_configuration,
|
||||||
|
kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
6,
|
||||||
|
kCipString,
|
||||||
|
EncodeCipString,
|
||||||
|
NULL, //not settable
|
||||||
|
&g_tcpip.hostname,
|
||||||
|
kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
|
||||||
|
|
||||||
|
#endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
|
||||||
|
|
||||||
|
InsertAttribute(instance,
|
||||||
|
7,
|
||||||
|
kCipAny,
|
||||||
|
EncodeSafetyNetworkNumber,
|
||||||
|
NULL,
|
||||||
|
&dummy_data_field,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
8,
|
||||||
|
kCipUsint,
|
||||||
|
EncodeCipUsint,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.mcast_ttl_value,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
9,
|
||||||
|
kCipAny,
|
||||||
|
EncodeCipTcpIpMulticastConfiguration,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.mcast_config,
|
||||||
|
kGetableSingleAndAll);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
10,
|
||||||
|
kCipBool,
|
||||||
|
EncodeCipBool,
|
||||||
|
NULL,
|
||||||
|
&g_tcpip.select_acd,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
11,
|
||||||
|
kCipBool,
|
||||||
|
EncodeCipLastConflictDetected,
|
||||||
|
NULL,
|
||||||
|
&dummy_data_field,
|
||||||
|
kGetableAllDummy);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
12,
|
||||||
|
kCipBool,
|
||||||
|
EncodeCipBool,
|
||||||
|
NULL,
|
||||||
|
&dummy_data_field, kGetableAllDummy);
|
||||||
|
InsertAttribute(instance,
|
||||||
|
13,
|
||||||
|
kCipUint,
|
||||||
|
EncodeCipUint,
|
||||||
|
DecodeCipTcpIpInterfaceEncapsulationInactivityTimeout,
|
||||||
|
&g_tcpip.encapsulation_inactivity_timeout,
|
||||||
|
kSetAndGetAble | kNvDataFunc);
|
||||||
|
|
||||||
|
InsertService(tcp_ip_class, kGetAttributeSingle,
|
||||||
|
&GetAttributeSingle,
|
||||||
|
"GetAttributeSingle");
|
||||||
|
|
||||||
|
InsertService(tcp_ip_class, kGetAttributeAll, &GetAttributeAll,
|
||||||
|
"GetAttributeAll");
|
||||||
|
|
||||||
|
InsertService(tcp_ip_class, kSetAttributeSingle,
|
||||||
|
&SetAttributeSingle,
|
||||||
|
"SetAttributeSingle");
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutdownTcpIpInterface(void) {
|
||||||
|
/*Only free the resources if they are initialized */
|
||||||
|
if (NULL != g_tcpip.hostname.string) {
|
||||||
|
CipFree(g_tcpip.hostname.string);
|
||||||
|
g_tcpip.hostname.string = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL != g_tcpip.interface_configuration.domain_name.string) {
|
||||||
|
CipFree(g_tcpip.interface_configuration.domain_name.string);
|
||||||
|
g_tcpip.interface_configuration.domain_name.string = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function calculates the multicast base address to be used for CIP
|
||||||
|
* connections from the current IP setting. The algorithm is implemented
|
||||||
|
* according to CIP spec Volume 2,
|
||||||
|
* section 3-5.3 "Multicast Address Allocation for EtherNet/IP"
|
||||||
|
*/
|
||||||
|
void CipTcpIpCalculateMulticastIp(CipTcpIpObject *const tcpip) {
|
||||||
|
/* Multicast base address according to spec: 239.192.1.0 */
|
||||||
|
static const CipUdint cip_mcast_base_addr = 0xEFC00100;
|
||||||
|
|
||||||
|
/* Calculate the CIP multicast address. The multicast address is calculated, not input */
|
||||||
|
CipUdint host_id = ntohl(tcpip->interface_configuration.ip_address) &
|
||||||
|
~ntohl(tcpip->interface_configuration.network_mask);
|
||||||
|
host_id -= 1;
|
||||||
|
host_id &= 0x3ff;
|
||||||
|
|
||||||
|
tcpip->mcast_config.starting_multicast_address =
|
||||||
|
htonl(cip_mcast_base_addr + (host_id << 5) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EipUint16 GetEncapsulationInactivityTimeout(CipInstance *instance) {
|
||||||
|
CipAttributeStruct *attribute = GetCipAttribute(instance, 13);
|
||||||
|
OPENER_ASSERT(NULL != attribute);
|
||||||
|
CipUint *data = (CipUint *) attribute->data;
|
||||||
|
EipUint16 encapsulation_inactivity_timeout = *data;
|
||||||
|
return encapsulation_inactivity_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPTCPIPINTERFACE_H_
|
||||||
|
#define OPENER_CIPTCPIPINTERFACE_H_
|
||||||
|
|
||||||
|
/** @file ciptcpipinterface.h
|
||||||
|
* @brief Public interface of the TCP/IP Interface Object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @brief TCP/IP Interface class code */
|
||||||
|
static const CipUint kCipTcpIpInterfaceClassCode = 0xF5U;
|
||||||
|
|
||||||
|
/* Declare constants for status attribute (#1) */
|
||||||
|
/** Indicates a pending configuration change in the TTL Value and/or Mcast Config attributes.*/
|
||||||
|
static const CipDword kTcpipStatusMcastPend = 0x10U;
|
||||||
|
/** Indicates a pending configuration change in the Interface Configuration attribute. */
|
||||||
|
static const CipDword kTcpipStatusIfaceCfgPend = 0x20U;
|
||||||
|
/** Indicates when an IP address conflict has been detected by ACD. */
|
||||||
|
static const CipDword kTcpipStatusAcdStatus = 0x40U;
|
||||||
|
/** Indicates when an IP address conflict has been detected by ACD or the defense failed. */
|
||||||
|
static const CipDword kTcpipStatusAcdFault = 0x80U;
|
||||||
|
|
||||||
|
/* Declare constants for config_control attribute (#3) */
|
||||||
|
static const CipDword kTcpipCfgCtrlStaticIp = 0x00U; /**< IP configuration method is manual IP assignment */
|
||||||
|
static const CipDword kTcpipCfgCtrlBootp = 0x01U; /**< IP configuration method is BOOTP */
|
||||||
|
static const CipDword kTcpipCfgCtrlDhcp = 0x02U; /**< IP configuration method is DHCP */
|
||||||
|
static const CipDword kTcpipCfgCtrlMethodMask = 0x0FU; /**< bit mask for the method field */
|
||||||
|
static const CipDword kTcpipCfgCtrlDnsEnable = 0x10U; /**< enables DNS resolution on originator devices */
|
||||||
|
|
||||||
|
/** @brief Multicast Configuration struct, called Mcast config
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct multicast_address_configuration {
|
||||||
|
CipUsint alloc_control; /**< 0 for default multicast address generation algorithm; 1 for multicast addresses according to Num MCast and MCast Start Addr */
|
||||||
|
CipUsint reserved_shall_be_zero; /**< shall be zero */
|
||||||
|
CipUint number_of_allocated_multicast_addresses; /**< Number of IP multicast addresses allocated */
|
||||||
|
CipUdint starting_multicast_address; /**< Starting multicast address from which Num Mcast addresses are allocated */
|
||||||
|
} MulticastAddressConfiguration;
|
||||||
|
|
||||||
|
/** @brief Declaration of the TCP/IP object's structure type
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipDword status; /**< attribute #1 TCP status */
|
||||||
|
CipDword config_capability; /**< attribute #2 bitmap of capability flags */
|
||||||
|
CipDword config_control; /**< attribute #3 bitmap: control the interface configuration method: static / BOOTP / DHCP */
|
||||||
|
CipEpath physical_link_object; /**< attribute #4 references the Ethernet Link object for this interface */
|
||||||
|
CipTcpIpInterfaceConfiguration interface_configuration;/**< attribute #5 IP, network mask, gateway, name server 1 & 2, domain name*/
|
||||||
|
CipString hostname; /**< #6 host name*/
|
||||||
|
CipUsint mcast_ttl_value; /**< #8 the time to live value to be used for multi-cast connections */
|
||||||
|
|
||||||
|
/** #9 The multicast configuration for this device */
|
||||||
|
MulticastAddressConfiguration mcast_config;
|
||||||
|
CipBool select_acd; /**< attribute #10 - Is ACD enabled? */
|
||||||
|
|
||||||
|
/** #13 Number of seconds of inactivity before TCP connection is closed */
|
||||||
|
CipUint encapsulation_inactivity_timeout;
|
||||||
|
} CipTcpIpObject;
|
||||||
|
|
||||||
|
|
||||||
|
/* global public variables */
|
||||||
|
extern CipTcpIpObject g_tcpip; /**< declaration of TCP/IP object instance 1 data */
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
/** @brief Initializing the data structures of the TCP/IP interface object
|
||||||
|
*
|
||||||
|
* @return kEipStatusOk on success, otherwise kEipStatusError
|
||||||
|
*/
|
||||||
|
EipStatus CipTcpIpInterfaceInit(void);
|
||||||
|
|
||||||
|
/** @brief Clean up the allocated data of the TCP/IP interface object.
|
||||||
|
*
|
||||||
|
* Currently this is the host name string and the domain name string.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ShutdownTcpIpInterface(void);
|
||||||
|
|
||||||
|
/** @brief Calculate Multicast address base from current IP setting
|
||||||
|
*
|
||||||
|
* @param tcpip pointer to TCP/IP object
|
||||||
|
*/
|
||||||
|
void CipTcpIpCalculateMulticastIp(CipTcpIpObject *const tcpip);
|
||||||
|
|
||||||
|
/** @brief Public Method to get Encapsulation Inactivity Timeout Value
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
EipUint16 GetEncapsulationInactivityTimeout(CipInstance *instance);
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPTCPIPINTERFACE_H_ */
|
||||||
99
OrginalSourceRepo/OpENer-master/source/src/cip/ciptypes.c
Normal file
99
OrginalSourceRepo/OpENer-master/source/src/cip/ciptypes.c
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include <ciptypes.h>
|
||||||
|
#include <endianconv.h>
|
||||||
|
#include <trace.h>
|
||||||
|
|
||||||
|
|
||||||
|
const CipInstanceNum kCipInstanceNumMax = UINT16_MAX;
|
||||||
|
|
||||||
|
|
||||||
|
/* functions*/
|
||||||
|
size_t GetCipDataTypeLength(EipUint8 type, const EipUint8 *data) {
|
||||||
|
|
||||||
|
size_t length = 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case kCipBool:
|
||||||
|
case kCipSint:
|
||||||
|
case kCipUsint:
|
||||||
|
case kCipByte:
|
||||||
|
length = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipInt:
|
||||||
|
case kCipUint:
|
||||||
|
case kCipWord:
|
||||||
|
case kCipUsintUsint:
|
||||||
|
case kCipItime:
|
||||||
|
case kCipDate:
|
||||||
|
case kCipEngUnit:
|
||||||
|
length = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipDint:
|
||||||
|
case kCipUdint:
|
||||||
|
case kCipDword:
|
||||||
|
case kCipStime:
|
||||||
|
case kCipFtime:
|
||||||
|
case kCipTime:
|
||||||
|
case kCipReal:
|
||||||
|
case kCipTimeOfDay:
|
||||||
|
length = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipLint:
|
||||||
|
case kCipUlint:
|
||||||
|
case kCipLreal:
|
||||||
|
case kCipLword:
|
||||||
|
case kCipLtime:
|
||||||
|
length = 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCip6Usint:
|
||||||
|
length = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipString:
|
||||||
|
case kCipString2:
|
||||||
|
case kCipStringN:
|
||||||
|
if(NULL != data){
|
||||||
|
length = GetIntFromMessage(&data) + 2; // string length + 2 bytes length indicator
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipShortString:
|
||||||
|
if(NULL != data){
|
||||||
|
length = GetSintFromMessage(&data) + 1; // string length + 1 byte length indicator
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipEpath:
|
||||||
|
if(NULL != data){
|
||||||
|
length = GetIntFromMessage(&data) + 2; // path size + 2 bytes path size indicator
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCipByteArray:
|
||||||
|
if (NULL != data) {
|
||||||
|
CipByteArray *byte_array = (CipByteArray*) data;
|
||||||
|
length = byte_array->length;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_ERR("GetCipDataTypeLength ERROR\n");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* TODO: missing data types:
|
||||||
|
* kCipAny
|
||||||
|
* kCipDateAndTime
|
||||||
|
* kCipStringI
|
||||||
|
* kCipMemberList
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
502
OrginalSourceRepo/OpENer-master/source/src/cip/ciptypes.h
Normal file
502
OrginalSourceRepo/OpENer-master/source/src/cip/ciptypes.h
Normal file
|
|
@ -0,0 +1,502 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CIPTYPES_H_
|
||||||
|
#define OPENER_CIPTYPES_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "networkhandler.h"
|
||||||
|
#include "enipmessage.h"
|
||||||
|
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
|
||||||
|
/** @brief Enum containing the encoding values for CIP data types for CIP
|
||||||
|
* Messages */
|
||||||
|
typedef enum cip_data_types {
|
||||||
|
kCipAny = 0x00, /**< data type that can not be directly encoded */
|
||||||
|
kCipBool = 0xC1, /**< boolean data type */
|
||||||
|
kCipSint = 0xC2, /**< 8-bit signed integer */
|
||||||
|
kCipInt = 0xC3, /**< 16-bit signed integer */
|
||||||
|
kCipDint = 0xC4, /**< 32-bit signed integer */
|
||||||
|
kCipLint = 0xC5, /**< 64-bit signed integer */
|
||||||
|
kCipUsint = 0xC6, /**< 8-bit unsigned integer */
|
||||||
|
kCipUint = 0xC7, /**< 16-bit unsigned integer */
|
||||||
|
kCipUdint = 0xC8, /**< 32-bit unsigned integer */
|
||||||
|
kCipUlint = 0xC9, /**< 64-bit unsigned integer */
|
||||||
|
kCipReal = 0xCA, /**< Single precision floating point */
|
||||||
|
kCipLreal = 0xCB, /**< Double precision floating point*/
|
||||||
|
kCipStime = 0xCC, /**< Synchronous time information*, type of DINT */
|
||||||
|
kCipDate = 0xCD, /**< Date only*/
|
||||||
|
kCipTimeOfDay = 0xCE, /**< Time of day */
|
||||||
|
kCipDateAndTime = 0xCF, /**< Date and time of day */
|
||||||
|
kCipString = 0xD0, /**< Character string, 1 byte per character */
|
||||||
|
kCipByte = 0xD1, /**< 8-bit bit string */
|
||||||
|
kCipWord = 0xD2, /**< 16-bit bit string */
|
||||||
|
kCipDword = 0xD3, /**< 32-bit bit string */
|
||||||
|
kCipLword = 0xD4, /**< 64-bit bit string */
|
||||||
|
kCipString2 = 0xD5, /**< Character string, 2 byte per character */
|
||||||
|
kCipFtime = 0xD6, /**< Duration in micro-seconds, high resolution; range of DINT */
|
||||||
|
kCipLtime = 0xD7, /**< Duration in micro-seconds, high resolution, range of LINT */
|
||||||
|
kCipItime = 0xD8, /**< Duration in milli-seconds, short; range of INT*/
|
||||||
|
kCipStringN = 0xD9, /**< Character string, N byte per character */
|
||||||
|
kCipShortString = 0xDA, /**< Character string, 1 byte per character, 1 byte
|
||||||
|
length indicator */
|
||||||
|
kCipTime = 0xDB, /**< Duration in milli-seconds; range of DINT */
|
||||||
|
kCipEpath = 0xDC, /**< CIP path segments*/
|
||||||
|
kCipEngUnit = 0xDD, /**< Engineering Units, range of UINT*/
|
||||||
|
/* definition of some CIP structs */
|
||||||
|
/* need to be validated in IEC 61131-3 subclause 2.3.3 */
|
||||||
|
/* TODO: Check these codes */
|
||||||
|
kCipUsintUsint = 0xA0, /**< Used for CIP Identity attribute 4 Revision*/
|
||||||
|
kCipUdintUdintUdintUdintUdintString = 0xA1, /**< TCP/IP attribute 5 - IP address, subnet mask, gateway, IP name
|
||||||
|
server 1, IP name server 2, domain name*/
|
||||||
|
kCip6Usint = 0xA2, /**< Struct for MAC Address (six USINTs)*/
|
||||||
|
kCipMemberList = 0xA3, /**< */
|
||||||
|
kCipByteArray = 0xA4, /**< */
|
||||||
|
kInternalUint6 = 0xF0, /**< bogus hack, for port class attribute 9, TODO
|
||||||
|
figure out the right way to handle it */
|
||||||
|
kCipStringI
|
||||||
|
} CipDataType;
|
||||||
|
|
||||||
|
/** @brief returns the size of CIP data types in bytes
|
||||||
|
* @param type CIP data type
|
||||||
|
* @param data use data pointer if data length is variable, else set NULL
|
||||||
|
* @return size of CIP data type in bytes
|
||||||
|
* */
|
||||||
|
size_t GetCipDataTypeLength(EipUint8 type,
|
||||||
|
const EipUint8 *data);
|
||||||
|
|
||||||
|
/** @brief Definition of CIP service codes
|
||||||
|
*
|
||||||
|
* An Enum with all CIP service codes. Common services codes range from 0x01 to
|
||||||
|
*****0x1C
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/* Start CIP common services */
|
||||||
|
kGetAttributeAll = 0x01,
|
||||||
|
kSetAttributeAll = 0x02,
|
||||||
|
kGetAttributeList = 0x03,
|
||||||
|
kSetAttributeList = 0x04,
|
||||||
|
kReset = 0x05,
|
||||||
|
kStart = 0x06,
|
||||||
|
kStop = 0x07,
|
||||||
|
kCreate = 0x08,
|
||||||
|
kDelete = 0x09,
|
||||||
|
kMultipleServicePacket = 0x0A,
|
||||||
|
kApplyAttributes = 0x0D,
|
||||||
|
kGetAttributeSingle = 0x0E,
|
||||||
|
kSetAttributeSingle = 0x10,
|
||||||
|
kFindNextObjectInstance = 0x11,
|
||||||
|
kRestore = 0x15,
|
||||||
|
kSave = 0x16,
|
||||||
|
kNoOperation = 0x17,
|
||||||
|
kGetMember = 0x18,
|
||||||
|
kSetMember = 0x19,
|
||||||
|
kInsertMember = 0x1A,
|
||||||
|
kRemoveMember = 0x1B,
|
||||||
|
kGroupSync = 0x1C,
|
||||||
|
kGetConnectionPointMemberList = 0x1D,
|
||||||
|
/* End CIP common services */
|
||||||
|
|
||||||
|
/* Start CIP object-specific services */
|
||||||
|
kEthLinkGetAndClear = 0x4C, /**< Ethernet Link object's Get_And_Clear service */
|
||||||
|
kForwardOpen = 0x54,
|
||||||
|
kLargeForwardOpen = 0x5B,
|
||||||
|
kForwardClose = 0x4E,
|
||||||
|
kUnconnectedSend = 0x52,
|
||||||
|
kGetConnectionOwner = 0x5A,
|
||||||
|
kGetConnectionData = 0x56,
|
||||||
|
kSearchConnectionData = 0x57
|
||||||
|
/* End CIP object-specific services */
|
||||||
|
} CIPServiceCode;
|
||||||
|
|
||||||
|
/** @brief Definition of Get and Set Flags for CIP Attributes */
|
||||||
|
typedef enum { /* TODO: Rework */
|
||||||
|
kNotSetOrGetable = 0x00, /**< Neither set-able nor get-able */
|
||||||
|
kGetableAll = 0x01, /**< Get-able, also part of Get Attribute All service */
|
||||||
|
kGetableSingle = 0x02, /**< Get-able via Get Attribute */
|
||||||
|
kSetable = 0x04, /**< Set-able via Set Attribute */
|
||||||
|
/* combined for convenience */
|
||||||
|
kSetAndGetAble = 0x07, /**< both set and get-able */
|
||||||
|
kGetableSingleAndAll = 0x03, /**< both single and all */
|
||||||
|
/* Flags to control the usage of callbacks per attribute from the Get* and Set* services */
|
||||||
|
kGetableAllDummy = 0x08, /**< Get-able but a dummy Attribute */
|
||||||
|
kPreGetFunc = 0x10, /**< enable pre get callback */
|
||||||
|
kPostGetFunc = 0x20, /**< enable post get callback */
|
||||||
|
kPreSetFunc = 0x40, /**< enable pre set callback */
|
||||||
|
kPostSetFunc = 0x80, /**< enable post set callback */
|
||||||
|
kNvDataFunc = 0x80, /**< enable Non Volatile data callback, is the same as @ref kPostSetFunc */
|
||||||
|
} CIPAttributeFlag;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kIoConnectionEventOpened,
|
||||||
|
kIoConnectionEventTimedOut,
|
||||||
|
kIoConnectionEventClosed
|
||||||
|
} IoConnectionEvent;
|
||||||
|
|
||||||
|
/** @brief CIP Byte Array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 length; /**< Length of the Byte Array */
|
||||||
|
EipByte *data; /**< Pointer to the data */
|
||||||
|
} CipByteArray;
|
||||||
|
|
||||||
|
/** @brief CIP Short String
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint8 length; /**< Length of the String (8 bit value) */
|
||||||
|
EipByte *string; /**< Pointer to the string data */
|
||||||
|
} CipShortString;
|
||||||
|
|
||||||
|
/** @brief CIP String
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 length; /**< Length of the String (16 bit value) */
|
||||||
|
CipByte *string; /**< Pointer to the string data */
|
||||||
|
} CipString;
|
||||||
|
|
||||||
|
/** @brief CIP String2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 length; /**< Length of the String (16 bit value) */
|
||||||
|
CipWord *string; /**< Pointer to the string data */
|
||||||
|
} CipString2;
|
||||||
|
|
||||||
|
/** @brief CIP String with variable symbol size
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 size; /**< Amount of bytes per symbol */
|
||||||
|
EipUint16 length; /**< Length of the String (16 bit value) */
|
||||||
|
EipByte *string; /**< Pointer to the string data */
|
||||||
|
} CipStringN;
|
||||||
|
|
||||||
|
/** @brief STRINGI definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct cip_type_string_i_struct CipStringIStruct;
|
||||||
|
|
||||||
|
typedef struct cip_string_i {
|
||||||
|
CipUsint number_of_strings;
|
||||||
|
CipStringIStruct *array_of_string_i_structs;
|
||||||
|
} CipStringI;
|
||||||
|
|
||||||
|
typedef enum cip_type_string_i_character_set {
|
||||||
|
kCipStringICharSet_ISO_8859_1_1987 = 4,
|
||||||
|
kCipStringICharSet_ISO_8859_2_1987 = 5,
|
||||||
|
kCipStringICharSet_ISO_8859_3_1988 = 6,
|
||||||
|
kCipStringICharSet_ISO_8859_4_1988 = 7,
|
||||||
|
kCipStringICharSet_ISO_8859_5_1988 = 8,
|
||||||
|
kCipStringICharSet_ISO_8859_6_1987 = 9,
|
||||||
|
kCipStringICharSet_ISO_8859_7_1987 = 10,
|
||||||
|
kCipStringICharSet_ISO_8859_8_1989 = 11,
|
||||||
|
kCipStringICharSet_ISO_8859_9_1989 = 12,
|
||||||
|
kCipStringICharSet_ISO_10646_UCS_2 = 1000,
|
||||||
|
kCipStringICharSet_ISO_10646_UCS_4 = 1001
|
||||||
|
} CipStringICharacterSet;
|
||||||
|
|
||||||
|
typedef struct cip_type_string_i_struct {
|
||||||
|
CipUsint language_char_1;
|
||||||
|
CipUsint language_char_2;
|
||||||
|
CipUsint language_char_3;
|
||||||
|
CipUint char_string_struct; /**< EPath Either 0xD0, 0xD5, 0xD9, or 0xDA */
|
||||||
|
CipUint character_set; /**< Character set of the string */
|
||||||
|
CipOctet *string; /**< Pointer to the string data */
|
||||||
|
} CipStringIStruct;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Highest CIP instance number.
|
||||||
|
*
|
||||||
|
* Largest value that can be used to represent or count CIP instances;
|
||||||
|
* intentended for use when validating instance numbers encoded as data types
|
||||||
|
* that can contain illegal values.
|
||||||
|
*/
|
||||||
|
extern const CipInstanceNum kCipInstanceNumMax;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Struct for padded EPATHs
|
||||||
|
*
|
||||||
|
* Here the class code is referenced as class ID - see Vol. 1 C-1.4.2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint8 path_size; /**< Path size in 16 bit words (path_size * 16 bit) */
|
||||||
|
EipUint16 class_id; /**< Class ID of the linked object */
|
||||||
|
CipInstanceNum instance_number; /**< Requested Instance Number of the linked object */
|
||||||
|
EipUint16 attribute_number; /**< Requested Attribute Number of the linked object */
|
||||||
|
} CipEpath;
|
||||||
|
|
||||||
|
typedef enum connection_point_type {
|
||||||
|
kConnectionPointTypeProducing = 0,
|
||||||
|
kConnectionPointTypeConsuming,
|
||||||
|
kConnectionPointTypeConfig,
|
||||||
|
kConnectionPointTypeMaxValue
|
||||||
|
} ConnectionPointType;
|
||||||
|
|
||||||
|
/** @brief CIP Connection Path
|
||||||
|
*
|
||||||
|
* As an EPath the naming scheme of Vol. 1 C-1.4.2. has been used
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint8 path_size; /**< Path size in 16 bit words (path_size * 16 bit) */
|
||||||
|
EipUint32 class_id; /**< Class ID of the linked object */
|
||||||
|
EipUint32 connection_point[kConnectionPointTypeMaxValue];
|
||||||
|
EipUint8 data_segment;
|
||||||
|
EipUint8 *segment_data;
|
||||||
|
} CipConnectionPath;
|
||||||
|
|
||||||
|
/** @brief Struct storing the CIP revision */
|
||||||
|
typedef struct {
|
||||||
|
EipUint8 major_revision;
|
||||||
|
EipUint8 minor_revision;
|
||||||
|
} CipRevision;
|
||||||
|
|
||||||
|
/** @brief CIP Message Router Request
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUsint service;
|
||||||
|
CipEpath request_path;
|
||||||
|
size_t request_data_size;
|
||||||
|
const CipOctet *data;
|
||||||
|
} CipMessageRouterRequest;
|
||||||
|
|
||||||
|
#define MAX_SIZE_OF_ADD_STATUS 2 /* for now we support extended status codes up to 2 16bit values there is mostly only one 16bit value used */
|
||||||
|
|
||||||
|
typedef struct enip_message ENIPMessage;
|
||||||
|
|
||||||
|
/** @brief CIP Message Router Response
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUsint reply_service; /**< Reply service code, the requested service code +
|
||||||
|
0x80 */
|
||||||
|
CipOctet reserved; /**< Reserved; Shall be zero */
|
||||||
|
CipUsint general_status; /**< One of the General Status codes listed in CIP
|
||||||
|
Specification Volume 1, Appendix B */
|
||||||
|
CipUsint size_of_additional_status; /**< Number of additional 16 bit words in
|
||||||
|
Additional Status Array */
|
||||||
|
EipUint16 additional_status[MAX_SIZE_OF_ADD_STATUS]; /**< Array of 16 bit words; Additional status;
|
||||||
|
If SizeOfAdditionalStatus is 0. there is no
|
||||||
|
Additional Status */
|
||||||
|
ENIPMessage message; /* The constructed message */
|
||||||
|
} CipMessageRouterResponse;
|
||||||
|
|
||||||
|
/** @brief self-describing data encoding for CIP types */
|
||||||
|
typedef void (*CipAttributeEncodeInMessage)(const void *const data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @brief self-describing data decoding for CIP types */
|
||||||
|
typedef int (*CipAttributeDecodeFromMessage)(void *const data,
|
||||||
|
CipMessageRouterRequest *
|
||||||
|
const message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response);
|
||||||
|
|
||||||
|
/** @brief Structure to describe a single CIP attribute of an object
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 attribute_number; /**< The attribute number of this attribute. */
|
||||||
|
EipUint8 type; /**< The @ref CipDataType of this attribute. */
|
||||||
|
CipAttributeEncodeInMessage encode; /**< Self-describing its data encoding */
|
||||||
|
CipAttributeDecodeFromMessage decode; /**< Self-describing its data decoding */
|
||||||
|
CIPAttributeFlag attribute_flags; /**< See @ref CIPAttributeFlag declaration for valid values. */
|
||||||
|
void *data;
|
||||||
|
} CipAttributeStruct;
|
||||||
|
|
||||||
|
/** @brief Type definition of one instance of an Ethernet/IP object
|
||||||
|
*
|
||||||
|
* All instances are stored in a linked list that originates from the CipClass::instances
|
||||||
|
* pointer of the @ref CipClass structure.
|
||||||
|
*/
|
||||||
|
typedef struct cip_instance {
|
||||||
|
CipInstanceNum instance_number; /**< this instance's number (unique within the class) */
|
||||||
|
CipAttributeStruct *attributes; /**< pointer to an array of attributes which
|
||||||
|
is unique to this instance */
|
||||||
|
struct cip_class *cip_class; /**< class the instance belongs to */
|
||||||
|
struct cip_instance *next; /**< next instance, all instances of a class live
|
||||||
|
in a linked list */
|
||||||
|
void *data; /**< pointer to instance data struct */
|
||||||
|
} CipInstance;
|
||||||
|
|
||||||
|
/** @ingroup CIP_API
|
||||||
|
* @typedef EipStatus (*CipGetSetCallback)(
|
||||||
|
* CipInstance *const instance,
|
||||||
|
* CipAttributeStruct *const attribute,
|
||||||
|
* CipByte service
|
||||||
|
* )
|
||||||
|
* @brief Signature definition of callback functions for Set and Get services
|
||||||
|
*
|
||||||
|
* @param instance CIP instance involved in the Set or Get service
|
||||||
|
* @param attribute CIP attribute involved in the Set or Get service
|
||||||
|
* @param service service code of currently executed service
|
||||||
|
* @return status of kEipStatusOk or kEipStatusError on failure
|
||||||
|
*/
|
||||||
|
typedef EipStatus (*CipGetSetCallback)(CipInstance *const instance,
|
||||||
|
CipAttributeStruct *const attribute,
|
||||||
|
CipByte service);
|
||||||
|
|
||||||
|
/** @ingroup CIP_API
|
||||||
|
* @typedef EipStatus (*CipCallback)(
|
||||||
|
* CipInstance *const instance,
|
||||||
|
* CipMessageRouterRequest *message_router_request,
|
||||||
|
* CipMessageRouterResponse *message_router_response
|
||||||
|
* )
|
||||||
|
* @brief Signature definition of callback functions for CIP services
|
||||||
|
*
|
||||||
|
* @param instance CIP instance involved in common services
|
||||||
|
* @param message_router_request pointer to request.
|
||||||
|
* @param message_router_response pointer to response.
|
||||||
|
* @return status of kEipStatusOk or kEipStatusError on failure
|
||||||
|
*/
|
||||||
|
typedef EipStatus (*CipCallback)(CipInstance *const instance,
|
||||||
|
const CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response);
|
||||||
|
|
||||||
|
/** @brief Type definition of CipClass that is a subclass of CipInstance */
|
||||||
|
typedef struct cip_class {
|
||||||
|
CipInstance class_instance; /**< This is the instance that contains the
|
||||||
|
class attributes of this class. */
|
||||||
|
/* the rest of these are specific to the Class class only. */
|
||||||
|
CipUdint class_code; /**< class code */
|
||||||
|
EipUint16 revision; /**< class revision*/
|
||||||
|
EipUint16 max_instance; /**< largest instance number existing in the class */
|
||||||
|
EipUint16 number_of_instances; /**< number of instances in the class (not
|
||||||
|
including instance 0) */
|
||||||
|
EipUint16 number_of_attributes; /**< number of attributes of each instance */
|
||||||
|
EipUint16 highest_attribute_number; /**< highest defined attribute number
|
||||||
|
(attribute numbers are not necessarily
|
||||||
|
consecutive) */
|
||||||
|
uint8_t *get_single_bit_mask; /**< bit mask for GetAttributeSingle */
|
||||||
|
uint8_t *set_bit_mask; /**< bit mask for SetAttributeSingle */
|
||||||
|
uint8_t *get_all_bit_mask; /**< bit mask for GetAttributeAll */
|
||||||
|
|
||||||
|
EipUint16 number_of_services; /**< number of services supported */
|
||||||
|
CipInstance *instances; /**< pointer to the list of instances */
|
||||||
|
struct cip_service_struct *services; /**< pointer to the array of services */
|
||||||
|
char *class_name; /**< class name */
|
||||||
|
/** Is called in GetAttributeSingle* before the response is assembled from
|
||||||
|
* the object's attributes */
|
||||||
|
CipGetSetCallback PreGetCallback;
|
||||||
|
/** Is called in GetAttributeSingle* after the response has been sent. */
|
||||||
|
CipGetSetCallback PostGetCallback;
|
||||||
|
/** Is called in SetAttributeSingle* before the received data is moved
|
||||||
|
* to the object's attributes */
|
||||||
|
CipGetSetCallback PreSetCallback;
|
||||||
|
/** Is called in SetAttributeSingle* after the received data was set
|
||||||
|
* in the object's attributes. */
|
||||||
|
CipGetSetCallback PostSetCallback;
|
||||||
|
|
||||||
|
/** Is called in Create before the instance is created. */
|
||||||
|
CipCallback PreCreateCallback;
|
||||||
|
/** Is called in Create after the instance has been created. */
|
||||||
|
CipCallback PostCreateCallback;
|
||||||
|
/** Is called in Delete before the instance is deleted. */
|
||||||
|
CipCallback PreDeleteCallback;
|
||||||
|
/** Is called in Delete after the instance has been deleted. */
|
||||||
|
CipCallback PostDeleteCallback;
|
||||||
|
/** Is called in Reset service */
|
||||||
|
CipCallback PreResetCallback;
|
||||||
|
/** Is called in Reset service. */
|
||||||
|
CipCallback PostResetCallback;
|
||||||
|
|
||||||
|
} CipClass;
|
||||||
|
|
||||||
|
/** @ingroup CIP_API
|
||||||
|
* @typedef EipStatus (*CipServiceFunction)(CipInstance *const instance,
|
||||||
|
* CipMessageRouterRequest *const message_router_request,
|
||||||
|
* CipMessageRouterResponse *const message_router_response,
|
||||||
|
* const struct sockaddr *originator_address, const CipSessionHandle encapsulation_session)
|
||||||
|
* @brief Signature definition for the implementation of CIP services.
|
||||||
|
*
|
||||||
|
* CIP services have to follow this signature in order to be handled correctly
|
||||||
|
* by the stack.
|
||||||
|
* @param instance the instance which was referenced in the service
|
||||||
|
* request
|
||||||
|
* @param message_router_request request data
|
||||||
|
* @param message_router_response storage for the response data, including a buffer for
|
||||||
|
* extended data
|
||||||
|
* @param originator_address address of the originator as received from socket
|
||||||
|
* @param encapsulation_session associated encapsulation session of the explicit message
|
||||||
|
* @return kEipOkSend if service could be executed successfully and a response
|
||||||
|
* should be sent
|
||||||
|
*/
|
||||||
|
typedef EipStatus (*CipServiceFunction)(CipInstance *const instance,
|
||||||
|
CipMessageRouterRequest *const
|
||||||
|
message_router_request,
|
||||||
|
CipMessageRouterResponse *const
|
||||||
|
message_router_response,
|
||||||
|
const struct sockaddr *
|
||||||
|
originator_address,
|
||||||
|
const CipSessionHandle encapsulation_session);
|
||||||
|
|
||||||
|
/** @brief Service descriptor. These are stored in an array */
|
||||||
|
typedef struct cip_service_struct {
|
||||||
|
EipUint8 service_number; /**< service number*/
|
||||||
|
CipServiceFunction service_function; /**< pointer to a function call*/
|
||||||
|
char *name; /**< name of the service */
|
||||||
|
} CipServiceStruct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Struct for saving TCP/IP interface information
|
||||||
|
*
|
||||||
|
* All addresses are stored in network byte order.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUdint ip_address;
|
||||||
|
CipUdint network_mask;
|
||||||
|
CipUdint gateway;
|
||||||
|
CipUdint name_server;
|
||||||
|
CipUdint name_server_2;
|
||||||
|
CipString domain_name;
|
||||||
|
} CipTcpIpInterfaceConfiguration;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EipUint8 path_size;
|
||||||
|
EipUint32 port; /* support up to 32 bit path*/
|
||||||
|
EipUint32 address;
|
||||||
|
} CipRoutePath;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EipByte priority;
|
||||||
|
EipUint8 timeout_ticks;
|
||||||
|
EipUint16 message_request_size;
|
||||||
|
CipMessageRouterRequest message_request;
|
||||||
|
CipMessageRouterResponse *message_response;
|
||||||
|
EipUint8 reserved;
|
||||||
|
CipRoutePath route_path;
|
||||||
|
void *data;
|
||||||
|
} CipUnconnectedSendParameter;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
CipUint num_conn_entries;
|
||||||
|
CipBool *conn_open_bits;
|
||||||
|
} CipConnectionManagerConnectionEntryList;
|
||||||
|
|
||||||
|
/* these are used for creating the getAttributeAll masks
|
||||||
|
TODO there might be a way simplifying this using __VARARGS__ in #define */
|
||||||
|
#define MASK1(a) ( 1 << (a) )
|
||||||
|
#define MASK2(a, b) ( 1 << (a) | 1 << (b) )
|
||||||
|
#define MASK3(a, b, c) ( 1 << (a) | 1 << (b) | 1 << (c) )
|
||||||
|
#define MASK4(a, b, c, d) ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) )
|
||||||
|
#define MASK5(a, b, c, d, e) \
|
||||||
|
( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) )
|
||||||
|
#define MASK6(a, b, c, d, e, f) \
|
||||||
|
( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) )
|
||||||
|
#define MASK7(a, b, c, d, e, f, g) \
|
||||||
|
( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) | 1 << (g) )
|
||||||
|
#define MASK8(a, b, c, d, e, f, g, h) \
|
||||||
|
( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) | \
|
||||||
|
1 << (g) | 1 << (h) )
|
||||||
|
|
||||||
|
#endif /* OPENER_CIPTYPES_H_ */
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
INCLUDE(${CMAKE_BINARY_DIR}/cip_objects/CMakeLists.txt)
|
||||||
31
OrginalSourceRepo/OpENer-master/source/src/enet_encap/CMakeLists.txt
Executable file
31
OrginalSourceRepo/OpENer-master/source/src/enet_encap/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#######################################
|
||||||
|
# Ethernet encapsulation library #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
set( ENET_ENCAP_SRC cpf.c encap.c endianconv.c )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform-specific includes #
|
||||||
|
#######################################
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
add_library( ENET_ENCAP ${ENET_ENCAP_SRC} )
|
||||||
|
|
||||||
|
if( OPENER_INSTALL_AS_LIB )
|
||||||
|
install(TARGETS ENET_ENCAP
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
install(DIRECTORY ${ENET_ENCAP_SRC_DIR}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILES_MATCHING PATTERN "*.h"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries( ENET_ENCAP Utils)
|
||||||
723
OrginalSourceRepo/OpENer-master/source/src/enet_encap/cpf.c
Normal file
723
OrginalSourceRepo/OpENer-master/source/src/enet_encap/cpf.c
Normal file
|
|
@ -0,0 +1,723 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cpf.h"
|
||||||
|
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "ciperror.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "encap.h"
|
||||||
|
#include "enipmessage.h"
|
||||||
|
|
||||||
|
const size_t kItemCountFieldSize = 2; /**< The size of the item count field in the message */
|
||||||
|
const size_t KItemDataTypeIdFieldLength = 2; /**< The size of the item count field in the message */
|
||||||
|
|
||||||
|
/** @brief Size, in bytes, of the encoded sequenced address item data field.
|
||||||
|
*
|
||||||
|
* Data type and value per @cite CipVol2, Table 2-6.6.
|
||||||
|
*/
|
||||||
|
const EipUint16 kSequencedAddressItemLength = 8;
|
||||||
|
|
||||||
|
CipCommonPacketFormatData g_common_packet_format_data_item; /**< CPF global data items */
|
||||||
|
|
||||||
|
static void InitializeMessageRouterResponse(
|
||||||
|
CipMessageRouterResponse *const message_router_response) {
|
||||||
|
memset(message_router_response, 0, sizeof(*message_router_response) );
|
||||||
|
InitializeENIPMessage(&message_router_response->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus NotifyCommonPacketFormat(const EncapsulationData *const received_data,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
EipStatus return_value = kEipStatusError;
|
||||||
|
CipMessageRouterResponse message_router_response;
|
||||||
|
InitializeMessageRouterResponse(&message_router_response);
|
||||||
|
|
||||||
|
if(kEipStatusError
|
||||||
|
== (return_value =
|
||||||
|
CreateCommonPacketFormatStructure(received_data->
|
||||||
|
current_communication_buffer_position,
|
||||||
|
received_data->data_length,
|
||||||
|
&
|
||||||
|
g_common_packet_format_data_item) ) )
|
||||||
|
{
|
||||||
|
OPENER_TRACE_ERR("notifyCPF: error from createCPFstructure\n");
|
||||||
|
} else {
|
||||||
|
return_value = kEipStatusOkSend; /* In cases of errors we normally need to send an error response */
|
||||||
|
if(g_common_packet_format_data_item.address_item.type_id ==
|
||||||
|
kCipItemIdNullAddress) /* check if NullAddressItem received, otherwise it is no unconnected message and should not be here*/
|
||||||
|
{ /* found null address item*/
|
||||||
|
if(g_common_packet_format_data_item.data_item.type_id ==
|
||||||
|
kCipItemIdUnconnectedDataItem) { /* unconnected data item received*/
|
||||||
|
return_value = NotifyMessageRouter(
|
||||||
|
g_common_packet_format_data_item.data_item.data,
|
||||||
|
g_common_packet_format_data_item.data_item.length,
|
||||||
|
&message_router_response,
|
||||||
|
originator_address,
|
||||||
|
received_data->session_handle);
|
||||||
|
if(return_value != kEipStatusError) {
|
||||||
|
SkipEncapsulationHeader(outgoing_message);
|
||||||
|
/* TODO: Here we get the status. What to do? kEipStatusError from AssembleLinearMessage().
|
||||||
|
* Its not clear how to transport this error information to the requester. */
|
||||||
|
EipStatus status = AssembleLinearMessage(&message_router_response,
|
||||||
|
&g_common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
(void)status; /* Suppress unused variable warning. */
|
||||||
|
|
||||||
|
/* Save pointer and move to start for Encapusulation Header */
|
||||||
|
CipOctet *buffer = outgoing_message->current_message_position;
|
||||||
|
outgoing_message->current_message_position =
|
||||||
|
outgoing_message->message_buffer;
|
||||||
|
GenerateEncapsulationHeader(received_data,
|
||||||
|
outgoing_message->used_message_length,
|
||||||
|
received_data->session_handle,
|
||||||
|
kEncapsulationProtocolSuccess,
|
||||||
|
outgoing_message);
|
||||||
|
/* Move pointer back to last octet */
|
||||||
|
outgoing_message->current_message_position = buffer;
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* wrong data item detected*/
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyCPF: got something besides the expected CIP_ITEM_ID_UNCONNECTEDMESSAGE\n");
|
||||||
|
GenerateEncapsulationHeader(received_data,
|
||||||
|
0,
|
||||||
|
received_data->session_handle,
|
||||||
|
kEncapsulationProtocolIncorrectData,
|
||||||
|
outgoing_message);
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyCPF: got something besides the expected CIP_ITEM_ID_NULL\n");
|
||||||
|
GenerateEncapsulationHeader(received_data,
|
||||||
|
0,
|
||||||
|
received_data->session_handle,
|
||||||
|
kEncapsulationProtocolIncorrectData,
|
||||||
|
outgoing_message);
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus NotifyConnectedCommonPacketFormat(
|
||||||
|
const EncapsulationData *const received_data,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
EipStatus return_value = CreateCommonPacketFormatStructure(
|
||||||
|
received_data->current_communication_buffer_position,
|
||||||
|
received_data->data_length,
|
||||||
|
&g_common_packet_format_data_item);
|
||||||
|
|
||||||
|
if(kEipStatusError == return_value) {
|
||||||
|
OPENER_TRACE_ERR("notifyConnectedCPF: error from createCPFstructure\n");
|
||||||
|
} else {
|
||||||
|
return_value = kEipStatusError; /* For connected explicit messages status always has to be 0*/
|
||||||
|
if(g_common_packet_format_data_item.address_item.type_id ==
|
||||||
|
kCipItemIdConnectionAddress) /* check if ConnectedAddressItem received, otherwise it is no connected message and should not be here*/
|
||||||
|
{ /* ConnectedAddressItem item */
|
||||||
|
CipConnectionObject *connection_object = GetConnectedObject(
|
||||||
|
g_common_packet_format_data_item.address_item.data.connection_identifier);
|
||||||
|
if(NULL != connection_object) {
|
||||||
|
/* reset the watchdog timer */
|
||||||
|
ConnectionObjectResetInactivityWatchdogTimerValue(connection_object);
|
||||||
|
|
||||||
|
/*TODO check connection id and sequence count */
|
||||||
|
if(g_common_packet_format_data_item.data_item.type_id ==
|
||||||
|
kCipItemIdConnectedDataItem) { /* connected data item received*/
|
||||||
|
EipUint8 *buffer = g_common_packet_format_data_item.data_item.data;
|
||||||
|
g_common_packet_format_data_item.address_item.data.sequence_number =
|
||||||
|
GetUintFromMessage( (const EipUint8 **const ) &buffer );
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"Class 3 sequence number: %d, last sequence number: %d\n",
|
||||||
|
g_common_packet_format_data_item.address_item.data.sequence_number,
|
||||||
|
connection_object->sequence_count_consuming);
|
||||||
|
if(connection_object->sequence_count_consuming ==
|
||||||
|
g_common_packet_format_data_item.address_item.data.sequence_number)
|
||||||
|
{
|
||||||
|
memcpy(outgoing_message,
|
||||||
|
&(connection_object->last_reply_sent),
|
||||||
|
sizeof(ENIPMessage) );
|
||||||
|
outgoing_message->current_message_position =
|
||||||
|
outgoing_message->message_buffer;
|
||||||
|
/* Regenerate encapsulation header for new message */
|
||||||
|
outgoing_message->used_message_length -=
|
||||||
|
ENCAPSULATION_HEADER_LENGTH;
|
||||||
|
GenerateEncapsulationHeader(received_data,
|
||||||
|
outgoing_message->used_message_length,
|
||||||
|
received_data->session_handle,
|
||||||
|
kEncapsulationProtocolSuccess,
|
||||||
|
outgoing_message);
|
||||||
|
outgoing_message->current_message_position = buffer;
|
||||||
|
/* End regenerate encapsulation header for new message */
|
||||||
|
return kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
connection_object->sequence_count_consuming =
|
||||||
|
g_common_packet_format_data_item.address_item.data.sequence_number;
|
||||||
|
|
||||||
|
ConnectionObjectResetInactivityWatchdogTimerValue(connection_object);
|
||||||
|
|
||||||
|
CipMessageRouterResponse message_router_response;
|
||||||
|
InitializeMessageRouterResponse(&message_router_response);
|
||||||
|
return_value = NotifyMessageRouter(buffer,
|
||||||
|
g_common_packet_format_data_item.data_item.length - 2,
|
||||||
|
&message_router_response,
|
||||||
|
originator_address,
|
||||||
|
received_data->session_handle);
|
||||||
|
|
||||||
|
if(return_value != kEipStatusError) {
|
||||||
|
g_common_packet_format_data_item.address_item.data.
|
||||||
|
connection_identifier =
|
||||||
|
connection_object->cip_produced_connection_id;
|
||||||
|
SkipEncapsulationHeader(outgoing_message);
|
||||||
|
/* TODO: Here we get the status. What to do? kEipStatusError from AssembleLinearMessage().
|
||||||
|
* Its not clear how to transport this error information to the requester. */
|
||||||
|
EipStatus status = AssembleLinearMessage(&message_router_response,
|
||||||
|
&g_common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
(void)status; /* Suppress unused variable warning. */
|
||||||
|
|
||||||
|
CipOctet *pos = outgoing_message->current_message_position;
|
||||||
|
outgoing_message->current_message_position =
|
||||||
|
outgoing_message->message_buffer;
|
||||||
|
GenerateEncapsulationHeader(received_data,
|
||||||
|
outgoing_message->used_message_length,
|
||||||
|
received_data->session_handle,
|
||||||
|
kEncapsulationProtocolSuccess,
|
||||||
|
outgoing_message);
|
||||||
|
outgoing_message->current_message_position = pos;
|
||||||
|
memcpy(&connection_object->last_reply_sent,
|
||||||
|
outgoing_message,
|
||||||
|
sizeof(ENIPMessage) );
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* wrong data item detected*/
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyConnectedCPF: got something besides the expected CIP_ITEM_ID_UNCONNECTEDMESSAGE\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyConnectedCPF: connection with given ID could not be found\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"notifyConnectedCPF: got something besides the expected CIP_ITEM_ID_NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return outgoing_message->used_message_length;
|
||||||
|
return (0 !=
|
||||||
|
outgoing_message->used_message_length ? kEipStatusOkSend :
|
||||||
|
kEipStatusOk); /* TODO: What would the right EipStatus to return? */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates Common Packet Format structure out of data.
|
||||||
|
* @param data Pointer to data which need to be structured.
|
||||||
|
* @param data_length Length of data in pa_Data.
|
||||||
|
* @param common_packet_format_data Pointer to structure of CPF data item.
|
||||||
|
*
|
||||||
|
* @return kEipStatusOk .. success
|
||||||
|
* kEipStatusError .. error
|
||||||
|
*/
|
||||||
|
EipStatus CreateCommonPacketFormatStructure(const EipUint8 *data,
|
||||||
|
size_t data_length,
|
||||||
|
CipCommonPacketFormatData *common_packet_format_data)
|
||||||
|
{
|
||||||
|
|
||||||
|
common_packet_format_data->address_info_item[0].type_id = 0;
|
||||||
|
common_packet_format_data->address_info_item[1].type_id = 0;
|
||||||
|
|
||||||
|
size_t length_count = 0;
|
||||||
|
CipUint item_count = GetUintFromMessage(&data);
|
||||||
|
//OPENER_ASSERT(4U >= item_count);/* Sanitizing data - probably needs to be changed for productive code */
|
||||||
|
common_packet_format_data->item_count = item_count;
|
||||||
|
length_count += 2;
|
||||||
|
if(common_packet_format_data->item_count >= 1U) {
|
||||||
|
common_packet_format_data->address_item.type_id = GetUintFromMessage(&data);
|
||||||
|
common_packet_format_data->address_item.length = GetUintFromMessage(&data);
|
||||||
|
length_count += 4;
|
||||||
|
if(common_packet_format_data->address_item.length >= 4) {
|
||||||
|
common_packet_format_data->address_item.data.connection_identifier =
|
||||||
|
GetUdintFromMessage(&data);
|
||||||
|
length_count += 4;
|
||||||
|
}
|
||||||
|
if(common_packet_format_data->address_item.length == 8) {
|
||||||
|
common_packet_format_data->address_item.data.sequence_number =
|
||||||
|
GetUdintFromMessage(&data);
|
||||||
|
length_count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(common_packet_format_data->item_count >= 2) {
|
||||||
|
common_packet_format_data->data_item.type_id = GetUintFromMessage(&data);
|
||||||
|
common_packet_format_data->data_item.length = GetUintFromMessage(&data);
|
||||||
|
common_packet_format_data->data_item.data = (EipUint8 *) data;
|
||||||
|
if(data_length >=
|
||||||
|
length_count + 4 + common_packet_format_data->data_item.length) {
|
||||||
|
data += common_packet_format_data->data_item.length;
|
||||||
|
length_count += (4 + common_packet_format_data->data_item.length);
|
||||||
|
} else {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data type per CIP Volume 2, Edition 1.4, Table 2-6.1. */
|
||||||
|
CipUint address_item_count = (CipUint)(common_packet_format_data->item_count - 2U);
|
||||||
|
|
||||||
|
for(size_t j = 0; j < (address_item_count > 2 ? 2 : address_item_count);
|
||||||
|
j++) /* TODO there needs to be a limit check here???*/
|
||||||
|
{
|
||||||
|
common_packet_format_data->address_info_item[j].type_id =
|
||||||
|
GetIntFromMessage(&data);
|
||||||
|
OPENER_TRACE_INFO("Sockaddr type id: %x\n",
|
||||||
|
common_packet_format_data->address_info_item[j].type_id);
|
||||||
|
length_count += 2;
|
||||||
|
if( (common_packet_format_data->address_info_item[j].type_id ==
|
||||||
|
kCipItemIdSocketAddressInfoOriginatorToTarget)
|
||||||
|
|| (common_packet_format_data->address_info_item[j].type_id ==
|
||||||
|
kCipItemIdSocketAddressInfoTargetToOriginator) ) {
|
||||||
|
common_packet_format_data->address_info_item[j].length =
|
||||||
|
GetIntFromMessage(&data);
|
||||||
|
common_packet_format_data->address_info_item[j].sin_family =
|
||||||
|
GetIntFromMessage(&data);
|
||||||
|
common_packet_format_data->address_info_item[j].sin_port =
|
||||||
|
GetIntFromMessage(&data);
|
||||||
|
common_packet_format_data->address_info_item[j].sin_addr =
|
||||||
|
GetUdintFromMessage(&data);
|
||||||
|
for(size_t i = 0; i < 8; i++) {
|
||||||
|
common_packet_format_data->address_info_item[j].nasin_zero[i] = *data;
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
length_count += 18;
|
||||||
|
} else { /* no sockaddr item found */
|
||||||
|
common_packet_format_data->address_info_item[j].type_id = 0; /* mark as not set */
|
||||||
|
data -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* set the addressInfoItems to not set if they were not received */
|
||||||
|
if(common_packet_format_data->item_count < 4) {
|
||||||
|
common_packet_format_data->address_info_item[1].type_id = 0;
|
||||||
|
if(common_packet_format_data->item_count < 3) {
|
||||||
|
common_packet_format_data->address_info_item[0].type_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(length_count == data_length) { /* length of data is equal to length of Addr and length of Data */
|
||||||
|
return kEipStatusOk;
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_WARN(
|
||||||
|
"something is wrong with the length in Message Router @ CreateCommonPacketFormatStructure\n");
|
||||||
|
if(common_packet_format_data->item_count > 2) {
|
||||||
|
/* there is an optional packet in data stream which is not sockaddr item */
|
||||||
|
return kEipStatusOk;
|
||||||
|
} else { /* something with the length was wrong */
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes a Null Address Item into the message frame
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeNullAddressItem(ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(kCipItemIdNullAddress, outgoing_message);
|
||||||
|
/* null address item -> address length set to 0 */
|
||||||
|
AddIntToMessage(0, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a Connected Address Item into the message frame
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeConnectedAddressItem(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* connected data item -> address length set to 4 and copy ConnectionIdentifier */
|
||||||
|
AddIntToMessage(kCipItemIdConnectionAddress, outgoing_message);
|
||||||
|
AddIntToMessage(4, outgoing_message);
|
||||||
|
AddDintToMessage(
|
||||||
|
common_packet_format_data_item->address_item.data.connection_identifier,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes a sequenced address item into the message
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item Common Packet Format item which is used in the encoding
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeSequencedAddressItem(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
/* sequenced address item -> address length set to 8 and copy ConnectionIdentifier and SequenceNumber */
|
||||||
|
AddIntToMessage(kCipItemIdSequencedAddressItem, outgoing_message);
|
||||||
|
AddIntToMessage(kSequencedAddressItemLength, outgoing_message);
|
||||||
|
AddDintToMessage(
|
||||||
|
common_packet_format_data_item->address_item.data.connection_identifier,
|
||||||
|
outgoing_message);
|
||||||
|
AddDintToMessage(
|
||||||
|
common_packet_format_data_item->address_item.data.sequence_number,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds the item count to the message frame
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeItemCount(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(common_packet_format_data_item->item_count, outgoing_message); /* item count */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the data item type to the message frame
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeDataItemType(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(common_packet_format_data_item->data_item.type_id,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the data item section length to the message frame
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeDataItemLength(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(common_packet_format_data_item->data_item.length,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the data items to the message frame
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeDataItemData(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
memcpy(outgoing_message->current_message_position,
|
||||||
|
common_packet_format_data_item->data_item.data,
|
||||||
|
common_packet_format_data_item->data_item.length);
|
||||||
|
outgoing_message->current_message_position +=
|
||||||
|
common_packet_format_data_item->data_item.length;
|
||||||
|
outgoing_message->used_message_length +=
|
||||||
|
common_packet_format_data_item->data_item.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the Connected Data item length
|
||||||
|
*
|
||||||
|
* @param message_router_response The Router Response message which shall be answered
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
|
||||||
|
void EncodeConnectedDataItemLength(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage( (EipUint16) (message_router_response->message.
|
||||||
|
used_message_length + 4 + 2 /* TODO: Magic numbers */
|
||||||
|
+ (2 *
|
||||||
|
message_router_response->
|
||||||
|
size_of_additional_status) ),
|
||||||
|
outgoing_message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes a sequence number into the message
|
||||||
|
*
|
||||||
|
* @param common_packet_format_data_item
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeSequenceNumber(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(
|
||||||
|
(EipUint16) common_packet_format_data_item->address_item.data.sequence_number,
|
||||||
|
outgoing_message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the reply service code for the requested service
|
||||||
|
*
|
||||||
|
* @param message_router_response The router response message data structure to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeReplyService(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddSintToMessage(message_router_response->reply_service, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the reserved byte in the message router response
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeReservedFieldOfLengthByte(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddSintToMessage(message_router_response->reserved, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the general status of a Router Response
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeGeneralStatus(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddSintToMessage(message_router_response->general_status, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the length of the extended status data part
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
|
||||||
|
void EncodeExtendedStatusLength(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddSintToMessage(message_router_response->size_of_additional_status,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the extended status data items
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeExtendedStatusDataItems(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
for(size_t i = 0;
|
||||||
|
i < message_router_response->size_of_additional_status &&
|
||||||
|
i < MAX_SIZE_OF_ADD_STATUS; i++) {
|
||||||
|
AddIntToMessage(message_router_response->additional_status[i],
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the extended status (length and data) into the message
|
||||||
|
*
|
||||||
|
* This function uses EncodeExtendedStatusLength and EncodeExtendedStatusDataItems
|
||||||
|
* to encode the complete extended status information into the message
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
|
||||||
|
void EncodeExtendedStatus(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
EncodeExtendedStatusLength(message_router_response, outgoing_message);
|
||||||
|
EncodeExtendedStatusDataItems(message_router_response, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode the data item length of the unconnected data segment
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void EncodeUnconnectedDataItemLength(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage( (EipUint16) (message_router_response->message.
|
||||||
|
used_message_length + 4 /* TODO: Magic number */
|
||||||
|
+ (2 *
|
||||||
|
message_router_response->
|
||||||
|
size_of_additional_status) ),
|
||||||
|
outgoing_message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the Message Router Response data
|
||||||
|
*
|
||||||
|
* @param message_router_response Router Response message to be processed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeMessageRouterResponseData(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
memcpy(outgoing_message->current_message_position,
|
||||||
|
message_router_response->message.message_buffer,
|
||||||
|
message_router_response->message.used_message_length);
|
||||||
|
|
||||||
|
outgoing_message->current_message_position +=
|
||||||
|
message_router_response->message.used_message_length;
|
||||||
|
outgoing_message->used_message_length +=
|
||||||
|
message_router_response->message.used_message_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the sockaddr info type id into the message
|
||||||
|
*
|
||||||
|
* @param item_type
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeSockaddrInfoItemTypeId(int item_type,
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
OPENER_ASSERT(item_type == 0 || item_type == 1);
|
||||||
|
AddIntToMessage(
|
||||||
|
common_packet_format_data_item->address_info_item[item_type].type_id,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encodes the sockaddr info length into the message
|
||||||
|
*
|
||||||
|
* @param item_type
|
||||||
|
* @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
|
||||||
|
* @param outgoing_message The outgoing message object
|
||||||
|
*/
|
||||||
|
void EncodeSockaddrInfoLength(int item_type,
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(
|
||||||
|
common_packet_format_data_item->address_info_item[item_type].length,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus AssembleLinearMessage(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
if(message_router_response) {
|
||||||
|
/* add Interface Handle and Timeout = 0 -> only for SendRRData and SendUnitData necessary */
|
||||||
|
AddDintToMessage(0, outgoing_message);
|
||||||
|
AddIntToMessage(0, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeItemCount(common_packet_format_data_item, outgoing_message);
|
||||||
|
|
||||||
|
/* process Address Item */
|
||||||
|
switch(common_packet_format_data_item->address_item.type_id) {
|
||||||
|
case kCipItemIdNullAddress: {
|
||||||
|
EncodeNullAddressItem(outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kCipItemIdConnectionAddress: {
|
||||||
|
EncodeConnectedAddressItem(common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kCipItemIdSequencedAddressItem: {
|
||||||
|
EncodeSequencedAddressItem(common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_INFO("Unknown CIP Item in AssembleLinearMessage");
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process Data Item */
|
||||||
|
if( (common_packet_format_data_item->data_item.type_id ==
|
||||||
|
kCipItemIdUnconnectedDataItem)
|
||||||
|
|| (common_packet_format_data_item->data_item.type_id ==
|
||||||
|
kCipItemIdConnectedDataItem) ) {
|
||||||
|
|
||||||
|
if(message_router_response) {
|
||||||
|
EncodeDataItemType(common_packet_format_data_item, outgoing_message);
|
||||||
|
|
||||||
|
if(common_packet_format_data_item->data_item.type_id ==
|
||||||
|
kCipItemIdConnectedDataItem) { /* Connected Item */
|
||||||
|
EncodeConnectedDataItemLength(message_router_response,
|
||||||
|
outgoing_message);
|
||||||
|
EncodeSequenceNumber(&g_common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
} else { /* Unconnected Item */
|
||||||
|
EncodeUnconnectedDataItemLength(message_router_response,
|
||||||
|
outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write message router response into linear memory */
|
||||||
|
EncodeReplyService(message_router_response, outgoing_message);
|
||||||
|
EncodeReservedFieldOfLengthByte(message_router_response,
|
||||||
|
outgoing_message);
|
||||||
|
EncodeGeneralStatus(message_router_response, outgoing_message);
|
||||||
|
EncodeExtendedStatus(message_router_response, outgoing_message);
|
||||||
|
EncodeMessageRouterResponseData(message_router_response,
|
||||||
|
outgoing_message);
|
||||||
|
} else { /* connected IO Message to send */
|
||||||
|
EncodeDataItemType(common_packet_format_data_item, outgoing_message);
|
||||||
|
|
||||||
|
EncodeDataItemLength(common_packet_format_data_item, outgoing_message);
|
||||||
|
|
||||||
|
EncodeDataItemData(common_packet_format_data_item, outgoing_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process SockAddr Info Items */
|
||||||
|
/* make sure first the O->T and then T->O appears on the wire.
|
||||||
|
* EtherNet/IP specification doesn't demand it, but there are EIP
|
||||||
|
* devices which depend on CPF items to appear in the order of their
|
||||||
|
* ID number */
|
||||||
|
for(int type = kCipItemIdSocketAddressInfoOriginatorToTarget;
|
||||||
|
type <= kCipItemIdSocketAddressInfoTargetToOriginator; type++) {
|
||||||
|
for(int j = 0; j < 2; j++) {
|
||||||
|
if(common_packet_format_data_item->address_info_item[j].type_id == type) {
|
||||||
|
EncodeSockaddrInfoItemTypeId(j,
|
||||||
|
common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
EncodeSockaddrInfoLength(j,
|
||||||
|
common_packet_format_data_item,
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
EncapsulateIpAddress(
|
||||||
|
common_packet_format_data_item->address_info_item[j].sin_port,
|
||||||
|
common_packet_format_data_item->address_info_item[j].sin_addr,
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
|
||||||
|
8,
|
||||||
|
outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssembleIOMessage(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
AssembleLinearMessage(0, common_packet_format_data_item, outgoing_message);
|
||||||
|
}
|
||||||
151
OrginalSourceRepo/OpENer-master/source/src/enet_encap/cpf.h
Normal file
151
OrginalSourceRepo/OpENer-master/source/src/enet_encap/cpf.h
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_CPF_H_
|
||||||
|
#define OPENER_CPF_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
#include "encap.h"
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief CPF is Common Packet Format
|
||||||
|
* CPF packet := \<number of items\> {\<items\>}
|
||||||
|
* item := \<TypeID\> \<Length\> \<data\>
|
||||||
|
* \<number of items\> := two bytes
|
||||||
|
* \<TypeID\> := two bytes
|
||||||
|
* \<Length\> := two bytes
|
||||||
|
* \<data\> := \<the number of bytes specified by Length\>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @brief Definition of Item ID numbers used for address and data items in CPF structures */
|
||||||
|
typedef enum {
|
||||||
|
kCipItemIdNullAddress = 0x0000, /**< Type: Address; Indicates that encapsulation routing is not needed. */
|
||||||
|
kCipItemIdListIdentityResponse = 0x000C,
|
||||||
|
kCipItemIdConnectionAddress = 0x00A1, /**< Type: Address; Connection-based, used for connected messages, see Vol.2, p.42 */
|
||||||
|
kCipItemIdConnectedDataItem = 0x00B1, /**< Type: Data; Connected data item, see Vol.2, p.43 */
|
||||||
|
kCipItemIdUnconnectedDataItem = 0x00B2, /**< Type: Data; Unconnected message */
|
||||||
|
kCipItemIdListServiceResponse = 0x0100,
|
||||||
|
kCipItemIdSocketAddressInfoOriginatorToTarget = 0x8000, /**< Type: Data; Sockaddr info item originator to target */
|
||||||
|
kCipItemIdSocketAddressInfoTargetToOriginator = 0x8001, /**< Type: Data; Sockaddr info item target to originator */
|
||||||
|
kCipItemIdSequencedAddressItem = 0x8002 /**< Sequenced Address item */
|
||||||
|
} CipItemId;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EipUint32 connection_identifier;
|
||||||
|
EipUint32 sequence_number;
|
||||||
|
} AddressData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
CipUint type_id;
|
||||||
|
CipUint length;
|
||||||
|
AddressData data;
|
||||||
|
} AddressItem;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 type_id;
|
||||||
|
EipUint16 length;
|
||||||
|
EipUint8 *data;
|
||||||
|
} DataItem;
|
||||||
|
|
||||||
|
/** @brief CPF Sockaddr Item
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
CipUint type_id; /**< Either 0x8000 for O->T or 0x8001 for T->O */
|
||||||
|
CipUint length; /**< Length shall be 16 bytes */
|
||||||
|
CipInt sin_family; /**< Shall be AF_INET = 2 in big endian order */
|
||||||
|
CipUint sin_port; /**< For point-to-point connection this shall be set to the used UDP port (recommended port = 0x08AE). For multicast this shall be set to 0x08AE and treated by the receiver as don't care. Big endian order */
|
||||||
|
CipUdint sin_addr; /**< For multicast connections shall be set to the multicast address. For point-to-point shall be treated as don't care, recommended value 0. Big endian order. */
|
||||||
|
CipUsint nasin_zero[8]; /**< Length of 8, Recommended value zero */
|
||||||
|
} SocketAddressInfoItem;
|
||||||
|
|
||||||
|
/* this one case of a CPF packet is supported:*/
|
||||||
|
/** @brief A variant of a CPF packet, including item count, one address item, one data item, and two Sockaddr Info items */
|
||||||
|
typedef struct {
|
||||||
|
EipUint16 item_count; /**< Up to four for this structure allowed */
|
||||||
|
AddressItem address_item;
|
||||||
|
DataItem data_item;
|
||||||
|
SocketAddressInfoItem address_info_item[2];
|
||||||
|
} CipCommonPacketFormatData;
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* Parse the CPF data from a received unconnected explicit message and
|
||||||
|
* hand the data on to the message router
|
||||||
|
*
|
||||||
|
* @param received_data pointer to the encapsulation structure with the received message
|
||||||
|
* @param originator_address Address struct of the originator
|
||||||
|
* @param outgoing_message The outgoing ENIP message struct
|
||||||
|
* @return kEipStatusOkSend: a response needs to be sent, others: EIP stack status
|
||||||
|
*/
|
||||||
|
EipStatus NotifyCommonPacketFormat(
|
||||||
|
const EncapsulationData *const received_data,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* Parse the CPF data from a received connected explicit message, check
|
||||||
|
* the connection status, update any timers, and hand the data on to
|
||||||
|
* the message router
|
||||||
|
*
|
||||||
|
* @param received_data pointer to the encapsulation structure with the received message
|
||||||
|
* @param originator_address Address struct of the originator
|
||||||
|
* @param outgoing_message The outgoing ENIP message struct
|
||||||
|
* @return kEipStatusOkSend: a response needs to be sent, others: EIP stack status
|
||||||
|
*/
|
||||||
|
EipStatus NotifyConnectedCommonPacketFormat(
|
||||||
|
const EncapsulationData *const received_data,
|
||||||
|
const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* Create CPF structure out of the received data.
|
||||||
|
* @param data pointer to data which need to be structured.
|
||||||
|
* @param data_length length of data in pa_Data.
|
||||||
|
* @param common_packet_format_data pointer to structure of CPF data item.
|
||||||
|
* @return status
|
||||||
|
* EIP_OK .. success
|
||||||
|
* EIP_ERROR .. error
|
||||||
|
*/
|
||||||
|
EipStatus CreateCommonPacketFormatStructure(
|
||||||
|
const EipUint8 *data,
|
||||||
|
size_t data_length,
|
||||||
|
CipCommonPacketFormatData *common_packet_format_data);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* Copy data from CPFDataItem into linear memory in message for transmission over in encapsulation.
|
||||||
|
* @param common_packet_format_data_item pointer to CPF structure which has to be aligned into linear memory.
|
||||||
|
* @param message Modified ENIPMessage struct
|
||||||
|
* @return length of modification in bytes
|
||||||
|
* kEipStatusError .. error
|
||||||
|
*/
|
||||||
|
void AssembleIOMessage(
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const message);
|
||||||
|
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Copy data from message_router_response struct and common_packet_format_data_item into
|
||||||
|
* ENIPMessage struct outgoing_message via encapsulation.
|
||||||
|
*
|
||||||
|
* @param message_router_response pointer to message router response which has to be aligned into linear memory.
|
||||||
|
* @param common_packet_format_data_item pointer to CPF structure which has to be aligned into linear memory.
|
||||||
|
* @param outgoing_message Modified ENIPMessage struct
|
||||||
|
* @return length of modification in bytes
|
||||||
|
* kEipStatusError .. error
|
||||||
|
*/
|
||||||
|
EipStatus AssembleLinearMessage(
|
||||||
|
const CipMessageRouterResponse *const message_router_response,
|
||||||
|
const CipCommonPacketFormatData *const common_packet_format_data_item,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Data storage for the any CPF data
|
||||||
|
* Currently we are single threaded and need only one CPF at the time.
|
||||||
|
* For future extensions towards multithreading maybe more CPF data items may be necessary
|
||||||
|
*/
|
||||||
|
extern CipCommonPacketFormatData g_common_packet_format_data_item;
|
||||||
|
|
||||||
|
#endif /* OPENER_CPF_H_ */
|
||||||
739
OrginalSourceRepo/OpENer-master/source/src/enet_encap/encap.c
Normal file
739
OrginalSourceRepo/OpENer-master/source/src/enet_encap/encap.c
Normal file
|
|
@ -0,0 +1,739 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "encap.h"
|
||||||
|
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "opener_user_conf.h"
|
||||||
|
#include "cpf.h"
|
||||||
|
#include "endianconv.h"
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipmessagerouter.h"
|
||||||
|
#include "cipconnectionmanager.h"
|
||||||
|
#include "cipidentity.h"
|
||||||
|
#include "ciptcpipinterface.h"
|
||||||
|
#include "generic_networkhandler.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "socket_timer.h"
|
||||||
|
#include "opener_error.h"
|
||||||
|
|
||||||
|
/* IP address data taken from TCPIPInterfaceObject*/
|
||||||
|
const EipUint16 kSupportedProtocolVersion = 1; /**< Supported Encapsulation protocol version */
|
||||||
|
|
||||||
|
const CipUdint kEncapsulationHeaderOptionsFlag = 0x00; /**< Mask of which options are supported as of the current CIP specs no other option value as 0 should be supported.*/
|
||||||
|
|
||||||
|
const int kEncapsulationHeaderSessionHandlePosition = 4; /**< the position of the session handle within the encapsulation header*/
|
||||||
|
|
||||||
|
const EipUint16 kListIdentityDefaultDelayTime = 2000; /**< Default delay time for List Identity response */
|
||||||
|
const EipUint16 kListIdentityMinimumDelayTime = 500; /**< Minimum delay time for List Identity response */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kSessionStatusInvalid = -1,
|
||||||
|
kSessionStatusValid = 0
|
||||||
|
} SessionStatus;
|
||||||
|
|
||||||
|
const int kSenderContextSize = 8; /**< size of sender context in encapsulation header*/
|
||||||
|
|
||||||
|
/** @brief definition of known encapsulation commands */
|
||||||
|
typedef enum {
|
||||||
|
kEncapsulationCommandNoOperation = 0x0000, /**< only allowed for TCP */
|
||||||
|
kEncapsulationCommandListServices = 0x0004, /**< allowed for both UDP and TCP */
|
||||||
|
kEncapsulationCommandListIdentity = 0x0063, /**< allowed for both UDP and TCP */
|
||||||
|
kEncapsulationCommandListInterfaces = 0x0064, /**< optional, allowed for both UDP and TCP */
|
||||||
|
kEncapsulationCommandRegisterSession = 0x0065, /**< only allowed for TCP */
|
||||||
|
kEncapsulationCommandUnregisterSession = 0x0066, /**< only allowed for TCP */
|
||||||
|
kEncapsulationCommandSendRequestReplyData = 0x006F, /**< only allowed for TCP */
|
||||||
|
kEncapsulationCommandSendUnitData = 0x0070 /**< only allowed for TCP */
|
||||||
|
} EncapsulationCommand;
|
||||||
|
|
||||||
|
/** @brief definition of capability flags */
|
||||||
|
typedef enum {
|
||||||
|
kCapabilityFlagsCipTcp = 0x0020,
|
||||||
|
kCapabilityFlagsCipUdpClass0or1 = 0x0100
|
||||||
|
} CapabilityFlags;
|
||||||
|
|
||||||
|
#define ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES 2 /**< According to EIP spec at least 2 delayed message requests should be supported */
|
||||||
|
|
||||||
|
/* Encapsulation layer data */
|
||||||
|
|
||||||
|
/** @brief Delayed Encapsulation Message structure */
|
||||||
|
typedef struct {
|
||||||
|
EipInt32 time_out; /**< time out in milli seconds */
|
||||||
|
int socket; /**< associated socket */
|
||||||
|
struct sockaddr_in receiver;
|
||||||
|
ENIPMessage outgoing_message;
|
||||||
|
} DelayedEncapsulationMessage;
|
||||||
|
|
||||||
|
EncapsulationServiceInformation g_service_information;
|
||||||
|
|
||||||
|
int g_registered_sessions[OPENER_NUMBER_OF_SUPPORTED_SESSIONS];
|
||||||
|
|
||||||
|
DelayedEncapsulationMessage g_delayed_encapsulation_messages[ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES];
|
||||||
|
|
||||||
|
/*** private functions ***/
|
||||||
|
void HandleReceivedListIdentityCommandTcp(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void HandleReceivedListIdentityCommandUdp(const int socket,
|
||||||
|
const struct sockaddr_in *const from_address,
|
||||||
|
const EncapsulationData *const receive_data);
|
||||||
|
|
||||||
|
EipStatus HandleReceivedUnregisterSessionCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
EipStatus HandleReceivedSendUnitDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
EipStatus HandleReceivedInvalidCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
int GetFreeSessionIndex(void);
|
||||||
|
|
||||||
|
SessionStatus CheckRegisteredSessions(const EncapsulationData *const receive_data);
|
||||||
|
|
||||||
|
void DetermineDelayTime(const EipByte *buffer_start, DelayedEncapsulationMessage *const delayed_message_buffer);
|
||||||
|
|
||||||
|
/* @brief Initializes session list and interface information. */
|
||||||
|
void EncapsulationInit(void) {
|
||||||
|
|
||||||
|
DetermineEndianess();
|
||||||
|
|
||||||
|
/*initialize random numbers for random delayed response message generation
|
||||||
|
* we use the ip address as seed as suggested in the spec */
|
||||||
|
srand(g_tcpip.interface_configuration.ip_address);
|
||||||
|
|
||||||
|
/* initialize Sessions to invalid == free session */
|
||||||
|
for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; i++) {
|
||||||
|
g_registered_sessions[i] = kEipInvalidSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
|
||||||
|
g_delayed_encapsulation_messages[i].socket = kEipInvalidSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*TODO make the service information configurable*/
|
||||||
|
/* initialize service information */
|
||||||
|
g_service_information.type_code = kCipItemIdListServiceResponse;
|
||||||
|
g_service_information.length = sizeof(g_service_information);
|
||||||
|
g_service_information.encapsulation_protocol_version = 1;
|
||||||
|
g_service_information.capability_flags = kCapabilityFlagsCipTcp | kCapabilityFlagsCipUdpClass0or1;
|
||||||
|
snprintf((char*) g_service_information.name_of_service, sizeof(g_service_information.name_of_service), "Communications");
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus HandleReceivedExplictTcpData(int socket, EipUint8 *buffer, size_t length, int *number_of_remaining_bytes, struct sockaddr *originator_address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
OPENER_TRACE_INFO("Handles data for TCP socket: %d\n", socket);
|
||||||
|
EipStatus return_value = kEipStatusOk;
|
||||||
|
EncapsulationData encapsulation_data = { 0 };
|
||||||
|
/* eat the encapsulation header*/
|
||||||
|
/* the structure contains a pointer to the encapsulated data*/
|
||||||
|
/* returns how many bytes are left after the encapsulated data*/
|
||||||
|
const int remaining_bytes = CreateEncapsulationStructure(buffer, length, &encapsulation_data);
|
||||||
|
|
||||||
|
if(remaining_bytes >= 0) {
|
||||||
|
*number_of_remaining_bytes = remaining_bytes;
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Fragmented packet detected! Fragmented packets are not supported!\n");
|
||||||
|
*number_of_remaining_bytes = 0;
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kEncapsulationHeaderOptionsFlag == encapsulation_data.options) /*TODO generate appropriate error response*/
|
||||||
|
{
|
||||||
|
if(*number_of_remaining_bytes >= 0) /* check if the message is corrupt: header size + claimed payload size > than what we actually received*/
|
||||||
|
{
|
||||||
|
/* full package or more received */
|
||||||
|
encapsulation_data.status = kEncapsulationProtocolSuccess;
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
/* most of these functions need a reply to be send */
|
||||||
|
switch(encapsulation_data.command_code){
|
||||||
|
case (kEncapsulationCommandNoOperation):
|
||||||
|
OPENER_TRACE_INFO("NOP\n");
|
||||||
|
/* NOP needs no reply and does nothing */
|
||||||
|
return_value = kEipStatusOk;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandListServices):
|
||||||
|
OPENER_TRACE_INFO("List services\n");
|
||||||
|
HandleReceivedListServicesCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandListIdentity):
|
||||||
|
OPENER_TRACE_INFO("List identity\n");
|
||||||
|
HandleReceivedListIdentityCommandTcp(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandListInterfaces):
|
||||||
|
OPENER_TRACE_INFO("List interfaces\n");
|
||||||
|
HandleReceivedListInterfacesCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandRegisterSession):
|
||||||
|
OPENER_TRACE_INFO("Register session\n");
|
||||||
|
HandleReceivedRegisterSessionCommand(socket, &encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandUnregisterSession):
|
||||||
|
OPENER_TRACE_INFO("unregister session\n");
|
||||||
|
return_value = HandleReceivedUnregisterSessionCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandSendRequestReplyData):
|
||||||
|
OPENER_TRACE_INFO("Send Request/Reply Data\n");
|
||||||
|
return_value = HandleReceivedSendRequestResponseDataCommand(&encapsulation_data, originator_address, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandSendUnitData):
|
||||||
|
OPENER_TRACE_INFO("Send Unit Data\n");
|
||||||
|
return_value = HandleReceivedSendUnitDataCommand(&encapsulation_data, originator_address, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return_value = HandleReceivedInvalidCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus HandleReceivedExplictUdpData(const int socket, const struct sockaddr_in *from_address, const EipUint8 *buffer, const size_t buffer_length,
|
||||||
|
int *number_of_remaining_bytes,
|
||||||
|
bool unicast, ENIPMessage *const outgoing_message) {
|
||||||
|
EipStatus return_value = kEipStatusOk;
|
||||||
|
EncapsulationData encapsulation_data = { 0 };
|
||||||
|
/* eat the encapsulation header*/
|
||||||
|
/* the structure contains a pointer to the encapsulated data*/
|
||||||
|
/* returns how many bytes are left after the encapsulated data*/
|
||||||
|
const int remaining_bytes = CreateEncapsulationStructure(buffer, buffer_length, &encapsulation_data);
|
||||||
|
|
||||||
|
if(remaining_bytes >= 0) {
|
||||||
|
*number_of_remaining_bytes = remaining_bytes;
|
||||||
|
} else {
|
||||||
|
OPENER_TRACE_ERR("Fragmented packet detected! Fragmented packets are not supported!\n");
|
||||||
|
*number_of_remaining_bytes = 0;
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kEncapsulationHeaderOptionsFlag == encapsulation_data.options) /*TODO generate appropriate error response*/
|
||||||
|
{
|
||||||
|
if(*number_of_remaining_bytes >= 0) /* check if the message is corrupt: header size + claimed payload size > than what we actually received*/
|
||||||
|
{
|
||||||
|
/* full package or more received */
|
||||||
|
encapsulation_data.status = kEncapsulationProtocolSuccess;
|
||||||
|
return_value = kEipStatusOkSend;
|
||||||
|
/* most of these functions need a reply to be send */
|
||||||
|
switch(encapsulation_data.command_code){
|
||||||
|
case (kEncapsulationCommandListServices):
|
||||||
|
OPENER_TRACE_INFO("List Service\n");
|
||||||
|
HandleReceivedListServicesCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandListIdentity):
|
||||||
|
OPENER_TRACE_INFO("List Identity\n");
|
||||||
|
if(unicast == true) {
|
||||||
|
HandleReceivedListIdentityCommandTcp(&encapsulation_data, outgoing_message);
|
||||||
|
} else {
|
||||||
|
HandleReceivedListIdentityCommandUdp(socket,
|
||||||
|
from_address,
|
||||||
|
&encapsulation_data);
|
||||||
|
/* as the response has to be delayed do not send it now */
|
||||||
|
return_value = kEipStatusOk;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (kEncapsulationCommandListInterfaces):
|
||||||
|
OPENER_TRACE_INFO("List Interfaces\n");
|
||||||
|
HandleReceivedListInterfacesCommand(&encapsulation_data, outgoing_message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* The following commands are not to be sent via UDP */
|
||||||
|
case (kEncapsulationCommandNoOperation):
|
||||||
|
case (kEncapsulationCommandRegisterSession):
|
||||||
|
case (kEncapsulationCommandUnregisterSession):
|
||||||
|
case (kEncapsulationCommandSendRequestReplyData):
|
||||||
|
case (kEncapsulationCommandSendUnitData):
|
||||||
|
default:
|
||||||
|
OPENER_TRACE_INFO("No command\n");
|
||||||
|
//TODO: Check this
|
||||||
|
encapsulation_data.status = kEncapsulationProtocolInvalidCommand;
|
||||||
|
encapsulation_data.data_length = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkipEncapsulationHeader(ENIPMessage *const outgoing_message) {
|
||||||
|
/* Move pointer over Header, but do not add to size */
|
||||||
|
outgoing_message->current_message_position += ENCAPSULATION_HEADER_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateEncapsulationHeader(const EncapsulationData *const receive_data, const size_t command_specific_data_length, const CipSessionHandle session_handle,
|
||||||
|
const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message) {
|
||||||
|
AddIntToMessage(receive_data->command_code, outgoing_message);
|
||||||
|
AddIntToMessage(command_specific_data_length, outgoing_message);
|
||||||
|
AddDintToMessage(session_handle, outgoing_message); //Session handle
|
||||||
|
AddDintToMessage(encapsulation_protocol_status, outgoing_message); //Status
|
||||||
|
memcpy(outgoing_message->current_message_position, receive_data->sender_context, kSenderContextSize); // sender context
|
||||||
|
outgoing_message->current_message_position += kSenderContextSize;
|
||||||
|
outgoing_message->used_message_length += kSenderContextSize;
|
||||||
|
AddDintToMessage(0, outgoing_message); // options
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief generate reply with "Communications Services" + compatibility Flags.
|
||||||
|
* @param receive_data pointer to structure with received data
|
||||||
|
* @param outgoing_message The outgoing ENIP message
|
||||||
|
*/
|
||||||
|
void HandleReceivedListServicesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
/* Create encapsulation header */
|
||||||
|
const size_t kListServicesCommandSpecificDataLength = sizeof(CipUint) + sizeof(g_service_information);
|
||||||
|
GenerateEncapsulationHeader(receive_data, kListServicesCommandSpecificDataLength, 0,
|
||||||
|
/* Session handle will be ignored */
|
||||||
|
kEncapsulationProtocolSuccess,
|
||||||
|
/* Protocol status */
|
||||||
|
outgoing_message);
|
||||||
|
|
||||||
|
/* Command specific data copy Interface data to msg for sending */
|
||||||
|
AddIntToMessage(1, outgoing_message); // Item count
|
||||||
|
AddIntToMessage(g_service_information.type_code, outgoing_message);
|
||||||
|
AddIntToMessage((EipUint16) (g_service_information.length - 4), outgoing_message);
|
||||||
|
AddIntToMessage(g_service_information.encapsulation_protocol_version, outgoing_message);
|
||||||
|
AddIntToMessage(g_service_information.capability_flags, outgoing_message);
|
||||||
|
memcpy(outgoing_message->current_message_position, g_service_information.name_of_service, sizeof(g_service_information.name_of_service));
|
||||||
|
outgoing_message->current_message_position += sizeof(g_service_information.name_of_service);
|
||||||
|
outgoing_message->used_message_length += sizeof(g_service_information.name_of_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReceivedListInterfacesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
/* Encapsulation header */
|
||||||
|
const size_t kListInterfacesCommandSpecificDataLength = sizeof(CipUint);
|
||||||
|
|
||||||
|
GenerateEncapsulationHeader(receive_data, kListInterfacesCommandSpecificDataLength, 0,
|
||||||
|
/* Session handle will be ignored */
|
||||||
|
kEncapsulationProtocolSuccess, outgoing_message);
|
||||||
|
/* Command specific data */
|
||||||
|
AddIntToMessage(0x0000, outgoing_message); /* Set Item Count to 0: no Target Items follow. */
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReceivedListIdentityCommandTcp(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
EncapsulateListIdentityResponseMessage(receive_data, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReceivedListIdentityCommandUdp(const int socket,
|
||||||
|
const struct sockaddr_in *const from_address,
|
||||||
|
const EncapsulationData *const receive_data)
|
||||||
|
{
|
||||||
|
DelayedEncapsulationMessage *delayed_message_buffer = NULL;
|
||||||
|
ENIPMessage *p_outgoing_message = NULL;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
|
||||||
|
if(kEipInvalidSocket == g_delayed_encapsulation_messages[i].socket) {
|
||||||
|
delayed_message_buffer = &(g_delayed_encapsulation_messages[i]);
|
||||||
|
|
||||||
|
p_outgoing_message = &(delayed_message_buffer->outgoing_message);
|
||||||
|
InitializeENIPMessage(p_outgoing_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(NULL != delayed_message_buffer) {
|
||||||
|
delayed_message_buffer->socket = socket;
|
||||||
|
memcpy((&delayed_message_buffer->receiver), from_address, sizeof(struct sockaddr_in));
|
||||||
|
|
||||||
|
DetermineDelayTime(receive_data->communication_buffer_start, delayed_message_buffer);
|
||||||
|
|
||||||
|
EncapsulateListIdentityResponseMessage(receive_data, p_outgoing_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint ListIdentityGetCipIdentityItemLength() {
|
||||||
|
return sizeof(CipUint) + sizeof(CipInt) + sizeof(CipUint) + sizeof(CipUdint) + 8 * sizeof(CipUsint) + sizeof(CipUint) + sizeof(CipUint) + sizeof(CipUint)
|
||||||
|
+ 2 * sizeof(CipUsint) + sizeof(CipWord) + sizeof(CipUdint) + sizeof(CipUsint) + g_identity.product_name.length + sizeof(CipUsint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeListIdentityCipIdentityItem(ENIPMessage *const outgoing_message) {
|
||||||
|
/* Item ID*/
|
||||||
|
const CipUint kItemIDCipIdentity = 0x0C;
|
||||||
|
AddIntToMessage(kItemIDCipIdentity, outgoing_message);
|
||||||
|
|
||||||
|
AddIntToMessage(ListIdentityGetCipIdentityItemLength(), outgoing_message);
|
||||||
|
|
||||||
|
AddIntToMessage(kSupportedProtocolVersion, outgoing_message);
|
||||||
|
|
||||||
|
EncapsulateIpAddress(htons(kOpenerEthernetPort), g_tcpip.interface_configuration.ip_address, outgoing_message);
|
||||||
|
|
||||||
|
/** Array of USINT - length 8 shall be set to zero */
|
||||||
|
FillNextNMessageOctetsWithValueAndMoveToNextPosition(0, 8, outgoing_message);
|
||||||
|
|
||||||
|
AddIntToMessage(g_identity.vendor_id, outgoing_message);
|
||||||
|
AddIntToMessage(g_identity.device_type, outgoing_message);
|
||||||
|
AddIntToMessage(g_identity.product_code, outgoing_message);
|
||||||
|
AddSintToMessage(g_identity.revision.major_revision, outgoing_message);
|
||||||
|
AddSintToMessage(g_identity.revision.minor_revision, outgoing_message);
|
||||||
|
AddIntToMessage(g_identity.status, outgoing_message);
|
||||||
|
AddDintToMessage(g_identity.serial_number, outgoing_message);
|
||||||
|
AddSintToMessage((unsigned char) g_identity.product_name.length, outgoing_message);
|
||||||
|
//TODO Change to EncodeCipString
|
||||||
|
memcpy(outgoing_message->current_message_position, g_identity.product_name.string, g_identity.product_name.length);
|
||||||
|
outgoing_message->current_message_position += g_identity.product_name.length;
|
||||||
|
outgoing_message->used_message_length += g_identity.product_name.length;
|
||||||
|
|
||||||
|
AddSintToMessage(g_identity.state, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncapsulateListIdentityResponseMessage(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
const CipUint kEncapsulationCommandListIdentityLength = ListIdentityGetCipIdentityItemLength() + sizeof(CipUint) + sizeof(CipUint) + sizeof(CipUint); /* Last element is item count */
|
||||||
|
|
||||||
|
GenerateEncapsulationHeader(receive_data, kEncapsulationCommandListIdentityLength, 0,
|
||||||
|
/* Session handle will be ignored by receiver */
|
||||||
|
kEncapsulationProtocolSuccess, outgoing_message);
|
||||||
|
|
||||||
|
AddIntToMessage(1, outgoing_message); /* Item count: one item */
|
||||||
|
EncodeListIdentityCipIdentityItem(outgoing_message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetermineDelayTime(const EipByte *buffer_start, DelayedEncapsulationMessage *const delayed_message_buffer) {
|
||||||
|
|
||||||
|
buffer_start += 12; /* start of the sender context */
|
||||||
|
EipUint16 maximum_delay_time = GetUintFromMessage((const EipUint8** const ) &buffer_start);
|
||||||
|
|
||||||
|
if(0 == maximum_delay_time) {
|
||||||
|
maximum_delay_time = kListIdentityDefaultDelayTime;
|
||||||
|
} else if(kListIdentityMinimumDelayTime > maximum_delay_time) { /* if maximum_delay_time is between 1 and 500ms set it to 500ms */
|
||||||
|
maximum_delay_time = kListIdentityMinimumDelayTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
delayed_message_buffer->time_out = rand() % maximum_delay_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncapsulateRegisterSessionCommandResponseMessage(const EncapsulationData *const receive_data, const CipSessionHandle session_handle,
|
||||||
|
const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
/* Encapsulation header */
|
||||||
|
const size_t kListInterfacesCommandSpecificDataLength = sizeof(CipUint) + sizeof(CipUint);
|
||||||
|
assert(kListInterfacesCommandSpecificDataLength == 4);
|
||||||
|
GenerateEncapsulationHeader(receive_data, kListInterfacesCommandSpecificDataLength, session_handle, encapsulation_protocol_status, outgoing_message);
|
||||||
|
|
||||||
|
AddIntToMessage(1, outgoing_message); /* protocol version*/
|
||||||
|
AddIntToMessage(0, outgoing_message); /* Options flag, shall be set to zero */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief Check supported protocol, generate session handle, send replay back to originator.
|
||||||
|
* @param socket Socket this request is associated to. Needed for double register check
|
||||||
|
* @param receive_data Pointer to received data with request/response.
|
||||||
|
*/
|
||||||
|
void HandleReceivedRegisterSessionCommand(int socket, const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
int session_index = 0;
|
||||||
|
CipSessionHandle session_handle = 0;
|
||||||
|
EncapsulationProtocolErrorCode encapsulation_protocol_status = kEncapsulationProtocolSuccess;
|
||||||
|
|
||||||
|
EipUint16 protocol_version = GetUintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position);
|
||||||
|
EipUint16 option_flag = GetUintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position);
|
||||||
|
|
||||||
|
/* check if requested protocol version is supported and the register session option flag is zero*/
|
||||||
|
if((0 < protocol_version) && (protocol_version <= kSupportedProtocolVersion) && (0 == option_flag)) { /*Option field should be zero*/
|
||||||
|
/* check if the socket has already a session open */
|
||||||
|
for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(g_registered_sessions[i] == socket) {
|
||||||
|
/* the socket has already registered a session this is not allowed*/
|
||||||
|
OPENER_TRACE_INFO(
|
||||||
|
"Error: A session is already registered at socket %d\n",
|
||||||
|
socket);
|
||||||
|
session_handle = i + 1; /*return the already assigned session back, the cip spec is not clear about this needs to be tested*/
|
||||||
|
encapsulation_protocol_status = kEncapsulationProtocolInvalidCommand;
|
||||||
|
session_index = kSessionStatusInvalid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kSessionStatusInvalid != session_index) {
|
||||||
|
session_index = GetFreeSessionIndex();
|
||||||
|
if(kSessionStatusInvalid == session_index) /* no more sessions available */
|
||||||
|
{
|
||||||
|
encapsulation_protocol_status = kEncapsulationProtocolInsufficientMemory;
|
||||||
|
} else { /* successful session registered */
|
||||||
|
SocketTimer *socket_timer = SocketTimerArrayGetEmptySocketTimer(g_timestamps,
|
||||||
|
OPENER_NUMBER_OF_SUPPORTED_SESSIONS);
|
||||||
|
SocketTimerSetSocket(socket_timer, socket);
|
||||||
|
SocketTimerSetLastUpdate(socket_timer, g_actual_time);
|
||||||
|
g_registered_sessions[session_index] = socket; /* store associated socket */
|
||||||
|
session_handle = (CipSessionHandle)(session_index + 1);
|
||||||
|
encapsulation_protocol_status = kEncapsulationProtocolSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { /* protocol not supported */
|
||||||
|
encapsulation_protocol_status = kEncapsulationProtocolUnsupportedProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncapsulateRegisterSessionCommandResponseMessage(receive_data, session_handle, encapsulation_protocol_status, outgoing_message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Unregister encapsulation session
|
||||||
|
* @param receive_data Pointer to structure with data and header information.
|
||||||
|
* @param outgoing_message The outgoing ENIP message
|
||||||
|
* @return kEipStatusOkSend: a response needs to be sent, others: EIP stack status
|
||||||
|
*
|
||||||
|
* Close all corresponding TCP connections and delete session handle.
|
||||||
|
*/
|
||||||
|
EipStatus HandleReceivedUnregisterSessionCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
OPENER_TRACE_INFO("encap.c: Unregister Session Command\n");
|
||||||
|
if((0 < receive_data->session_handle) && (receive_data->session_handle <=
|
||||||
|
OPENER_NUMBER_OF_SUPPORTED_SESSIONS)) {
|
||||||
|
CipSessionHandle i = receive_data->session_handle - 1;
|
||||||
|
if(kEipInvalidSocket != g_registered_sessions[i]) {
|
||||||
|
CloseTcpSocket(g_registered_sessions[i]);
|
||||||
|
g_registered_sessions[i] = kEipInvalidSocket;
|
||||||
|
CloseClass3ConnectionBasedOnSession(i + 1);
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no such session registered */
|
||||||
|
GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
|
||||||
|
return kEipStatusOkSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Call Connection Manager.
|
||||||
|
* @param receive_data Pointer to structure with data and header information.
|
||||||
|
* @param originator_address Address of the originator as received from socket
|
||||||
|
* @param outgoing_message The outgoing ENIP message
|
||||||
|
*/
|
||||||
|
EipStatus HandleReceivedSendUnitDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
EipStatus return_value = kEipStatusOkSend;
|
||||||
|
/*EipStatus*/return_value = kEipStatusOk; /* TODO: Shouldn't this be kEipStatusOk cause we must not send any response if data_length < 6? */
|
||||||
|
|
||||||
|
if(receive_data->data_length >= 6) {
|
||||||
|
/* Command specific data UDINT .. Interface Handle, UINT .. Timeout, CPF packets */
|
||||||
|
/* don't use the data yet */
|
||||||
|
GetDintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over null interface handle*/
|
||||||
|
GetIntFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over unused timeout value*/
|
||||||
|
((EncapsulationData* const ) receive_data)->data_length -= 6; /* the rest is in CPF format*/
|
||||||
|
|
||||||
|
if(kSessionStatusValid == CheckRegisteredSessions(receive_data)) /* see if the EIP session is registered*/
|
||||||
|
{
|
||||||
|
return_value = NotifyConnectedCommonPacketFormat(receive_data, originator_address, outgoing_message);
|
||||||
|
} else { /* received a package with non registered session handle */
|
||||||
|
InitializeENIPMessage(outgoing_message);
|
||||||
|
GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
|
||||||
|
return_value = kEipStatusOkSend; /* TODO: Needs to be here if line with first TODO of this function is adjusted. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Call UCMM or Message Router if UCMM not implemented.
|
||||||
|
* @param receive_data Pointer to structure with data and header information.
|
||||||
|
* @param originator_address Address of the originator as received from socket
|
||||||
|
* @param outgoing_message The outgoing ENIP message
|
||||||
|
* @return status kEipStatusOk .. success.
|
||||||
|
* kEipStatusOkSend .. success & need to send response
|
||||||
|
* kEipStatusError .. error
|
||||||
|
*/
|
||||||
|
EipStatus HandleReceivedSendRequestResponseDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
EipStatus return_value = kEipStatusOkSend;
|
||||||
|
/* EipStatus*/return_value = kEipStatusOk; /* TODO: Shouldn't this be kEipStatusOk cause we must not send any response if data_length < 6? */
|
||||||
|
|
||||||
|
if(receive_data->data_length >= 6) {
|
||||||
|
/* Command specific data UDINT .. Interface Handle, UINT .. Timeout, CPF packets */
|
||||||
|
/* don't use the data yet */
|
||||||
|
GetDintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over null interface handle*/
|
||||||
|
GetIntFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over unused timeout value*/
|
||||||
|
((EncapsulationData* const ) receive_data)->data_length -= 6; /* the rest is in CPF format*/
|
||||||
|
|
||||||
|
if(kSessionStatusValid == CheckRegisteredSessions(receive_data)) /* see if the EIP session is registered*/
|
||||||
|
{
|
||||||
|
return_value = NotifyCommonPacketFormat(receive_data, originator_address, outgoing_message);
|
||||||
|
} else { /* received a package with non registered session handle */
|
||||||
|
InitializeENIPMessage(outgoing_message);
|
||||||
|
GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
|
||||||
|
return_value = kEipStatusOkSend; /* TODO: Needs to be here if line with first TODO of this function is adjusted. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus HandleReceivedInvalidCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
/* Encapsulation header */
|
||||||
|
GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidCommand, outgoing_message);
|
||||||
|
return kEipStatusOkSend;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief search for available sessions an return index.
|
||||||
|
* @return return index of free session in anRegisteredSessions.
|
||||||
|
* kInvalidSession .. no free session available
|
||||||
|
*/
|
||||||
|
int GetFreeSessionIndex(void) {
|
||||||
|
for(int session_index = 0; session_index < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; session_index++) {
|
||||||
|
if(kEipInvalidSocket == g_registered_sessions[session_index]) {
|
||||||
|
return session_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kSessionStatusInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief copy data from pa_buf in little endian to host in structure.
|
||||||
|
* @param receive_buffer Received message
|
||||||
|
* @param receive_buffer_length Length of the data in receive_buffer. Might be more than one message
|
||||||
|
* @param encapsulation_data structure to which data shall be copied
|
||||||
|
* @return return difference between bytes in pa_buf an data_length
|
||||||
|
* 0 .. full package received
|
||||||
|
* >0 .. more than one packet received
|
||||||
|
* <0 .. only fragment of data portion received
|
||||||
|
*/
|
||||||
|
int_fast32_t CreateEncapsulationStructure(const EipUint8 *receive_buffer,
|
||||||
|
size_t receive_buffer_length,
|
||||||
|
EncapsulationData *const encapsulation_data)
|
||||||
|
{
|
||||||
|
encapsulation_data->communication_buffer_start = (EipUint8*) receive_buffer;
|
||||||
|
encapsulation_data->command_code = GetUintFromMessage(&receive_buffer);
|
||||||
|
encapsulation_data->data_length = GetUintFromMessage(&receive_buffer);
|
||||||
|
encapsulation_data->session_handle = GetUdintFromMessage(&receive_buffer);
|
||||||
|
encapsulation_data->status = GetUdintFromMessage(&receive_buffer);
|
||||||
|
|
||||||
|
memcpy(encapsulation_data->sender_context, receive_buffer, kSenderContextSize);
|
||||||
|
receive_buffer += kSenderContextSize;
|
||||||
|
encapsulation_data->options = GetUdintFromMessage(&receive_buffer);
|
||||||
|
encapsulation_data->current_communication_buffer_position = (EipUint8*) receive_buffer;
|
||||||
|
|
||||||
|
/* Ensure buffer length fits in an int32 before casting in the return expression. */
|
||||||
|
OPENER_ASSERT(INT32_MAX >= receive_buffer_length);
|
||||||
|
|
||||||
|
return ( (int32_t)receive_buffer_length - ENCAPSULATION_HEADER_LENGTH -
|
||||||
|
encapsulation_data->data_length );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Check if received package belongs to registered session.
|
||||||
|
* @param receive_data Received data.
|
||||||
|
* @return 0 .. Session registered
|
||||||
|
* kInvalidSession .. invalid session -> return unsupported command received
|
||||||
|
*/
|
||||||
|
SessionStatus CheckRegisteredSessions(const EncapsulationData *const receive_data) {
|
||||||
|
|
||||||
|
/* Skip the check when fuzzing
|
||||||
|
in order to increase our code coverage
|
||||||
|
we are simply bypassing all the session checks
|
||||||
|
*/
|
||||||
|
#ifdef FUZZING_AFL
|
||||||
|
return kSessionStatusValid;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if((0 < receive_data->session_handle) && (receive_data->session_handle <=
|
||||||
|
OPENER_NUMBER_OF_SUPPORTED_SESSIONS)) {
|
||||||
|
if(kEipInvalidSocket != g_registered_sessions[receive_data->session_handle - 1]) {
|
||||||
|
return kSessionStatusValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kSessionStatusInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseSessionBySessionHandle(const CipConnectionObject *const connection_object) {
|
||||||
|
OPENER_TRACE_INFO("encap.c: Close session by handle\n");
|
||||||
|
CipSessionHandle session_handle = connection_object->associated_encapsulation_session;
|
||||||
|
CloseTcpSocket(g_registered_sessions[session_handle - 1]);
|
||||||
|
g_registered_sessions[session_handle - 1] = kEipInvalidSocket;
|
||||||
|
OPENER_TRACE_INFO("encap.c: Close session by handle done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseSession(int socket) {
|
||||||
|
OPENER_TRACE_INFO("encap.c: Close session\n");
|
||||||
|
for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(g_registered_sessions[i] == socket) {
|
||||||
|
CloseTcpSocket(socket);
|
||||||
|
g_registered_sessions[i] = kEipInvalidSocket;
|
||||||
|
CloseClass3ConnectionBasedOnSession(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}OPENER_TRACE_INFO("encap.c: Close session done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveSession(const int socket) {
|
||||||
|
OPENER_TRACE_INFO("encap.c: Removing session\n");
|
||||||
|
for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(g_registered_sessions[i] == socket) {
|
||||||
|
g_registered_sessions[i] = kEipInvalidSocket;
|
||||||
|
CloseClass3ConnectionBasedOnSession(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}OPENER_TRACE_INFO("encap.c: Session removed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncapsulationShutDown(void) {
|
||||||
|
OPENER_TRACE_INFO("encap.c: Encapsulation shutdown\n");
|
||||||
|
for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(kEipInvalidSocket != g_registered_sessions[i]) {
|
||||||
|
CloseTcpSocket(g_registered_sessions[i]);
|
||||||
|
g_registered_sessions[i] = kEipInvalidSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManageEncapsulationMessages(const MilliSeconds elapsed_time) {
|
||||||
|
for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
|
||||||
|
if(kEipInvalidSocket != g_delayed_encapsulation_messages[i].socket) {
|
||||||
|
g_delayed_encapsulation_messages[i].time_out -= elapsed_time;
|
||||||
|
if(0 >= g_delayed_encapsulation_messages[i].time_out) {
|
||||||
|
/* If delay is reached or passed, send the UDP message */
|
||||||
|
sendto(g_delayed_encapsulation_messages[i].socket, (char*) g_delayed_encapsulation_messages[i].outgoing_message.message_buffer,
|
||||||
|
g_delayed_encapsulation_messages[i].outgoing_message.used_message_length, 0, (struct sockaddr*) &(g_delayed_encapsulation_messages[i].receiver),
|
||||||
|
sizeof(struct sockaddr));
|
||||||
|
g_delayed_encapsulation_messages[i].socket = kEipInvalidSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseEncapsulationSessionBySockAddr(const CipConnectionObject *const connection_object) {
|
||||||
|
for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(kEipInvalidSocket != g_registered_sessions[i]) {
|
||||||
|
struct sockaddr_in encapsulation_session_addr = { 0 };
|
||||||
|
socklen_t addrlength = sizeof(encapsulation_session_addr);
|
||||||
|
if(getpeername(g_registered_sessions[i], (struct sockaddr*) &encapsulation_session_addr, &addrlength) < 0) { /* got error */
|
||||||
|
int error_code = GetSocketErrorNumber();
|
||||||
|
char *error_message = GetErrorMessage(error_code);
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"encap.c: error on getting peer name on closing session: %d - %s\n",
|
||||||
|
error_code, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
}
|
||||||
|
if(encapsulation_session_addr.sin_addr.s_addr == connection_object->originator_address.sin_addr.s_addr) {
|
||||||
|
CloseSession(g_registered_sessions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipSessionHandle GetSessionFromSocket(const int socket_handle) {
|
||||||
|
for(CipSessionHandle i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
|
||||||
|
if(socket_handle == g_registered_sessions[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OPENER_NUMBER_OF_SUPPORTED_SESSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseClass3ConnectionBasedOnSession(CipSessionHandle encapsulation_session_handle) {
|
||||||
|
DoublyLinkedListNode *node = connection_list.first;
|
||||||
|
while(NULL != node) {
|
||||||
|
CipConnectionObject *connection_object = node->data;
|
||||||
|
if(kConnectionObjectTransportClassTriggerTransportClass3 == ConnectionObjectGetTransportClassTriggerTransportClass(connection_object)
|
||||||
|
&& connection_object->associated_encapsulation_session == encapsulation_session_handle) {
|
||||||
|
connection_object->connection_close_function(connection_object);
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
115
OrginalSourceRepo/OpENer-master/source/src/enet_encap/encap.h
Normal file
115
OrginalSourceRepo/OpENer-master/source/src/enet_encap/encap.h
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_ENCAP_H_
|
||||||
|
#define OPENER_ENCAP_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
#include "generic_networkhandler.h"
|
||||||
|
|
||||||
|
/** @file encap.h
|
||||||
|
* @brief This file contains the public interface of the encapsulation layer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup ENCAP OpENer Ethernet encapsulation layer
|
||||||
|
* The Ethernet encapsulation layer handles provides the abstraction between the Ethernet and the CIP layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*** defines ***/
|
||||||
|
|
||||||
|
#define ENCAPSULATION_HEADER_LENGTH 24
|
||||||
|
|
||||||
|
/** @brief definition of status codes in encapsulation protocol
|
||||||
|
* All other codes are either legacy codes, or reserved for future use
|
||||||
|
* */
|
||||||
|
typedef enum {
|
||||||
|
kEncapsulationProtocolSuccess = 0x0000,
|
||||||
|
kEncapsulationProtocolInvalidCommand = 0x0001,
|
||||||
|
kEncapsulationProtocolInsufficientMemory = 0x0002,
|
||||||
|
kEncapsulationProtocolIncorrectData = 0x0003,
|
||||||
|
kEncapsulationProtocolInvalidSessionHandle = 0x0064,
|
||||||
|
kEncapsulationProtocolInvalidLength = 0x0065,
|
||||||
|
kEncapsulationProtocolUnsupportedProtocol = 0x0069
|
||||||
|
} EncapsulationProtocolErrorCode;
|
||||||
|
|
||||||
|
/*** structs ***/
|
||||||
|
typedef struct encapsulation_data {
|
||||||
|
CipUint command_code;
|
||||||
|
CipUint data_length;
|
||||||
|
CipSessionHandle session_handle;
|
||||||
|
CipUdint status;
|
||||||
|
CipOctet sender_context[8]; /**< length of 8, according to the specification */
|
||||||
|
CipUdint options;
|
||||||
|
const EipUint8 *communication_buffer_start; /**< Pointer to the communication buffer used for this message */
|
||||||
|
const EipUint8 *current_communication_buffer_position; /**< The current position in the communication buffer during the decoding process */
|
||||||
|
} EncapsulationData;
|
||||||
|
|
||||||
|
typedef struct encapsulation_service_information {
|
||||||
|
EipUint16 type_code;
|
||||||
|
EipUint16 length;
|
||||||
|
EipUint16 encapsulation_protocol_version;
|
||||||
|
EipUint16 capability_flags;
|
||||||
|
EipInt8 name_of_service[16];
|
||||||
|
} EncapsulationServiceInformation;
|
||||||
|
|
||||||
|
/*** global variables (public) ***/
|
||||||
|
|
||||||
|
/*** public functions ***/
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Initialize the encapsulation layer.
|
||||||
|
*/
|
||||||
|
void EncapsulationInit(void);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Shutdown the encapsulation layer.
|
||||||
|
*
|
||||||
|
* This means that all open sessions including their sockets are closed.
|
||||||
|
*/
|
||||||
|
void EncapsulationShutDown(void);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Handle delayed encapsulation message responses
|
||||||
|
*
|
||||||
|
* Certain encapsulation message requests require a delayed sending of the response
|
||||||
|
* message. This functions checks if messages need to be sent and performs the
|
||||||
|
* sending.
|
||||||
|
*/
|
||||||
|
void ManageEncapsulationMessages(const MilliSeconds elapsed_time);
|
||||||
|
|
||||||
|
CipSessionHandle GetSessionFromSocket(const int socket_handle);
|
||||||
|
|
||||||
|
void RemoveSession(const int socket);
|
||||||
|
|
||||||
|
void CloseSessionBySessionHandle(const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void CloseEncapsulationSessionBySockAddr(const CipConnectionObject *const connection_object);
|
||||||
|
|
||||||
|
void CloseClass3ConnectionBasedOnSession(CipSessionHandle encapsulation_session_handle);
|
||||||
|
|
||||||
|
/* No reason to use this functions outside the encapsulation layer, they are here for testing */
|
||||||
|
typedef struct enip_message ENIPMessage;
|
||||||
|
|
||||||
|
void EncapsulateListIdentityResponseMessage(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
int_fast32_t CreateEncapsulationStructure(const EipUint8 *receive_buffer,
|
||||||
|
size_t receive_buffer_length,
|
||||||
|
EncapsulationData *const encapsulation_data);
|
||||||
|
|
||||||
|
void SkipEncapsulationHeader(ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void GenerateEncapsulationHeader(const EncapsulationData *const receive_data, const size_t command_specific_data_length, const CipSessionHandle session_handle,
|
||||||
|
const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void HandleReceivedListServicesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void HandleReceivedListInterfacesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void HandleReceivedRegisterSessionCommand(int socket, const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
EipStatus HandleReceivedSendRequestResponseDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
#endif /* OPENER_ENCAP_H_ */
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#elif defined STM32
|
||||||
|
#include "lwip/inet.h"
|
||||||
|
#else
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "endianconv.h"
|
||||||
|
|
||||||
|
OpenerEndianess g_opener_platform_endianess = kOpenerEndianessUnknown;
|
||||||
|
|
||||||
|
/* THESE ROUTINES MODIFY THE BUFFER POINTER*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads EIP_UINT8 from *buffer and converts little endian to host.
|
||||||
|
* @param buffer pointer where data should be reed.
|
||||||
|
* @return EIP_UINT8 data value
|
||||||
|
*/
|
||||||
|
CipSint GetSintFromMessage(const EipUint8 **const buffer) {
|
||||||
|
const unsigned char *const buffer_address = (unsigned char *) *buffer;
|
||||||
|
EipUint8 data = buffer_address[0];
|
||||||
|
*buffer += 1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipByte GetByteFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
CipByte data = buffer[0];
|
||||||
|
*buffer_address += 1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUsint GetUsintFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
CipUsint data = buffer[0];
|
||||||
|
*buffer_address += 1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipBool GetBoolFromMessage(const EipBool8 **const buffer_address) {
|
||||||
|
const EipBool8 *buffer = *buffer_address;
|
||||||
|
EipBool8 data = buffer[0];
|
||||||
|
*buffer_address += 1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* little-endian-to-host unsigned 16 bit*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads EIP_UINT16 from *buffer and converts little endian to host.
|
||||||
|
* @param buffer pointer where data should be reed.
|
||||||
|
* @return EIP_UINT16 data value
|
||||||
|
*/
|
||||||
|
CipInt GetIntFromMessage(const EipUint8 **const buffer) {
|
||||||
|
const unsigned char *const buffer_address = (unsigned char *) *buffer;
|
||||||
|
EipUint16 data = buffer_address[0] | buffer_address[1] << 8;
|
||||||
|
*buffer += 2;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUint GetUintFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
EipUint16 data = buffer[0] | buffer[1] << 8;
|
||||||
|
*buffer_address += 2;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipWord GetWordFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
EipUint16 data = buffer[0] | buffer[1] << 8;
|
||||||
|
*buffer_address += 2;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads EIP_UINT32 from *buffer and converts little endian to host.
|
||||||
|
* @param buffer pointer where data should be reed.
|
||||||
|
* @return EIP_UNÍT32 value
|
||||||
|
*/
|
||||||
|
CipDint GetDintFromMessage(const EipUint8 **const buffer) {
|
||||||
|
const unsigned char *p = (unsigned char *) *buffer;
|
||||||
|
EipUint32 data = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
|
||||||
|
*buffer += 4;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint GetUdintFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
CipUdint data = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] <<
|
||||||
|
24;
|
||||||
|
*buffer_address += 4;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipUdint GetDwordFromMessage(const CipOctet **const buffer_address) {
|
||||||
|
const CipOctet *buffer = *buffer_address;
|
||||||
|
CipDword data = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] <<
|
||||||
|
24;
|
||||||
|
*buffer_address += 4;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief converts UINT8 data from host to little endian an writes it to buffer.
|
||||||
|
* @param data value to be written
|
||||||
|
* @param buffer pointer where data should be written.
|
||||||
|
*/
|
||||||
|
void AddSintToMessage(const EipUint8 data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
outgoing_message->current_message_position[0] = (unsigned char) data;
|
||||||
|
outgoing_message->current_message_position += 1;
|
||||||
|
outgoing_message->used_message_length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief converts UINT16 data from host to little endian an writes it to buffer.
|
||||||
|
* @param data value to be written
|
||||||
|
* @param buffer pointer where data should be written.
|
||||||
|
*/
|
||||||
|
void AddIntToMessage(const EipUint16 data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
outgoing_message->current_message_position[0] = (unsigned char) data;
|
||||||
|
outgoing_message->current_message_position[1] = (unsigned char) (data >> 8);
|
||||||
|
outgoing_message->current_message_position += 2;
|
||||||
|
outgoing_message->used_message_length += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts UINT32 data from host to little endian and writes it to buffer.
|
||||||
|
* @param data value to be written
|
||||||
|
* @param buffer pointer where data should be written.
|
||||||
|
*/
|
||||||
|
void AddDintToMessage(const EipUint32 data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
outgoing_message->current_message_position[0] = (unsigned char) data;
|
||||||
|
outgoing_message->current_message_position[1] = (unsigned char) (data >> 8);
|
||||||
|
outgoing_message->current_message_position[2] = (unsigned char) (data >> 16);
|
||||||
|
outgoing_message->current_message_position[3] = (unsigned char) (data >> 24);
|
||||||
|
|
||||||
|
outgoing_message->current_message_position += 4;
|
||||||
|
outgoing_message->used_message_length += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads EipUint64 from *pa_buf and converts little endian to host.
|
||||||
|
* @param pa_buf pointer where data should be reed.
|
||||||
|
* @return EipUint64 value
|
||||||
|
*/
|
||||||
|
EipUint64 GetLintFromMessage(const EipUint8 **const buffer) {
|
||||||
|
const EipUint8 *buffer_address = *buffer;
|
||||||
|
EipUint64 data =
|
||||||
|
( ( ( (EipUint64) buffer_address[0] ) << 56 ) & 0xFF00000000000000LL ) +
|
||||||
|
( ( ( (EipUint64) buffer_address[1] ) << 48 ) & 0x00FF000000000000LL )
|
||||||
|
+ ( ( ( (EipUint64) buffer_address[2] ) << 40 ) &
|
||||||
|
0x0000FF0000000000LL ) +
|
||||||
|
( ( ( (EipUint64) buffer_address[3] ) << 32 ) & 0x000000FF00000000LL )
|
||||||
|
+ ( ( ( (EipUint64) buffer_address[4] ) << 24 ) &
|
||||||
|
0x00000000FF000000 ) +
|
||||||
|
( ( ( (EipUint64) buffer_address[5] ) << 16 ) & 0x0000000000FF0000 )
|
||||||
|
+ ( ( ( (EipUint64) buffer_address[6] ) << 8 ) &
|
||||||
|
0x000000000000FF00 ) +
|
||||||
|
( ( (EipUint64) buffer_address[7] ) & 0x00000000000000FF );
|
||||||
|
*buffer += 8;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts EipUint64 data from host to little endian and writes it to buffer.
|
||||||
|
* @param data value to be written
|
||||||
|
* @param buffer pointer where data should be written.
|
||||||
|
*/
|
||||||
|
void AddLintToMessage(const EipUint64 data,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
|
||||||
|
outgoing_message->current_message_position[0] = (EipUint8) (data);
|
||||||
|
outgoing_message->current_message_position[1] = (EipUint8) (data >> 8);
|
||||||
|
outgoing_message->current_message_position[2] = (EipUint8) (data >> 16);
|
||||||
|
outgoing_message->current_message_position[3] = (EipUint8) (data >> 24);
|
||||||
|
outgoing_message->current_message_position[4] = (EipUint8) (data >> 32);
|
||||||
|
outgoing_message->current_message_position[5] = (EipUint8) (data >> 40);
|
||||||
|
outgoing_message->current_message_position[6] = (EipUint8) (data >> 48);
|
||||||
|
outgoing_message->current_message_position[7] = (EipUint8) (data >> 56);
|
||||||
|
outgoing_message->current_message_position += 8;
|
||||||
|
outgoing_message->used_message_length += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncapsulateIpAddress(EipUint16 port,
|
||||||
|
EipUint32 address,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
if(kOpENerEndianessLittle == g_opener_platform_endianess) {
|
||||||
|
AddIntToMessage(htons(AF_INET), outgoing_message);
|
||||||
|
AddIntToMessage(port, outgoing_message);
|
||||||
|
AddDintToMessage(address, outgoing_message);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(kOpENerEndianessBig == g_opener_platform_endianess) {
|
||||||
|
|
||||||
|
AddIntToMessage(htons(AF_INET), outgoing_message);
|
||||||
|
|
||||||
|
AddSintToMessage( (unsigned char) (port >> 8), outgoing_message );
|
||||||
|
AddSintToMessage( (unsigned char) port, outgoing_message );
|
||||||
|
|
||||||
|
AddSintToMessage( (unsigned char) address, outgoing_message );
|
||||||
|
AddSintToMessage( (unsigned char) (address >> 8), outgoing_message );
|
||||||
|
AddSintToMessage( (unsigned char) (address >> 16), outgoing_message );
|
||||||
|
AddSintToMessage( (unsigned char) (address >> 24), outgoing_message );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"No endianess detected! Probably the DetermineEndianess function was not executed!");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Detects Endianess of the platform and sets global g_nOpENerPlatformEndianess variable accordingly
|
||||||
|
*
|
||||||
|
* Detects Endianess of the platform and sets global variable g_nOpENerPlatformEndianess accordingly,
|
||||||
|
* whereas 0 equals little endian and 1 equals big endian
|
||||||
|
*/
|
||||||
|
void DetermineEndianess() {
|
||||||
|
int i = 1;
|
||||||
|
const char *const p = (char *) &i;
|
||||||
|
if(p[0] == 1) {
|
||||||
|
g_opener_platform_endianess = kOpENerEndianessLittle;
|
||||||
|
} else {
|
||||||
|
g_opener_platform_endianess = kOpENerEndianessBig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns global variable g_nOpENerPlatformEndianess, whereas 0 equals little endian and 1 equals big endian
|
||||||
|
*
|
||||||
|
* @return 0 equals little endian and 1 equals big endian
|
||||||
|
*/
|
||||||
|
int GetEndianess() {
|
||||||
|
return g_opener_platform_endianess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveMessageNOctets(const int amount_of_bytes_moved,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
outgoing_message->current_message_position += amount_of_bytes_moved;
|
||||||
|
outgoing_message->used_message_length += amount_of_bytes_moved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillNextNMessageOctetsWith(CipOctet value,
|
||||||
|
unsigned int amount_of_bytes_written,
|
||||||
|
ENIPMessage *const outgoing_message) {
|
||||||
|
memset(outgoing_message->current_message_position,
|
||||||
|
value,
|
||||||
|
amount_of_bytes_written);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value,
|
||||||
|
unsigned int amount_of_filled_bytes,
|
||||||
|
ENIPMessage *const outgoing_message)
|
||||||
|
{
|
||||||
|
FillNextNMessageOctetsWith(value, amount_of_filled_bytes, outgoing_message);
|
||||||
|
MoveMessageNOctets(amount_of_filled_bytes, outgoing_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef OPENER_ENDIANCONV_H_
|
||||||
|
#define OPENER_ENDIANCONV_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "ciptypes.h"
|
||||||
|
|
||||||
|
/** @file endianconv.h
|
||||||
|
* @brief Responsible for Endianess conversion
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kOpenerEndianessUnknown = -1,
|
||||||
|
kOpENerEndianessLittle = 0,
|
||||||
|
kOpENerEndianessBig = 1
|
||||||
|
} OpenerEndianess;
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
* @brief Reads EIP_UINT8 from *buffer and converts little endian to host.
|
||||||
|
* @param buffer pointer where data should be reed.
|
||||||
|
* @return EIP_UINT8 data value
|
||||||
|
*/
|
||||||
|
CipSint GetSintFromMessage(const EipUint8 **const buffer);
|
||||||
|
|
||||||
|
CipByte GetByteFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
CipUsint GetUsintFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
CipBool GetBoolFromMessage(const EipBool8 **const buffer_address);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief Get an 16Bit integer from the network buffer, and moves pointer beyond the 16 bit value
|
||||||
|
* @param buffer Pointer to the network buffer array. This pointer will be incremented by 2!
|
||||||
|
* @return Extracted 16 bit integer value
|
||||||
|
*/
|
||||||
|
CipInt GetIntFromMessage(const EipUint8 **const buffer);
|
||||||
|
|
||||||
|
CipUint GetUintFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
CipWord GetWordFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief Get an 32Bit integer from the network buffer.
|
||||||
|
* @param buffer pointer to the network buffer array. This pointer will be incremented by 4!
|
||||||
|
* @return Extracted 32 bit integer value
|
||||||
|
*/
|
||||||
|
CipDint GetDintFromMessage(const EipUint8 **const buffer);
|
||||||
|
|
||||||
|
CipUdint GetUdintFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
CipUdint GetDwordFromMessage(const CipOctet **const buffer_address);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief converts UINT8 data from host to little endian an writes it to buffer.
|
||||||
|
* @param data value to be written
|
||||||
|
* @param buffer pointer where data should be written.
|
||||||
|
*/
|
||||||
|
void AddSintToMessage(const EipUint8 data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief Write an 16Bit integer to the network buffer.
|
||||||
|
* @param data value to write
|
||||||
|
* @param buffer pointer to the network buffer array. This pointer will be incremented by 2!
|
||||||
|
*
|
||||||
|
* @return Length in bytes of the encoded message
|
||||||
|
*/
|
||||||
|
void AddIntToMessage(const EipUint16 data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief Write an 32Bit integer to the network buffer.
|
||||||
|
* @param data value to write
|
||||||
|
* @param buffer pointer to the network buffer array. This pointer will be incremented by 4!
|
||||||
|
*
|
||||||
|
* @return Length in bytes of the encoded message
|
||||||
|
*/
|
||||||
|
void AddDintToMessage(const EipUint32 data,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
EipUint64 GetLintFromMessage(const EipUint8 **const buffer);
|
||||||
|
|
||||||
|
/** @ingroup ENCAP
|
||||||
|
*
|
||||||
|
* @brief Write an 64Bit integer to the network buffer.
|
||||||
|
* @param data value to write
|
||||||
|
* @param buffer pointer to the network buffer array. This pointer will be incremented by 8!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void AddLintToMessage(const EipUint64 pa_unData,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** @brief Encapsulate the sockaddr information as necessary for the Common Packet Format data items
|
||||||
|
*
|
||||||
|
* Converts and adds the provided port and IP address into an common packet format message
|
||||||
|
*
|
||||||
|
* @param port Port of the socket, has to be provided in big-endian
|
||||||
|
* @param address IP address of the socket, has to be provided in big-endian
|
||||||
|
* @param communication_buffer The message buffer for sending the message
|
||||||
|
*/
|
||||||
|
void EncapsulateIpAddress(EipUint16 port,
|
||||||
|
EipUint32 address,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
/** Identify if we are running on a big or little endian system and set
|
||||||
|
* variable.
|
||||||
|
*/
|
||||||
|
void DetermineEndianess(void);
|
||||||
|
|
||||||
|
/** @brief Return the endianess identified on system startup
|
||||||
|
* @return
|
||||||
|
* - -1 endianess has not been identified up to now
|
||||||
|
* - 0 little endian system
|
||||||
|
* - 1 big endian system
|
||||||
|
*/
|
||||||
|
int GetEndianess(void);
|
||||||
|
|
||||||
|
void MoveMessageNOctets(const int amount_of_bytes_moved,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void FillNextNMessageOctetsWith(CipOctet value,
|
||||||
|
unsigned int amount_of_bytes_written,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
void FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value,
|
||||||
|
unsigned int amount_of_filled_bytes,
|
||||||
|
ENIPMessage *const outgoing_message);
|
||||||
|
|
||||||
|
#endif /* OPENER_ENDIANCONV_H_ */
|
||||||
1173
OrginalSourceRepo/OpENer-master/source/src/opener_api.h
Normal file
1173
OrginalSourceRepo/OpENer-master/source/src/opener_api.h
Normal file
File diff suppressed because it is too large
Load diff
36
OrginalSourceRepo/OpENer-master/source/src/ports/CMakeLists.txt
Executable file
36
OrginalSourceRepo/OpENer-master/source/src/ports/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#######################################
|
||||||
|
# OpENer ports #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
add_subdirectory( ${OpENer_PLATFORM} )
|
||||||
|
add_subdirectory( nvdata )
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform-specific includes #
|
||||||
|
#######################################
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
set( PLATFORM_GENERIC_SRC generic_networkhandler.c socket_timer.c )
|
||||||
|
|
||||||
|
add_library( PLATFORM_GENERIC ${PLATFORM_GENERIC_SRC} )
|
||||||
|
|
||||||
|
if( OPENER_INSTALL_AS_LIB )
|
||||||
|
install(TARGETS PLATFORM_GENERIC
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
install(DIRECTORY ${PORTS_SRC_DIR}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
USE_SOURCE_PERMISSIONS
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.h"
|
||||||
|
PATTERN "sample_application" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
add_subdirectory(sample_application)
|
||||||
|
|
||||||
|
set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c)
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform specific things #
|
||||||
|
#######################################
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
set (PLATFORMLIBNAME ${OpENer_PLATFORM}PLATFORM)
|
||||||
|
|
||||||
|
add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC})
|
||||||
|
|
||||||
|
if( NOT OpENer_TESTS)
|
||||||
|
add_executable(OpENer main.c)
|
||||||
|
target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA ws2_32 iphlpapi ${OpENer_CIP_OBJECTS} )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add additional CIP Objects
|
||||||
|
string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT )
|
||||||
|
if( OpENer_HAS_ADDITIONAL_OBJECT )
|
||||||
|
message(STATUS "Additional activated objects: ${OpENer_ADD_CIP_OBJECTS}")
|
||||||
|
string(REPLACE " " ";" OpENer_ADD_CIP_OBJECTS_LIST ${OpENer_ADD_CIP_OBJECTS} )
|
||||||
|
foreach(CIP_OBJECT IN LISTS OpENer_ADD_CIP_OBJECTS_LIST)
|
||||||
|
include_directories(${${CIP_OBJECT}_SOURCE_DIR})
|
||||||
|
target_link_libraries( OpENer ${CIP_OBJECT} )
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(STATUS "No additional activated objects")
|
||||||
|
endif()
|
||||||
183
OrginalSourceRepo/OpENer-master/source/src/ports/MINGW/main.c
Normal file
183
OrginalSourceRepo/OpENer-master/source/src/ports/MINGW/main.c
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "generic_networkhandler.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "cipethernetlink.h"
|
||||||
|
#include "ciptcpipinterface.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "networkconfig.h"
|
||||||
|
#include "doublylinkedlist.h"
|
||||||
|
#include "cipconnectionobject.h"
|
||||||
|
#include "nvdata.h"
|
||||||
|
|
||||||
|
#define BringupNetwork(if_name, method, if_cfg, hostname) (0)
|
||||||
|
#define ShutdownNetwork(if_name) (0)
|
||||||
|
|
||||||
|
/** If OpENer is aborted by a signal it returns the sum of the signal number
|
||||||
|
* and this define. */
|
||||||
|
#define RET_SHOW_SIGNAL 200
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/** @brief Signal handler function for ending stack execution
|
||||||
|
*
|
||||||
|
* @param signal the signal we received
|
||||||
|
*/
|
||||||
|
static void LeaveStack(int signal);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/** @brief Execute OpENer stack loop function
|
||||||
|
*
|
||||||
|
* @param thread_arg dummy argument
|
||||||
|
* @returns NO_ERROR at the moment
|
||||||
|
*
|
||||||
|
* The call signature is chosen to be able to pass this function directly as
|
||||||
|
* parameter for CreateThread().
|
||||||
|
*/
|
||||||
|
static DWORD executeEventLoop(LPVOID thread_arg);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/** @brief Flag indicating if the stack should end its execution
|
||||||
|
*/
|
||||||
|
volatile int g_end_stack = 0;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int main(int argc,
|
||||||
|
char *arg[]) {
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Wrong number of command line parameters!\n");
|
||||||
|
fprintf(stderr, "Usage: %s [interface index | interface name]\n", arg[0]);
|
||||||
|
fprintf(stderr, "\te.g. ./OpENer \"Ethernet 2\"\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DoublyLinkedListInitialize(&connection_list,
|
||||||
|
CipConnectionObjectListArrayAllocator,
|
||||||
|
CipConnectionObjectListArrayFree);
|
||||||
|
/* Fetch MAC address from the platform. This tests also if the interface
|
||||||
|
* is present. */
|
||||||
|
uint8_t iface_mac[6];
|
||||||
|
if (kEipStatusError == IfaceGetMacAddress(arg[1], iface_mac) ) {
|
||||||
|
printf("Network interface %s not found.\n", arg[1]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for a real device the serial number should be unique per device */
|
||||||
|
SetDeviceSerialNumber(123456789);
|
||||||
|
|
||||||
|
/* unique_connection_id should be sufficiently random or incremented and stored
|
||||||
|
* in non-volatile memory each time the device boots. This is used as the upper
|
||||||
|
* 16 bits of the connection id. Here we use random number approach, first seed
|
||||||
|
* the PRNG to ensure we don't get the same value on every startup.
|
||||||
|
*/
|
||||||
|
srand(time(NULL));
|
||||||
|
EipUint16 unique_connection_id = (EipUint16)rand();
|
||||||
|
|
||||||
|
/* Setup the CIP Layer. All objects are initialized with the default
|
||||||
|
* values for the attribute contents. */
|
||||||
|
CipStackInit(unique_connection_id);
|
||||||
|
|
||||||
|
CipEthernetLinkSetMac(iface_mac);
|
||||||
|
|
||||||
|
/* The current host name is used as a default. This value is kept in the
|
||||||
|
* case NvdataLoad() needs to recreate the TCP/IP object's settings from
|
||||||
|
* the defaults on the first start without a valid TCP/IP configuration
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
GetHostName(&g_tcpip.hostname);
|
||||||
|
|
||||||
|
/* The CIP objects are now created and initialized with their default values.
|
||||||
|
* After that any NV data values are loaded to change the attribute contents
|
||||||
|
* to the stored configuration.
|
||||||
|
*/
|
||||||
|
if (kEipStatusError == NvdataLoad() ) {
|
||||||
|
OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bring up network interface or start DHCP client ... */
|
||||||
|
EipStatus status = BringupNetwork(arg[1],
|
||||||
|
g_tcpip.config_control,
|
||||||
|
&g_tcpip.interface_configuration,
|
||||||
|
&g_tcpip.hostname);
|
||||||
|
if (status < 0) {
|
||||||
|
OPENER_TRACE_ERR("BringUpNetwork() failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register signal handler for SIGINT and SIGTERM that are "supported"
|
||||||
|
* under Windows. */
|
||||||
|
g_end_stack = 0;
|
||||||
|
signal(SIGINT, LeaveStack);
|
||||||
|
signal(SIGTERM, LeaveStack);
|
||||||
|
|
||||||
|
/* Next actions depend on the set network configuration method. */
|
||||||
|
CipDword network_config_method = g_tcpip.config_control &
|
||||||
|
kTcpipCfgCtrlMethodMask;
|
||||||
|
if (kTcpipCfgCtrlStaticIp == network_config_method) {
|
||||||
|
OPENER_TRACE_INFO("Static network configuration done\n");
|
||||||
|
}
|
||||||
|
if (kTcpipCfgCtrlDhcp == network_config_method) {
|
||||||
|
OPENER_TRACE_INFO("DHCP network configuration started\n");
|
||||||
|
/* DHCP should already have been started with BringupNetwork(). Wait
|
||||||
|
* here for IP present (DHCP done) or abort through g_end_stack. */
|
||||||
|
status = IfaceWaitForIp(arg[1], -1, &g_end_stack);
|
||||||
|
OPENER_TRACE_INFO("DHCP wait for interface: status %d, g_end_stack=%d\n",
|
||||||
|
status, g_end_stack);
|
||||||
|
if (kEipStatusOk == status && 0 == g_end_stack) {
|
||||||
|
/* Read IP configuration received via DHCP from interface and store in
|
||||||
|
* the TCP/IP object.*/
|
||||||
|
status = IfaceGetConfiguration(arg[1], &g_tcpip.interface_configuration);
|
||||||
|
if (status < 0) {
|
||||||
|
OPENER_TRACE_WARN("Problems getting interface configuration\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The network initialization of the EIP stack for the NetworkHandler. */
|
||||||
|
if (!g_end_stack && kEipStatusOk == NetworkHandlerInitialize() ) {
|
||||||
|
|
||||||
|
(void) executeEventLoop(NULL);
|
||||||
|
|
||||||
|
/* clean up network state */
|
||||||
|
NetworkHandlerFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close remaining sessions and connections, clean up used data */
|
||||||
|
ShutdownCipStack();
|
||||||
|
|
||||||
|
/* Shut down the network interface now. */
|
||||||
|
(void) ShutdownNetwork(arg[1]);
|
||||||
|
|
||||||
|
if(0 != g_end_stack) {
|
||||||
|
printf("OpENer aborted by signal %d.\n", g_end_stack);
|
||||||
|
return RET_SHOW_SIGNAL + g_end_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LeaveStack(int signal) {
|
||||||
|
if (SIGINT == signal || SIGTERM == signal) {
|
||||||
|
g_end_stack = signal;
|
||||||
|
}
|
||||||
|
OPENER_TRACE_STATE("got signal %d\n", signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD executeEventLoop(LPVOID thread_arg) {
|
||||||
|
/* The event loop. Put other processing you need done continually in here */
|
||||||
|
while (0 == g_end_stack) {
|
||||||
|
if ( kEipStatusOk != NetworkHandlerProcessCyclic() ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,501 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/* ---------- Include files ---------------------------- */
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include "networkconfig.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
|
||||||
|
#include "cipcommon.h"
|
||||||
|
#include "cipstring.h"
|
||||||
|
#include "opener_api.h"
|
||||||
|
#include "opener_error.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
/* ---------- Macro definitions ------------------------ */
|
||||||
|
#define MALLOC(x) malloc(x)
|
||||||
|
#define FREE(x) free(x)
|
||||||
|
|
||||||
|
/* ----- Windows types PRI macros ------------- */
|
||||||
|
#define PRIDW "lu"
|
||||||
|
#define PRIUL "lu"
|
||||||
|
#define PRIuSZT PRIuPTR
|
||||||
|
#define PRIxSZT PRIxPTR
|
||||||
|
|
||||||
|
/* ---------- Local functions implementation ----------- */
|
||||||
|
|
||||||
|
/** @brief Extract interface index from string if string is a number
|
||||||
|
*
|
||||||
|
* @param iface interface identifier string
|
||||||
|
* @return converted number; on failure errno != 0
|
||||||
|
*
|
||||||
|
* Convert a number (interface index) from the supplied interface identifier
|
||||||
|
* string if possible. On conversion success @p errno is set to zero.
|
||||||
|
* On failure @p errno is set to ERANGE or EINVAL.
|
||||||
|
*/
|
||||||
|
static ULONG StrToIfaceIdx(const char *iface) {
|
||||||
|
ULONG iface_idx;
|
||||||
|
char *end;
|
||||||
|
/* The input string is a decimal interface index number or an
|
||||||
|
* interface name. */
|
||||||
|
errno = 0; /* To distinguish success / failure later */
|
||||||
|
iface_idx = strtoul(iface, &end, 10);
|
||||||
|
/* overflow is signaled by (errno == ERANGE) */
|
||||||
|
|
||||||
|
if( (iface == end) || /* No digits were found */
|
||||||
|
('\0' != *end) ) { /* More characters after number */
|
||||||
|
errno = EINVAL; /* Signal conversion failure */
|
||||||
|
}
|
||||||
|
return iface_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Derive the interface index from a given interface alias name string
|
||||||
|
*
|
||||||
|
* @param iface interface identifier string
|
||||||
|
* @param if_index pointer to return the derived interface index
|
||||||
|
* @return Success: NO_ERROR, Failure any matching Windows error code
|
||||||
|
*
|
||||||
|
* The interface alias name supplied via the @p iface parameter is first
|
||||||
|
* converted from a multi-byte string to a wide character string to be able to
|
||||||
|
* call ConvertInterfaceAliasToLuid(). This function returns the Locally
|
||||||
|
* Unique IDentifier (LUID) if an interface matching the alias name is found.
|
||||||
|
* The match doesn't need to be exact. A matching unique prefix of the alias
|
||||||
|
* is sufficient.
|
||||||
|
* The LUID can then be used to retrieve the interface index needed for the
|
||||||
|
* other functions by calling ConvertInterfaceLuidToIndex().
|
||||||
|
*/
|
||||||
|
static DWORD ConvertToIndexFromFakeAlias(const char *iface,
|
||||||
|
PNET_IFINDEX iface_idx) {
|
||||||
|
WCHAR *p_if_alias;
|
||||||
|
NET_LUID if_luid;
|
||||||
|
|
||||||
|
size_t mbtowc_rc = mbstowcs(NULL, iface, 0);
|
||||||
|
if( (size_t) -1 == mbtowc_rc ) { /* Invalid byte sequence encountered */
|
||||||
|
return ERROR_NO_UNICODE_TRANSLATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t wc_cnt = mbtowc_rc + 1U; /* +1U for nul character */
|
||||||
|
p_if_alias = MALLOC(sizeof(WCHAR) * wc_cnt);
|
||||||
|
if(NULL == p_if_alias) {
|
||||||
|
return ERROR_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) mbstowcs(p_if_alias, iface, wc_cnt);
|
||||||
|
DWORD cnv_status = ConvertInterfaceAliasToLuid(p_if_alias, &if_luid);
|
||||||
|
if(NETIO_SUCCESS(cnv_status) ) {
|
||||||
|
cnv_status = ConvertInterfaceLuidToIndex(&if_luid, iface_idx);
|
||||||
|
}
|
||||||
|
FREE(p_if_alias);
|
||||||
|
return cnv_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Determines the interface index number from string input
|
||||||
|
*
|
||||||
|
* @param iface string with interface index number or interface alias name
|
||||||
|
* @param iface_idx address of interface index destination variable
|
||||||
|
* @param EipStatusOk on success, EipStatusError on failure
|
||||||
|
*
|
||||||
|
* This function tries to determine a Windows interface index from the @ref iface
|
||||||
|
* string.
|
||||||
|
*
|
||||||
|
* At first it is tried to evaluate the input as a decimal number if that
|
||||||
|
* succeeds the function returns the converted number and EipStatusOk.
|
||||||
|
*
|
||||||
|
* If the input string is not a number it is assumed to be a Windows interface
|
||||||
|
* alias name. This function then in turn calls ConvertToIndexFromFakeAlias()
|
||||||
|
* to find an interface matching that alias.
|
||||||
|
*/
|
||||||
|
static EipStatus DetermineIfaceIndexByString(const char *iface,
|
||||||
|
PNET_IFINDEX iface_idx) {
|
||||||
|
*iface_idx = StrToIfaceIdx(iface);
|
||||||
|
|
||||||
|
BOOL arg_is_numerical = (0 == errno);
|
||||||
|
if(!arg_is_numerical) {
|
||||||
|
DWORD cnv_status = ConvertToIndexFromFakeAlias(iface, iface_idx);
|
||||||
|
if(NO_ERROR != cnv_status) {
|
||||||
|
char *error_message = GetErrorMessage(cnv_status);
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"ConvertToIndexFromFakeAlias() failed: %" PRIDW " - %s\n",
|
||||||
|
cnv_status,
|
||||||
|
error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Retrieve an IP_ADAPTER_ADDRESSES table in an allocated memory block
|
||||||
|
*
|
||||||
|
* @param flags specify what kind of information to include in the
|
||||||
|
* result
|
||||||
|
* @param pp_addr_table pointer to the location where to put the
|
||||||
|
* PIP_ADAPTER_ADDRESSES pointer
|
||||||
|
*
|
||||||
|
* This function encapsulates the needed memory allocation and retry logic that
|
||||||
|
* is needed to call GetAdaptersAddresses() successfully and to retrieve the
|
||||||
|
* complete IP_ADAPTER_ADDRESSES table.
|
||||||
|
* The @p flags parameter is used to tell the GetAdaptersAddresses() function
|
||||||
|
* which information to include and what information to exclude from the
|
||||||
|
* result.
|
||||||
|
*/
|
||||||
|
EipStatus RetrieveAdapterAddressesTable(ULONG flags,
|
||||||
|
PIP_ADAPTER_ADDRESSES *pp_addr_table) {
|
||||||
|
PIP_ADAPTER_ADDRESSES p_addr_table;
|
||||||
|
ULONG ret_val;
|
||||||
|
/* Start allocating with a guessed minimum size. */
|
||||||
|
ULONG outBufLen = 16 * sizeof(IP_ADAPTER_ADDRESSES);
|
||||||
|
do {
|
||||||
|
p_addr_table = (PIP_ADAPTER_ADDRESSES) MALLOC(outBufLen);
|
||||||
|
if(NULL == p_addr_table) {
|
||||||
|
OPENER_TRACE_ERR(
|
||||||
|
"Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
ret_val = GetAdaptersAddresses(AF_INET,
|
||||||
|
flags,
|
||||||
|
NULL,
|
||||||
|
p_addr_table,
|
||||||
|
&outBufLen);
|
||||||
|
|
||||||
|
if(ERROR_BUFFER_OVERFLOW == ret_val) {
|
||||||
|
FREE(p_addr_table);
|
||||||
|
p_addr_table = NULL;
|
||||||
|
}
|
||||||
|
} while(ERROR_BUFFER_OVERFLOW == ret_val);
|
||||||
|
|
||||||
|
if(NO_ERROR != ret_val || NULL == p_addr_table) {
|
||||||
|
if(NULL != p_addr_table) {
|
||||||
|
FREE(p_addr_table);
|
||||||
|
p_addr_table = NULL;
|
||||||
|
}
|
||||||
|
char *error_message = GetErrorMessage(ret_val);
|
||||||
|
OPENER_TRACE_ERR("GetAdaptersAddresses() failed: %" PRIUL " - %s\n",
|
||||||
|
ret_val, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
*pp_addr_table = p_addr_table;
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Converts a wide-character string to a CIP string.
|
||||||
|
*
|
||||||
|
* @param src Source wide-character string.
|
||||||
|
*
|
||||||
|
* @param dest Destination CIP string.
|
||||||
|
*
|
||||||
|
* @return kEipStatusOk if the conversion was successful;
|
||||||
|
* kEipStatusError if a memory allocation error occurred or
|
||||||
|
* the source string was too large.
|
||||||
|
*/
|
||||||
|
static DWORD WideToCipString(const WCHAR *const src,
|
||||||
|
CipString *const dest) {
|
||||||
|
void *buf = NULL;
|
||||||
|
|
||||||
|
OPENER_ASSERT(src != NULL);
|
||||||
|
OPENER_ASSERT(dest != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate the source string, ensuring two properties:
|
||||||
|
* 1) the source string can be encoded as multi-byte sequence
|
||||||
|
* 2) the number of characters fits in EipUint16, excluding
|
||||||
|
* the nul terminator.
|
||||||
|
*/
|
||||||
|
const size_t num_chars = wcstombs(NULL, src, 0);
|
||||||
|
if( (size_t) -1 == num_chars ) {
|
||||||
|
return ERROR_NO_UNICODE_TRANSLATION;
|
||||||
|
}
|
||||||
|
if(num_chars >= UINT16_MAX) {
|
||||||
|
return ERROR_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New buffer includes nul termination. */
|
||||||
|
const size_t buffer_size = num_chars + 1U;
|
||||||
|
|
||||||
|
if(num_chars) {
|
||||||
|
/* Allocate a new destination buffer. */
|
||||||
|
buf = MALLOC(buffer_size);
|
||||||
|
if(NULL == buf) {
|
||||||
|
return ERROR_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transfer the string to the new buffer. */
|
||||||
|
size_t cnv_chars = wcstombs(buf, src, buffer_size);
|
||||||
|
OPENER_ASSERT(cnv_chars == num_chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release the any previous string content. */
|
||||||
|
ClearCipString(dest);
|
||||||
|
|
||||||
|
/* Transfer the new content to the destination. */
|
||||||
|
dest->length = num_chars;
|
||||||
|
dest->string = buf;
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Extract IPv4 IP address from SOCKET_ADDRESS structure as CipUdint
|
||||||
|
*
|
||||||
|
* @param socket_address pointer to a Windows SOCKET_ADDRESS structure
|
||||||
|
* @return IPv4 address taken from @p socket_address
|
||||||
|
*/
|
||||||
|
static CipUdint GetIpFromSocketAddress(const SOCKET_ADDRESS *socket_address) {
|
||||||
|
SOCKADDR_IN *sin = ( (SOCKADDR_IN *) socket_address->lpSockaddr );
|
||||||
|
return sin->sin_addr.S_un.S_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Public functions implementation ---------- */
|
||||||
|
|
||||||
|
/* For Doxygen descriptions see opener_api.h. */
|
||||||
|
EipStatus IfaceGetMacAddress(const char *iface,
|
||||||
|
uint8_t *physical_address) {
|
||||||
|
ULONG iface_idx;
|
||||||
|
|
||||||
|
if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx) ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select what to include in or exclude from the adapter addresses table. */
|
||||||
|
const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST |
|
||||||
|
GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME;
|
||||||
|
PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
|
||||||
|
if(kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table) ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now search the right interface in the adapter addresses table. */
|
||||||
|
PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
|
||||||
|
while(NULL != p_addr_entry) {
|
||||||
|
if(iface_idx == p_addr_entry->IfIndex) {
|
||||||
|
/* Get MAC address from matched interface */
|
||||||
|
OPENER_TRACE_INFO("MAC address: %02" PRIX8 "-%02" PRIX8 "-%02" PRIX8
|
||||||
|
"-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "\n",
|
||||||
|
p_addr_entry->PhysicalAddress[0],
|
||||||
|
p_addr_entry->PhysicalAddress[1],
|
||||||
|
p_addr_entry->PhysicalAddress[2],
|
||||||
|
p_addr_entry->PhysicalAddress[3],
|
||||||
|
p_addr_entry->PhysicalAddress[4],
|
||||||
|
p_addr_entry->PhysicalAddress[5]);
|
||||||
|
memcpy_s(physical_address,
|
||||||
|
6,
|
||||||
|
p_addr_entry->PhysicalAddress,
|
||||||
|
p_addr_entry->PhysicalAddressLength);
|
||||||
|
break; /* leave search after iface_idx match */
|
||||||
|
}
|
||||||
|
p_addr_entry = p_addr_entry->Next;
|
||||||
|
}
|
||||||
|
FREE(p_addr_table);
|
||||||
|
|
||||||
|
/* Return success if we searched the table and had a match. */
|
||||||
|
return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Doxygen descriptions see opener_api.h. */
|
||||||
|
EipStatus IfaceGetConfiguration(const char *iface,
|
||||||
|
CipTcpIpInterfaceConfiguration *iface_cfg) {
|
||||||
|
ULONG iface_idx;
|
||||||
|
|
||||||
|
if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx) ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select what to include in or exclude from the adapter addresses table. */
|
||||||
|
const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
||||||
|
GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX;
|
||||||
|
PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
|
||||||
|
if(kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table) ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipTcpIpInterfaceConfiguration local_cfg;
|
||||||
|
memset(&local_cfg, 0x00, sizeof local_cfg);
|
||||||
|
|
||||||
|
/* Now search the right interface in the adapter addresses table. */
|
||||||
|
PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
|
||||||
|
while(NULL != p_addr_entry) {
|
||||||
|
if(iface_idx == p_addr_entry->IfIndex) {
|
||||||
|
|
||||||
|
if(IfOperStatusUp != p_addr_entry->OperStatus) {
|
||||||
|
OPENER_TRACE_ERR("IfaceGetConfiguration(): Interface '%s' is not up.\n",
|
||||||
|
iface);
|
||||||
|
FREE(p_addr_table);
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
/* Extract ip_addr, netmask, gateway, nameserver, nameserver 2, domain ... */
|
||||||
|
{
|
||||||
|
PIP_ADAPTER_UNICAST_ADDRESS pUnicast =
|
||||||
|
p_addr_entry->FirstUnicastAddress;
|
||||||
|
if(NULL != pUnicast) {
|
||||||
|
local_cfg.ip_address = GetIpFromSocketAddress(&pUnicast->Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PIP_ADAPTER_PREFIX pPrefix = p_addr_entry->FirstPrefix;
|
||||||
|
if(NULL != pPrefix) {
|
||||||
|
local_cfg.network_mask =
|
||||||
|
htonl(0xffffffff << (32U - pPrefix->PrefixLength) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PIP_ADAPTER_GATEWAY_ADDRESS pGateway =
|
||||||
|
p_addr_entry->FirstGatewayAddress;
|
||||||
|
if(NULL != pGateway) {
|
||||||
|
local_cfg.gateway = GetIpFromSocketAddress(&pGateway->Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer =
|
||||||
|
p_addr_entry->FirstDnsServerAddress;
|
||||||
|
if(NULL != pDnServer) {
|
||||||
|
local_cfg.name_server = GetIpFromSocketAddress(&pDnServer->Address);
|
||||||
|
pDnServer = pDnServer->Next;
|
||||||
|
if(NULL != pDnServer) {
|
||||||
|
local_cfg.name_server_2 =
|
||||||
|
GetIpFromSocketAddress(&pDnServer->Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DWORD ret_val = WideToCipString(p_addr_entry->DnsSuffix,
|
||||||
|
&local_cfg.domain_name);
|
||||||
|
if(NO_ERROR != ret_val) {
|
||||||
|
char *error_message = GetErrorMessage(ret_val);
|
||||||
|
OPENER_TRACE_ERR("WideToCipString(DnsSuffix) failed with error: %"
|
||||||
|
PRIDW " - %s\n", ret_val, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
FREE(p_addr_table);
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
break; /* leave search after iface_idx match */
|
||||||
|
}
|
||||||
|
p_addr_entry = p_addr_entry->Next;
|
||||||
|
}
|
||||||
|
FREE(p_addr_table);
|
||||||
|
|
||||||
|
if(p_addr_entry) {
|
||||||
|
/* Free first and then making a shallow copy of local_cfg.domain_name is
|
||||||
|
* ok, because local_cfg goes out of scope on return. */
|
||||||
|
ClearCipString(&iface_cfg->domain_name);
|
||||||
|
*iface_cfg = local_cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return success if we searched the table and had a match. */
|
||||||
|
return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Doxygen descriptions see opener_api.h. */
|
||||||
|
EipStatus IfaceWaitForIp(const char *const iface,
|
||||||
|
int timeout,
|
||||||
|
volatile int *const abort_wait) {
|
||||||
|
ULONG iface_idx;
|
||||||
|
|
||||||
|
if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx) ) {
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PMIB_IPADDRTABLE pmib_ipaddr_table = NULL;
|
||||||
|
ULONG addr_table_sz = 0;
|
||||||
|
uint32_t ipaddr;
|
||||||
|
|
||||||
|
#define WAIT_CYCLE_MS 100
|
||||||
|
/* Calculate cycles of SleepEx(WAIT_CYCLE_MS) needed. */
|
||||||
|
timeout *= (1000 / WAIT_CYCLE_MS);
|
||||||
|
do {
|
||||||
|
DWORD dw_ret;
|
||||||
|
ipaddr = 0U;
|
||||||
|
|
||||||
|
do {
|
||||||
|
dw_ret = GetIpAddrTable(pmib_ipaddr_table, &addr_table_sz, FALSE);
|
||||||
|
if(ERROR_INSUFFICIENT_BUFFER == dw_ret) {
|
||||||
|
if(pmib_ipaddr_table) {
|
||||||
|
FREE(pmib_ipaddr_table);
|
||||||
|
}
|
||||||
|
pmib_ipaddr_table = MALLOC(addr_table_sz);
|
||||||
|
if(NULL == pmib_ipaddr_table) {
|
||||||
|
OPENER_TRACE_ERR("Memory allocation failed for "
|
||||||
|
"MIB_IPADDRTABLE struct\n");
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(ERROR_INSUFFICIENT_BUFFER == dw_ret);
|
||||||
|
if(NO_ERROR != dw_ret) {
|
||||||
|
char *error_message = GetErrorMessage(dw_ret);
|
||||||
|
OPENER_TRACE_ERR("%s() failed with error: %" PRIDW " - %s\n",
|
||||||
|
__func__, dw_ret, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
return kEipStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search entry matching the interface index and determine IP address. */
|
||||||
|
for(int i = 0; i < (int) pmib_ipaddr_table->dwNumEntries; i++) {
|
||||||
|
if(pmib_ipaddr_table->table[i].dwIndex == iface_idx) {
|
||||||
|
if(0 ==
|
||||||
|
(pmib_ipaddr_table->table[i].wType &
|
||||||
|
(MIB_IPADDR_DELETED | MIB_IPADDR_DISCONNECTED |
|
||||||
|
MIB_IPADDR_TRANSIENT) ) ) {
|
||||||
|
ipaddr = pmib_ipaddr_table->table[i].dwAddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timeout > 0) {
|
||||||
|
--timeout;
|
||||||
|
}
|
||||||
|
} while( (0 == ipaddr) && (0 != timeout) && (0 == *abort_wait) &&
|
||||||
|
(0 == SleepEx(WAIT_CYCLE_MS, FALSE) ) );
|
||||||
|
|
||||||
|
OPENER_TRACE_INFO("IP=%08" PRIx32 ", timeout=%d\n",
|
||||||
|
(uint32_t)ntohl(ipaddr),
|
||||||
|
timeout);
|
||||||
|
if(pmib_ipaddr_table) {
|
||||||
|
FREE(pmib_ipaddr_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HOST_NAME_MAX 256 /* Should be long enough according rfc1132. */
|
||||||
|
void GetHostName(CipString *hostname) {
|
||||||
|
CipWord wVersionRequested;
|
||||||
|
WSADATA wsaData;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
|
||||||
|
wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
|
||||||
|
err = WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
if(err != 0) {
|
||||||
|
/* Tell the user that we could not find a usable Winsock DLL. */
|
||||||
|
char *error_message = GetErrorMessage(err);
|
||||||
|
printf("WSAStartup failed with error: %d - %s\n", err, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name_buf[HOST_NAME_MAX] = "";
|
||||||
|
err = gethostname(name_buf, sizeof(name_buf) );
|
||||||
|
if(0 != err) {
|
||||||
|
int error_code = GetSocketErrorNumber();
|
||||||
|
char *error_message = GetErrorMessage(error_code);
|
||||||
|
printf("gethostname() failed, %d - %s\n", error_code, error_message);
|
||||||
|
FreeErrorMessage(error_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetCipStringByCstr(hostname, name_buf);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
#include "networkhandler.h"
|
||||||
|
|
||||||
|
#include "generic_networkhandler.h"
|
||||||
|
|
||||||
|
MicroSeconds getMicroSeconds() {
|
||||||
|
LARGE_INTEGER performance_counter;
|
||||||
|
LARGE_INTEGER performance_frequency;
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&performance_counter);
|
||||||
|
QueryPerformanceFrequency(&performance_frequency);
|
||||||
|
|
||||||
|
return (MicroSeconds) (performance_counter.QuadPart * 1000000LL
|
||||||
|
/ performance_frequency.QuadPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
MilliSeconds GetMilliSeconds(void) {
|
||||||
|
return (MilliSeconds) (getMicroSeconds() / 1000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
EipStatus NetworkHandlerInitializePlatform(void) {
|
||||||
|
WSADATA wsaData;
|
||||||
|
const WORD wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
|
||||||
|
return kEipStatusOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutdownSocketPlatform(int socket_handle) {
|
||||||
|
#warning \
|
||||||
|
"Untested. Is a shutdown() needed under Windows like for the POSIX port?"
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseSocketPlatform(int socket_handle) {
|
||||||
|
closesocket(socket_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SetSocketToNonBlocking(int socket_handle) {
|
||||||
|
u_long iMode = 1;
|
||||||
|
return ioctlsocket(socket_handle, FIONBIO, &iMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SetQosOnSocket(int socket,
|
||||||
|
CipUsint qos_value) {
|
||||||
|
/* Quote from Vol. 2, Section 5-7.4.2 DSCP Value Attributes:
|
||||||
|
* Note that the DSCP value, if placed directly in the ToS field
|
||||||
|
* in the IP header, must be shifted left 2 bits. */
|
||||||
|
DWORD set_tos = qos_value << 2;
|
||||||
|
return setsockopt(socket,
|
||||||
|
IPPROTO_IP,
|
||||||
|
IP_TOS,
|
||||||
|
(char *)&set_tos,
|
||||||
|
sizeof(set_tos) );
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/** @file opener_error.c
|
||||||
|
* @author Martin Melik Merkumians
|
||||||
|
* @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "opener_error.h"
|
||||||
|
|
||||||
|
int GetSocketErrorNumber(void) {
|
||||||
|
return WSAGetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
char *GetErrorMessage(int error_number) {
|
||||||
|
char *error_message = NULL;
|
||||||
|
FormatMessageA(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
error_number,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPSTR)&error_message,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
return error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeErrorMessage(char *error_message) {
|
||||||
|
LocalFree(error_message);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2009, Rockwell Automation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#######################################
|
||||||
|
# Add common includes #
|
||||||
|
#######################################
|
||||||
|
opener_common_includes()
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add platform specific things #
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
opener_platform_support("INCLUDES")
|
||||||
|
|
||||||
|
if (OPENER_IS_DLR_DEVICE)
|
||||||
|
set( SAMPLE_APP_DLR_ONLY_SRC ethlinkcbs.c )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(SAMPLE_APP sampleapplication.c ${SAMPLE_APP_DLR_ONLY_SRC})
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue