# HG changeset patch # User Martin Troxler # Date 1263926027 -3600 # Node ID a93fc03eeb06dd99ccc7e828877b006a29f9b2f6 # Parent 65786b1d30431b41a9f4e8908843dbfa09d1bba5# Parent a9143f82c7c503f9cef67a6a4ff4f4befdb485ea Merged diff -r 65786b1d3043 -r a93fc03eeb06 .hgignore --- a/.hgignore Tue Jan 19 19:31:55 2010 +0100 +++ b/.hgignore Tue Jan 19 19:33:47 2010 +0100 @@ -32,6 +32,48 @@ 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 diff -r 65786b1d3043 -r a93fc03eeb06 FEATURES --- a/FEATURES Tue Jan 19 19:31:55 2010 +0100 +++ b/FEATURES Tue Jan 19 19:33:47 2010 +0100 @@ -14,13 +14,16 @@ - 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. diff -r 65786b1d3043 -r a93fc03eeb06 TODO --- a/TODO Tue Jan 19 19:31:55 2010 +0100 +++ b/TODO Tue Jan 19 19:33:47 2010 +0100 @@ -10,6 +10,37 @@ 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 @@ "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. diff -r 65786b1d3043 -r a93fc03eeb06 configure.ac --- a/configure.ac Tue Jan 19 19:31:55 2010 +0100 +++ b/configure.ac Tue Jan 19 19:33:47 2010 +0100 @@ -475,6 +475,30 @@ 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 #----------------------------------------------------------------------------- diff -r 65786b1d3043 -r a93fc03eeb06 devices/generic.c --- a/devices/generic.c Tue Jan 19 19:31:55 2010 +0100 +++ b/devices/generic.c Tue Jan 19 19:33:47 2010 +0100 @@ -70,16 +70,16 @@ 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 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 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 @@ 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 @@ 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 @@ 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 @@ 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); diff -r 65786b1d3043 -r a93fc03eeb06 documentation/ethercat_doc.tex --- a/documentation/ethercat_doc.tex Tue Jan 19 19:31:55 2010 +0100 +++ b/documentation/ethercat_doc.tex Tue Jan 19 19:33:47 2010 +0100 @@ -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 @@ \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 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 The native drivers operate the hardware without interrupts. + + \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 @@ \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 @@ \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 @@ 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 @@ to understand how Linux handles network devices and their drivers, respectively. -\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. - -\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{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{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. + +\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{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: \newsavebox\boxopen \sbox\boxopen{\lstinline+open()+} @@ -1027,17 +1050,15 @@ 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 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. - -%------------------------------------------------------------------------------ - -\section{EtherCAT Device Drivers} -\label{sec:drivers} - -There are a few requirements for Ethernet network devices to function as -EtherCAT devices, when connected to an EtherCAT bus. - -\paragraph{Dedicated Interfaces} - -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{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{Native EtherCAT Device Drivers} +\label{sec:native-drivers} + +There are a few requirements, that applies to Ethernet hardware when used with +a native Ethernet driver with EtherCAT functionality. + +\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. + +\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 @@ 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 @@ %------------------------------------------------------------------------------ -\section{Device Selection} -\label{sec:deviceselection} - -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}). +\section{Generic EtherCAT Device Driver} +\label{sec:generic-driver} + +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 @@ %------------------------------------------------------------------------------ -\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 @@ 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. - -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}). +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. + +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 @@ 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 @@ \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 @@ \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 @@ \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 @@ 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 diff -r 65786b1d3043 -r a93fc03eeb06 documentation/images/Makefile --- a/documentation/images/Makefile Tue Jan 19 19:31:55 2010 +0100 +++ b/documentation/images/Makefile Tue Jan 19 19:33:47 2010 +0100 @@ -6,7 +6,6 @@ FIGS := \ app-config.fig \ - architecture.fig \ attach.fig \ dc.fig \ fmmus.fig \ @@ -18,14 +17,22 @@ 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) #----------------------------------------------------------------------------- diff -r 65786b1d3043 -r a93fc03eeb06 documentation/images/architecture.fig --- a/documentation/images/architecture.fig Tue Jan 19 19:31:55 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 diff -r 65786b1d3043 -r a93fc03eeb06 documentation/images/architecture.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/documentation/images/architecture.svg Tue Jan 19 19:33:47 2010 +0100 @@ -0,0 +1,1036 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ecdev_*() + EtherCAT Master Module + EtherCAT + Ethernet + NIC + NIC + Hardware + + ecrt_*() + + Application Module + + + Task + + Kernelspace + Userspace + + ecrt_*() + + netif_*() + + + ApplicationInterface + + + + + + GenericEthernetDriver Module + + StandardEthernet Driver + + + + + + + + + + + + + + + + + NIC + + + + UserspaceApplication + + + + + libethercat + + + + ApplicationInterface + + + + + + 'ethercat'Tool + + + + CharacterDevice + + + + DeviceInterface + + + + + + Network Stack + + + + + net_device + + + + net_device + + + + Native EtherCAT-capable Ethernet Driver + + + net_device + + EtherCAT + + + GenericEthernetDevice + + Packet Socket + + + + Master 0 + + + + Master 1 + + diff -r 65786b1d3043 -r a93fc03eeb06 examples/Makefile.am --- a/examples/Makefile.am Tue Jan 19 19:31:55 2010 +0100 +++ b/examples/Makefile.am Tue Jan 19 19:33:47 2010 +0100 @@ -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 \ diff -r 65786b1d3043 -r a93fc03eeb06 examples/tty/Kbuild.in --- a/examples/tty/Kbuild.in Tue Jan 19 19:31:55 2010 +0100 +++ b/examples/tty/Kbuild.in Tue Jan 19 19:33:47 2010 +0100 @@ -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 \ diff -r 65786b1d3043 -r a93fc03eeb06 examples/tty/Makefile.am --- a/examples/tty/Makefile.am Tue Jan 19 19:31:55 2010 +0100 +++ b/examples/tty/Makefile.am Tue Jan 19 19:33:47 2010 +0100 @@ -31,9 +31,12 @@ # #------------------------------------------------------------------------------ +noinst_HEADERS = \ + serial.h \ + tty.c + EXTRA_DIST = \ - Kbuild.in \ - tty.c + Kbuild.in BUILT_SOURCES = \ Kbuild diff -r 65786b1d3043 -r a93fc03eeb06 examples/tty/serial.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/tty/serial.c Tue Jan 19 19:33:47 2010 +0100 @@ -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 +#include + +#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"); +} + +/*****************************************************************************/ diff -r 65786b1d3043 -r a93fc03eeb06 examples/tty/serial.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/tty/serial.h Tue Jan 19 19:33:47 2010 +0100 @@ -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 *); + +/*****************************************************************************/ diff -r 65786b1d3043 -r a93fc03eeb06 examples/tty/tty.c --- a/examples/tty/tty.c Tue Jan 19 19:31:55 2010 +0100 +++ b/examples/tty/tty.c Tue Jan 19 19:33:47 2010 +0100 @@ -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 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 @@ 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 @@ 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) { + printk(KERN_ERR PFX "Requesting master 0 failed.\n"); ret = -EBUSY; - printk(KERN_ERR PFX "Requesting master 0 failed.\n"); - goto out_tty; + goto out_return; } sema_init(&master_sem, 1); @@ -470,30 +196,18 @@ // 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; - } - - 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; - } + if (!sc) { + printk(KERN_ERR PFX "Failed to create slave config.\n"); + ret = -ENOMEM; + goto out_release_master; + } + + 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 @@ 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 @@ 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"); } diff -r 65786b1d3043 -r a93fc03eeb06 include/ecrt.h --- a/include/ecrt.h Tue Jan 19 19:31:55 2010 +0100 +++ b/include/ecrt.h Tue Jan 19 19:33:47 2010 +0100 @@ -208,8 +208,6 @@ /*****************************************************************************/ -#ifndef __KERNEL__ - /** Master information. * * This is used as an output parameter of ecrt_master(). @@ -246,8 +244,6 @@ 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 @@ uint32_t product_code /**< Expected product code. */ ); -#ifndef __KERNEL__ - /** Obtains master information. * * No memory is allocated on the heap in @@ -573,6 +567,8 @@ 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 diff -r 65786b1d3043 -r a93fc03eeb06 lib/master.c --- a/lib/master.c Tue Jan 19 19:31:55 2010 +0100 +++ b/lib/master.c Tue Jan 19 19:33:47 2010 +0100 @@ -156,7 +156,7 @@ 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; } diff -r 65786b1d3043 -r a93fc03eeb06 master/globals.h --- a/master/globals.h Tue Jan 19 19:31:55 2010 +0100 +++ b/master/globals.h Tue Jan 19 19:33:47 2010 +0100 @@ -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 diff -r 65786b1d3043 -r a93fc03eeb06 master/master.c --- a/master/master.c Tue Jan 19 19:31:55 2010 +0100 +++ b/master/master.c Tue Jan 19 19:33:47 2010 +0100 @@ -159,7 +159,9 @@ 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 @@ // 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); - } - - // 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); - } + 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); + } for (slave = master->slaves; slave < master->slaves + master->slave_count; @@ -689,84 +691,100 @@ /*****************************************************************************/ -/** 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_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_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); #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; -#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->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 %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) { -#endif - unsigned int time_us; - list_del_init(&datagram->queue); - datagram->state = EC_DATAGRAM_ERROR; + if (jiffies - datagram->jiffies_sent + > sdo_injection_timeout_jiffies) +#endif + { + 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); -#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 { + time_us = (unsigned int) + ((jiffies - datagram->jiffies_sent) * 1000000 / HZ); +#endif + 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); - } -#endif - } - } - } - } -} - -/*****************************************************************************/ - -/** sets the expected interval between calls to ecrt_master_send - and calculates the maximum amount of data to queue + 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. */ void ec_master_set_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; -} - + 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_TRANSMISSION_TIME_NS; + master->max_queue_size -= master->max_queue_size / 10; +} /*****************************************************************************/ @@ -780,18 +798,23 @@ 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 @@ /*****************************************************************************/ + +#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; - - t->task = NULL; - if (task) - wake_up_process(task); - - return HRTIMER_NORESTART; -} + 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); + + 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; -} -#endif - + 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; -#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); - - if (likely(t.task)) - schedule(); - - hrtimer_cancel(&t.timer); - mode = HRTIMER_MODE_ABS; - - } while (t.task && !signal_pending(current)); -} - + 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); + + if (likely(t.task)) + schedule(); + + hrtimer_cancel(&t.timer); + mode = HRTIMER_MODE_ABS; + + } while (t.task && !signal_pending(current)); +} + +#endif // EC_USE_HRTIMER /*****************************************************************************/ @@ -1187,9 +1223,14 @@ 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 @@ } 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 @@ 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 @@ 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 @@ /*****************************************************************************/ +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_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); diff -r 65786b1d3043 -r a93fc03eeb06 tty/module.c --- a/tty/module.c Tue Jan 19 19:31:55 2010 +0100 +++ b/tty/module.c Tue Jan 19 19:33:47 2010 +0100 @@ -40,6 +40,7 @@ #include #include #include +#include #include "../master/globals.h" #include "../include/ectty.h" @@ -379,7 +380,11 @@ /*****************************************************************************/ +#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 @@ 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 @@ /*****************************************************************************/ +#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 } /*****************************************************************************/