This commit is contained in:
Martin Troxler
2009-11-19 14:44:57 +01:00
41 changed files with 9139 additions and 229 deletions

92
.hgignore Normal file
View File

@@ -0,0 +1,92 @@
syntax: glob
**.ko
**.ko.cmd
**.lo
**.mod.c
**.o
**.o.cmd
**.swp
.tmp_versions
ChangeLog
Doxyfile
Kbuild
Makefile
Makefile.in
Module.markers
Module.symvers
TAGS
aclocal.m4
autoconf
autom4te.cache
config.h
config.h.in
config.log
config.status
configure
devices/Kbuild
devices/Makefile
devices/Makefile.in
devices/TAGS
devices/e1000/Kbuild
devices/e1000/Makefile
devices/e1000/Makefile.in
devices/modules.order
examples/Kbuild
examples/Makefile
examples/Makefile.in
examples/TAGS
examples/dc_rtai/Kbuild
examples/dc_rtai/Makefile
examples/dc_rtai/Makefile.in
examples/dc_user/.deps
examples/dc_user/.libs
examples/dc_user/Makefile
examples/dc_user/Makefile.in
examples/dc_user/TAGS
examples/dc_user/ec_dc_user_example
examples/mini/Kbuild
examples/mini/Makefile
examples/mini/Makefile.in
examples/mini/modules.order
examples/modules.order
examples/rtai/Kbuild
examples/rtai/Makefile
examples/rtai/Makefile.in
examples/user/.deps
examples/user/.libs
examples/user/Makefile
examples/user/Makefile.in
examples/user/TAGS
examples/user/ec_user_example
include/Makefile
include/Makefile.in
include/TAGS
lib/.deps
lib/.libs
lib/Makefile
lib/Makefile.in
lib/TAGS
lib/libethercat.la
libtool
m4/Makefile
m4/Makefile.in
m4/libtool.m4
m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
modules.order
script/Makefile
script/Makefile.in
script/init.d/Makefile
script/init.d/Makefile.in
script/init.d/ethercat
script/sysconfig/Makefile
script/sysconfig/Makefile.in
stamp-h1
tool/.deps
tool/Makefile
tool/Makefile.in
tool/TAGS
tool/ethercat

View File

@@ -81,14 +81,13 @@ clean-local:
@rm -f Modules.symvers
mydist:
svn2cl $(srcdir)
@SVNREV=`svnversion $(srcdir)` && \
$(MAKE) dist-bzip2 \
distdir=$(PACKAGE)-$(VERSION)-r$${SVNREV}
hg log --style=changelog $(srcdir) > ChangeLog
@REV=`hg id -i $(srcdir)` && \
$(MAKE) dist-bzip2 distdir=$(PACKAGE)-$(VERSION)-$${REV}
dist-hook:
if which svnversion >/dev/null 2>&1; then \
svnversion $(srcdir) 2>/dev/null >$(distdir)/svnrevision; \
if which hg >/dev/null 2>&1; then \
hg id -i $(srcdir) 2>/dev/null >$(distdir)/revision; \
fi
mrproper: clean cleandoc

7
NEWS
View File

@@ -26,7 +26,7 @@ Changes since 1.4.0:
* Added 8139too driver for kernels 2.6.25 (F. Pose), 2.6.26 (M. Luescher),
2.6.27, 2.6.28 and 2.6.29 (M. Goetze).
* Added e1000 driver for 2.6.26 (M. Luescher).
* Added r8169 driver for 2.6.24 and 2.6.28.
* Added r8169 driver for 2.6.24, 2.6.28 and 2.6.29.
* Debug interfaces are created with the Ethernet addresses of the attached
physical device.
* Improved error case return codes of many functions.
@@ -45,6 +45,8 @@ Changes since 1.4.0:
methods to let an application transfer SDOs before activating the master
(thanks to Stefan Weiser).
* Fixed SDO upload segment response (thanks to Christoph Peter).
* Fixed SDO upload segment response for 10 bytes mailbox length (thanks to
Joerg Mohre).
* SDO entry access rights are shown in 'ethercat sdos'.
* Added 64-bit data access macros to application header.
* Added debug level for all masters as a module parameter. Thanks to Erwin
@@ -59,7 +61,10 @@ Changes since 1.4.0:
* Module symbol versions file for ec_master.ko is installed to
prefix/modules/ec_master.symvers.
* Added 'ethercat eoe' command to display Ethernet over EtherCAT statistics.
* Added 'ethercat cstruct' command to output PDO information in C language.
* Significantly improved EoE bandwidth by running EoE processing in a kthread.
* Switched version control from Subversion to Mercurial.
* Implemented CompleteAccess for SDO downloads.
Changes in 1.4.0:

9
TODO
View File

@@ -20,7 +20,6 @@ Version 1.5.0:
"System Time" register instead of using the application time.
- Check if register 0x0980 is working, to avoid clearing it when
configuring.
- Create an interface to query the System Time Difference registers.
* Remove byte-swapping functions from user space.
* EoE:
- Only execute one EoE handler per cycle.
@@ -47,6 +46,14 @@ Version 1.5.0:
* Document ec_fsm_foe members.
* Test KBUILD_EXTRA_SYMBOLS.
* Remove default buffer size in SDO upload.
* Check for Enable SDO Complete Access flag.
* Implement CompleteAccess for command-line tool.
* Implement CompleteAccess for SDO uploads.
* Implement identifier parameter for cstruct command.
* Implement sync delimiter for cstruct command.
* Change SDO index at runtime for SDO request.
* Implement ecrt_slave_config_request_state().
* Output skipped datagrams again.
Future issues:

View File

@@ -31,24 +31,24 @@
#
#------------------------------------------------------------------------------
REV := $(shell if test -s $(src)/../svnrevision; then \
cat $(src)/../svnrevision; \
REV := $(shell if test -s $(src)/../revision; then \
cat $(src)/../revision; \
else \
svnversion $(src)/.. 2>/dev/null || echo "unknown"; \
hg id -i $(src)/.. 2>/dev/null || echo "unknown"; \
fi)
ifeq (@ENABLE_8139TOO@,1)
EC_8139TOO_OBJ := 8139too-@KERNEL_8139TOO@-ethercat.o
obj-m += ec_8139too.o
ec_8139too-objs := $(EC_8139TOO_OBJ)
CFLAGS_$(EC_8139TOO_OBJ) = -DSVNREV=$(REV)
CFLAGS_$(EC_8139TOO_OBJ) = -DREV=$(REV)
endif
ifeq (@ENABLE_E100@,1)
EC_E100_OBJ := e100-@KERNEL_E100@-ethercat.o
obj-m += ec_e100.o
ec_e100-objs := $(EC_E100_OBJ)
CFLAGS_$(EC_E100_OBJ) = -DSVNREV=$(REV)
CFLAGS_$(EC_E100_OBJ) = -DREV=$(REV)
endif
ifeq (@ENABLE_E1000@,1)
@@ -59,7 +59,7 @@ ifeq (@ENABLE_R8169@,1)
EC_R8169_OBJ := r8169-@KERNEL_R8169@-ethercat.o
obj-m += ec_r8169.o
ec_r8169-objs := $(EC_R8169_OBJ)
CFLAGS_$(EC_R8169_OBJ) = -DSVNREV=$(REV)
CFLAGS_$(EC_R8169_OBJ) = -DREV=$(REV)
endif
KBUILD_EXTRA_SYMBOLS := \

View File

@@ -71,7 +71,9 @@ noinst_HEADERS = \
r8169-2.6.24-ethercat.c \
r8169-2.6.24-orig.c \
r8169-2.6.28-ethercat.c \
r8169-2.6.28-orig.c
r8169-2.6.28-orig.c \
r8169-2.6.29-ethercat.c \
r8169-2.6.29-orig.c
EXTRA_DIST = \
Kbuild.in

View File

@@ -33,10 +33,10 @@
TOPDIR := $(src)/../..
REV := $(shell if test -s $(TOPDIR)/svnrevision; then \
cat $(TOPDIR)/svnrevision; \
REV := $(shell if test -s $(TOPDIR)/revision; then \
cat $(TOPDIR)/revision; \
else \
svnversion $(TOPDIR) 2>/dev/null || echo "unknown"; \
hg id -i $(TOPDIR) 2>/dev/null || echo "unknown"; \
fi)
ifeq (@ENABLE_E1000@,1)
@@ -47,7 +47,7 @@ ifeq (@ENABLE_E1000@,1)
e1000_param-@KERNEL_E1000@-ethercat.o
obj-m += ec_e1000.o
ec_e1000-objs := $(EC_E1000_OBJ)
CFLAGS_e1000_main-@KERNEL_E1000@-ethercat.o = -DSVNREV=$(REV)
CFLAGS_e1000_main-@KERNEL_E1000@-ethercat.o = -DREV=$(REV)
endif
KBUILD_EXTRA_SYMBOLS := \

File diff suppressed because it is too large Load Diff

3873
devices/r8169-2.6.29-orig.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@ LATEX_OPTIONS := -file-line-error-style
COMMANDS := \
alias \
config \
cstruct \
data \
debug \
domains \

View File

@@ -18,6 +18,7 @@
\usepackage{listings}
\usepackage{svn}
\usepackage{SIunits}
\usepackage{amsmath} % for \text{}
\usepackage{hyperref}
\hypersetup{pdfpagelabels,plainpages=false}
@@ -227,6 +228,20 @@ EtherCAT functionality (see chap.~\ref{chap:api}).
\end{itemize}
\item Distributed Clocks support (see sec.~\ref{sec:dc}).
\begin{itemize}
\item Configuration of the slave's DC parameters through the application
interface.
\item Synchronization (offset and drift compensation) of the distributed
slave clocks to the reference clock.
\item Optional synchronization of the reference clock to the master clock.
\end{itemize}
\item CANopen over EtherCAT (CoE)
\begin{itemize}
@@ -268,7 +283,7 @@ EtherCAT functionality (see chap.~\ref{chap:api}).
\end{itemize}
\item Userspace command-line-tool ``ethercat`` (see sec.~\ref{sec:tool})
\item Userspace command-line-tool ``ethercat'' (see sec.~\ref{sec:tool})
\begin{itemize}
@@ -283,7 +298,7 @@ EtherCAT functionality (see chap.~\ref{chap:api}).
\item Access to slave registers.
\item Slave SII (EEPROM) access.
\item Controlling application-layer states.
\item Generation of slave description XML from existing slaves.
\item Generation of slave description XML and C-code from existing slaves.
\end{itemize}
@@ -773,10 +788,10 @@ kernelspace and uses RTAI functionality, ordinary kernel semaphores would not
be sufficient. For that, an important design decision was made: The
application that reserved a master must have the total control, therefore it
has to take responsibility for providing the appropriate locking mechanisms.
If another instance wants to access the master, it has to request the master
lock by callbacks, that have to be set by the application. Moreover the
application can deny access to the master if it considers it to be awkward at
the moment.
If another instance wants to access the master, it has to request the bus
access via callbacks, that have to be provided by the application. Moreover
the application can deny access to the master if it considers it to be awkward
at the moment.
\begin{figure}[htbp]
\centering
@@ -788,10 +803,126 @@ the moment.
Figure~\ref{fig:locks} exemplary shows, how two processes share one master:
The application's cyclic task uses the master for process data exchange, while
the master-internal EoE process uses it to communicate with EoE-capable
slaves. Both have to acquire the master lock before access: The application
task can access the lock natively, while the EoE process has to use the
callbacks. See the application interface documentation (chap.~\ref{chap:api})
for how to use the locking callbacks.
slaves. Both have to access the bus from time to time, but the EoE process
does this by ``asking'' the application to do the bus access for it. In this
way, the application can use the appropriate locking mechanism to avoid
accessing the bus at the same time. See the application interface
documentation (chap.~\ref{chap:api}) for how to use these callbacks.
%------------------------------------------------------------------------------
\section{Distributed Clocks}
\label{sec:dc}
\index{Distributed Clocks}
From version 1.5, the master supports EtherCAT's ``Distributed Clocks''
feature. It is possible to synchronize the slave clocks on the bus to the
``reference clock'' (which is the local clock of the first slave with DC
support) and to synchronize the reference clock to the ``master clock'' (which
is the local clock of the master). All other clocks on the bus (after the
reference clock) are considered as ``slave clocks'' (see fig.~\ref{fig:dc}).
\begin{figure}[htbp]
\centering
\includegraphics[width=.8\textwidth]{images/dc}
\caption{Distributed Clocks}
\label{fig:dc}
\end{figure}
\paragraph{Local Clocks} Any EtherCAT slave that supports DC has a local clock
register with nanosecond resolution. If the slave is powered, the clock starts
from zero, meaning that when slaves are powered on at different times, their
clocks will have different values. These ``offsets'' have to be compensated by
the distributed clocks mechanism. On the other hand, the clocks do not run
exactly with the same speed, since the used quarts units have a natural
frequency deviation. This deviation is usually very small, but over longer
periods, the error would accumulate and the difference between local clocks
would grow. This clock ``drift'' has also to be compensated by the DC
mechanism.
\paragraph{Application Time} The common time base for the bus has to be
provided by the application. This application time $t_\text{app}$ is used
\begin{enumerate}
\item to configure the slaves' clock offsets (see below),
\item to program the slave's start times for sync pulse generation (see
below).
\item to synchronize the reference clock to the master clock (optional).
\end{enumerate}
\paragraph{Offset Compensation} For the offset compensation, each slave
provides a ``System Time Offset'' register $t_\text{off}$, that is added to
the internal clock value $t_\text{int}$ to get the ``System Time''
$t_\text{sys}$:
\begin{eqnarray}
t_\text{sys} & = & t_\text{int} + t_\text{off} \\
\Rightarrow t_\text{int} & = & t_\text{sys} - t_\text{off} \nonumber
\end{eqnarray}
The master reads the values of both registers to calculate a new system time
offset in a way, that the resulting system time shall match the master's
application time $t_\text{app}$:
\begin{eqnarray}
t_\text{sys} & \stackrel{!}{=} & t_\text{app} \\
\Rightarrow t_\text{int} + t_\text{off} & \stackrel{!}{=} & t_\text{app} \nonumber \\
\Rightarrow t_\text{off} & = & t_\text{app} - t_\text{int} \nonumber \\
\Rightarrow t_\text{off} & = & t_\text{app} - (t_\text{sys} - t_\text{off}) \nonumber \\
\Rightarrow t_\text{off} & = & t_\text{app} - t_\text{sys} + t_\text{off}
\end{eqnarray}
The small time offset error resulting from the different times of reading and
writing the registers will be compensated by the drift compensation.
\paragraph{Drift Compensation} The drift compensation is possible due to a
special mechanism in each DC-capable slave: A write operation to the ``System
time'' register will cause the internal time control loop to compare the
written time (minus the programmed transmission delay, see below) to the
current system time. The calculated time error will be used as an input to the
time controller, that will tune the local clock speed to be a little faster or
slower\footnote{The local slave clock will be incremented either with
\unit{9}{\nano\second}, \unit{10}{\nano\second} or \unit{11}{\nano\second}
every \unit{10}{\nano\second}.}, according to the sign of the error.
\paragraph{Transmission Delays} The Ethernet frame needs a small amount of
time to get from slave to slave. The resulting transmission delay times
accumulate on the bus and can reach microsecond magnitude and thus have to be
considered during the drift compensation. EtherCAT slaves supporting DC
provide a mechanism to measure the transmission delays: For each of the four
slave ports there is a receive time register. A write operation to the receive
time register of port 0 starts the measuring and the current system time is
latched and stored in a receive time register once the frame is received on
the corresponding port. The master can read out the relative receive times,
then calculate time delays between the slaves (using its knowledge of the bus
topology), and finally calculate the time delays from the reference clock to
each slave. These values are programmed into the slaves' transmission delay
registers. In this way, the drift compensation can reach nanosecond synchrony.
\paragraph{Checking Synchrony} DC-capable slaves provide the 32-bit ``System
time difference'' register at address \lstinline+0x092c+, where the system
time difference of the last drift compensation is stored in nanosecond
resolution and in sign-and-magnitude coding\footnote{This allows
broadcast-reading all system time difference registers on the bus to get an
upper approximation}. To check for bus synchrony, the system time difference
registers can also be cyclically read via the command-line-tool (see
sec.~\ref{sec:regaccess}):
\begin{lstlisting}
$ `\textbf{watch -n0 "ethercat reg\_read -p4 -tint32 0x92c"}`
\end{lstlisting}
\paragraph{Sync Signals} Synchronous clocks are only the prerequisite for
synchronous events on the bus. Each slave with DC support provides two ``sync
signals'', that can be programmed to create events, that will for example
cause the slave application to latch its inputs on a certain time. A sync
event can either be generated once or cyclically, depending on what makes
sense for the slave application. Programming the sync signals is a matter of
setting the so-called ``AssignActivate'' word and the sync signals' cycle- and
shift times. The AssignActivate word is slave-specific and has to be taken
from the XML slave description (\lstinline+Device+ $\rightarrow$
\lstinline+Dc+), where also typical sync signal configurations ``OpModes'' can
be found.
%------------------------------------------------------------------------------
@@ -2088,6 +2219,13 @@ sec.~\ref{sec:autonode} for how to install and configure it.
%------------------------------------------------------------------------------
\subsection{Output PDO information in C Language}
\label{sec:ethercat-cstruct}
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_cstruct}
%------------------------------------------------------------------------------
\subsection{Displaying Process Data}
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_data}
@@ -2148,6 +2286,7 @@ sec.~\ref{sec:autonode} for how to install and configure it.
%------------------------------------------------------------------------------
\subsection{Register Access}
\label{sec:regaccess}
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_reg_read}
@@ -2315,10 +2454,10 @@ character device (see chap.~\ref{chap:arch}, fig.~\ref{fig:arch} and
sec.~\ref{sec:cdev}).
The function calls of the kernel API are mapped to the userspace via an
\lstinline+ioctl()+ interface. Each function has its own \lstinline+ioctl()+
call. The kernel part of the interface calls the according API functions
directly, what results in a minimum additional delay (see
sec.~\ref{sec:usertiming}).
\lstinline+ioctl()+ interface. The userspace API functions share a set of
generic \lstinline+ioctl()+ calls. The kernel part of the interface calls the
according API functions directly, what results in a minimum additional delay
(see sec.~\ref{sec:usertiming}).
For performance reasons, the actual domain process data (see
sec.~\ref{sec:processdata}) are not copied between kernel and user memory on

View File

@@ -8,6 +8,7 @@ FIGS := \
app-config.fig \
architecture.fig \
attach.fig \
dc.fig \
fmmus.fig \
fsm-coedown.fig \
fsm-eoe.fig \

136
documentation/images/dc.fig Normal file
View File

@@ -0,0 +1,136 @@
#FIG 3.2
Portrait
Center
Metric
A4
100.00
Single
-2
1200 2
6 1215 1035 1755 1575
1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 1485 1305 225 225 1485 1305 1710 1305
1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 1485 1305 162 162 1485 1305 1647 1305
1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 1485 1305 23 23 1485 1305 1508 1305
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1485 1080 1485 1530
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1598 1110 1372 1500
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1680 1193 1290 1417
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1710 1305 1260 1305
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1680 1418 1290 1193
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
1598 1500 1373 1110
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
1485 1305 1530 1125
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
1485 1305 1575 1260
-6
6 4950 1890 5490 2430
1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 5220 2160 225 225 5220 2160 5445 2160
1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 5220 2160 162 162 5220 2160 5382 2160
1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 5220 2160 23 23 5220 2160 5243 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5220 1935 5220 2385
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5333 1965 5107 2355
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5415 2048 5025 2272
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5445 2160 4995 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5415 2273 5025 2048
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
5333 2355 5108 1965
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
5220 2160 5265 1980
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
5220 2160 5310 2115
-6
6 6030 1305 7380 2475
6 6795 1890 7335 2430
1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 7065 2160 225 225 7065 2160 7290 2160
1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 7065 2160 162 162 7065 2160 7227 2160
1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 7065 2160 23 23 7065 2160 7088 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7065 1935 7065 2385
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7178 1965 6952 2355
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7260 2048 6870 2272
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7290 2160 6840 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7260 2273 6870 2048
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
7178 2355 6953 1965
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
7065 2160 7110 1980
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
7065 2160 7155 2115
-6
2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5
7380 2475 6030 2475 6030 1305 7380 1305 7380 2475
4 1 0 49 -1 16 16 0.0000 4 180 810 6705 1665 Slave 2\001
-6
6 7875 1305 9225 2475
6 8640 1890 9180 2430
1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 8910 2160 225 225 8910 2160 9135 2160
1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 8910 2160 162 162 8910 2160 9072 2160
1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 8910 2160 23 23 8910 2160 8933 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
8910 1935 8910 2385
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
9023 1965 8797 2355
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
9105 2048 8715 2272
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
9135 2160 8685 2160
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
9105 2273 8715 2048
2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2
9023 2355 8798 1965
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
8910 2160 8955 1980
2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2
8910 2160 9000 2115
-6
2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5
9225 2475 7875 2475 7875 1305 9225 1305 9225 2475
4 1 0 49 -1 16 16 0.0000 4 180 810 8550 1665 Slave n\001
-6
2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5
1800 1620 450 1620 450 450 1800 450 1800 1620
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
1800 1080 2340 1935
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
3690 1935 4185 1935
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
1125 1935 1350 1530
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
4860 2880 5094 2391
2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5
5535 2475 4185 2475 4185 1305 5535 1305 5535 2475
2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5
3690 2475 2340 2475 2340 1305 3690 1305 3690 2475
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
5535 1935 6030 1935
2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
7380 1935 7875 1935
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8370 2880 8730 2385
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
7470 2880 7224 2391
4 1 0 49 -1 16 16 0.0000 4 180 735 1125 810 Master\001
4 1 0 50 -1 16 16 0.0000 4 180 1800 4860 3105 Reference Clock\001
4 1 0 50 -1 16 16 0.0000 4 180 1410 1125 2250 Master Clock\001
4 1 0 49 -1 16 16 0.0000 4 180 810 4860 1665 Slave 1\001
4 1 0 49 -1 16 16 0.0000 4 180 810 3015 1665 Slave 0\001
4 1 0 49 -1 16 10 0.0000 4 150 615 3015 2205 (No DC)\001
4 1 0 50 -1 16 16 0.0000 4 180 1395 7920 3105 Slave Clocks\001

View File

@@ -238,24 +238,28 @@ void run(long data)
/*****************************************************************************/
void send_callback(ec_master_t *master)
void send_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_send_ext(master);
ecrt_master_send_ext(m);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
void receive_callback(ec_master_t *master)
void receive_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
ecrt_master_receive(m);
rt_sem_signal(&master_sem);
}
}
@@ -281,7 +285,7 @@ int __init init_mod(void)
goto out_return;
}
ecrt_master_callbacks(master, send_callback, receive_callback);
ecrt_master_callbacks(master, send_callback, receive_callback, master);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@@ -354,19 +354,21 @@ void cyclic_task(unsigned long data)
/*****************************************************************************/
void send_callback(ec_master_t *master)
void send_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
down(&master_sem);
ecrt_master_send_ext(master);
ecrt_master_send_ext(m);
up(&master_sem);
}
/*****************************************************************************/
void receive_callback(ec_master_t *master)
void receive_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
down(&master_sem);
ecrt_master_receive(master);
ecrt_master_receive(m);
up(&master_sem);
}
@@ -392,8 +394,7 @@ int __init init_mini_module(void)
}
sema_init(&master_sem, 1);
ecrt_master_callbacks(master, send_callback, receive_callback);
ecrt_master_callbacks(master, send_callback, receive_callback, master);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@@ -243,24 +243,28 @@ void run(long data)
/*****************************************************************************/
void send_callback(ec_master_t *master)
void send_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_send_ext(master);
ecrt_master_send_ext(m);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
void receive_callback(ec_master_t *master)
void receive_callback(void *cb_data)
{
ec_master_t *m = (ec_master_t *) cb_data;
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
ecrt_master_receive(m);
rt_sem_signal(&master_sem);
}
}
@@ -288,7 +292,7 @@ int __init init_mod(void)
goto out_return;
}
ecrt_master_callbacks(master, send_callback, receive_callback);
ecrt_master_callbacks(master, send_callback, receive_callback, master);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@@ -57,7 +57,7 @@
/** Master version string
*/
#define EC_MASTER_VERSION VERSION " r" EC_STR(SVNREV)
#define EC_MASTER_VERSION VERSION " " EC_STR(REV)
/*****************************************************************************/

View File

@@ -45,7 +45,9 @@
* ecrt_slave_config_dc() to configure a slave for cyclic operation, and
* ecrt_master_application_time(), ecrt_master_sync_reference_clock() and
* ecrt_master_sync_slave_clocks() for offset and drift compensation. The
* EC_TIMEVAL2NANO() macro can be used for epoch time conversion.
* EC_TIMEVAL2NANO() macro can be used for epoch time conversion, while the
* ecrt_master_sync_monitor_queue() and ecrt_master_sync_monitor_process()
* methods can be used to monitor the synchrony.
* - Improved the callback mechanism. ecrt_master_callbacks() now takes two
* callback functions for sending and receiving datagrams.
* ecrt_master_send_ext() is used to execute the sending of non-application
@@ -53,6 +55,9 @@
* - Added watchdog configuration (method ecrt_slave_config_watchdog(),
* #ec_watchdog_mode_t, \a watchdog_mode parameter in ec_sync_info_t and
* ecrt_slave_config_sync_manager()).
* - Added ecrt_slave_config_complete_sdo() method to download an SDO during
* configuration via CompleteAccess.
* - Added ecrt_master_deactivate() to remove the bus configuration.
* - Added ecrt_open_master() and ecrt_master_reserve() separation for
* userspace.
* - Added bus information interface (methods ecrt_master(),
@@ -63,7 +68,7 @@
* methods to let an application transfer SDOs before activating the master.
* - Changed the meaning of the negative return values of
* ecrt_slave_config_reg_pdo_entry() and ecrt_slave_config_sdo*().
* - Imlemented the Vendor-specific over EtherCAT mailbox protocol. See
* - Implemented the Vendor-specific over EtherCAT mailbox protocol. See
* ecrt_slave_config_create_voe_handler().
* - Renamed ec_sdo_request_state_t to #ec_request_state_t, because it is also
* used by VoE handlers.
@@ -129,7 +134,7 @@
/** Timeval to nanoseconds conversion.
*
* This macro converts a unix epoch time to EtherCAT DC time.
* This macro converts a Unix epoch time to EtherCAT DC time.
*
* \see void ecrt_master_application_time()
*
@@ -237,7 +242,7 @@ typedef struct {
uint8_t al_state; /**< Current state of the slave. */
uint8_t error_flag; /**< Error flag for that slave. */
uint8_t sync_count; /**< Number of sync managers. */
uint16_t sdo_count; /**< Number of SDO's. */
uint16_t sdo_count; /**< Number of SDOs. */
char name[EC_MAX_STRING_LENGTH]; /**< Name of the slave. */
} ec_slave_info_t;
@@ -469,8 +474,8 @@ int ecrt_master_reserve(
* its parameters. Asynchronous master access (like EoE processing) is only
* possible if the callbacks have been set.
*
* The task of the send callback (\a request_cb) is to decide, if the bus is
* currently accessible. In this case, it can call the ecrt_master_send_ext()
* The task of the send callback (\a send_cb) is to decide, if the bus is
* currently accessible and whether or not to call the ecrt_master_send_ext()
* method.
*
* The task of the receive callback (\a receive_cb) is to decide, if a call to
@@ -478,8 +483,10 @@ int ecrt_master_reserve(
*/
void ecrt_master_callbacks(
ec_master_t *master, /**< EtherCAT master */
void (*send_cb)(ec_master_t *), /**< Datagram sending callback. */
void (*receive_cb)(ec_master_t *) /**< Receive callback. */
void (*send_cb)(void *), /**< Datagram sending callback. */
void (*receive_cb)(void *), /**< Receive callback. */
void *cb_data /**< Arbitraty pointer passed to the callback functions.
*/
);
#endif /* __KERNEL__ */
@@ -644,7 +651,7 @@ int ecrt_master_sdo_download(
* processing.
*
* \retval 0 Success.
* \retval -1 Error occured.
* \retval -1 Error occurred.
*/
int ecrt_master_sdo_upload(
ec_master_t *master, /**< EtherCAT master. */
@@ -679,6 +686,18 @@ int ecrt_master_activate(
ec_master_t *master /**< EtherCAT master. */
);
/** Deactivates the master.
*
* Removes the bus configuration. All objects created by
* ecrt_master_create_domain(), ecrt_master_slave_config(), ecrt_domain_data()
* ecrt_slave_config_create_sdo_request() and
* ecrt_slave_config_create_voe_handler() are freed, so pointers to them
* become invalid.
*/
void ecrt_master_deactivate(
ec_master_t *master /**< EtherCAT master. */
);
/** Sends all datagrams in the queue.
*
* This method takes all datagrams, that have been queued for transmission,
@@ -759,6 +778,28 @@ void ecrt_master_sync_slave_clocks(
ec_master_t *master /**< EtherCAT master. */
);
/** Queues the DC synchonity monitoring datagram for sending.
*
* The datagram broadcast-reads all "System time difference" registers (\a
* 0x092c) to get an upper estiomation of the DC synchony. The result can be
* checked with the ecrt_master_sync_monitor_process() method.
*/
void ecrt_master_sync_monitor_queue(
ec_master_t *master /**< EtherCAT master. */
);
/** Processes the DC synchonity monitoring datagram.
*
* If the sync monitoring datagram was sent before with
* ecrt_master_sync_monitor_queue(), the result can be queried with this
* method.
*
* \return Upper estination of the maximum time difference in ns.
*/
uint32_t ecrt_master_sync_monitor_process(
ec_master_t *master /**< EtherCAT master. */
);
/******************************************************************************
* Slave configuration methods
*****************************************************************************/
@@ -779,13 +820,17 @@ int ecrt_slave_config_sync_manager(
);
/** Configure a slave's watchdog times.
*/
*/
void ecrt_slave_config_watchdog(
ec_slave_config_t *sc, /**< Slave configuration. */
uint16_t watchdog_divider, /**< Number of 40 ns intervals. Used as a
base unit for all slave watchdogs. */
base unit for all slave watchdogs. If set
to zero, the value is not written, so the
default ist used. */
uint16_t watchdog_intervals /**< Number of base intervals for process
data watchdog. */
data watchdog. If set to zero, the value
is not written, so the default is used.
*/
);
/** Add a PDO to a sync manager's PDO assignment.
@@ -1031,6 +1076,23 @@ int ecrt_slave_config_sdo32(
uint32_t value /**< Value to set. */
);
/** Add configuration data for a complete SDO.
*
* The SDO data are transferred via CompleteAccess. Data for the first
* subindex (0) have to be included.
*
* \see ecrt_slave_config_sdo().
*
* \retval 0 Success.
* \retval <0 Error code.
*/
int ecrt_slave_config_complete_sdo(
ec_slave_config_t *sc, /**< Slave configuration. */
uint16_t index, /**< Index of the SDO to configure. */
const uint8_t *data, /**< Pointer to the data. */
size_t size /**< Size of the \a data. */
);
/** Create an SDO request to exchange SDOs during realtime operation.
*
* The created SDO request object is freed automatically when the master is

View File

@@ -336,6 +336,16 @@ int ecrt_master_activate(ec_master_t *master)
/*****************************************************************************/
void ecrt_master_deactivate(ec_master_t *master)
{
if (ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL) == -1) {
fprintf(stderr, "Failed to deactivate master: %s\n", strerror(errno));
return;
}
}
/*****************************************************************************/
void ecrt_master_send(ec_master_t *master)
{
if (ioctl(master->fd, EC_IOCTL_SEND, NULL) == -1) {
@@ -395,3 +405,28 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
}
/*****************************************************************************/
void ecrt_master_sync_monitor_queue(ec_master_t *master)
{
if (ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL) == -1) {
fprintf(stderr, "Failed to queue sync monitor datagram: %s\n",
strerror(errno));
}
}
/*****************************************************************************/
uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
{
uint32_t time_diff;
if (ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff) == -1) {
time_diff = 0xffffffff;
fprintf(stderr, "Failed to process sync monitor datagram: %s\n",
strerror(errno));
}
return time_diff;
}
/*****************************************************************************/

View File

@@ -294,6 +294,29 @@ int ecrt_slave_config_sdo(ec_slave_config_t *sc, uint16_t index,
data.subindex = subindex;
data.data = sdo_data;
data.size = size;
data.complete_access = 0;
if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) {
fprintf(stderr, "Failed to configure SDO.\n");
return -1; // FIXME
}
return 0;
}
/*****************************************************************************/
int ecrt_slave_config_complete_sdo(ec_slave_config_t *sc, uint16_t index,
const uint8_t *sdo_data, size_t size)
{
ec_ioctl_sc_sdo_t data;
data.config_index = sc->index;
data.index = index;
data.subindex = 0;
data.data = sdo_data;
data.size = size;
data.complete_access = 1;
if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) {
fprintf(stderr, "Failed to configure SDO.\n");

View File

@@ -71,12 +71,12 @@ ifeq (@ENABLE_DEBUG_IF@,1)
ec_master-objs += debug.o
endif
REV := $(shell if test -s $(src)/../svnrevision; then \
cat $(src)/../svnrevision; \
REV := $(shell if test -s $(src)/../revision; then \
cat $(src)/../revision; \
else \
svnversion $(src)/.. 2>/dev/null || echo "unknown"; \
hg id -i $(src)/.. 2>/dev/null || echo "unknown"; \
fi)
CFLAGS_module.o := -DSVNREV=$(REV)
CFLAGS_module.o := -DREV=$(REV)
#------------------------------------------------------------------------------

View File

@@ -171,8 +171,11 @@ int ec_cdev_ioctl_master(
data.slave_count = master->slave_count;
data.config_count = ec_master_config_count(master);
data.domain_count = ec_master_domain_count(master);
#ifdef EC_EOE
data.eoe_handler_count = ec_master_eoe_handler_count(master);
#endif
data.phase = (uint8_t) master->phase;
data.active = (uint8_t) master->active;
data.scan_busy = master->scan_busy;
up(&master->master_sem);
@@ -1256,9 +1259,12 @@ int ec_cdev_ioctl_config(
data.product_code = sc->product_code;
for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) {
data.syncs[i].dir = sc->sync_configs[i].dir;
data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode;
data.syncs[i].pdo_count =
ec_pdo_list_count(&sc->sync_configs[i].pdos);
}
data.watchdog_divider = sc->watchdog_divider;
data.watchdog_intervals = sc->watchdog_intervals;
data.sdo_count = ec_slave_config_sdo_count(sc);
data.slave_position = sc->slave ? sc->slave->ring_position : -1;
data.dc_assign_activate = sc->dc_assign_activate;
@@ -1429,7 +1435,8 @@ int ec_cdev_ioctl_config_sdo(
data.index = req->index;
data.subindex = req->subindex;
data.size = req->data_size;
memcpy(&data.data, req->data, min((u32) data.size, (u32) 4));
memcpy(&data.data, req->data,
min((u32) data.size, (u32) EC_MAX_SDO_DATA_SIZE));
up(&master->master_sem);
@@ -1441,6 +1448,8 @@ int ec_cdev_ioctl_config_sdo(
/*****************************************************************************/
#ifdef EC_EOE
/** Get EoE handler information.
*/
int ec_cdev_ioctl_eoe_handler(
@@ -1486,6 +1495,8 @@ int ec_cdev_ioctl_eoe_handler(
return 0;
}
#endif
/*****************************************************************************/
/** Request the master from userspace.
@@ -1622,7 +1633,7 @@ int ec_cdev_ioctl_activate(
}
ecrt_master_callbacks(master, ec_master_internal_send_cb,
ec_master_internal_receive_cb);
ec_master_internal_receive_cb, master);
ret = ecrt_master_activate(master);
if (ret < 0)
@@ -1637,6 +1648,23 @@ int ec_cdev_ioctl_activate(
/*****************************************************************************/
/** Deactivates the master.
*/
int ec_cdev_ioctl_deactivate(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg, /**< ioctl() argument. */
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
)
{
if (unlikely(!priv->requested))
return -EPERM;
ecrt_master_deactivate(master);
return 0;
}
/*****************************************************************************/
/** Send frames.
*/
int ec_cdev_ioctl_send(
@@ -1759,6 +1787,50 @@ int ec_cdev_ioctl_sync_slaves(
/*****************************************************************************/
/** Queue the sync monitoring datagram.
*/
int ec_cdev_ioctl_sync_mon_queue(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg, /**< ioctl() argument. */
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
)
{
if (unlikely(!priv->requested))
return -EPERM;
down(&master->io_sem);
ecrt_master_sync_monitor_queue(master);
up(&master->io_sem);
return 0;
}
/*****************************************************************************/
/** Processes the sync monitoring datagram.
*/
int ec_cdev_ioctl_sync_mon_process(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg, /**< ioctl() argument. */
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
)
{
uint32_t time_diff;
if (unlikely(!priv->requested))
return -EPERM;
down(&master->io_sem);
time_diff = ecrt_master_sync_monitor_process(master);
up(&master->io_sem);
if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Configure a sync manager.
*/
int ec_cdev_ioctl_sc_sync(
@@ -2111,8 +2183,12 @@ int ec_cdev_ioctl_sc_sdo(
up(&master->master_sem); // FIXME
ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data,
data.size);
if (data.complete_access) {
ret = ecrt_slave_config_complete_sdo(sc, data.index, sdo_data, data.size);
} else {
ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data,
data.size);
}
kfree(sdo_data);
return ret;
}
@@ -3228,8 +3304,10 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return ec_cdev_ioctl_config_pdo_entry(master, arg);
case EC_IOCTL_CONFIG_SDO:
return ec_cdev_ioctl_config_sdo(master, arg);
#ifdef EC_EOE
case EC_IOCTL_EOE_HANDLER:
return ec_cdev_ioctl_eoe_handler(master, arg);
#endif
case EC_IOCTL_REQUEST:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
@@ -3246,6 +3324,10 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
return ec_cdev_ioctl_activate(master, arg, priv);
case EC_IOCTL_DEACTIVATE:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
return ec_cdev_ioctl_deactivate(master, arg, priv);
case EC_IOCTL_SEND:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
@@ -3268,6 +3350,14 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
return ec_cdev_ioctl_sync_slaves(master, arg, priv);
case EC_IOCTL_SYNC_MON_QUEUE:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
return ec_cdev_ioctl_sync_mon_queue(master, arg, priv);
case EC_IOCTL_SYNC_MON_PROCESS:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;
return ec_cdev_ioctl_sync_mon_process(master, arg, priv);
case EC_IOCTL_SC_SYNC:
if (!(filp->f_mode & FMODE_WRITE))
return -EPERM;

View File

@@ -506,6 +506,12 @@ void ecdev_receive(
{
const void *ec_data = data + ETH_HLEN;
size_t ec_size = size - ETH_HLEN;
if (unlikely(!data)) {
EC_WARN("%s() called with NULL data.\n", __func__);
return;
}
device->rx_count++;
if (unlikely(device->master->debug_level > 1)) {

View File

@@ -1074,8 +1074,14 @@ void ec_fsm_coe_down_start(ec_fsm_coe_t *fsm /**< finite state machine */)
uint8_t size;
if (fsm->slave->master->debug_level) {
EC_DBG("Downloading SDO 0x%04X:%02X to slave %u.\n",
request->index, request->subindex, slave->ring_position);
char subidxstr[10];
if (request->complete_access) {
subidxstr[0] = 0x00;
} else {
sprintf(subidxstr, ":%02X", request->subindex);
}
EC_DBG("Downloading SDO 0x%04X%s to slave %u.\n",
request->index, subidxstr, slave->ring_position);
ec_print_data(request->data, request->data_size);
}
@@ -1097,9 +1103,11 @@ void ec_fsm_coe_down_start(ec_fsm_coe_t *fsm /**< finite state machine */)
EC_WRITE_U16(data, 0x2 << 12); // SDO request
EC_WRITE_U8 (data + 2, (0x3 // size specified, expedited
| size << 2
| ((request->complete_access ? 1 : 0) << 4)
| 0x1 << 5)); // Download request
EC_WRITE_U16(data + 3, request->index);
EC_WRITE_U8 (data + 5, request->subindex);
EC_WRITE_U8 (data + 5,
request->complete_access ? 0x00 : request->subindex);
memcpy(data + 6, request->data, request->data_size);
memset(data + 6 + request->data_size, 0x00, 4 - request->data_size);
@@ -1110,7 +1118,7 @@ void ec_fsm_coe_down_start(ec_fsm_coe_t *fsm /**< finite state machine */)
}
else { // request->data_size > 4, use normal transfer type
if (slave->configured_rx_mailbox_size < 6 + 10 + request->data_size) {
EC_ERR("SDO fragmenting not supported yet!\n");
EC_ERR("SDO fragmenting not supported yet!\n"); // FIXME
fsm->state = ec_fsm_coe_error;
return;
}
@@ -1124,9 +1132,11 @@ void ec_fsm_coe_down_start(ec_fsm_coe_t *fsm /**< finite state machine */)
EC_WRITE_U16(data, 0x2 << 12); // SDO request
EC_WRITE_U8 (data + 2, (0x1 // size indicator, normal
| ((request->complete_access ? 1 : 0) << 4)
| 0x1 << 5)); // Download request
EC_WRITE_U16(data + 3, request->index);
EC_WRITE_U8 (data + 5, request->subindex);
EC_WRITE_U8 (data + 5,
request->complete_access ? 0x00 : request->subindex);
EC_WRITE_U32(data + 6, request->data_size);
memcpy(data + 10, request->data, request->data_size);
@@ -1311,12 +1321,18 @@ void ec_fsm_coe_down_response(ec_fsm_coe_t *fsm /**< finite state machine */)
if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
char subidxstr[10];
fsm->state = ec_fsm_coe_error;
EC_ERR("SDO download 0x%04X:%02X (%u bytes) aborted on slave %u.\n",
request->index, request->subindex, request->data_size,
slave->ring_position);
if (request->complete_access) {
subidxstr[0] = 0x00;
} else {
sprintf(subidxstr, ":%02X", request->subindex);
}
EC_ERR("SDO download 0x%04X%s (%u bytes) aborted on slave %u.\n",
request->index, subidxstr, request->data_size,
slave->ring_position);
if (rec_size < 10) {
EC_ERR("Incomplete Abort command:\n");
EC_ERR("Incomplete abort command:\n");
ec_print_data(data, rec_size);
} else {
fsm->request->abort_code = EC_READ_U32(data + 6);
@@ -1805,7 +1821,6 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
uint8_t *data, mbox_prot;
size_t rec_size, data_size;
ec_sdo_request_t *request = fsm->request;
uint32_t seg_size;
unsigned int last_segment;
if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
@@ -1883,14 +1898,11 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
return;
}
last_segment = EC_READ_U8(data + 2) & 0x01;
seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
if (rec_size > 10) {
data_size = rec_size - 3; /* Header of segment upload is smaller than
normal upload */
} else { // == 10
/* seg_size contains the number of trailing bytes to ignore. */
data_size = rec_size - seg_size;
data_size = rec_size - 3; /* Header of segment upload is smaller than
normal upload */
if (rec_size == 10) {
uint8_t seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
data_size -= seg_size;
}
if (request->data_size + data_size > fsm->complete_size) {
@@ -1904,6 +1916,7 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
memcpy(request->data + request->data_size, data + 3, data_size);
request->data_size += data_size;
last_segment = EC_READ_U8(data + 2) & 0x01;
if (!last_segment) {
fsm->toggle = !fsm->toggle;
ec_fsm_coe_up_prepare_segment_request(fsm);

View File

@@ -933,9 +933,9 @@ void ec_fsm_slave_config_enter_watchdog(
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_slave_config_state_watchdog;
} else {
ec_fsm_slave_config_enter_pdo_sync(fsm);
}
ec_fsm_slave_config_enter_pdo_sync(fsm);
}
/*****************************************************************************/

View File

@@ -77,47 +77,52 @@
#define EC_IOCTL_CONFIG_PDO EC_IOWR(0x15, ec_ioctl_config_pdo_t)
#define EC_IOCTL_CONFIG_PDO_ENTRY EC_IOWR(0x16, ec_ioctl_config_pdo_entry_t)
#define EC_IOCTL_CONFIG_SDO EC_IOWR(0x17, ec_ioctl_config_sdo_t)
#ifdef EC_EOE
#define EC_IOCTL_EOE_HANDLER EC_IOWR(0x18, ec_ioctl_eoe_handler_t)
#endif
// Application interface
#define EC_IOCTL_REQUEST EC_IO(0x19)
#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1a)
#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x1b, ec_ioctl_config_t)
#define EC_IOCTL_ACTIVATE EC_IOR(0x1c, size_t)
#define EC_IOCTL_SEND EC_IO(0x1d)
#define EC_IOCTL_RECEIVE EC_IO(0x1e)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x1f, ec_master_state_t)
#define EC_IOCTL_APP_TIME EC_IOW(0x20, ec_ioctl_app_time_t)
#define EC_IOCTL_SYNC_REF EC_IO(0x21)
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x22)
#define EC_IOCTL_SC_SYNC EC_IOW(0x23, ec_ioctl_config_t)
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x24, ec_ioctl_config_t)
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x25, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x26, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x27, ec_ioctl_add_pdo_entry_t)
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x28, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x29, ec_ioctl_reg_pdo_entry_t)
#define EC_IOCTL_SC_DC EC_IOW(0x2a, ec_ioctl_config_t)
#define EC_IOCTL_SC_SDO EC_IOW(0x2b, ec_ioctl_sc_sdo_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2c, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SC_VOE EC_IOWR(0x2d, ec_ioctl_voe_t)
#define EC_IOCTL_SC_STATE EC_IOWR(0x2e, ec_ioctl_sc_state_t)
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x2f)
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x20)
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x31)
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x32, ec_ioctl_domain_state_t)
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x33, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x34, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x35, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x36, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x37, ec_ioctl_sdo_request_t)
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x38, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x39, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ EC_IOW(0x3a, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3b, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3c, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x3d, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_DATA EC_IOWR(0x3e, ec_ioctl_voe_t)
#define EC_IOCTL_DEACTIVATE EC_IO(0x1d)
#define EC_IOCTL_SEND EC_IO(0x1e)
#define EC_IOCTL_RECEIVE EC_IO(0x1f)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x20, ec_master_state_t)
#define EC_IOCTL_APP_TIME EC_IOW(0x21, ec_ioctl_app_time_t)
#define EC_IOCTL_SYNC_REF EC_IO(0x22)
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x23)
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x24)
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x25, uint32_t)
#define EC_IOCTL_SC_SYNC EC_IOW(0x26, ec_ioctl_config_t)
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x27, ec_ioctl_config_t)
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x28, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x29, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x2a, ec_ioctl_add_pdo_entry_t)
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x2b, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x2c, ec_ioctl_reg_pdo_entry_t)
#define EC_IOCTL_SC_DC EC_IOW(0x2d, ec_ioctl_config_t)
#define EC_IOCTL_SC_SDO EC_IOW(0x2e, ec_ioctl_sc_sdo_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2f, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SC_VOE EC_IOWR(0x20, ec_ioctl_voe_t)
#define EC_IOCTL_SC_STATE EC_IOWR(0x31, ec_ioctl_sc_state_t)
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x32)
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x33)
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x34)
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x35, ec_ioctl_domain_state_t)
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x36, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x37, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x38, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x39, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x3a, ec_ioctl_sdo_request_t)
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x3b, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x3c, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ EC_IOW(0x3d, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3e, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3f, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x40, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_DATA EC_IOWR(0x41, ec_ioctl_voe_t)
/*****************************************************************************/
@@ -129,8 +134,11 @@ typedef struct {
uint32_t slave_count;
uint32_t config_count;
uint32_t domain_count;
#ifdef EC_EOE
uint32_t eoe_handler_count;
#endif
uint8_t phase;
uint8_t active;
uint8_t scan_busy;
struct {
uint8_t address[6];
@@ -434,6 +442,11 @@ typedef struct {
/*****************************************************************************/
/** Maximum size for displayed SDO data.
* \todo Make this dynamic.
*/
#define EC_MAX_SDO_DATA_SIZE 1024
typedef struct {
// inputs
uint32_t config_index;
@@ -443,11 +456,13 @@ typedef struct {
uint16_t index;
uint8_t subindex;
uint32_t size;
uint8_t data[4];
uint8_t data[EC_MAX_SDO_DATA_SIZE];
} ec_ioctl_config_sdo_t;
/*****************************************************************************/
#ifdef EC_EOE
typedef struct {
// input
uint16_t eoe_index;
@@ -464,6 +479,8 @@ typedef struct {
uint32_t tx_queue_size;
} ec_ioctl_eoe_handler_t;
#endif
/*****************************************************************************/
typedef struct {
@@ -497,6 +514,7 @@ typedef struct {
uint8_t subindex;
const uint8_t *data;
size_t size;
uint8_t complete_access;
} ec_ioctl_sc_sdo_t;
/*****************************************************************************/

View File

@@ -122,6 +122,7 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
sema_init(&master->device_sem, 1);
master->phase = EC_ORPHANED;
master->active = 0;
master->injection_seq_fsm = 0;
master->injection_seq_rt = 0;
@@ -169,8 +170,10 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
sema_init(&master->io_sem, 1);
master->send_cb = NULL;
master->receive_cb = NULL;
master->cb_data = NULL;
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
master->app_cb_data = NULL;
INIT_LIST_HEAD(&master->sii_requests);
init_waitqueue_head(&master->sii_queue);
@@ -225,12 +228,23 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
EC_ERR("Failed to allocate synchronisation datagram.\n");
goto out_clear_ref_sync;
}
// init sync monitor datagram
ec_datagram_init(&master->sync_mon_datagram);
snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE, "syncmon");
ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
if (ret < 0) {
ec_datagram_clear(&master->sync_mon_datagram);
EC_ERR("Failed to allocate sync monitoring datagram.\n");
goto out_clear_sync;
}
ec_master_find_dc_ref_clock(master);
// init character device
ret = ec_cdev_init(&master->cdev, master, device_number);
if (ret)
goto out_clear_sync;
goto out_clear_sync_mon;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
master->class_device = device_create(class, NULL,
@@ -259,6 +273,8 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
out_clear_cdev:
ec_cdev_clear(&master->cdev);
out_clear_sync_mon:
ec_datagram_clear(&master->sync_mon_datagram);
out_clear_sync:
ec_datagram_clear(&master->sync_datagram);
out_clear_ref_sync:
@@ -297,6 +313,7 @@ void ec_master_clear(
ec_master_clear_slave_configs(master);
ec_master_clear_slaves(master);
ec_datagram_clear(&master->sync_mon_datagram);
ec_datagram_clear(&master->sync_datagram);
ec_datagram_clear(&master->ref_sync_datagram);
ec_fsm_master_clear(&master->fsm);
@@ -349,22 +366,25 @@ void ec_master_clear_slaves(ec_master_t *master)
master->dc_ref_clock = NULL;
// external requests are obsolete, so we wake pending waiters and remove them from the list
// SII
// external requests are obsolete, so we wake pending waiters and remove
// them from the list
//
// SII requests
while (1) {
ec_sii_write_request_t *request;
if (list_empty(&master->sii_requests))
break;
// get first request
request = list_entry(master->sii_requests.next, ec_sii_write_request_t,
list);
request = list_entry(master->sii_requests.next,
ec_sii_write_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding SII request, slave %u does not exist anymore.\n",
request->slave->ring_position);
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->sii_queue);
}
// Reg
// Register requests
while (1) {
ec_reg_request_t *request;
if (list_empty(&master->reg_requests))
@@ -378,7 +398,8 @@ void ec_master_clear_slaves(ec_master_t *master)
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->reg_queue);
}
// SDO
// SDO requests
while (1) {
ec_master_sdo_request_t *request;
if (list_empty(&master->slave_sdo_requests))
@@ -392,7 +413,8 @@ void ec_master_clear_slaves(ec_master_t *master)
request->req.state = EC_INT_REQUEST_FAILURE;
wake_up(&master->sdo_queue);
}
// FOE
// FoE requests
while (1) {
ec_master_foe_request_t *request;
if (list_empty(&master->foe_requests))
@@ -441,9 +463,10 @@ void ec_master_clear_domains(ec_master_t *master)
/** Internal sending callback.
*/
void ec_master_internal_send_cb(
ec_master_t *master /**< EtherCAT master. */
void *cb_data /**< Callback data. */
)
{
ec_master_t *master = (ec_master_t *) cb_data;
down(&master->io_sem);
ecrt_master_send_ext(master);
up(&master->io_sem);
@@ -454,9 +477,10 @@ void ec_master_internal_send_cb(
/** Internal receiving callback.
*/
void ec_master_internal_receive_cb(
ec_master_t *master /**< EtherCAT master. */
void *cb_data /**< Callback data. */
)
{
ec_master_t *master = (ec_master_t *) cb_data;
down(&master->io_sem);
ecrt_master_receive(master);
up(&master->io_sem);
@@ -532,6 +556,7 @@ int ec_master_enter_idle_phase(
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
master->cb_data = master;
master->phase = EC_IDLE;
ret = ec_master_thread_start(master, ec_master_idle_thread,
@@ -633,6 +658,7 @@ int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
master->phase = EC_OPERATION;
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
master->app_cb_data = NULL;
return ret;
out_allow:
@@ -645,66 +671,17 @@ out_allow:
/** Transition function from OPERATION to IDLE phase.
*/
void ec_master_leave_operation_phase(ec_master_t *master
/**< EtherCAT master */)
void ec_master_leave_operation_phase(
ec_master_t *master /**< EtherCAT master */
)
{
ec_slave_t *slave;
#ifdef EC_EOE
ec_eoe_t *eoe;
#endif
if (master->active)
ecrt_master_deactivate(master);
if (master->debug_level)
EC_DBG("OPERATION -> IDLE.\n");
master->phase = EC_IDLE;
#ifdef EC_EOE
ec_master_eoe_stop(master);
#endif
ec_master_thread_stop(master);
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
down(&master->master_sem);
ec_master_clear_domains(master);
ec_master_clear_slave_configs(master);
up(&master->master_sem);
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
// set states for all slaves
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
// mark for reconfiguration, because the master could have no
// possibility for a reconfiguration between two sequential operation
// phases.
slave->force_config = 1;
}
#ifdef EC_EOE
// ... but leave EoE slaves in OP
list_for_each_entry(eoe, &master->eoe_handlers, list) {
if (ec_eoe_is_open(eoe))
ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
}
#endif
master->app_time = 0ULL;
master->app_start_time = 0ULL;
master->has_start_time = 0;
if (ec_master_thread_start(master, ec_master_idle_thread,
"EtherCAT-IDLE"))
EC_WARN("Failed to restart master thread!\n");
#ifdef EC_EOE
ec_master_eoe_start(master);
#endif
master->allow_scan = 1;
master->allow_config = 1;
}
/*****************************************************************************/
@@ -1189,7 +1166,7 @@ static int ec_master_eoe_thread(void *priv_data)
goto schedule;
// receive datagrams
master->receive_cb(master);
master->receive_cb(master->cb_data);
// actual EoE processing
sth_to_send = 0;
@@ -1209,7 +1186,7 @@ static int ec_master_eoe_thread(void *priv_data)
}
// (try to) send datagrams
down(&master->ext_queue_sem);
master->send_cb(master);
master->send_cb(master->cb_data);
up(&master->ext_queue_sem);
}
@@ -1438,6 +1415,8 @@ const ec_domain_t *ec_master_find_domain_const(
/*****************************************************************************/
#ifdef EC_EOE
/** Get the number of EoE handlers.
*
* \return Number of EoE handlers.
@@ -1480,6 +1459,8 @@ const ec_eoe_t *ec_master_get_eoe_handler_const(
return NULL;
}
#endif
/*****************************************************************************/
/** Set the debug level.
@@ -1683,6 +1664,11 @@ int ecrt_master_activate(ec_master_t *master)
if (master->debug_level)
EC_DBG("ecrt_master_activate(master = 0x%x)\n", (u32) master);
if (master->active) {
EC_WARN("%s: Master already active!\n", __func__);
return 0;
}
down(&master->master_sem);
// finish all domains
@@ -1718,6 +1704,7 @@ int ecrt_master_activate(ec_master_t *master)
master->send_cb = master->app_send_cb;
master->receive_cb = master->app_receive_cb;
master->cb_data = master->app_cb_data;
ret = ec_master_thread_start(master, ec_master_operation_thread,
"EtherCAT-OP");
@@ -1731,11 +1718,80 @@ int ecrt_master_activate(ec_master_t *master)
master->allow_config = 1; // request the current configuration
master->allow_scan = 1; // allow re-scanning on topology change
master->active = 1;
return 0;
}
/*****************************************************************************/
void ecrt_master_deactivate(ec_master_t *master)
{
ec_slave_t *slave;
#ifdef EC_EOE
ec_eoe_t *eoe;
#endif
if (master->debug_level)
EC_DBG("ecrt_master_deactivate(master = 0x%x)\n", (u32) master);
if (!master->active) {
EC_WARN("%s: Master not active.\n", __func__);
return;
}
#ifdef EC_EOE
ec_master_eoe_stop(master);
#endif
ec_master_thread_stop(master);
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
master->cb_data = master;
down(&master->master_sem);
ec_master_clear_domains(master);
ec_master_clear_slave_configs(master);
up(&master->master_sem);
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
// set states for all slaves
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
// mark for reconfiguration, because the master could have no
// possibility for a reconfiguration between two sequential operation
// phases.
slave->force_config = 1;
}
#ifdef EC_EOE
// ... but leave EoE slaves in OP
list_for_each_entry(eoe, &master->eoe_handlers, list) {
if (ec_eoe_is_open(eoe))
ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
}
#endif
master->app_time = 0ULL;
master->app_start_time = 0ULL;
master->has_start_time = 0;
if (ec_master_thread_start(master, ec_master_idle_thread,
"EtherCAT-IDLE"))
EC_WARN("Failed to restart master thread!\n");
#ifdef EC_EOE
ec_master_eoe_start(master);
#endif
master->allow_scan = 1;
master->allow_config = 1;
master->active = 0;
}
/*****************************************************************************/
void ecrt_master_send(ec_master_t *master)
{
ec_datagram_t *datagram, *n;
@@ -1895,15 +1951,16 @@ ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
/*****************************************************************************/
void ecrt_master_callbacks(ec_master_t *master,
void (*send_cb)(ec_master_t *), void (*receive_cb)(ec_master_t *))
void (*send_cb)(void *), void (*receive_cb)(void *), void *cb_data)
{
if (master->debug_level)
EC_DBG("ecrt_master_callbacks(master = 0x%x, send_cb = 0x%x, "
" receive_cb = 0x%x)\n", (u32) master, (u32) send_cb,
(u32) receive_cb);
" receive_cb = 0x%x, cb_data = 0x%x)\n", (u32) master,
(u32) send_cb, (u32) receive_cb, (u32) cb_data);
master->app_send_cb = send_cb;
master->app_receive_cb = receive_cb;
master->app_cb_data = cb_data;
}
/*****************************************************************************/
@@ -1945,10 +2002,30 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
/*****************************************************************************/
void ecrt_master_sync_monitor_queue(ec_master_t *master)
{
ec_datagram_zero(&master->sync_mon_datagram);
ec_master_queue_datagram(master, &master->sync_mon_datagram);
}
/*****************************************************************************/
uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
{
if (master->sync_mon_datagram.state == EC_DATAGRAM_RECEIVED) {
return EC_READ_U32(master->sync_mon_datagram.data) & 0x7fffffff;
} else {
return 0xffffffff;
}
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_master_create_domain);
EXPORT_SYMBOL(ecrt_master_activate);
EXPORT_SYMBOL(ecrt_master_deactivate);
EXPORT_SYMBOL(ecrt_master_send);
EXPORT_SYMBOL(ecrt_master_send_ext);
EXPORT_SYMBOL(ecrt_master_receive);
@@ -1958,6 +2035,8 @@ EXPORT_SYMBOL(ecrt_master_state);
EXPORT_SYMBOL(ecrt_master_application_time);
EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
EXPORT_SYMBOL(ecrt_master_sync_monitor_queue);
EXPORT_SYMBOL(ecrt_master_sync_monitor_process);
/** \endcond */

View File

@@ -108,6 +108,7 @@ struct ec_master {
ec_fsm_master_t fsm; /**< Master state machine. */
ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
ec_master_phase_t phase; /**< Master phase. */
unsigned int active; /**< Master has been activated. */
unsigned int injection_seq_fsm; /**< Datagram injection sequence number
for the FSM side. */
unsigned int injection_seq_rt; /**< Datagram injection sequence number
@@ -125,6 +126,8 @@ struct ec_master {
reference clock to the master clock. */
ec_datagram_t sync_datagram; /**< Datagram used for DC drift
compensation. */
ec_datagram_t sync_mon_datagram; /**< Datagram used for DC synchronisation
monitoring. */
ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */
unsigned int scan_busy; /**< Current scan state. */
@@ -166,12 +169,14 @@ struct ec_master {
struct semaphore io_sem; /**< Semaphore used in \a IDLE phase. */
void (*send_cb)(ec_master_t *); /**< Current send datagrams callback. */
void (*receive_cb)(ec_master_t *); /**< Current receive datagrams callback. */
void (*app_send_cb)(ec_master_t *); /**< Application's send datagrams
void (*send_cb)(void *); /**< Current send datagrams callback. */
void (*receive_cb)(void *); /**< Current receive datagrams callback. */
void *cb_data; /**< Current callback data. */
void (*app_send_cb)(void *); /**< Application's send datagrams
callback. */
void (*app_receive_cb)(ec_master_t *); /**< Application's receive datagrams
void (*app_receive_cb)(void *); /**< Application's receive datagrams
callback. */
void *app_cb_data; /**< Application callback data. */
struct list_head sii_requests; /**< SII write requests. */
wait_queue_head_t sii_queue; /**< Wait queue for SII
@@ -236,8 +241,10 @@ unsigned int ec_master_domain_count(const ec_master_t *);
ec_domain_t *ec_master_find_domain(ec_master_t *, unsigned int);
const ec_domain_t *ec_master_find_domain_const(const ec_master_t *,
unsigned int);
#ifdef EC_EOE
uint16_t ec_master_eoe_handler_count(const ec_master_t *);
const ec_eoe_t *ec_master_get_eoe_handler_const(const ec_master_t *, uint16_t);
#endif
int ec_master_debug_level(ec_master_t *, unsigned int);
@@ -247,8 +254,8 @@ ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *, uint16_t,
void ec_master_calc_dc(ec_master_t *);
void ec_master_internal_send_cb(ec_master_t *);
void ec_master_internal_receive_cb(ec_master_t *);
void ec_master_internal_send_cb(void *);
void ec_master_internal_receive_cb(void *);
/*****************************************************************************/

View File

@@ -56,6 +56,7 @@ void ec_sdo_request_init(
ec_sdo_request_t *req /**< SDO request. */
)
{
req->complete_access = 0;
req->data = NULL;
req->mem_size = 0;
req->data_size = 0;
@@ -88,6 +89,7 @@ int ec_sdo_request_copy(
const ec_sdo_request_t *other /**< Other SDO request to copy from. */
)
{
req->complete_access = other->complete_access;
req->index = other->index;
req->subindex = other->subindex;
return ec_sdo_request_copy_data(req, other->data, other->data_size);

View File

@@ -52,6 +52,7 @@ struct ec_sdo_request {
uint8_t *data; /**< Pointer to SDO data. */
size_t mem_size; /**< Size of SDO data memory. */
size_t data_size; /**< Size of SDO data. */
uint8_t complete_access; /**< SDO shall be transferred completely. */
uint32_t issue_timeout; /**< Maximum time in ms, the processing of the
request may take. */
uint32_t response_timeout; /**< Maximum time in ms, the transfer is

View File

@@ -794,6 +794,48 @@ int ecrt_slave_config_sdo32(ec_slave_config_t *sc, uint16_t index,
/*****************************************************************************/
int ecrt_slave_config_complete_sdo(ec_slave_config_t *sc, uint16_t index,
const uint8_t *data, size_t size)
{
ec_slave_t *slave = sc->slave;
ec_sdo_request_t *req;
int ret;
if (sc->master->debug_level)
EC_DBG("ecrt_slave_config_complete_sdo(sc = 0x%x, index = 0x%04X, "
"data = 0x%x, size = %u)\n", (u32) sc,
index, (u32) data, size);
if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return -EPROTONOSUPPORT; // protocol not supported
}
if (!(req = (ec_sdo_request_t *)
kmalloc(sizeof(ec_sdo_request_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO configuration!\n");
return -ENOMEM;
}
ec_sdo_request_init(req);
ec_sdo_request_address(req, index, 0);
req->complete_access = 1;
ret = ec_sdo_request_copy_data(req, data, size);
if (ret < 0) {
ec_sdo_request_clear(req);
kfree(req);
return ret;
}
down(&sc->master->master_sem);
list_add_tail(&req->list, &sc->sdo_configs);
up(&sc->master->master_sem);
return 0;
}
/*****************************************************************************/
/** Same as ecrt_slave_config_create_sdo_request(), but with ERR_PTR() return
* value.
*/
@@ -923,6 +965,7 @@ EXPORT_SYMBOL(ecrt_slave_config_sdo);
EXPORT_SYMBOL(ecrt_slave_config_sdo8);
EXPORT_SYMBOL(ecrt_slave_config_sdo16);
EXPORT_SYMBOL(ecrt_slave_config_sdo32);
EXPORT_SYMBOL(ecrt_slave_config_complete_sdo);
EXPORT_SYMBOL(ecrt_slave_config_create_sdo_request);
EXPORT_SYMBOL(ecrt_slave_config_create_voe_handler);
EXPORT_SYMBOL(ecrt_slave_config_state);

198
tool/CommandCStruct.cpp Normal file
View File

@@ -0,0 +1,198 @@
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
****************************************************************************/
#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
#include "CommandCStruct.h"
/*****************************************************************************/
CommandCStruct::CommandCStruct():
Command("cstruct", "Generate slave PDO information in C language.")
{
}
/*****************************************************************************/
string CommandCStruct::helpString() const
{
stringstream str;
str << getName() << " [OPTIONS]" << endl
<< endl
<< getBriefDescription() << endl
<< endl
<< "The output C code can be used directly with the" << endl
<< "ecrt_slave_config_pdos() function of the application" << endl
<< "interface." << endl
<< endl
<< "Command-specific options:" << endl
<< " --alias -a <alias>" << endl
<< " --position -p <pos> Slave selection. See the help of" << endl
<< " the 'slaves' command." << endl
<< endl
<< numericInfo();
return str.str();
}
/****************************************************************************/
void CommandCStruct::execute(MasterDevice &m, const StringVector &args)
{
SlaveList slaves;
SlaveList::const_iterator si;
if (args.size()) {
stringstream err;
err << "'" << getName() << "' takes no arguments!";
throwInvalidUsageException(err);
}
m.open(MasterDevice::Read);
slaves = selectedSlaves(m);
for (si = slaves.begin(); si != slaves.end(); si++) {
generateSlaveCStruct(m, *si);
}
}
/****************************************************************************/
void CommandCStruct::generateSlaveCStruct(
MasterDevice &m,
const ec_ioctl_slave_t &slave
)
{
ec_ioctl_slave_sync_t sync;
ec_ioctl_slave_sync_pdo_t pdo;
ec_ioctl_slave_sync_pdo_entry_t entry;
unsigned int i, j, k, pdo_pos = 0, entry_pos = 0;
stringstream id, syncs, pdos, entries;
if (!slave.sync_count)
return;
id << "slave_" << dec << slave.position << "_";
for (i = 0; i < slave.sync_count; i++) {
m.getSync(&sync, slave.position, i);
syncs << " {" << dec << sync.sync_index
<< ", " << (EC_READ_BIT(&sync.control_register, 2) ?
"EC_DIR_OUTPUT" : "EC_DIR_INPUT")
<< ", " << dec << (unsigned int) sync.pdo_count
<< ", ";
if (sync.pdo_count) {
syncs << id.str() << "pdos + " << dec << pdo_pos;
} else {
syncs << "NULL";
}
syncs << ", " << (EC_READ_BIT(&sync.control_register, 6) ?
"EC_WD_ENABLE" : "EC_WD_DISABLE")
<< "},";
syncs << endl;
pdo_pos += sync.pdo_count;
for (j = 0; j < sync.pdo_count; j++) {
m.getPdo(&pdo, slave.position, i, j);
pdos << " {0x" << hex << setfill('0')
<< setw(4) << pdo.index
<< ", " << dec << (unsigned int) pdo.entry_count
<< ", ";
if (pdo.entry_count) {
pdos << id.str() << "pdo_entries + " << dec << entry_pos;
} else {
pdos << "NULL";
}
pdos << "},";
if (strlen((const char *) pdo.name)) {
pdos << " /* " << pdo.name << " */";
}
pdos << endl;
entry_pos += pdo.entry_count;
for (k = 0; k < pdo.entry_count; k++) {
m.getPdoEntry(&entry, slave.position, i, j, k);
entries << " {0x" << hex << setfill('0')
<< setw(4) << entry.index
<< ", 0x" << setw(2) << (unsigned int) entry.subindex
<< ", " << dec << (unsigned int) entry.bit_length
<< "},";
if (strlen((const char *) entry.name)) {
entries << " /* " << entry.name << " */";
}
entries << endl;
}
}
}
cout
<< "/* Slave " << slave.position;
if (strlen(slave.order)) {
cout << ", \"" << slave.order << "\"";
}
cout << endl
<< " * Vendor ID: 0x" << hex << setfill('0')
<< setw(8) << slave.vendor_id << endl
<< " * Product code: 0x" << hex << setfill('0')
<< setw(8) << slave.product_code << endl
<< " * Revision number: 0x" << hex << setfill('0')
<< setw(8) << slave.revision_number << endl
<< " */" << endl
<< endl;
if (entry_pos) {
cout << "ec_pdo_entry_info_t " << id.str()
<< "pdo_entries[] = {" << endl
<< entries.str()
<< "};" << endl
<< endl;
}
if (pdo_pos) {
cout << "ec_pdo_info_t " << id.str() << "pdos[] = {" << endl
<< pdos.str()
<< "};" << endl
<< endl;
}
cout << "ec_sync_info_t " << id.str() << "syncs[] = {" << endl
<< syncs.str()
<< "};" << endl
<< endl;
}
/*****************************************************************************/

52
tool/CommandCStruct.h Normal file
View File

@@ -0,0 +1,52 @@
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
****************************************************************************/
#ifndef __COMMANDCSTRUCT_H__
#define __COMMANDCSTRUCT_H__
#include "Command.h"
/****************************************************************************/
class CommandCStruct:
public Command
{
public:
CommandCStruct();
string helpString() const;
void execute(MasterDevice &, const StringVector &);
protected:
void generateSlaveCStruct(MasterDevice &, const ec_ioctl_slave_t &);
};
/****************************************************************************/
#endif

View File

@@ -131,7 +131,7 @@ void CommandConfig::showDetailedConfigs(
)
{
ConfigList::const_iterator configIter;
unsigned int j, k, l;
unsigned int i, j, k, l;
ec_ioctl_slave_t slave;
ec_ioctl_config_pdo_t pdo;
ec_ioctl_config_pdo_entry_t entry;
@@ -159,11 +159,34 @@ void CommandConfig::showDetailedConfigs(
cout << "none" << endl;
}
cout << "Watchdog divider: ";
if (configIter->watchdog_divider) {
cout << dec << configIter->watchdog_divider;
} else {
cout << "(Default)";
}
cout << endl
<< "Watchdog intervals: ";
if (configIter->watchdog_intervals) {
cout << dec << configIter->watchdog_intervals;
} else {
cout << "(Default)";
}
cout << endl;
for (j = 0; j < EC_MAX_SYNC_MANAGERS; j++) {
if (configIter->syncs[j].pdo_count) {
cout << "SM" << dec << j << " ("
cout << "SM" << dec << j << ", Dir: "
<< (configIter->syncs[j].dir == EC_DIR_INPUT
? "Input" : "Output") << ")" << endl;
? "Input" : "Output") << ", Watchdog: ";
switch (configIter->syncs[j].watchdog_mode) {
case EC_WD_DEFAULT: cout << "Default"; break;
case EC_WD_ENABLE: cout << "Enable"; break;
case EC_WD_DISABLE: cout << "Disable"; break;
default: cout << "???"; break;
}
cout << endl;
for (k = 0; k < configIter->syncs[j].pdo_count; k++) {
m.getConfigPdo(&pdo, configIter->config_index, j, k);
@@ -194,26 +217,23 @@ void CommandConfig::showDetailedConfigs(
<< hex << setfill('0')
<< setw(4) << sdo.index << ":"
<< setw(2) << (unsigned int) sdo.subindex
<< ", " << dec << sdo.size << " byte: " << hex;
<< ", " << dec << sdo.size << " byte" << endl;
switch (sdo.size) {
case 1:
cout << "0x" << setw(2)
<< (unsigned int) *(uint8_t *) &sdo.data;
break;
case 2:
cout << "0x" << setw(4)
<< le16_to_cpup(&sdo.data);
break;
case 4:
cout << "0x" << setw(8)
<< le32_to_cpup(&sdo.data);
break;
default:
cout << "???";
cout << " " << hex;
for (i = 0; i < min((uint32_t) sdo.size,
(uint32_t) EC_MAX_SDO_DATA_SIZE); i++) {
cout << setw(2) << (unsigned int) sdo.data[i];
if ((i + 1) % 16 == 0 && i < sdo.size - 1) {
cout << endl << " ";
} else {
cout << " ";
}
}
cout << endl;
if (sdo.size > EC_MAX_SDO_DATA_SIZE) {
cout << " ..." << endl;
}
}
} else {
cout << " None." << endl;

View File

@@ -1,6 +1,6 @@
/*****************************************************************************
*
* $Id: CommandSlaves.cpp 1767 2009-05-08 13:35:06Z fp $
* $Id$
*
* Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH
*

View File

@@ -1,6 +1,6 @@
/*****************************************************************************
*
* $Id: CommandSlaves.h 1667 2009-02-24 12:51:39Z fp $
* $Id$
*
* Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH
*

View File

@@ -91,6 +91,7 @@ void CommandMaster::execute(MasterDevice &m, const StringVector &args)
}
cout << endl
<< " Active: " << (data.active ? "yes" : "no") << endl
<< " Slaves: " << data.slave_count << endl
<< " Ethernet devices:" << endl;

View File

@@ -27,21 +27,23 @@
#
# ---
#
# vim: syntax=make
# vim: syntax=automake
#
#------------------------------------------------------------------------------
EXTRA_DIST =
bin_PROGRAMS = ethercat
ethercat_SOURCES = \
Command.cpp \
CommandAlias.cpp \
CommandConfig.cpp \
CommandCStruct.cpp \
CommandData.cpp \
CommandDebug.cpp \
CommandDomains.cpp \
CommandDownload.cpp \
CommandEoe.cpp \
CommandFoeRead.cpp \
CommandFoeWrite.cpp \
CommandGraph.cpp \
@@ -64,15 +66,21 @@ ethercat_SOURCES = \
main.cpp \
sii_crc.cpp
if ENABLE_EOE
ethercat_SOURCES += CommandEoe.cpp
else
EXTRA_DIST += CommandEoe.cpp
endif
noinst_HEADERS = \
Command.h \
CommandAlias.h \
CommandConfig.h \
CommandCStruct.h \
CommandData.h \
CommandDebug.h \
CommandDomains.h \
CommandDownload.h \
CommandEoe.h \
CommandFoeRead.h \
CommandFoeWrite.h \
CommandGraph.h \
@@ -94,12 +102,18 @@ noinst_HEADERS = \
SdoCommand.h \
sii_crc.h
REV = `if test -s $(top_srcdir)/svnrevision; then \
cat $(top_srcdir)/svnrevision; \
if ENABLE_EOE
noinst_HEADERS += CommandEoe.h
else
EXTRA_DIST += CommandEoe.h
endif
REV = `if test -s $(top_srcdir)/revision; then \
cat $(top_srcdir)/revision; \
else \
svnversion $(srcdir)/.. 2>/dev/null || echo "unknown"; \
hg id -i $(top_srcdir) 2>/dev/null || echo "unknown"; \
fi`
ethercat_CXXFLAGS = -I$(top_srcdir)/master -Wall -DSVNREV=$(REV)
ethercat_CXXFLAGS = -I$(top_srcdir)/master -Wall -DREV=$(REV)
#------------------------------------------------------------------------------

View File

@@ -485,6 +485,8 @@ void MasterDevice::requestState(
/****************************************************************************/
#ifdef EC_EOE
void MasterDevice::getEoeHandler(
ec_ioctl_eoe_handler_t *eoe,
uint16_t eoeHandlerIndex
@@ -499,4 +501,6 @@ void MasterDevice::getEoeHandler(
}
}
#endif
/*****************************************************************************/

View File

@@ -117,7 +117,9 @@ class MasterDevice
void requestState(uint16_t, uint8_t);
void readFoe(ec_ioctl_slave_foe_t *);
void writeFoe(ec_ioctl_slave_foe_t *);
#ifdef EC_EOE
void getEoeHandler(ec_ioctl_eoe_handler_t *, uint16_t);
#endif
private:
unsigned int index;

View File

@@ -37,11 +37,14 @@ using namespace std;
#include "CommandAlias.h"
#include "CommandConfig.h"
#include "CommandCStruct.h"
#include "CommandData.h"
#include "CommandDebug.h"
#include "CommandDomains.h"
#include "CommandDownload.h"
#include "CommandEoe.h"
#ifdef EC_EOE
# include "CommandEoe.h"
#endif
#include "CommandFoeRead.h"
#include "CommandFoeWrite.h"
#include "CommandGraph.h"
@@ -293,11 +296,14 @@ int main(int argc, char **argv)
commandList.push_back(new CommandAlias());
commandList.push_back(new CommandConfig());
commandList.push_back(new CommandCStruct());
commandList.push_back(new CommandData());
commandList.push_back(new CommandDebug());
commandList.push_back(new CommandDomains());
commandList.push_back(new CommandDownload());
#ifdef EC_EOE
commandList.push_back(new CommandEoe());
#endif
commandList.push_back(new CommandFoeRead());
commandList.push_back(new CommandFoeWrite());
commandList.push_back(new CommandGraph());