This commit is contained in:
Dominik Staubli 2010-01-21 11:10:22 +01:00
commit c27bbf7495
21 changed files with 2233 additions and 822 deletions

View File

@ -32,6 +32,48 @@ devices/e1000/Kbuild
devices/e1000/Makefile
devices/e1000/Makefile.in
devices/modules.order
documentation/ethercat_doc.aux
documentation/ethercat_doc.idx
documentation/ethercat_doc.lof
documentation/ethercat_doc.log
documentation/ethercat_doc.lot
documentation/ethercat_doc.nlo
documentation/ethercat_doc.out
documentation/ethercat_doc.pdf
documentation/ethercat_doc.toc
documentation/external
documentation/graphs/fsm_change.pdf
documentation/graphs/fsm_change.ps
documentation/graphs/fsm_eoe.pdf
documentation/graphs/fsm_eoe.ps
documentation/graphs/fsm_master.pdf
documentation/graphs/fsm_master.ps
documentation/graphs/fsm_pdo_conf.pdf
documentation/graphs/fsm_pdo_conf.ps
documentation/graphs/fsm_pdo_entry_conf.pdf
documentation/graphs/fsm_pdo_entry_conf.ps
documentation/graphs/fsm_pdo_entry_read.pdf
documentation/graphs/fsm_pdo_entry_read.ps
documentation/graphs/fsm_pdo_read.pdf
documentation/graphs/fsm_pdo_read.ps
documentation/graphs/fsm_sii.pdf
documentation/graphs/fsm_sii.ps
documentation/graphs/fsm_slave_conf.pdf
documentation/graphs/fsm_slave_conf.ps
documentation/graphs/fsm_slave_scan.pdf
documentation/graphs/fsm_slave_scan.ps
documentation/images/app-config.pdf
documentation/images/architecture.pdf
documentation/images/attach.pdf
documentation/images/dc.pdf
documentation/images/fmmus.pdf
documentation/images/fsm-coedown.pdf
documentation/images/fsm-eoe.pdf
documentation/images/interrupt.pdf
documentation/images/master-locks.pdf
documentation/images/masters.pdf
documentation/images/phases.pdf
documentation/images/statetrans.pdf
examples/Kbuild
examples/Makefile
examples/Makefile.in

View File

@ -14,13 +14,16 @@ General Features:
- Runs as kernel module for Linux 2.6.
- Multiple masters possible on one machine.
* EtherCAT-capable versions of standard Linux drivers for wide-spread
Ethernet devices.
- Interrupt-less operation of Ethernet devices.
- Easy implementation of additional Ethernet drivers through common device
interface.
* Native EtherCAT-capable versions of standard Linux drivers for wide-spread
Ethernet devices, as well as a generic driver for all chips supported by the
Linux kernel.
- Interrupt-less operation of Ethernet devices when using native drivers.
- Easy implementation of additional native Ethernet drivers through common
device interface.
- Operation possible with any device supported by the standard drivers,
including PCMCIA devices.
- For any other hardware, the generic driver can be used. It uses the lower
layers of the Linux network stack.
* Supports any realtime environment through independent architecture.
- RTAI, Xenomai, RT-Preempt, etc.

64
TODO
View File

@ -10,6 +10,37 @@ vim700: spelllang=en spell
Version 1.5.0:
* Fix link detection in generic driver.
* Remove byte-swapping functions from user space.
* Implement 'ethercat foe_read --output-file ...'.
* Use ec_datagram_zero() wherever possible.
* Fix arguments of reg_read.
* Number layout for reg_read.
* Finish library implementation.
* Rescan command.
* Document ec_fsm_foe members.
* Implement identifier parameter for cstruct command.
* Implement sync delimiter for cstruct command.
* Change SDO index at runtime for SDO request.
* Output skipped datagrams again.
* Output warning on unmatched slave configuration.
* Output warning when send_ext() is called in illegal context.
* Output hexadecimal values in 'ethercat xml'.
* Add native drivers from 2.6.24 up to 2.6.31.
Future issues:
* Implement ecrt_slave_config_request_state().
* Implement CompleteAccess for command-line tool.
* Implement CompleteAccess for SDO uploads.
* Check for Enable SDO Complete Access flag.
* Remove default buffer size in SDO upload.
* Improve application-triggered SDO transfers by moving the state machine into
the SDO handlers.
* Check for ioctl() interface version.
* Remove allow_scanning flag.
* Override sync manager size?
* Show Record / Array / List type of SDOs.
* Distributed clocks:
- Check 32/64 bit operations.
- Use vendor correction factors when calculating transmission delays.
@ -20,39 +51,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.
* Remove byte-swapping functions from user space.
* Implement 'ethercat foe_read --output-file ...'.
* Use ec_datagram_zero() where possible.
* Fix arguments of reg_read.
* Number layout for reg_read.
* Show Record / Array / List type of SDOs.
* Finish library implementation.
* Rescan command.
* Override sync manager size?
* Check force_config flag before error.
* Remove allow_scanning flag.
* Check for ioctl() interface version.
* Improve application-triggered SDO transfers by moving the state machine into
the SDO handlers.
* 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.
* Output warning on unmatched slave configuration.
* ec_direction_t default
* Send_ext context warn
* XML hex
* r8169
Future issues:
* Mailbox protocol handlers.
* Mailbox state machine using toggle bits.
* External memory for SDO transfers.

View File

@ -474,6 +474,30 @@ if test "x${cycles}" = "x1"; then
AC_DEFINE([EC_HAVE_CYCLES], [1], [Use CPU timestamp counter])
fi
#------------------------------------------------------------------------------
# High-resolution timer support
#------------------------------------------------------------------------------
AC_ARG_ENABLE([hrtimer],
AS_HELP_STRING([--enable-hrtimer],
[Use high-resolution timer for scheduling (default: no)]),
[
case "${enableval}" in
yes) hrtimer=1
;;
no) hrtimer=0
;;
*) AC_MSG_ERROR([Invalid value for --enable-hrtimer])
;;
esac
],
[hrtimer=0]
)
if test "x${hrtimer}" = "x1"; then
AC_DEFINE([EC_USE_HRTIMER], [1], [Use hrtimer for scheduling])
fi
#------------------------------------------------------------------------------
# Command-line tool
#-----------------------------------------------------------------------------

View File

@ -70,16 +70,16 @@ struct list_head generic_devices;
typedef struct {
struct list_head list;
struct net_device *netdev;
struct net_device *used_netdev;
struct socket *socket;
struct net_device *used_netdev;
struct socket *socket;
ec_device_t *ecdev;
uint8_t *rx_buf;
} ec_gen_device_t;
typedef struct {
struct list_head list;
struct net_device *netdev;
char name[IFNAMSIZ];
struct net_device *netdev;
char name[IFNAMSIZ];
int ifindex;
uint8_t dev_addr[ETH_ALEN];
} ec_gen_interface_desc_t;
@ -202,7 +202,7 @@ int ec_gen_device_create_socket(
int ret;
struct sockaddr_ll sa;
dev->rx_buf = kmalloc(EC_GEN_RX_BUF_SIZE, GFP_KERNEL);
dev->rx_buf = kmalloc(EC_GEN_RX_BUF_SIZE, GFP_KERNEL);
if (!dev->rx_buf) {
return -ENOMEM;
}
@ -242,8 +242,8 @@ int ec_gen_device_offer(
{
int ret = 0;
dev->used_netdev = desc->netdev;
memcpy(dev->netdev->dev_addr, desc->dev_addr, ETH_ALEN);
dev->used_netdev = desc->netdev;
memcpy(dev->netdev->dev_addr, desc->dev_addr, ETH_ALEN);
dev->ecdev = ecdev_offer(dev->netdev, ec_gen_poll, THIS_MODULE);
if (dev->ecdev) {
@ -254,7 +254,7 @@ int ec_gen_device_offer(
ecdev_withdraw(dev->ecdev);
dev->ecdev = NULL;
} else {
ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev)); // FIXME
ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev)); // FIXME
ret = 1;
}
}
@ -296,7 +296,7 @@ int ec_gen_device_start_xmit(
size_t len = skb->len;
int ret;
ecdev_set_link(dev->ecdev,netif_carrier_ok(dev->used_netdev));
ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev));
iov.iov_base = skb->data;
iov.iov_len = len;
@ -319,8 +319,9 @@ void ec_gen_device_poll(
struct kvec iov;
int ret, budget = 10; // FIXME
ecdev_set_link(dev->ecdev,netif_carrier_ok(dev->used_netdev));
do {
ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev));
do {
iov.iov_base = dev->rx_buf;
iov.iov_len = EC_GEN_RX_BUF_SIZE;
memset(&msg, 0, sizeof(msg));
@ -414,7 +415,7 @@ int __init ec_gen_init_module(void)
goto out_err;
}
strncpy(desc->name, netdev->name, IFNAMSIZ);
desc->netdev = netdev;
desc->netdev = netdev;
desc->ifindex = netdev->ifindex;
memcpy(desc->dev_addr, netdev->dev_addr, ETH_ALEN);
list_add_tail(&desc->list, &descs);

View File

@ -16,7 +16,7 @@
\usepackage{makeidx}
\usepackage[refpage]{nomencl}
\usepackage{listings}
\usepackage{svn}
\usepackage[nofancy]{rcsinfo}
\usepackage{SIunits}
\usepackage{amsmath} % for \text{}
\usepackage{hyperref}
@ -62,8 +62,7 @@
\newcommand{\IgH}{\raisebox{-0.7667ex}
{\includegraphics[height=2.2ex]{images/ighsign}}}
\SVN $Date$
\SVN $Revision$
\rcsInfo $RCSId$
\newcommand{\masterversion}{1.5.0}
\newcommand{\linenum}[1]{\normalfont\textcircled{\tiny #1}}
@ -71,6 +70,10 @@
\makeindex
\makenomenclature
% Revision and date on inner footer
\ifoot[\scriptsize\rcsInfoRevision, \rcsInfoDate]
{\scriptsize\rcsInfoRevision, \rcsInfoDate}
%------------------------------------------------------------------------------
\begin{document}
@ -84,7 +87,7 @@
{\Huge\bf IgH \includegraphics[height=2.4ex]{images/ethercat}
Master \masterversion\\[1ex]
Preliminary Documentation}
Documentation}
\vspace{1ex}
\rule{\textwidth}{1.5mm}
@ -93,13 +96,15 @@
\url{fp@igh-essen.com}\\[1ex] Ingenieurgemeinschaft \IgH}
\vspace{\fill}
{\Large Essen, \SVNDate\\[1ex]
Revision \SVNRevision}
{\Large Essen, \rcsInfoLongDate\\[1ex]
Revision \rcsInfoRevision}
\end{center}
\end{titlepage}
%------------------------------------------------------------------------------
\pagestyle{scrplain}
\tableofcontents
\listoftables
\listoffigures
@ -172,15 +177,20 @@ The list below gives a short summary of the master features.
\item Implemented according to IEC 61158-12 \cite{dlspec} \cite{alspec}.
\item Comes with EtherCAT-capable drivers for several common Ethernet devices.
\item Comes with EtherCAT-capable native drivers for several common Ethernet
chips, as well as a generic driver for all chips supported by the Linux
kernel.
\begin{itemize}
\item The Ethernet hardware is operated without interrupts.
\item The native drivers operate the hardware without interrupts.
\item Drivers for additional Ethernet hardware can easily be implemented
using the common device interface (see sec.~\ref{sec:ecdev}) provided by the
master module.
\item Native drivers for additional Ethernet hardware can easily be
implemented using the common device interface (see sec.~\ref{sec:ecdev})
provided by the master module.
\item For any other hardware, the generic driver can be used. It uses the
lower layers of the Linux network stack.
\end{itemize}
@ -192,9 +202,9 @@ independent architecture.
\begin{itemize}
\item RTAI\nomenclature{RTAI}{Realtime Application Interface},
\item RTAI\nomenclature{RTAI}{Realtime Application Interface} \cite{rtai},
ADEOS\nomenclature{ADEOS}{Adaptive Domain Environment for Operating
Systems}, etc.
Systems}, RT-Preempt \cite{rt-preempt}, etc.
\item It runs well even without realtime extensions.
@ -362,7 +372,7 @@ Figure~\ref{fig:arch} gives a general overview of the master architecture.
\begin{figure}[htbp]
\centering
\includegraphics[width=.9\textwidth]{images/architecture}
\includegraphics[width=\textwidth]{images/architecture}
\caption{Master Architecture}
\label{fig:arch}
\end{figure}
@ -933,8 +943,29 @@ The EtherCAT protocol is based on the Ethernet standard, so a master relies on
standard Ethernet hardware to communicate with the bus.
The term \textit{device} is used as a synonym for Ethernet network interface
hardware. There are device driver modules that handle Ethernet hardware, which
a master can use to connect to an EtherCAT bus.
hardware.
\paragraph{Native Ethernet Device Drivers} There are native device driver
modules (see sec.~\ref{sec:native-drivers}) that handle Ethernet hardware,
which a master can use to connect to an EtherCAT bus. They offer their
Ethernet hardware to the master module via the device interface (see
sec.~\ref{sec:ecdev}) and must be capable to prepare Ethernet devices either
for EtherCAT (realtime) operation or for ``normal'' operation using the
kernel's network stack. The advantage of this approach is that the master can
operate nearly directly on the hardware, which allows a high performance. The
disadvantage is, that there has to be an EtherCAT-capable version of the
original Ethernet driver.
\paragraph{Generic Ethernet Device Driver} From master version 1.5, there is a
generic Ethernet device driver module (see sec.~\ref{sec:generic-driver}),
that uses the lower layers of the network stack to connect to the hardware.
The advantage is, that arbitrary Ethernet hardware can be used for EtherCAT
operation, independently of the actual hardware driver (so all Linux Ethernet
drivers are supported without modifications). The disadvantage is, that this
approach does not support realtime extensions like RTAI, because the Linux
network stack is addressed. Moreover the performance is a little worse than
the native approach, because the Ethernet frame data have to traverse the
network stack.
%------------------------------------------------------------------------------
@ -947,52 +978,44 @@ Ethernet device to communicate with the bus. Therefore it is necessary
to understand how Linux handles network devices and their drivers,
respectively.
\paragraph{Tasks of a Network Driver}
\paragraph{Tasks of a Network Driver} Network device drivers usually handle
the lower two layers of the OSI model, that is the physical layer and the
data-link layer. A network device itself natively handles the physical layer
issues: It represents the hardware to connect to the medium and to send and
receive data in the way, the physical layer protocol describes. The network
device driver is responsible for getting data from the kernel's networking
stack and forwarding it to the hardware, that does the physical transmission.
If data is received by the hardware respectively, the driver is notified
(usually by means of an interrupt) and has to read the data from the hardware
memory and forward it to the network stack. There are a few more tasks, a
network device driver has to handle, including queue control, statistics and
device dependent features.
Network device drivers usually handle the lower two layers of the OSI model,
that is the physical layer and the data-link layer. A network device itself
natively handles the physical layer issues: It represents the hardware to
connect to the medium and to send and receive data in the way, the physical
layer protocol describes. The network device driver is responsible for getting
data from the kernel's networking stack and forwarding it to the hardware,
that does the physical transmission. If data is received by the hardware
respectively, the driver is notified (usually by means of an interrupt) and
has to read the data from the hardware memory and forward it to the network
stack. There are a few more tasks, a network device driver has to handle,
including queue control, statistics and device dependent features.
\paragraph{Driver Startup} Usually, a driver searches for compatible devices
on module loading. For PCI drivers, this is done by scanning the PCI bus and
checking for known device IDs. If a device is found, data structures are
allocated and the device is taken into operation.
\paragraph{Driver Startup}
\paragraph{Interrupt Operation}\index{Interrupt} A network device usually
provides a hardware interrupt that is used to notify the driver of received
frames and success of transmission, or errors, respectively. The driver has to
register an interrupt service routine
(ISR\index{ISR}\nomenclature{ISR}{Interrupt Service Routine}), that is
executed each time, the hardware signals such an event. If the interrupt was
thrown by the own device (multiple devices can share one hardware interrupt),
the reason for the interrupt has to be determined by reading the device's
interrupt register. For example, if the flag for received frames is set, frame
data has to be copied from hardware to kernel memory and passed to the network
stack.
Usually, a driver searches for compatible devices on module loading.
For PCI drivers, this is done by scanning the PCI bus and checking for
known device IDs. If a device is found, data structures are allocated
and the device is taken into operation.
\paragraph{Interrupt Operation}
\index{Interrupt}
A network device usually provides a hardware interrupt that is used to
notify the driver of received frames and success of transmission, or
errors, respectively. The driver has to register an interrupt service
routine (ISR\index{ISR}\nomenclature{ISR}{Interrupt Service Routine}),
that is executed each time, the hardware signals such an event. If the
interrupt was thrown by the own device (multiple devices can share one
hardware interrupt), the reason for the interrupt has to be determined
by reading the device's interrupt register. For example, if the flag
for received frames is set, frame data has to be copied from hardware
to kernel memory and passed to the network stack.
\paragraph{The \lstinline+net_device+ Structure}
\index{net\_device}
The driver registers a \lstinline+net_device+ structure for each device to
communicate with the network stack and to create a ``network interface''. In
case of an Ethernet driver, this interface appears as \textit{ethX}, where X
is a number assigned by the kernel on registration. The \lstinline+net_device+
structure receives events (either from userspace or from the network stack)
via several callbacks, which have to be set before registration. Not every
callback is mandatory, but for reasonable operation the ones below are needed
in any case:
\paragraph{The \lstinline+net_device+ Structure}\index{net\_device} The driver
registers a \lstinline+net_device+ structure for each device to communicate
with the network stack and to create a ``network interface''. In case of an
Ethernet driver, this interface appears as \textit{ethX}, where X is a number
assigned by the kernel on registration. The \lstinline+net_device+ structure
receives events (either from userspace or from the network stack) via several
callbacks, which have to be set before registration. Not every callback is
mandatory, but for reasonable operation the ones below are needed in any case:
\newsavebox\boxopen
\sbox\boxopen{\lstinline+open()+}
@ -1027,17 +1050,15 @@ error happened, the appropriate counter in this structure has to be increased.
The actual registration is done with the \lstinline+register_netdev()+ call,
unregistering is done with \lstinline+unregister_netdev()+.
\paragraph{The \lstinline+netif+ Interface}
\index{netif}
All other communication in the direction interface $\to$ network stack is done
via the \lstinline+netif_*()+ calls. For example, on successful device opening,
the network stack has to be notified, that it can now pass frames to the
\paragraph{The \lstinline+netif+ Interface}\index{netif} All other
communication in the direction interface $\to$ network stack is done via the
\lstinline+netif_*()+ calls. For example, on successful device opening, the
network stack has to be notified, that it can now pass frames to the
interface. This is done by calling \lstinline+netif_start_queue()+. After this
call, the \lstinline+hard_start_xmit()+ callback can be called by the network
stack. Furthermore a network driver usually manages a frame transmission queue.
If this gets filled up, the network stack has to be told to stop passing
further frames for a while. This happens with a call to
stack. Furthermore a network driver usually manages a frame transmission
queue. If this gets filled up, the network stack has to be told to stop
passing further frames for a while. This happens with a call to
\lstinline+netif_stop_queue()+. If some frames have been sent, and there is
enough space again to queue new frames, this can be notified with
\lstinline+netif_wake_queue()+. Another important call is
@ -1049,48 +1070,42 @@ network performance on Linux. Read more in
network stack, that was just received by the device. Frame data has to be
included in a so-called ``socket buffer'' for that (see below).
\paragraph{Socket Buffers}
\index{Socket buffer}
Socket buffers are the basic data type for the whole network stack. They serve
as containers for network data and are able to quickly add data headers and
footers, or strip them off again. Therefore a socket buffer consists of an
allocated buffer and several pointers that mark beginning of the buffer
(\lstinline+head+), beginning of data (\lstinline+data+), end of data
(\lstinline+tail+) and end of buffer (\lstinline+end+). In addition, a socket
buffer holds network header information and (in case of received data) a
pointer to the \lstinline+net_device+, it was received on. There exist
functions that create a socket buffer (\lstinline+dev_alloc_skb()+), add data
either from front (\lstinline+skb_push()+) or back (\lstinline+skb_put()+),
remove data from front (\lstinline+skb_pull()+) or back
(\lstinline+skb_trim()+), or delete the buffer (\lstinline+kfree_skb()+). A
socket buffer is passed from layer to layer, and is freed by the layer that
uses it the last time. In case of sending, freeing has to be done by the
network driver.
\paragraph{Socket Buffers}\index{Socket buffer} Socket buffers are the basic
data type for the whole network stack. They serve as containers for network
data and are able to quickly add data headers and footers, or strip them off
again. Therefore a socket buffer consists of an allocated buffer and several
pointers that mark beginning of the buffer (\lstinline+head+), beginning of
data (\lstinline+data+), end of data (\lstinline+tail+) and end of buffer
(\lstinline+end+). In addition, a socket buffer holds network header
information and (in case of received data) a pointer to the
\lstinline+net_device+, it was received on. There exist functions that create
a socket buffer (\lstinline+dev_alloc_skb()+), add data either from front
(\lstinline+skb_push()+) or back (\lstinline+skb_put()+), remove data from
front (\lstinline+skb_pull()+) or back (\lstinline+skb_trim()+), or delete the
buffer (\lstinline+kfree_skb()+). A socket buffer is passed from layer to
layer, and is freed by the layer that uses it the last time. In case of
sending, freeing has to be done by the network driver.
%------------------------------------------------------------------------------
\section{EtherCAT Device Drivers}
\label{sec:drivers}
\section{Native EtherCAT Device Drivers}
\label{sec:native-drivers}
There are a few requirements for Ethernet network devices to function as
EtherCAT devices, when connected to an EtherCAT bus.
There are a few requirements, that applies to Ethernet hardware when used with
a native Ethernet driver with EtherCAT functionality.
\paragraph{Dedicated Interfaces}
\paragraph{Dedicated Hardware} For performance and realtime purposes, the
EtherCAT master needs direct and exclusive access to the Ethernet hardware.
This implies that the network device must not be connected to the kernel's
network stack as usual, because the kernel would try to use it as an ordinary
Ethernet device.
For performance and realtime purposes, the EtherCAT master needs direct and
exclusive access to the Ethernet hardware. This implies that the network device
must not be connected to the kernel's network stack as usual, because the
kernel would try to use it as an ordinary Ethernet device.
\paragraph{Interrupt-less Operation}
\index{Interrupt}
EtherCAT frames travel through the logical EtherCAT ring and are then sent back
to the master. Communication is highly deterministic: A frame is sent and will
be received again after a constant time, so there is no need to notify the
driver about frame reception: The master can instead query the hardware for
received frames, if it expects them to be already received.
\paragraph{Interrupt-less Operation}\index{Interrupt} EtherCAT frames travel
through the logical EtherCAT ring and are then sent back to the master.
Communication is highly deterministic: A frame is sent and will be received
again after a constant time, so there is no need to notify the driver about
frame reception: The master can instead query the hardware for received
frames, if it expects them to be already received.
Figure~\ref{fig:interrupt} shows two workflows for cyclic frame transmission
and reception with and without interrupts.
@ -1123,16 +1138,15 @@ incidences contribute to increasing the jitter. Besides, if a realtime
extension (like RTAI) is used, some additional effort would have to be made to
prioritize interrupts.
\paragraph{Ethernet and EtherCAT Devices}
Another issue lies in the way Linux handles devices of the same type. For
example, a PCI\nomenclature{PCI}{Peripheral Component Interconnect, Computer
Bus} driver scans the PCI bus for devices it can handle. Then it registers
itself as the responsible driver for all of the devices found. The problem is,
that an unmodified driver can not be told to ignore a device because it will
be used for EtherCAT later. There must be a way to handle multiple devices of
the same type, where one is reserved for EtherCAT, while the other is treated
as an ordinary Ethernet device.
\paragraph{Ethernet and EtherCAT Devices} Another issue lies in the way Linux
handles devices of the same type. For example, a
PCI\nomenclature{PCI}{Peripheral Component Interconnect, Computer Bus} driver
scans the PCI bus for devices it can handle. Then it registers itself as the
responsible driver for all of the devices found. The problem is, that an
unmodified driver can not be told to ignore a device because it will be used
for EtherCAT later. There must be a way to handle multiple devices of the same
type, where one is reserved for EtherCAT, while the other is treated as an
ordinary Ethernet device.
For all this reasons, the author decided that the only acceptable solution is
to modify standard Ethernet drivers in a way that they keep their normal
@ -1160,15 +1174,64 @@ The chosen approach has the following disadvantages:
%------------------------------------------------------------------------------
\section{Device Selection}
\label{sec:deviceselection}
\section{Generic EtherCAT Device Driver}
\label{sec:generic-driver}
After loading the master module, at least one EtherCAT-capable network driver
module has to be loaded, that offers its devices to the master (see
sec.~\ref{sec:ecdev}. The master module knows the devices to choose from the
module parameters (see sec.~\ref{sec:mastermod}). If the init script is used
to start the master, the drivers and devices to use can be specified in the
sysconfig file (see sec.~\ref{sec:sysconfig}).
Since there are approaches to enable the complete Linux kernel for realtime
operation \cite{rt-preempt}, it is possible to operate without native
implementations of EtherCAT-capable Ethernet device drivers and use the Linux
network stack instead. Fig.~\ref{fig:arch} shows the ``Generic Ethernet Driver
Module'', that connects to local Ethernet devices via the network stack. The
kernel module is named \lstinline+ec_generic+ and can be loaded after the
master module like a native EtherCAT-capable Ethernet driver.
The generic device driver scans the network stack for interfaces, that have
been registered by Ethernet device drivers. It offers all possible devices to
the EtherCAT master. If the master accepts a device, the generic driver
creates a packet socket (see \lstinline+man 7 packet+) with
\lstinline+socket_type+ set to \lstinline+SOCK_RAW+, bound to that device. All
functions of the device interface (see sec.~\ref{sec:ecdev}) will then operate
on that socket.
Below are the advantages of this solution:
\begin{itemize}
\item Any Ethernet hardware, that is covered by a Linux Ethernet driver can be
used for EtherCAT.
\item No modifications have to be made to the actual Ethernet drivers.
\end{itemize}
The generic approach has the following disadvantages:
\begin{itemize}
\item The performance is a little worse than the native approach, because the
frame data have to traverse the lower layers of the network stack.
\item It is not possible to use in-kernel realtime extensions like RTAI with
the generic driver, because the network stack code uses dynamic memory
allocations and other things, that could cause the system to freeze in
realtime context.
\end{itemize}
%------------------------------------------------------------------------------
\section{Providing Ethernet Devices}
\label{sec:providing-devices}
After loading the master module, additional module(s) have to be loaded to
offer devices to the master(s) (see sec.~\ref{sec:ecdev}). The master module
knows the devices to choose from the module parameters (see
sec.~\ref{sec:mastermod}). If the init script is used to start the master, the
drivers and devices to use can be specified in the sysconfig file (see
sec.~\ref{sec:sysconfig}).
Modules offering Ethernet devices can be
\begin{itemize}
\item native EtherCAT-capable network driver modules (see
sec.~\ref{sec:native-drivers}) or
\item the generic EtherCAT device driver module (see
sec.~\ref{sec:generic-driver}).
\end{itemize}
%------------------------------------------------------------------------------
@ -1196,14 +1259,15 @@ sec.~\ref{sec:gendoc} for generation instructions).
%------------------------------------------------------------------------------
\section{Patching Network Drivers}
\section{Patching Native Network Drivers}
\label{sec:patching}
\index{Network drivers}
This section will describe, how to make a standard Ethernet driver
EtherCAT-capable. Unfortunately, there is no standard procedure to enable an
Ethernet driver for use with the EtherCAT master, but there are a few common
techniques.
EtherCAT-capable, using the native approach (see
sec.~\ref{sec:native-drivers}). Unfortunately, there is no standard procedure
to enable an Ethernet driver for use with the EtherCAT master, but there are a
few common techniques.
\begin{enumerate}
@ -2605,14 +2669,21 @@ the EtherCAT master. It has to be executed with one of the parameters
EtherCAT buses can always be monitored by inserting a switch between master
and slaves. This allows to connect another PC with a network monitor like
Wireshark~\cite{wireshark}, for example.
Wireshark~\cite{wireshark}, for example. It is also possible to listen to
local network interfaces on the machine running the EtherCAT master directly.
If the generic Ethernet driver (see sec.~\ref{sec:generic-driver}) is used,
the network monitor can directly listen on the network interface connected to
the EtherCAT bus.
For convenience, so-called ``debug interfaces'' are supported. Debug
interfaces are virtual network interfaces allowing to capture EtherCAT traffic
with a network monitor (like Wireshark or tcpdump) running on the master
machine without using external hardware. To use this functionality, the master
sources have to be configured with the \lstinline+--enable-debug-if+ switch
(see sec.~\ref{sec:installation}).
When using native Ethernet drivers (see sec.~\ref{sec:native-drivers}), there
are no local network interfaces to listen to, because the Ethernet devices
used for EtherCAT are not registered at the network stack. For that case,
so-called ``debug interfaces'' are supported, which are virtual network
interfaces allowing to capture EtherCAT traffic with a network monitor (like
Wireshark or tcpdump) running on the master machine without using external
hardware. To use this functionality, the master sources have to be configured
with the \lstinline+--enable-debug-if+ switch (see
sec.~\ref{sec:installation}).
Every EtherCAT master registers a read-only network interface per attached
physical Ethernet device. The network interfaces are named \textit{ecdbgmX}
@ -2644,8 +2715,8 @@ Please note, that the frame rate can be very high. With an application
connected, the debug interface can produce thousands of frames per second.
\paragraph{Attention} The socket buffers needed for the operation of debug
interfaces have to be allocated dynamically. Some Linux realtime extensions do
not allow this in realtime context!
interfaces have to be allocated dynamically. Some Linux realtime extensions
(like RTAI) do not allow this in realtime context!
%------------------------------------------------------------------------------
@ -2794,19 +2865,58 @@ latency. These are two points on the author's to-do list.
\label{sec:installation}
\index{Master!Installation}
\section{Getting the Software}
\label{sec:getting}
There are several ways to get the master software:
\begin{enumerate}
\item An official release (for example \masterversion), can be downloaded from
the master's website\footnote{\url{http://etherlab.org/en/ethercat/index.php}}
at~the EtherLab project~\cite{etherlab} as a tarball.
\item The most recent development revision (and moreover any other revision)
can be obtained via the Mercurial~\cite{mercurial} repository on the master's
project page on
SourceForge.net\footnote{\url{http://sourceforge.net/projects/etherlabmaster}}.
The whole repository can be cloned with the command
\begin{lstlisting}[breaklines=true]
hg clone http://etherlabmaster.hg.sourceforge.net/hgweb/etherlabmaster/etherlabmaster `\textit{local-dir}`
\end{lstlisting}
\item Without a local Mercurial installation, tarballs of arbitrary revisions
can be downloaded via the ``bz2'' links in the browsable repository
pages\footnote{\url{http://etherlabmaster.hg.sourceforge.net/hgweb/etherlabmaster/etherlabmaster}}.
\end{enumerate}
\section{Building the Software}
The current EtherCAT master code is available at~\cite{etherlab} or can be
obtained from the EtherLab CD. The \textit{tar.bz2} file has to be unpacked
with the commands below (or similar):
After downloading a tarball or cloning the repository as described in
sec.~\ref{sec:getting}, the sources have to be prepared and configured for the
build process.
When a tarball was downloaded, it has to be extracted with the following
commands:
\begin{lstlisting}[gobble=2]
$ `\textbf{tar xjf ethercat-\masterversion.tar.bz2}`
$ `\textbf{cd ethercat-\masterversion/}`
\end{lstlisting}
The tarball was created with GNU Autotools, so the build process
follows the below commands:
The software configuration is managed with Autoconf~\cite{autoconf} so the
released versions contain a \lstinline+configure+ shell script, that has to be
executed for configuration (see below).
\paragraph{Bootstrap} When downloading or cloning directly from the
repository, the \lstinline+configure+ script does not yet exist. It can be
created via the \lstinline+bootstrap.sh+ script in the master sources. The
autoconf and automake packages are required for this.
\paragraph{Configuration and Build} The configuration and the build process
follow the below commands:
\begin{lstlisting}[gobble=2]
$ `\textbf{./configure}`
@ -2832,6 +2942,11 @@ Table~\ref{tab:config} lists important configuration switches and options.
\hline
\lstinline+--enable-tool+ & Build the command-line tool ``ethercat'' (see
sec.~\ref{sec:tool}). & yes\\
\lstinline+--enable-userlib+ & Build the userspace library. & yes\\
\lstinline+--enable-eoe+ & Enable EoE support & yes\\
\lstinline+--enable-cycles+ & Use CPU timestamp counter. Enable this on Intel
@ -2855,6 +2970,13 @@ architecture to get finer timing calculation. & no\\
\lstinline+--with-e1000-kernel+ & e1000 kernel & $\dagger$\\
\lstinline+--enable-r8169+ & Enable r8169 driver & no\\
\lstinline+--with-r8169-kernel+ & r8169 kernel & $\dagger$\\
\lstinline+--enable-generic+ & Build the generic Ethernet driver (see
sec.~\ref{sec:generic-driver}). & no\\
\end{tabular}
\vspace{2mm}
@ -3049,11 +3171,19 @@ misunderstandings. In: IEE journal ``Computing and Control Engineering'',
2004.
\bibitem{rtai} RTAI. The RealTime Application Interface for Linux from DIAPM.
\url{http://www.rtai.org}, 2006.
\url{https://www.rtai.org}, 2010.
\bibitem{rt-preempt} RT PREEMPT HOWTO.
\url{http://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO}, 2010.
\bibitem{doxygen} Doxygen. Source code documentation generator tool.
\url{http://www.stack.nl/~dimitri/doxygen}, 2008.
\bibitem{mercurial} Mercurial SCM. \url{http://mercurial.selenic.com}, 2010.
\bibitem{autoconf} Autoconf -- GNU Project -- Free Software Foundation (FSF).
\url{http://www.gnu.org/software/autoconf}, 2010.
\end{thebibliography}
\printnomenclature

View File

@ -6,7 +6,6 @@
FIGS := \
app-config.fig \
architecture.fig \
attach.fig \
dc.fig \
fmmus.fig \
@ -18,14 +17,22 @@ FIGS := \
phases.fig \
statetrans.fig
PDFS = $(FIGS:.fig=.pdf)
FIGPDFS = $(FIGS:.fig=.pdf)
all: $(PDFS)
SVGS := \
architecture.svg
SVGPDFS = $(SVGS:.svg=.pdf)
all: $(FIGPDFS) $(SVGPDFS)
%.pdf: %.fig
fig2dev -L pdf -z A4 -p xxx -c $< $@
%.pdf: %.svg
inkscape --export-pdf=$@ $<
clean:
@rm -rv $(PDFS)
@rm -rv $(FIGPDFS) $(SVGPDFS)
#-----------------------------------------------------------------------------

View File

@ -1,176 +0,0 @@
#FIG 3.2
Portrait
Center
Metric
A4
100.00
Single
-2
1200 2
5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 7245.000 5985.000 6975 5985 7245 5715 7515 5985
6 5085 7965 5850 8820
2 3 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 8
5175 7965 5760 7965 5760 8775 5175 8775 5175 8415 5085 8415
5085 7965 5175 7965
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
5490 8190 5670 8190 5670 8370 5490 8370 5490 8190
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
5310 8505 5400 8505 5400 8595 5310 8595 5310 8505
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8055 5085 8055
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8145 5085 8145
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8190 5085 8190
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8280 5085 8280
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8235 5085 8235
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8100 5085 8100
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8010 5085 8010
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8325 5085 8325
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5175 8370 5085 8370
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
5850 8775 5085 8775 5085 8815 5850 8815 5850 8775
-6
6 6435 7965 7200 8820
2 3 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 8
6525 7965 7110 7965 7110 8775 6525 8775 6525 8415 6435 8415
6435 7965 6525 7965
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
6840 8190 7020 8190 7020 8370 6840 8370 6840 8190
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
6660 8505 6750 8505 6750 8595 6660 8595 6660 8505
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8055 6435 8055
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8145 6435 8145
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8190 6435 8190
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8280 6435 8280
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8235 6435 8235
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8100 6435 8100
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8010 6435 8010
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8325 6435 8325
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
6525 8370 6435 8370
2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5
7200 8775 6435 8775 6435 8815 7200 8815 7200 8775
-6
6 4905 5445 5985 6030
5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 5445.000 5985.000 4950 5985 5445 5490 5940 5985
4 1 0 50 -1 16 10 0.0000 4 120 465 5445 5760 Device\001
4 1 0 50 -1 16 10 0.0000 4 120 615 5445 5925 Interface\001
-6
6 3870 4275 4500 5355
5 1 0 1 0 7 50 -1 20 0.000 0 0 0 0 3958.125 4815.000 3915 4320 4455 4815 3915 5310
4 1 0 49 -1 16 10 4.7124 4 150 765 4162 4822 Application\001
4 1 0 49 -1 16 10 4.7124 4 120 615 3997 4822 Interface\001
-6
6 5205 2648 6480 3293
5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 5842.000 2655.000 6472 2655 5842 3285 5212 2655
4 1 0 50 -1 16 12 0.0000 4 135 600 5842 3105 Device\001
4 1 0 50 -1 16 12 0.0000 4 135 825 5842 2880 Character\001
-6
6 3870 945 4500 2025
5 1 0 1 0 7 49 -1 -1 0.000 0 0 0 0 3958.125 1485.000 3915 990 4455 1485 3915 1980
4 1 0 48 -1 16 10 4.7124 4 150 765 4162 1492 Application\001
4 1 0 48 -1 16 10 4.7124 4 120 615 3997 1492 Interface\001
-6
6 2160 855 3420 2115
1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 2790 1485 585 585 2790 1485 3375 1485
4 1 0 49 -1 16 12 0.0000 4 180 945 2790 1665 Application\001
4 1 0 49 -1 16 12 0.0000 4 180 885 2790 1440 Userspace\001
-6
1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 2484 4871 459 459 2025 4860 2944 4882
1 2 0 1 0 7 50 -1 -1 0.000 1 0.0000 5445 4815 810 495 4635 4320 6255 5310
1 4 0 1 0 7 50 -1 -1 4.000 1 0.0000 7058 1658 495 495 7553 2153 6563 1163
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
5445 7965 5445 7425
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
6795 7965 6795 7425
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
5445 6885 5445 5985
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
5535 8820 5625 8820 5625 8910 5535 8910 5535 8820
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
6885 8820 6975 8820 6975 8910 6885 8910 6885 8820
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
7245 6885 7245 5985
2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5
7605 7605 7605 6345 4635 6345 4635 7605 7605 7605
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
4905 6885 5985 6885 5985 7425 4905 7425 4905 6885
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
6255 6885 7335 6885 7335 7425 6255 7425 6255 6885
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
5445 5490 5445 5310
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
4635 4815 4455 4815
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
3915 4815 3465 4815
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
7605 3645 6885 3645 6885 5985 7605 5985 7605 3645
2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
1575 7785 7785 7785
2 4 0 1 0 7 50 -1 -1 0.000 0 0 9 0 0 5
1755 5985 1755 3645 3465 3645 3465 5985 1755 5985
2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
7785 8280 7785 2205
2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
1575 8280 1575 2205
2 4 0 1 0 7 50 -1 -1 0.000 0 0 8 0 0 5
6435 5985 6435 3645 3915 3645 3915 5985 6435 5985
2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
7785 2655 1575 2655
2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
5850 3285 5850 3645
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
3915 1485 3375 1485
2 4 0 1 0 7 50 -1 20 0.000 0 0 7 0 0 5
5040 2250 3915 2250 3915 720 5040 720 5040 2250
3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 3
5580 8910 5355 9045 4770 9090
0.000 -1.000 0.000
3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 3
6931 8910 6390 9270 4770 9450
0.000 -1.000 0.000
3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 3
6570 1665 6120 1890 5985 2655
0.000 -1.000 0.000
3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 3
5040 1485 5580 1800 5715 2655
0.000 -1.000 0.000
4 1 0 50 -1 16 10 0.0000 4 150 750 5445 7200 net_device\001
4 1 0 50 -1 16 10 0.0000 4 150 750 6795 7200 net_device\001
4 2 0 50 -1 12 10 0.0000 4 105 810 5355 6210 ecdev_*()\001
4 1 0 50 -1 16 12 0.0000 4 135 1545 6345 6570 EtherCAT Network\001
4 1 0 50 -1 16 12 0.0000 4 135 1200 6345 6750 Driver Module\001
4 0 0 50 -1 16 12 0.0000 4 135 2130 4005 3870 EtherCAT Master Module\001
4 1 0 50 -1 16 12 1.5708 4 135 1200 7290 4815 Network Stack\001
4 2 0 50 -1 16 12 0.0000 4 135 810 4725 9135 EtherCAT\001
4 2 0 50 -1 16 12 0.0000 4 135 690 4725 9495 Ethernet\001
4 2 0 50 -1 16 12 0.0000 4 135 315 4995 8100 NIC\001
4 2 0 50 -1 16 12 0.0000 4 135 315 6345 8100 NIC\001
4 0 0 50 -1 16 12 0.0000 4 135 810 1665 8010 Hardware\001
4 2 0 50 -1 12 10 4.7124 4 105 720 3645 4725 ecrt_*()\001
4 0 0 50 -1 16 12 0.0000 4 180 945 1845 3870 Application\001
4 0 0 50 -1 16 12 0.0000 4 135 630 1845 4095 Module\001
4 1 0 50 -1 16 12 0.0000 4 135 390 2475 4950 Task\001
4 0 0 50 -1 16 12 0.0000 4 180 1050 1665 2880 Kernelspace\001
4 0 0 50 -1 16 12 0.0000 4 180 885 1665 2565 Userspace\001
4 1 0 50 -1 16 12 4.7124 4 135 870 4635 1530 libethercat\001
4 2 0 50 -1 12 10 4.7124 4 105 720 3645 1395 ecrt_*()\001
4 2 0 50 -1 12 10 0.0000 4 105 810 7155 6210 netif_*()\001
4 1 0 50 -1 16 12 0.0000 4 135 735 5445 4905 Master 0\001
4 1 0 50 -1 16 12 0.0000 4 135 360 7065 1845 Tool\001
4 1 0 50 -1 16 12 0.0000 4 135 765 7065 1620 'ethercat'\001

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -27,12 +27,19 @@
#
#------------------------------------------------------------------------------
SUBDIRS =
if ENABLE_USERLIB
SUBDIRS = \
SUBDIRS += \
dc_user \
user
endif
if ENABLE_TTY
SUBDIRS += \
tty
endif
DIST_SUBDIRS = \
dc_rtai \
dc_user \

View File

@ -33,7 +33,11 @@
obj-m := ec_tty_example.o
ec_tty_example-objs := tty.o
ec_tty_example-objs := \
serial.o \
tty.o
CFLAGS_tty.o := -I$(src)
KBUILD_EXTRA_SYMBOLS := \
@abs_top_builddir@/Module.symvers \

View File

@ -31,10 +31,13 @@
#
#------------------------------------------------------------------------------
EXTRA_DIST = \
Kbuild.in \
noinst_HEADERS = \
serial.h \
tty.c
EXTRA_DIST = \
Kbuild.in
BUILT_SOURCES = \
Kbuild

455
examples/tty/serial.c Normal file
View File

@ -0,0 +1,455 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2008 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 <linux/module.h>
#include <linux/err.h>
#include "../../include/ecrt.h" // EtherCAT realtime interface
#include "../../include/ectty.h" // EtherCAT TTY interface
/*****************************************************************************/
// Optional features
#define PFX "ec_tty_example: "
/*****************************************************************************/
#define VendorIdBeckhoff 0x00000002
#define ProductCodeBeckhoffEL6002 0x17723052
#define Beckhoff_EL6002 VendorIdBeckhoff, ProductCodeBeckhoffEL6002
typedef enum {
SER_REQUEST_INIT,
SER_WAIT_FOR_INIT_RESPONSE,
SER_READY
} serial_state_t;
typedef struct {
struct list_head list;
ec_tty_t *tty;
ec_slave_config_t *sc;
size_t max_tx_data_size;
size_t max_rx_data_size;
u8 *tx_data;
u8 tx_data_size;
serial_state_t state;
u8 tx_request_toggle;
u8 tx_accepted_toggle;
u8 rx_request_toggle;
u8 rx_accepted_toggle;
u16 control;
u32 off_ctrl;
u32 off_tx;
u32 off_status;
u32 off_rx;
} el6002_t;
LIST_HEAD(handlers);
/*****************************************************************************/
/* Beckhoff EL6002
* Vendor ID: 0x00000002
* Product code: 0x17723052
* Revision number: 0x00100000
*/
ec_pdo_entry_info_t el6002_pdo_entries[] = {
{0x7001, 0x01, 16}, /* Ctrl */
{0x7000, 0x11, 8}, /* Data Out 0 */
{0x7000, 0x12, 8}, /* Data Out 1 */
{0x7000, 0x13, 8}, /* Data Out 2 */
{0x7000, 0x14, 8}, /* Data Out 3 */
{0x7000, 0x15, 8}, /* Data Out 4 */
{0x7000, 0x16, 8}, /* Data Out 5 */
{0x7000, 0x17, 8}, /* Data Out 6 */
{0x7000, 0x18, 8}, /* Data Out 7 */
{0x7000, 0x19, 8}, /* Data Out 8 */
{0x7000, 0x1a, 8}, /* Data Out 9 */
{0x7000, 0x1b, 8}, /* Data Out 10 */
{0x7000, 0x1c, 8}, /* Data Out 11 */
{0x7000, 0x1d, 8}, /* Data Out 12 */
{0x7000, 0x1e, 8}, /* Data Out 13 */
{0x7000, 0x1f, 8}, /* Data Out 14 */
{0x7000, 0x20, 8}, /* Data Out 15 */
{0x7000, 0x21, 8}, /* Data Out 16 */
{0x7000, 0x22, 8}, /* Data Out 17 */
{0x7000, 0x23, 8}, /* Data Out 18 */
{0x7000, 0x24, 8}, /* Data Out 19 */
{0x7000, 0x25, 8}, /* Data Out 20 */
{0x7000, 0x26, 8}, /* Data Out 21 */
{0x7011, 0x01, 16}, /* Ctrl */
{0x7010, 0x11, 8}, /* Data Out 0 */
{0x7010, 0x12, 8}, /* Data Out 1 */
{0x7010, 0x13, 8}, /* Data Out 2 */
{0x7010, 0x14, 8}, /* Data Out 3 */
{0x7010, 0x15, 8}, /* Data Out 4 */
{0x7010, 0x16, 8}, /* Data Out 5 */
{0x7010, 0x17, 8}, /* Data Out 6 */
{0x7010, 0x18, 8}, /* Data Out 7 */
{0x7010, 0x19, 8}, /* Data Out 8 */
{0x7010, 0x1a, 8}, /* Data Out 9 */
{0x7010, 0x1b, 8}, /* Data Out 10 */
{0x7010, 0x1c, 8}, /* Data Out 11 */
{0x7010, 0x1d, 8}, /* Data Out 12 */
{0x7010, 0x1e, 8}, /* Data Out 13 */
{0x7010, 0x1f, 8}, /* Data Out 14 */
{0x7010, 0x20, 8}, /* Data Out 15 */
{0x7010, 0x21, 8}, /* Data Out 16 */
{0x7010, 0x22, 8}, /* Data Out 17 */
{0x7010, 0x23, 8}, /* Data Out 18 */
{0x7010, 0x24, 8}, /* Data Out 19 */
{0x7010, 0x25, 8}, /* Data Out 20 */
{0x7010, 0x26, 8}, /* Data Out 21 */
{0x6001, 0x01, 16}, /* Status */
{0x6000, 0x11, 8}, /* Data In 0 */
{0x6000, 0x12, 8}, /* Data In 1 */
{0x6000, 0x13, 8}, /* Data In 2 */
{0x6000, 0x14, 8}, /* Data In 3 */
{0x6000, 0x15, 8}, /* Data In 4 */
{0x6000, 0x16, 8}, /* Data In 5 */
{0x6000, 0x17, 8}, /* Data In 6 */
{0x6000, 0x18, 8}, /* Data In 7 */
{0x6000, 0x19, 8}, /* Data In 8 */
{0x6000, 0x1a, 8}, /* Data In 9 */
{0x6000, 0x1b, 8}, /* Data In 10 */
{0x6000, 0x1c, 8}, /* Data In 11 */
{0x6000, 0x1d, 8}, /* Data In 12 */
{0x6000, 0x1e, 8}, /* Data In 13 */
{0x6000, 0x1f, 8}, /* Data In 14 */
{0x6000, 0x20, 8}, /* Data In 15 */
{0x6000, 0x21, 8}, /* Data In 16 */
{0x6000, 0x22, 8}, /* Data In 17 */
{0x6000, 0x23, 8}, /* Data In 18 */
{0x6000, 0x24, 8}, /* Data In 19 */
{0x6000, 0x25, 8}, /* Data In 20 */
{0x6000, 0x26, 8}, /* Data In 21 */
{0x6011, 0x01, 16}, /* Status */
{0x6010, 0x11, 8}, /* Data In 0 */
{0x6010, 0x12, 8}, /* Data In 1 */
{0x6010, 0x13, 8}, /* Data In 2 */
{0x6010, 0x14, 8}, /* Data In 3 */
{0x6010, 0x15, 8}, /* Data In 4 */
{0x6010, 0x16, 8}, /* Data In 5 */
{0x6010, 0x17, 8}, /* Data In 6 */
{0x6010, 0x18, 8}, /* Data In 7 */
{0x6010, 0x19, 8}, /* Data In 8 */
{0x6010, 0x1a, 8}, /* Data In 9 */
{0x6010, 0x1b, 8}, /* Data In 10 */
{0x6010, 0x1c, 8}, /* Data In 11 */
{0x6010, 0x1d, 8}, /* Data In 12 */
{0x6010, 0x1e, 8}, /* Data In 13 */
{0x6010, 0x1f, 8}, /* Data In 14 */
{0x6010, 0x20, 8}, /* Data In 15 */
{0x6010, 0x21, 8}, /* Data In 16 */
{0x6010, 0x22, 8}, /* Data In 17 */
{0x6010, 0x23, 8}, /* Data In 18 */
{0x6010, 0x24, 8}, /* Data In 19 */
{0x6010, 0x25, 8}, /* Data In 20 */
{0x6010, 0x26, 8}, /* Data In 21 */
};
ec_pdo_info_t el6002_pdos[] = {
{0x1604, 23, el6002_pdo_entries + 0}, /* COM RxPDO-Map Outputs Ch.1 */
{0x1605, 23, el6002_pdo_entries + 23}, /* COM RxPDO-Map Outputs Ch.2 */
{0x1a04, 23, el6002_pdo_entries + 46}, /* COM TxPDO-Map Inputs Ch.1 */
{0x1a05, 23, el6002_pdo_entries + 69}, /* COM TxPDO-Map Inputs Ch.2 */
};
ec_sync_info_t el6002_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 2, el6002_pdos + 0, EC_WD_DISABLE},
{3, EC_DIR_INPUT, 2, el6002_pdos + 2, EC_WD_DISABLE},
{0xff}
};
/****************************************************************************/
int el6002_init(el6002_t *ser, ec_master_t *master, u16 position,
ec_domain_t *domain)
{
int ret = 0;
ser->tty = ectty_create();
if (IS_ERR(ser->tty)) {
printk(KERN_ERR PFX "Failed to create tty.\n");
ret = PTR_ERR(ser->tty);
goto out_return;
}
ser->sc = NULL;
ser->max_tx_data_size = 22;
ser->max_rx_data_size = 22;
ser->tx_data = NULL;
ser->tx_data_size = 0;
ser->state = SER_REQUEST_INIT;
ser->tx_request_toggle = 0;
ser->rx_accepted_toggle = 0;
ser->control = 0x0000;
ser->off_ctrl = 0;
ser->off_tx = 0;
ser->off_status = 0;
ser->off_rx = 0;
if (!(ser->sc = ecrt_master_slave_config(
master, 0, position, Beckhoff_EL6002))) {
printk(KERN_ERR PFX "Failed to create slave configuration.\n");
ret = -EBUSY;
goto out_free_tty;
}
if (ecrt_slave_config_pdos(ser->sc, EC_END, el6002_syncs)) {
printk(KERN_ERR PFX "Failed to configure PDOs.\n");
ret = -ENOMEM;
goto out_free_tty;
}
ret = ecrt_slave_config_reg_pdo_entry(
ser->sc, 0x7001, 0x01, domain, NULL);
if (ret < 0) {
printk(KERN_ERR PFX "Failed to register PDO entry.\n");
goto out_free_tty;
}
ser->off_ctrl = ret;
ret = ecrt_slave_config_reg_pdo_entry(
ser->sc, 0x7000, 0x11, domain, NULL);
if (ret < 0) {
printk(KERN_ERR PFX "Failed to register PDO entry.\n");
goto out_free_tty;
}
ser->off_tx = ret;
ret = ecrt_slave_config_reg_pdo_entry(
ser->sc, 0x6001, 0x01, domain, NULL);
if (ret < 0) {
printk(KERN_ERR PFX "Failed to register PDO entry.\n");
goto out_free_tty;
}
ser->off_status = ret;
ret = ecrt_slave_config_reg_pdo_entry(
ser->sc, 0x6000, 0x11, domain, NULL);
if (ret < 0) {
printk(KERN_ERR PFX "Failed to register PDO entry.\n");
goto out_free_tty;
}
ser->off_rx = ret;
if (ser->max_tx_data_size > 0) {
ser->tx_data = kmalloc(ser->max_tx_data_size, GFP_KERNEL);
if (ser->tx_data == NULL) {
ret = -ENOMEM;
goto out_free_tty;
}
}
return 0;
out_free_tty:
ectty_free(ser->tty);
out_return:
return ret;
}
/****************************************************************************/
void el6002_clear(el6002_t *ser)
{
ectty_free(ser->tty);
if (ser->tx_data) {
kfree(ser->tx_data);
}
}
/****************************************************************************/
void el6002_run(el6002_t *ser, u8 *pd)
{
u16 status = EC_READ_U16(pd + ser->off_status);
u8 *rx_data = pd + ser->off_rx;
uint8_t tx_accepted_toggle, rx_request_toggle;
switch (ser->state) {
case SER_READY:
/* Send data */
tx_accepted_toggle = status & 0x0001;
if (tx_accepted_toggle != ser->tx_accepted_toggle) { // ready
ser->tx_data_size =
ectty_tx_data(ser->tty, ser->tx_data, ser->max_tx_data_size);
if (ser->tx_data_size) {
printk(KERN_INFO PFX "Sending %u bytes.\n", ser->tx_data_size);
ser->tx_request_toggle = !ser->tx_request_toggle;
ser->tx_accepted_toggle = tx_accepted_toggle;
}
}
/* Receive data */
rx_request_toggle = status & 0x0002;
if (rx_request_toggle != ser->rx_request_toggle) {
uint8_t rx_data_size = status >> 8;
ser->rx_request_toggle = rx_request_toggle;
printk(KERN_INFO PFX "Received %u bytes.\n", rx_data_size);
ectty_rx_data(ser->tty, rx_data, rx_data_size);
ser->rx_accepted_toggle = !ser->rx_accepted_toggle;
}
ser->control =
ser->tx_request_toggle |
ser->rx_accepted_toggle << 1 |
ser->tx_data_size << 8;
break;
case SER_REQUEST_INIT:
if (status & (1 << 2)) {
ser->control = 0x0000;
ser->state = SER_WAIT_FOR_INIT_RESPONSE;
} else {
ser->control = 1 << 2; // CW.2, request initialization
}
break;
case SER_WAIT_FOR_INIT_RESPONSE:
if (!(status & (1 << 2))) {
printk(KERN_INFO PFX "Init successful.\n");
ser->tx_accepted_toggle = 1;
ser->control = 0x0000;
ser->state = SER_READY;
}
break;
}
EC_WRITE_U16(pd + ser->off_ctrl, ser->control);
memcpy(pd + ser->off_tx, ser->tx_data, ser->tx_data_size);
}
/*****************************************************************************/
void run_serial_devices(u8 *pd)
{
el6002_t *ser;
list_for_each_entry(ser, &handlers, list) {
el6002_run(ser, pd);
}
}
/*****************************************************************************/
int create_serial_devices(ec_master_t *master, ec_domain_t *domain)
{
int i, ret;
ec_master_info_t master_info;
ec_slave_info_t slave_info;
el6002_t *ser, *next;
printk(KERN_INFO PFX "Registering serial devices...\n");
ret = ecrt_master(master, &master_info);
if (ret) {
printk(KERN_ERR PFX "Failed to obtain master information.\n");
goto out_return;
}
for (i = 0; i < master_info.slave_count; i++) {
ret = ecrt_master_get_slave(master, i, &slave_info);
if (ret) {
printk(KERN_ERR PFX "Failed to obtain slave information.\n");
goto out_free_handlers;
}
if (slave_info.vendor_id != VendorIdBeckhoff
|| slave_info.product_code != ProductCodeBeckhoffEL6002) {
continue;
}
printk(KERN_INFO PFX "Creating handler for serial device"
" at position %i\n", i);
ser = kmalloc(sizeof(*ser), GFP_KERNEL);
if (!ser) {
printk(KERN_ERR PFX "Failed to allocate serial device object.\n");
ret = -ENOMEM;
goto out_free_handlers;
}
ret = el6002_init(ser, master, i, domain);
if (ret) {
printk(KERN_ERR PFX "Failed to init serial device object.\n");
kfree(ser);
goto out_free_handlers;
}
list_add_tail(&ser->list, &handlers);
}
printk(KERN_INFO PFX "Finished.\n");
return 0;
out_free_handlers:
list_for_each_entry_safe(ser, next, &handlers, list) {
list_del(&ser->list);
el6002_clear(ser);
kfree(ser);
}
out_return:
return ret;
}
/*****************************************************************************/
void free_serial_devices(void)
{
el6002_t *ser, *next;
printk(KERN_INFO PFX "Cleaning up serial devices...\n");
list_for_each_entry_safe(ser, next, &handlers, list) {
list_del(&ser->list);
el6002_clear(ser);
kfree(ser);
}
printk(KERN_INFO PFX "Finished cleaning up serial devices.\n");
}
/*****************************************************************************/

35
examples/tty/serial.h Normal file
View File

@ -0,0 +1,35 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2008 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.
*
*****************************************************************************/
int create_serial_devices(ec_master_t *, ec_domain_t *);
void free_serial_devices(void);
void run_serial_devices(u8 *);
/*****************************************************************************/

View File

@ -40,7 +40,8 @@
#endif
#include "../../include/ecrt.h" // EtherCAT realtime interface
#include "../../include/ectty.h" // EtherCAT TTY interface
#include "serial.h"
/*****************************************************************************/
@ -70,264 +71,11 @@ static struct timer_list timer;
static uint8_t *domain1_pd; // process data memory
#define BusCouplerPos 0, 0
#define SerialPos 0, 1
#define Beckhoff_EK1100 0x00000002, 0x044c2c52
#define Beckhoff_EL6002 0x00000002, 0x17723052
// offsets for PDO entries
static unsigned int off_ctrl;
static unsigned int off_tx;
static unsigned int off_status;
static unsigned int off_rx;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{SerialPos, Beckhoff_EL6002, 0x7001, 0x01, &off_ctrl},
{SerialPos, Beckhoff_EL6002, 0x7000, 0x11, &off_tx},
{SerialPos, Beckhoff_EL6002, 0x6001, 0x01, &off_status},
{SerialPos, Beckhoff_EL6002, 0x6000, 0x11, &off_rx},
{}
};
static unsigned int counter = 0;
typedef enum {
SER_REQUEST_INIT,
SER_WAIT_FOR_INIT_RESPONSE,
SER_READY
} serial_state_t;
typedef struct {
size_t max_tx_data_size;
size_t max_rx_data_size;
uint8_t *tx_data;
uint8_t tx_data_size;
serial_state_t state;
uint8_t tx_request_toggle;
uint8_t tx_accepted_toggle;
uint8_t rx_request_toggle;
uint8_t rx_accepted_toggle;
uint16_t control;
} serial_device_t;
static serial_device_t *ser = NULL;
static ec_tty_t *tty = NULL;
/*****************************************************************************/
/* Slave 1, "EL6002"
* Vendor ID: 0x00000002
* Product code: 0x17723052
* Revision number: 0x00100000
*/
ec_pdo_entry_info_t slave_1_pdo_entries[] = {
{0x7001, 0x01, 16}, /* Ctrl */
{0x7000, 0x11, 8}, /* Data Out 0 */
{0x7000, 0x12, 8}, /* Data Out 1 */
{0x7000, 0x13, 8}, /* Data Out 2 */
{0x7000, 0x14, 8}, /* Data Out 3 */
{0x7000, 0x15, 8}, /* Data Out 4 */
{0x7000, 0x16, 8}, /* Data Out 5 */
{0x7000, 0x17, 8}, /* Data Out 6 */
{0x7000, 0x18, 8}, /* Data Out 7 */
{0x7000, 0x19, 8}, /* Data Out 8 */
{0x7000, 0x1a, 8}, /* Data Out 9 */
{0x7000, 0x1b, 8}, /* Data Out 10 */
{0x7000, 0x1c, 8}, /* Data Out 11 */
{0x7000, 0x1d, 8}, /* Data Out 12 */
{0x7000, 0x1e, 8}, /* Data Out 13 */
{0x7000, 0x1f, 8}, /* Data Out 14 */
{0x7000, 0x20, 8}, /* Data Out 15 */
{0x7000, 0x21, 8}, /* Data Out 16 */
{0x7000, 0x22, 8}, /* Data Out 17 */
{0x7000, 0x23, 8}, /* Data Out 18 */
{0x7000, 0x24, 8}, /* Data Out 19 */
{0x7000, 0x25, 8}, /* Data Out 20 */
{0x7000, 0x26, 8}, /* Data Out 21 */
{0x7011, 0x01, 16}, /* Ctrl */
{0x7010, 0x11, 8}, /* Data Out 0 */
{0x7010, 0x12, 8}, /* Data Out 1 */
{0x7010, 0x13, 8}, /* Data Out 2 */
{0x7010, 0x14, 8}, /* Data Out 3 */
{0x7010, 0x15, 8}, /* Data Out 4 */
{0x7010, 0x16, 8}, /* Data Out 5 */
{0x7010, 0x17, 8}, /* Data Out 6 */
{0x7010, 0x18, 8}, /* Data Out 7 */
{0x7010, 0x19, 8}, /* Data Out 8 */
{0x7010, 0x1a, 8}, /* Data Out 9 */
{0x7010, 0x1b, 8}, /* Data Out 10 */
{0x7010, 0x1c, 8}, /* Data Out 11 */
{0x7010, 0x1d, 8}, /* Data Out 12 */
{0x7010, 0x1e, 8}, /* Data Out 13 */
{0x7010, 0x1f, 8}, /* Data Out 14 */
{0x7010, 0x20, 8}, /* Data Out 15 */
{0x7010, 0x21, 8}, /* Data Out 16 */
{0x7010, 0x22, 8}, /* Data Out 17 */
{0x7010, 0x23, 8}, /* Data Out 18 */
{0x7010, 0x24, 8}, /* Data Out 19 */
{0x7010, 0x25, 8}, /* Data Out 20 */
{0x7010, 0x26, 8}, /* Data Out 21 */
{0x6001, 0x01, 16}, /* Status */
{0x6000, 0x11, 8}, /* Data In 0 */
{0x6000, 0x12, 8}, /* Data In 1 */
{0x6000, 0x13, 8}, /* Data In 2 */
{0x6000, 0x14, 8}, /* Data In 3 */
{0x6000, 0x15, 8}, /* Data In 4 */
{0x6000, 0x16, 8}, /* Data In 5 */
{0x6000, 0x17, 8}, /* Data In 6 */
{0x6000, 0x18, 8}, /* Data In 7 */
{0x6000, 0x19, 8}, /* Data In 8 */
{0x6000, 0x1a, 8}, /* Data In 9 */
{0x6000, 0x1b, 8}, /* Data In 10 */
{0x6000, 0x1c, 8}, /* Data In 11 */
{0x6000, 0x1d, 8}, /* Data In 12 */
{0x6000, 0x1e, 8}, /* Data In 13 */
{0x6000, 0x1f, 8}, /* Data In 14 */
{0x6000, 0x20, 8}, /* Data In 15 */
{0x6000, 0x21, 8}, /* Data In 16 */
{0x6000, 0x22, 8}, /* Data In 17 */
{0x6000, 0x23, 8}, /* Data In 18 */
{0x6000, 0x24, 8}, /* Data In 19 */
{0x6000, 0x25, 8}, /* Data In 20 */
{0x6000, 0x26, 8}, /* Data In 21 */
{0x6011, 0x01, 16}, /* Status */
{0x6010, 0x11, 8}, /* Data In 0 */
{0x6010, 0x12, 8}, /* Data In 1 */
{0x6010, 0x13, 8}, /* Data In 2 */
{0x6010, 0x14, 8}, /* Data In 3 */
{0x6010, 0x15, 8}, /* Data In 4 */
{0x6010, 0x16, 8}, /* Data In 5 */
{0x6010, 0x17, 8}, /* Data In 6 */
{0x6010, 0x18, 8}, /* Data In 7 */
{0x6010, 0x19, 8}, /* Data In 8 */
{0x6010, 0x1a, 8}, /* Data In 9 */
{0x6010, 0x1b, 8}, /* Data In 10 */
{0x6010, 0x1c, 8}, /* Data In 11 */
{0x6010, 0x1d, 8}, /* Data In 12 */
{0x6010, 0x1e, 8}, /* Data In 13 */
{0x6010, 0x1f, 8}, /* Data In 14 */
{0x6010, 0x20, 8}, /* Data In 15 */
{0x6010, 0x21, 8}, /* Data In 16 */
{0x6010, 0x22, 8}, /* Data In 17 */
{0x6010, 0x23, 8}, /* Data In 18 */
{0x6010, 0x24, 8}, /* Data In 19 */
{0x6010, 0x25, 8}, /* Data In 20 */
{0x6010, 0x26, 8}, /* Data In 21 */
};
ec_pdo_info_t slave_1_pdos[] = {
{0x1604, 23, slave_1_pdo_entries + 0}, /* COM RxPDO-Map Outputs Ch.1 */
{0x1605, 23, slave_1_pdo_entries + 23}, /* COM RxPDO-Map Outputs Ch.2 */
{0x1a04, 23, slave_1_pdo_entries + 46}, /* COM TxPDO-Map Inputs Ch.1 */
{0x1a05, 23, slave_1_pdo_entries + 69}, /* COM TxPDO-Map Inputs Ch.2 */
};
ec_sync_info_t slave_1_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 2, slave_1_pdos + 0, EC_WD_DISABLE},
{3, EC_DIR_INPUT, 2, slave_1_pdos + 2, EC_WD_DISABLE},
{0xff}
};
/****************************************************************************/
int serial_init(serial_device_t *ser, size_t max_tx, size_t max_rx)
{
ser->max_tx_data_size = max_tx;
ser->max_rx_data_size = max_rx;
ser->tx_data = NULL;
ser->tx_data_size = 0;
ser->state = SER_REQUEST_INIT;
ser->tx_request_toggle = 0;
ser->rx_accepted_toggle = 0;
ser->control = 0x0000;
if (max_tx > 0) {
ser->tx_data = kmalloc(max_tx, GFP_KERNEL);
if (ser->tx_data == NULL) {
return -ENOMEM;
}
}
return 0;
}
/****************************************************************************/
void serial_clear(serial_device_t *ser)
{
if (ser->tx_data) {
kfree(ser->tx_data);
}
}
/****************************************************************************/
void serial_run(serial_device_t *ser, uint16_t status, uint8_t *rx_data)
{
uint8_t tx_accepted_toggle, rx_request_toggle;
switch (ser->state) {
case SER_READY:
/* Send data */
tx_accepted_toggle = status & 0x0001;
if (tx_accepted_toggle != ser->tx_accepted_toggle) { // ready
ser->tx_data_size =
ectty_tx_data(tty, ser->tx_data, ser->max_tx_data_size);
if (ser->tx_data_size) {
printk(KERN_INFO PFX "Sending %u bytes.\n", ser->tx_data_size);
ser->tx_request_toggle = !ser->tx_request_toggle;
ser->tx_accepted_toggle = tx_accepted_toggle;
}
}
/* Receive data */
rx_request_toggle = status & 0x0002;
if (rx_request_toggle != ser->rx_request_toggle) {
uint8_t rx_data_size = status >> 8;
ser->rx_request_toggle = rx_request_toggle;
printk(KERN_INFO PFX "Received %u bytes.\n", rx_data_size);
ectty_rx_data(tty, rx_data, rx_data_size);
ser->rx_accepted_toggle = !ser->rx_accepted_toggle;
}
ser->control =
ser->tx_request_toggle |
ser->rx_accepted_toggle << 1 |
ser->tx_data_size << 8;
break;
case SER_REQUEST_INIT:
if (status & (1 << 2)) {
ser->control = 0x0000;
ser->state = SER_WAIT_FOR_INIT_RESPONSE;
} else {
ser->control = 1 << 2; // CW.2, request initialization
}
break;
case SER_WAIT_FOR_INIT_RESPONSE:
if (!(status & (1 << 2))) {
printk(KERN_INFO PFX "Init successful.\n");
ser->tx_accepted_toggle = 1;
ser->control = 0x0000;
ser->state = SER_READY;
}
break;
}
}
/*****************************************************************************/
void check_domain1_state(void)
@ -388,9 +136,7 @@ void cyclic_task(unsigned long data)
check_master_state();
}
serial_run(ser, EC_READ_U16(domain1_pd + off_status), domain1_pd + off_rx);
EC_WRITE_U16(domain1_pd + off_ctrl, ser->control);
memcpy(domain1_pd + off_tx, ser->tx_data, 22);
run_serial_devices(domain1_pd);
// send process data
down(&master_sem);
@ -432,31 +178,11 @@ int __init init_mini_module(void)
printk(KERN_INFO PFX "Starting...\n");
ser = kmalloc(sizeof(*ser), GFP_KERNEL);
if (!ser) {
printk(KERN_ERR PFX "Failed to allocate serial device object.\n");
ret = -ENOMEM;
goto out_return;
}
ret = serial_init(ser, 22, 22);
if (ret) {
printk(KERN_ERR PFX "Failed to init serial device object.\n");
goto out_free_serial;
}
tty = ectty_create();
if (IS_ERR(tty)) {
printk(KERN_ERR PFX "Failed to create tty.\n");
ret = PTR_ERR(tty);
goto out_serial;
}
master = ecrt_request_master(0);
if (!master) {
ret = -EBUSY;
printk(KERN_ERR PFX "Requesting master 0 failed.\n");
goto out_tty;
ret = -EBUSY;
goto out_return;
}
sema_init(&master_sem, 1);
@ -470,30 +196,18 @@ int __init init_mini_module(void)
// Create configuration for bus coupler
sc = ecrt_master_slave_config(master, BusCouplerPos, Beckhoff_EK1100);
if (!sc)
return -1;
if (!(sc = ecrt_master_slave_config(
master, SerialPos, Beckhoff_EL6002))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
return -1;
if (!sc) {
printk(KERN_ERR PFX "Failed to create slave config.\n");
ret = -ENOMEM;
goto out_release_master;
}
printk("Configuring PDOs...\n");
if (ecrt_slave_config_pdos(sc, EC_END, slave_1_syncs)) {
printk(KERN_ERR PFX "Failed to configure PDOs.\n");
return -1;
}
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
printk(KERN_ERR PFX "PDO entry registration failed!\n");
return -1;
}
create_serial_devices(master, domain1);
printk(KERN_INFO PFX "Activating master...\n");
if (ecrt_master_activate(master)) {
printk(KERN_ERR PFX "Failed to activate master!\n");
goto out_release_master;
goto out_free_serial;
}
// Get internal process data for domain
@ -508,15 +222,11 @@ int __init init_mini_module(void)
printk(KERN_INFO PFX "Started.\n");
return 0;
out_free_serial:
free_serial_devices();
out_release_master:
printk(KERN_ERR PFX "Releasing master...\n");
ecrt_release_master(master);
out_tty:
ectty_free(tty);
out_serial:
serial_clear(ser);
out_free_serial:
kfree(ser);
out_return:
printk(KERN_ERR PFX "Failed to load. Aborting.\n");
return ret;
@ -530,13 +240,11 @@ void __exit cleanup_mini_module(void)
del_timer_sync(&timer);
free_serial_devices();
printk(KERN_INFO PFX "Releasing master...\n");
ecrt_release_master(master);
ectty_free(tty);
serial_clear(ser);
kfree(ser);
printk(KERN_INFO PFX "Unloading.\n");
}

View File

@ -208,8 +208,6 @@ typedef struct {
/*****************************************************************************/
#ifndef __KERNEL__
/** Master information.
*
* This is used as an output parameter of ecrt_master().
@ -246,8 +244,6 @@ typedef struct {
char name[EC_MAX_STRING_LENGTH]; /**< Name of the slave. */
} ec_slave_info_t;
#endif // #ifndef __KERNEL__
/*****************************************************************************/
/** Domain working counter interpretation.
@ -539,8 +535,6 @@ ec_slave_config_t *ecrt_master_slave_config(
uint32_t product_code /**< Expected product code. */
);
#ifndef __KERNEL__
/** Obtains master information.
*
* No memory is allocated on the heap in
@ -573,6 +567,8 @@ int ecrt_master_get_slave(
information */
);
#ifndef __KERNEL__
/** Returns the proposed configuration of a slave's sync manager.
*
* Fills a given ec_sync_info_t structure with the attributes of a sync

View File

@ -156,7 +156,7 @@ int ecrt_master_get_slave(ec_master_t *master, uint16_t slave_position,
slave_info->error_flag = data.error_flag;
slave_info->sync_count = data.sync_count;
slave_info->sdo_count = data.sdo_count;
strncpy(slave_info->name, data.name, EC_IOCTL_STRING_SIZE);
strncpy(slave_info->name, data.name, EC_MAX_STRING_LENGTH);
return 0;
}

View File

@ -770,19 +770,14 @@ void ec_fsm_slave_scan_state_regalias(
}
if (datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
fsm->state = ec_fsm_slave_scan_state_error;
EC_ERR("Failed to read reg alias of slave %u: ",
fsm->slave->ring_position);
ec_datagram_print_wc_error(datagram);
return;
EC_WARN("Failed to read reg alias of slave %u.\n",
fsm->slave->ring_position);
} else {
slave->sii.alias = EC_READ_U16(datagram->data);
if (slave->master->debug_level)
EC_DBG("Alias of slave %u is %u.\n",
slave->ring_position,slave->sii.alias);
}
slave->sii.alias = EC_READ_U16(datagram->data);
if (slave->master->debug_level)
EC_DBG("Alias of slave %u is %u.\n",
slave->ring_position,slave->sii.alias);
if (slave->sii.mailbox_protocols & EC_MBOX_COE) {
ec_fsm_slave_scan_enter_preop(fsm);
} else {

View File

@ -51,8 +51,11 @@
/** SDO injection timeout in microseconds. */
#define EC_SDO_INJECTION_TIMEOUT 10000
/** time to send a byte in nanoseconds. */
#define EC_BYTE_TRANSMITION_TIME 80
/** Time to send a byte in nanoseconds.
*
* t_ns = 1 / (100 MBit/s / 8 bit/byte) = 80 ns/byte
*/
#define EC_BYTE_TRANSMISSION_TIME_NS 80
/** Number of state machine retries on datagram timeout. */
#define EC_FSM_RETRIES 3

View File

@ -159,7 +159,9 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
sema_init(&master->ext_queue_sem, 1);
INIT_LIST_HEAD(&master->external_datagram_queue);
ec_master_set_send_interval(master,1000000 / HZ); // send interval in IDLE phase
// send interval in IDLE phase
ec_master_set_send_interval(master, 1000000 / HZ);
INIT_LIST_HEAD(&master->domains);
@ -373,35 +375,35 @@ void ec_master_clear_slaves(ec_master_t *master)
// 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
// 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);
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);
}
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);
}
// Register requests
while (1) {
ec_reg_request_t *request;
if (list_empty(&master->reg_requests))
break;
// get first request
request = list_entry(master->reg_requests.next,
ec_reg_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding Reg request, slave %u does not exist anymore.\n",
request->slave->ring_position);
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->reg_queue);
}
// Register requests
while (1) {
ec_reg_request_t *request;
if (list_empty(&master->reg_requests))
break;
// get first request
request = list_entry(master->reg_requests.next,
ec_reg_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding Reg request, slave %u does not exist anymore.\n",
request->slave->ring_position);
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->reg_queue);
}
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
@ -689,85 +691,101 @@ void ec_master_leave_operation_phase(
/*****************************************************************************/
/** Injects external datagrams that fit into the datagram queue
/** Injects external datagrams that fit into the datagram queue.
*/
void ec_master_inject_external_datagrams(
ec_master_t *master /**< EtherCAT master */
)
ec_master_t *master /**< EtherCAT master */
)
{
ec_datagram_t *datagram, *n;
size_t queue_size = 0;
list_for_each_entry(datagram, &master->datagram_queue, queue) {
queue_size += datagram->data_size;
}
list_for_each_entry_safe(datagram, n, &master->external_datagram_queue, queue) {
queue_size += datagram->data_size;
if (queue_size <= master->max_queue_size) {
list_del_init(&datagram->queue);
ec_datagram_t *datagram, *n;
size_t queue_size = 0;
list_for_each_entry(datagram, &master->datagram_queue, queue) {
queue_size += datagram->data_size;
}
list_for_each_entry_safe(datagram, n, &master->external_datagram_queue,
queue) {
queue_size += datagram->data_size;
if (queue_size <= master->max_queue_size) {
list_del_init(&datagram->queue);
#if DEBUG_INJECT
if (master->debug_level) {
EC_DBG("Injecting external datagram %08x size=%u, queue_size=%u\n",(unsigned int)datagram,datagram->data_size,queue_size);
}
if (master->debug_level) {
EC_DBG("Injecting external datagram %08x size=%u,"
" queue_size=%u\n", (unsigned int) datagram,
datagram->data_size, queue_size);
}
#endif
#ifdef EC_HAVE_CYCLES
datagram->cycles_sent = 0;
datagram->cycles_sent = 0;
#endif
datagram->jiffies_sent = 0;
ec_master_queue_datagram(master, datagram);
}
else {
if (datagram->data_size > master->max_queue_size) {
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
EC_ERR("External datagram %08x is too large, size=%u, max_queue_size=%u\n",(unsigned int)datagram,datagram->data_size,master->max_queue_size);
}
else {
datagram->jiffies_sent = 0;
ec_master_queue_datagram(master, datagram);
}
else {
if (datagram->data_size > master->max_queue_size) {
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
EC_ERR("External datagram %p is too large,"
" size=%u, max_queue_size=%u\n",
datagram, datagram->data_size,
master->max_queue_size);
} else {
#ifdef EC_HAVE_CYCLES
cycles_t cycles_now = get_cycles();
if (cycles_now - datagram->cycles_sent
> sdo_injection_timeout_cycles) {
cycles_t cycles_now = get_cycles();
if (cycles_now - datagram->cycles_sent
> sdo_injection_timeout_cycles)
#else
if (jiffies - datagram->jiffies_sent
> sdo_injection_timeout_jiffies) {
if (jiffies - datagram->jiffies_sent
> sdo_injection_timeout_jiffies)
#endif
unsigned int time_us;
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
{
unsigned int time_us;
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
#ifdef EC_HAVE_CYCLES
time_us = (unsigned int) ((cycles_now - datagram->cycles_sent) * 1000LL) / cpu_khz;
time_us = (unsigned int)
((cycles_now - datagram->cycles_sent) * 1000LL)
/ cpu_khz;
#else
time_us = (unsigned int) ((jiffies - datagram->jiffies_sent) * 1000000 / HZ);
time_us = (unsigned int)
((jiffies - datagram->jiffies_sent) * 1000000 / HZ);
#endif
EC_ERR("Timeout %u us: injecting external datagram %08x size=%u, max_queue_size=%u\n",time_us,(unsigned int)datagram,datagram->data_size,master->max_queue_size);
}
else {
EC_ERR("Timeout %u us: injecting external datagram %p"
" size=%u, max_queue_size=%u\n",
time_us, datagram,
datagram->data_size, master->max_queue_size);
}
#if DEBUG_INJECT
if (master->debug_level) {
EC_DBG("Deferred injecting of external datagram %08x size=%u, queue_size=%u\n",(unsigned int)datagram,datagram->data_size,queue_size);
}
else if (master->debug_level) {
EC_DBG("Deferred injecting of external datagram %p"
" size=%u, queue_size=%u\n",
datagram, datagram->data_size, queue_size);
}
#endif
}
}
}
}
}
}
}
}
/*****************************************************************************/
/** sets the expected interval between calls to ecrt_master_send
and calculates the maximum amount of data to queue
/** Sets the expected interval between calls to ecrt_master_send
* and calculates the maximum amount of data to queue.
*/
void ec_master_set_send_interval(
ec_master_t *master, /**< EtherCAT master */
size_t send_interval /**< send interval */
)
ec_master_t *master, /**< EtherCAT master */
size_t send_interval /**< Send interval */
)
{
master->send_interval = send_interval;
master->max_queue_size = (send_interval * 1000) / EC_BYTE_TRANSMITION_TIME;
master->max_queue_size -= master->max_queue_size / 10;
master->send_interval = send_interval;
master->max_queue_size =
(send_interval * 1000) / EC_BYTE_TRANSMISSION_TIME_NS;
master->max_queue_size -= master->max_queue_size / 10;
}
/*****************************************************************************/
/** Places an external datagram in the sdo datagram queue.
@ -780,18 +798,23 @@ void ec_master_queue_external_datagram(
ec_datagram_t *queued_datagram;
down(&master->io_sem);
// check, if the datagram is already queued
list_for_each_entry(queued_datagram, &master->external_datagram_queue, queue) {
list_for_each_entry(queued_datagram, &master->external_datagram_queue,
queue) {
if (queued_datagram == datagram) {
datagram->state = EC_DATAGRAM_QUEUED;
return;
}
}
#if DEBUG_INJECT
if (master->debug_level) {
EC_DBG("Requesting external datagram %08x size=%u\n",(unsigned int)datagram,datagram->data_size);
EC_DBG("Requesting external datagram %p size=%u\n",
datagram, datagram->data_size);
}
#endif
list_add_tail(&datagram->queue, &master->external_datagram_queue);
datagram->state = EC_DATAGRAM_QUEUED;
#ifdef EC_HAVE_CYCLES
@ -1116,66 +1139,79 @@ void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
#ifdef EC_USE_HRTIMER
/*
* Sleep related functions:
*/
static enum hrtimer_restart ec_master_nanosleep_wakeup(struct hrtimer *timer)
{
struct hrtimer_sleeper *t =
container_of(timer, struct hrtimer_sleeper, timer);
struct task_struct *task = t->task;
struct hrtimer_sleeper *t =
container_of(timer, struct hrtimer_sleeper, timer);
struct task_struct *task = t->task;
t->task = NULL;
if (task)
wake_up_process(task);
t->task = NULL;
if (task)
wake_up_process(task);
return HRTIMER_NORESTART;
return HRTIMER_NORESTART;
}
/*****************************************************************************/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
/* compatibility with new hrtimer interface */
static inline ktime_t hrtimer_get_expires(const struct hrtimer *timer)
{
return timer->expires;
return timer->expires;
}
/*****************************************************************************/
static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
{
timer->expires = time;
timer->expires = time;
}
#endif
/*****************************************************************************/
void ec_master_nanosleep(const unsigned long nsecs)
{
struct hrtimer_sleeper t;
enum hrtimer_mode mode = HRTIMER_MODE_REL;
hrtimer_init(&t.timer, CLOCK_MONOTONIC,mode);
t.timer.function = ec_master_nanosleep_wakeup;
t.task = current;
struct hrtimer_sleeper t;
enum hrtimer_mode mode = HRTIMER_MODE_REL;
hrtimer_init(&t.timer, CLOCK_MONOTONIC, mode);
t.timer.function = ec_master_nanosleep_wakeup;
t.task = current;
#ifdef CONFIG_HIGH_RES_TIMERS
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_RESTART;
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_RESTART;
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26)
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28)
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
t.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
#endif
#endif
hrtimer_set_expires(&t.timer, ktime_set(0,nsecs));
do {
set_current_state(TASK_INTERRUPTIBLE);
hrtimer_start(&t.timer, hrtimer_get_expires(&t.timer), mode);
hrtimer_set_expires(&t.timer, ktime_set(0, nsecs));
if (likely(t.task))
schedule();
do {
set_current_state(TASK_INTERRUPTIBLE);
hrtimer_start(&t.timer, hrtimer_get_expires(&t.timer), mode);
hrtimer_cancel(&t.timer);
mode = HRTIMER_MODE_ABS;
if (likely(t.task))
schedule();
} while (t.task && !signal_pending(current));
hrtimer_cancel(&t.timer);
mode = HRTIMER_MODE_ABS;
} while (t.task && !signal_pending(current));
}
#endif // EC_USE_HRTIMER
/*****************************************************************************/
@ -1187,9 +1223,14 @@ static int ec_master_idle_thread(void *priv_data)
ec_slave_t *slave = NULL;
int fsm_exec;
size_t sent_bytes;
ec_master_set_send_interval(master,1000000 / HZ); // send interval in IDLE phase
// send interval in IDLE phase
ec_master_set_send_interval(master, 1000000 / HZ);
if (master->debug_level)
EC_DBG("Idle thread running with send interval = %d us, max data size=%d\n",master->send_interval,master->max_queue_size);
EC_DBG("Idle thread running with send interval = %d us,"
" max data size=%d\n", master->send_interval,
master->max_queue_size);
while (!kthread_should_stop()) {
ec_datagram_output_stats(&master->fsm_datagram);
@ -1218,17 +1259,29 @@ static int ec_master_idle_thread(void *priv_data)
}
ec_master_inject_external_datagrams(master);
ecrt_master_send(master);
sent_bytes = master->main_device.tx_skb[master->main_device.tx_ring_index]->len;
sent_bytes = master->main_device.tx_skb[
master->main_device.tx_ring_index]->len;
up(&master->io_sem);
if (ec_fsm_master_idle(&master->fsm))
ec_master_nanosleep(master->send_interval*1000);
else
ec_master_nanosleep(sent_bytes*EC_BYTE_TRANSMITION_TIME);
if (ec_fsm_master_idle(&master->fsm)) {
#ifdef EC_USE_HRTIMER
ec_master_nanosleep(master->send_interval * 1000);
#else
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
#endif
} else {
#ifdef EC_USE_HRTIMER
ec_master_nanosleep(sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS);
#else
schedule();
#endif
}
}
if (master->debug_level)
EC_DBG("Master IDLE thread exiting...\n");
return 0;
}
@ -1241,11 +1294,16 @@ static int ec_master_operation_thread(void *priv_data)
ec_master_t *master = (ec_master_t *) priv_data;
ec_slave_t *slave = NULL;
int fsm_exec;
if (master->debug_level)
EC_DBG("Operation thread running with fsm interval = %d us, max data size=%d\n",master->send_interval,master->max_queue_size);
EC_DBG("Operation thread running with fsm interval = %d us,"
" max data size=%d\n",
master->send_interval,
master->max_queue_size);
while (!kthread_should_stop()) {
ec_datagram_output_stats(&master->fsm_datagram);
if (master->injection_seq_rt == master->injection_seq_fsm) {
// output statistics
ec_master_output_stats(master);
@ -1266,8 +1324,19 @@ static int ec_master_operation_thread(void *priv_data)
if (fsm_exec)
master->injection_seq_fsm++;
}
#ifdef EC_USE_HRTIMER
// the op thread should not work faster than the sending RT thread
ec_master_nanosleep(master->send_interval*1000);
ec_master_nanosleep(master->send_interval * 1000);
#else
if (ec_fsm_master_idle(&master->fsm)) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
}
else {
schedule();
}
#endif
}
if (master->debug_level)
@ -2138,6 +2207,56 @@ ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
/*****************************************************************************/
int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
{
if (master->debug_level)
EC_DBG("ecrt_master(master = 0x%p, master_info = 0x%p)\n",
master, master_info);
master_info->slave_count = master->slave_count;
master_info->link_up = master->main_device.link_state;
master_info->scan_busy = master->scan_busy;
master_info->app_time = master->app_time;
return 0;
}
/*****************************************************************************/
int ecrt_master_get_slave(ec_master_t *master, uint16_t slave_position,
ec_slave_info_t *slave_info)
{
const ec_slave_t *slave;
if (down_interruptible(&master->master_sem)) {
return -EINTR;
}
slave = ec_master_find_slave_const(master, 0, slave_position);
slave_info->position = slave->ring_position;
slave_info->vendor_id = slave->sii.vendor_id;
slave_info->product_code = slave->sii.product_code;
slave_info->revision_number = slave->sii.revision_number;
slave_info->serial_number = slave->sii.serial_number;
slave_info->alias = slave->sii.alias;
slave_info->current_on_ebus = slave->sii.current_on_ebus;
slave_info->al_state = slave->current_state;
slave_info->error_flag = slave->error_flag;
slave_info->sync_count = slave->sii.sync_count;
slave_info->sdo_count = ec_slave_sdo_count(slave);
if (slave->sii.name) {
strncpy(slave_info->name, slave->sii.name, EC_MAX_STRING_LENGTH);
} else {
slave_info->name[0] = 0;
}
up(&master->master_sem);
return 0;
}
/*****************************************************************************/
void ecrt_master_callbacks(ec_master_t *master,
void (*send_cb)(void *), void (*receive_cb)(void *), void *cb_data)
{
@ -2218,6 +2337,8 @@ EXPORT_SYMBOL(ecrt_master_send);
EXPORT_SYMBOL(ecrt_master_send_ext);
EXPORT_SYMBOL(ecrt_master_receive);
EXPORT_SYMBOL(ecrt_master_callbacks);
EXPORT_SYMBOL(ecrt_master);
EXPORT_SYMBOL(ecrt_master_get_slave);
EXPORT_SYMBOL(ecrt_master_slave_config);
EXPORT_SYMBOL(ecrt_master_state);
EXPORT_SYMBOL(ecrt_master_application_time);

View File

@ -40,6 +40,7 @@
#include <linux/tty_flip.h>
#include <linux/termios.h>
#include <linux/timer.h>
#include <linux/version.h>
#include "../master/globals.h"
#include "../include/ectty.h"
@ -379,7 +380,11 @@ static int ec_tty_write(
/*****************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
static int ec_tty_put_char(struct tty_struct *tty, unsigned char ch)
#else
static void ec_tty_put_char(struct tty_struct *tty, unsigned char ch)
#endif
{
ec_tty_t *t = (ec_tty_t *) tty->driver_data;
@ -390,8 +395,14 @@ static void ec_tty_put_char(struct tty_struct *tty, unsigned char ch)
if (ec_tty_tx_space(t)) {
t->tx_buffer[t->tx_write_idx] = ch;
t->tx_write_idx = (t->tx_write_idx + 1) % EC_TTY_TX_BUFFER_SIZE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
return 1;
#endif
} else {
printk(KERN_WARNING PFX "%s(): Dropped a byte!\n", __func__);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
return 0;
#endif
}
}
@ -506,11 +517,19 @@ static void ec_tty_hangup(struct tty_struct *tty)
/*****************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
static int ec_tty_break(struct tty_struct *tty, int break_state)
#else
static void ec_tty_break(struct tty_struct *tty, int break_state)
#endif
{
#if EC_TTY_DEBUG >= 2
printk(KERN_INFO PFX "%s(break_state = %i).\n", __func__, break_state);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
return -EIO; // not implemented
#endif
}
/*****************************************************************************/