Merge
This commit is contained in:
commit
c27bbf7495
42
.hgignore
42
.hgignore
|
|
@ -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
|
||||
|
|
|
|||
13
FEATURES
13
FEATURES
|
|
@ -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
64
TODO
|
|
@ -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.
|
||||
|
|
|
|||
24
configure.ac
24
configure.ac
|
|
@ -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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -31,10 +31,13 @@
|
|||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
EXTRA_DIST = \
|
||||
Kbuild.in \
|
||||
noinst_HEADERS = \
|
||||
serial.h \
|
||||
tty.c
|
||||
|
||||
EXTRA_DIST = \
|
||||
Kbuild.in
|
||||
|
||||
BUILT_SOURCES = \
|
||||
Kbuild
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -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 *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
359
master/master.c
359
master/master.c
|
|
@ -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);
|
||||
|
|
|
|||
19
tty/module.c
19
tty/module.c
|
|
@ -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
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
Loading…
Reference in New Issue