Merge x86_64 fixes.
This commit is contained in:
commit
21dacadf01
2
NEWS
2
NEWS
|
|
@ -45,6 +45,8 @@ Changes since 1.4.0:
|
|||
methods to let an application transfer SDOs before activating the master
|
||||
(thanks to Stefan Weiser).
|
||||
* Fixed SDO upload segment response (thanks to Christoph Peter).
|
||||
* Fixed SDO upload segment response for 10 bytes mailbox length (thanks to
|
||||
Joerg Mohre).
|
||||
* SDO entry access rights are shown in 'ethercat sdos'.
|
||||
* Added 64-bit data access macros to application header.
|
||||
* Added debug level for all masters as a module parameter. Thanks to Erwin
|
||||
|
|
|
|||
6
TODO
6
TODO
|
|
@ -20,7 +20,6 @@ Version 1.5.0:
|
|||
"System Time" register instead of using the application time.
|
||||
- Check if register 0x0980 is working, to avoid clearing it when
|
||||
configuring.
|
||||
- Create an interface to query the System Time Difference registers.
|
||||
* Remove byte-swapping functions from user space.
|
||||
* EoE:
|
||||
- Only execute one EoE handler per cycle.
|
||||
|
|
@ -50,6 +49,11 @@ Version 1.5.0:
|
|||
* Check for Enable SDO Complete Access flag.
|
||||
* Implement CompleteAccess for command-line tool.
|
||||
* Implement CompleteAccess for SDO uploads.
|
||||
* Implement identifier parameter for cstruct command.
|
||||
* Implement sync delimiter for cstruct command.
|
||||
* Change SDO index at runtime for SDO request.
|
||||
* Implement ecrt_slave_config_request_state().
|
||||
* Output skipped datagrams again.
|
||||
|
||||
Future issues:
|
||||
|
||||
|
|
|
|||
23
configure.ac
23
configure.ac
|
|
@ -101,6 +101,29 @@ AC_SUBST(LINUX_KERNEL_VERSION,[$linuxversion])
|
|||
AC_SUBST(LINUX_MOD_PATH,[/lib/modules/$kernelrelease/ethercat])
|
||||
AC_MSG_RESULT([$LINUX_SOURCE_DIR (Kernel $LINUX_KERNEL_RELEASE)])
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Generic Ethernet driver
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_ENABLE([generic],
|
||||
AS_HELP_STRING([--enable-generic],
|
||||
[Enable generic Ethernet driver]),
|
||||
[
|
||||
case "${enableval}" in
|
||||
yes) enablegeneric=1
|
||||
;;
|
||||
no) enablegeneric=0
|
||||
;;
|
||||
*) AC_MSG_ERROR([Invalid value for --enable-generic])
|
||||
;;
|
||||
esac
|
||||
],
|
||||
[enablegeneric=0]
|
||||
)
|
||||
|
||||
AM_CONDITIONAL(ENABLE_GENERIC, test "x$enablegeneric" = "x1")
|
||||
AC_SUBST(ENABLE_GENERIC,[$enablegeneric])
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# 8139too driver
|
||||
#------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ REV := $(shell if test -s $(src)/../revision; then \
|
|||
hg id -i $(src)/.. 2>/dev/null || echo "unknown"; \
|
||||
fi)
|
||||
|
||||
ifeq (@ENABLE_GENERIC@,1)
|
||||
EC_GENERIC_OBJ := generic.o
|
||||
obj-m += ec_generic.o
|
||||
ec_generic-objs := $(EC_GENERIC_OBJ)
|
||||
CFLAGS_$(EC_GENERIC_OBJ) = -DREV=$(REV)
|
||||
endif
|
||||
|
||||
ifeq (@ENABLE_8139TOO@,1)
|
||||
EC_8139TOO_OBJ := 8139too-@KERNEL_8139TOO@-ethercat.o
|
||||
obj-m += ec_8139too.o
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ noinst_HEADERS = \
|
|||
e100-2.6.29-ethercat.c \
|
||||
e100-2.6.29-orig.c \
|
||||
ecdev.h \
|
||||
generic.c \
|
||||
r8169-2.6.24-ethercat.c \
|
||||
r8169-2.6.24-orig.c \
|
||||
r8169-2.6.28-ethercat.c \
|
||||
|
|
@ -86,6 +87,9 @@ modules:
|
|||
|
||||
modules_install:
|
||||
mkdir -p $(DESTDIR)$(LINUX_MOD_PATH)
|
||||
if ENABLE_GENERIC
|
||||
cp $(srcdir)/ec_generic.ko $(DESTDIR)$(LINUX_MOD_PATH)
|
||||
endif
|
||||
if ENABLE_8139TOO
|
||||
cp $(srcdir)/ec_8139too.ko $(DESTDIR)$(LINUX_MOD_PATH)
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,407 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* $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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/** \file
|
||||
* EtherCAT generic Ethernet device module.
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/if_arp.h> /* ARPHRD_ETHER */
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "../globals.h"
|
||||
#include "ecdev.h"
|
||||
|
||||
#define PFX "ec_generic: "
|
||||
|
||||
#define ETH_P_ETHERCAT 0x88A4
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int __init ec_gen_init_module(void);
|
||||
void __exit ec_gen_cleanup_module(void);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** \cond */
|
||||
|
||||
MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
|
||||
MODULE_DESCRIPTION("EtherCAT master generic Ethernet device module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(EC_MASTER_VERSION);
|
||||
|
||||
/** \endcond */
|
||||
|
||||
struct list_head generic_devices;
|
||||
|
||||
typedef struct {
|
||||
struct list_head list;
|
||||
struct net_device *netdev;
|
||||
struct socket *socket;
|
||||
ec_device_t *ecdev;
|
||||
} ec_gen_device_t;
|
||||
|
||||
int ec_gen_device_open(ec_gen_device_t *);
|
||||
int ec_gen_device_stop(ec_gen_device_t *);
|
||||
int ec_gen_device_start_xmit(ec_gen_device_t *, struct sk_buff *);
|
||||
void ec_gen_device_poll(ec_gen_device_t *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int ec_gen_netdev_open(struct net_device *dev)
|
||||
{
|
||||
ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
|
||||
return ec_gen_device_open(gendev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int ec_gen_netdev_stop(struct net_device *dev)
|
||||
{
|
||||
ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
|
||||
return ec_gen_device_stop(gendev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int ec_gen_netdev_start_xmit(
|
||||
struct sk_buff *skb,
|
||||
struct net_device *dev
|
||||
)
|
||||
{
|
||||
ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
|
||||
return ec_gen_device_start_xmit(gendev, skb);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ec_gen_poll(struct net_device *dev)
|
||||
{
|
||||
ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
|
||||
ec_gen_device_poll(gendev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const struct net_device_ops ec_gen_netdev_ops = {
|
||||
.ndo_open = ec_gen_netdev_open,
|
||||
.ndo_stop = ec_gen_netdev_stop,
|
||||
.ndo_start_xmit = ec_gen_netdev_start_xmit,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Init generic device.
|
||||
*/
|
||||
int ec_gen_device_init(
|
||||
ec_gen_device_t *dev,
|
||||
struct net_device *real_netdev
|
||||
)
|
||||
{
|
||||
ec_gen_device_t **priv;
|
||||
char null = 0x00;
|
||||
|
||||
dev->ecdev = NULL;
|
||||
dev->socket = NULL;
|
||||
|
||||
dev->netdev = alloc_netdev(sizeof(ec_gen_device_t *), &null, ether_setup);
|
||||
if (!dev->netdev) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(dev->netdev->dev_addr, real_netdev->dev_addr, ETH_ALEN);
|
||||
dev->netdev->netdev_ops = &ec_gen_netdev_ops;
|
||||
priv = netdev_priv(dev->netdev);
|
||||
*priv = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Clear generic device.
|
||||
*/
|
||||
void ec_gen_device_clear(
|
||||
ec_gen_device_t *dev
|
||||
)
|
||||
{
|
||||
if (dev->ecdev) {
|
||||
ecdev_close(dev->ecdev);
|
||||
ecdev_withdraw(dev->ecdev);
|
||||
}
|
||||
if (dev->socket) {
|
||||
sock_release(dev->socket);
|
||||
}
|
||||
free_netdev(dev->netdev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Creates a network socket.
|
||||
*/
|
||||
int ec_gen_device_create_socket(
|
||||
ec_gen_device_t *dev,
|
||||
struct net_device *real_netdev
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
struct sockaddr_ll sa;
|
||||
|
||||
ret = sock_create_kern(PF_PACKET, SOCK_RAW, htons(ETH_P_ETHERCAT), &dev->socket);
|
||||
if (ret) {
|
||||
printk(KERN_ERR PFX "Failed to create socket.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
printk(KERN_ERR PFX "Binding socket to interface %i (%s).\n",
|
||||
real_netdev->ifindex, real_netdev->name);
|
||||
|
||||
memset(&sa, 0x00, sizeof(sa));
|
||||
sa.sll_family = AF_PACKET;
|
||||
sa.sll_protocol = htons(ETH_P_ETHERCAT);
|
||||
sa.sll_ifindex = real_netdev->ifindex;
|
||||
ret = kernel_bind(dev->socket, (struct sockaddr *) &sa, sizeof(sa));
|
||||
if (ret) {
|
||||
printk(KERN_ERR PFX "Failed to bind() socket to interface.\n");
|
||||
sock_release(dev->socket);
|
||||
dev->socket = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Offer generic device to master.
|
||||
*/
|
||||
int ec_gen_device_offer(
|
||||
ec_gen_device_t *dev,
|
||||
struct net_device *real_netdev
|
||||
)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dev->ecdev = ecdev_offer(dev->netdev, ec_gen_poll, THIS_MODULE);
|
||||
if (dev->ecdev) {
|
||||
if (ec_gen_device_create_socket(dev, real_netdev)) {
|
||||
ecdev_withdraw(dev->ecdev);
|
||||
dev->ecdev = NULL;
|
||||
} else if (ecdev_open(dev->ecdev)) {
|
||||
ecdev_withdraw(dev->ecdev);
|
||||
dev->ecdev = NULL;
|
||||
} else {
|
||||
ecdev_set_link(dev->ecdev, 1); // FIXME
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Open the device.
|
||||
*/
|
||||
int ec_gen_device_open(
|
||||
ec_gen_device_t *dev
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Stop the device.
|
||||
*/
|
||||
int ec_gen_device_stop(
|
||||
ec_gen_device_t *dev
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ec_gen_device_start_xmit(
|
||||
ec_gen_device_t *dev,
|
||||
struct sk_buff *skb
|
||||
)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
size_t len = skb->len;
|
||||
int ret;
|
||||
|
||||
iov.iov_base = skb->data;
|
||||
iov.iov_len = len;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
ret = kernel_sendmsg(dev->socket, &msg, &iov, 1, len);
|
||||
|
||||
return ret == len ? NETDEV_TX_OK : NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Polls the device.
|
||||
*/
|
||||
void ec_gen_device_poll(
|
||||
ec_gen_device_t *dev
|
||||
)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
char buf[2000]; // FIXME
|
||||
int ret, budget = 10; // FIXME
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 2000;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
do {
|
||||
ret = kernel_recvmsg(dev->socket, &msg, &iov, 1, iov.iov_len,
|
||||
MSG_DONTWAIT);
|
||||
if (ret > 0) {
|
||||
ecdev_receive(dev->ecdev, buf, ret);
|
||||
} else if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
budget--;
|
||||
} while (budget);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Offer device.
|
||||
*/
|
||||
int offer_device(
|
||||
struct net_device *netdev
|
||||
)
|
||||
{
|
||||
ec_gen_device_t *gendev;
|
||||
int ret = 0;
|
||||
|
||||
gendev = kmalloc(sizeof(ec_gen_device_t), GFP_KERNEL);
|
||||
if (!gendev) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = ec_gen_device_init(gendev, netdev);
|
||||
if (ret) {
|
||||
kfree(gendev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ec_gen_device_offer(gendev, netdev)) {
|
||||
list_add_tail(&gendev->list, &generic_devices);
|
||||
} else {
|
||||
ec_gen_device_clear(gendev);
|
||||
kfree(gendev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Clear devices.
|
||||
*/
|
||||
void clear_devices(void)
|
||||
{
|
||||
ec_gen_device_t *gendev, *next;
|
||||
|
||||
list_for_each_entry_safe(gendev, next, &generic_devices, list) {
|
||||
list_del(&gendev->list);
|
||||
ec_gen_device_clear(gendev);
|
||||
kfree(gendev);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Module initialization.
|
||||
*
|
||||
* Initializes \a master_count masters.
|
||||
* \return 0 on success, else < 0
|
||||
*/
|
||||
int __init ec_gen_init_module(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct net_device *netdev;
|
||||
|
||||
printk(KERN_INFO PFX "EtherCAT master generic Ethernet device module %s\n",
|
||||
EC_MASTER_VERSION);
|
||||
|
||||
INIT_LIST_HEAD(&generic_devices);
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
for_each_netdev(&init_net, netdev) {
|
||||
if (netdev->type != ARPHRD_ETHER)
|
||||
continue;
|
||||
ret = offer_device(netdev);
|
||||
if (ret) {
|
||||
read_unlock(&dev_base_lock);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
read_unlock(&dev_base_lock);
|
||||
return ret;
|
||||
|
||||
out_err:
|
||||
clear_devices();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Module cleanup.
|
||||
*
|
||||
* Clears all master instances.
|
||||
*/
|
||||
void __exit ec_gen_cleanup_module(void)
|
||||
{
|
||||
clear_devices();
|
||||
printk(KERN_INFO PFX "Unloading.\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** \cond */
|
||||
|
||||
module_init(ec_gen_init_module);
|
||||
module_exit(ec_gen_cleanup_module);
|
||||
|
||||
/** \endcond */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -45,7 +45,9 @@
|
|||
* ecrt_slave_config_dc() to configure a slave for cyclic operation, and
|
||||
* ecrt_master_application_time(), ecrt_master_sync_reference_clock() and
|
||||
* ecrt_master_sync_slave_clocks() for offset and drift compensation. The
|
||||
* EC_TIMEVAL2NANO() macro can be used for epoch time conversion.
|
||||
* EC_TIMEVAL2NANO() macro can be used for epoch time conversion, while the
|
||||
* ecrt_master_sync_monitor_queue() and ecrt_master_sync_monitor_process()
|
||||
* methods can be used to monitor the synchrony.
|
||||
* - Improved the callback mechanism. ecrt_master_callbacks() now takes two
|
||||
* callback functions for sending and receiving datagrams.
|
||||
* ecrt_master_send_ext() is used to execute the sending of non-application
|
||||
|
|
@ -55,6 +57,7 @@
|
|||
* ecrt_slave_config_sync_manager()).
|
||||
* - Added ecrt_slave_config_complete_sdo() method to download an SDO during
|
||||
* configuration via CompleteAccess.
|
||||
* - Added ecrt_master_deactivate() to remove the bus configuration.
|
||||
* - Added ecrt_open_master() and ecrt_master_reserve() separation for
|
||||
* userspace.
|
||||
* - Added bus information interface (methods ecrt_master(),
|
||||
|
|
@ -682,6 +685,18 @@ int ecrt_master_activate(
|
|||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/** Deactivates the master.
|
||||
*
|
||||
* Removes the bus configuration. All objects created by
|
||||
* ecrt_master_create_domain(), ecrt_master_slave_config(), ecrt_domain_data()
|
||||
* ecrt_slave_config_create_sdo_request() and
|
||||
* ecrt_slave_config_create_voe_handler() are freed, so pointers to them
|
||||
* become invalid.
|
||||
*/
|
||||
void ecrt_master_deactivate(
|
||||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/** Sends all datagrams in the queue.
|
||||
*
|
||||
* This method takes all datagrams, that have been queued for transmission,
|
||||
|
|
@ -762,6 +777,28 @@ void ecrt_master_sync_slave_clocks(
|
|||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/** Queues the DC synchonity monitoring datagram for sending.
|
||||
*
|
||||
* The datagram broadcast-reads all "System time difference" registers (\a
|
||||
* 0x092c) to get an upper estiomation of the DC synchony. The result can be
|
||||
* checked with the ecrt_master_sync_monitor_process() method.
|
||||
*/
|
||||
void ecrt_master_sync_monitor_queue(
|
||||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/** Processes the DC synchonity monitoring datagram.
|
||||
*
|
||||
* If the sync monitoring datagram was sent before with
|
||||
* ecrt_master_sync_monitor_queue(), the result can be queried with this
|
||||
* method.
|
||||
*
|
||||
* \return Upper estination of the maximum time difference in ns.
|
||||
*/
|
||||
uint32_t ecrt_master_sync_monitor_process(
|
||||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/******************************************************************************
|
||||
* Slave configuration methods
|
||||
*****************************************************************************/
|
||||
|
|
@ -782,13 +819,17 @@ int ecrt_slave_config_sync_manager(
|
|||
);
|
||||
|
||||
/** Configure a slave's watchdog times.
|
||||
*/
|
||||
*/
|
||||
void ecrt_slave_config_watchdog(
|
||||
ec_slave_config_t *sc, /**< Slave configuration. */
|
||||
uint16_t watchdog_divider, /**< Number of 40 ns intervals. Used as a
|
||||
base unit for all slave watchdogs. */
|
||||
base unit for all slave watchdogs. If set
|
||||
to zero, the value is not written, so the
|
||||
default ist used. */
|
||||
uint16_t watchdog_intervals /**< Number of base intervals for process
|
||||
data watchdog. */
|
||||
data watchdog. If set to zero, the value
|
||||
is not written, so the default is used.
|
||||
*/
|
||||
);
|
||||
|
||||
/** Add a PDO to a sync manager's PDO assignment.
|
||||
|
|
|
|||
35
lib/master.c
35
lib/master.c
|
|
@ -335,6 +335,16 @@ int ecrt_master_activate(ec_master_t *master)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_deactivate(ec_master_t *master)
|
||||
{
|
||||
if (ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL) == -1) {
|
||||
fprintf(stderr, "Failed to deactivate master: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_send(ec_master_t *master)
|
||||
{
|
||||
if (ioctl(master->fd, EC_IOCTL_SEND, NULL) == -1) {
|
||||
|
|
@ -394,3 +404,28 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
|
|||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_sync_monitor_queue(ec_master_t *master)
|
||||
{
|
||||
if (ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL) == -1) {
|
||||
fprintf(stderr, "Failed to queue sync monitor datagram: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
|
||||
{
|
||||
uint32_t time_diff;
|
||||
|
||||
if (ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff) == -1) {
|
||||
time_diff = 0xffffffff;
|
||||
fprintf(stderr, "Failed to process sync monitor datagram: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return time_diff;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ int ec_cdev_ioctl_master(
|
|||
data.eoe_handler_count = ec_master_eoe_handler_count(master);
|
||||
#endif
|
||||
data.phase = (uint8_t) master->phase;
|
||||
data.active = (uint8_t) master->active;
|
||||
data.scan_busy = master->scan_busy;
|
||||
up(&master->master_sem);
|
||||
|
||||
|
|
@ -1647,6 +1648,23 @@ int ec_cdev_ioctl_activate(
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Deactivates the master.
|
||||
*/
|
||||
int ec_cdev_ioctl_deactivate(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
unsigned long arg, /**< ioctl() argument. */
|
||||
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
|
||||
)
|
||||
{
|
||||
if (unlikely(!priv->requested))
|
||||
return -EPERM;
|
||||
|
||||
ecrt_master_deactivate(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Send frames.
|
||||
*/
|
||||
int ec_cdev_ioctl_send(
|
||||
|
|
@ -1769,6 +1787,50 @@ int ec_cdev_ioctl_sync_slaves(
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Queue the sync monitoring datagram.
|
||||
*/
|
||||
int ec_cdev_ioctl_sync_mon_queue(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
unsigned long arg, /**< ioctl() argument. */
|
||||
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
|
||||
)
|
||||
{
|
||||
if (unlikely(!priv->requested))
|
||||
return -EPERM;
|
||||
|
||||
down(&master->io_sem);
|
||||
ecrt_master_sync_monitor_queue(master);
|
||||
up(&master->io_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Processes the sync monitoring datagram.
|
||||
*/
|
||||
int ec_cdev_ioctl_sync_mon_process(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
unsigned long arg, /**< ioctl() argument. */
|
||||
ec_cdev_priv_t *priv /**< Private data structure of file handle. */
|
||||
)
|
||||
{
|
||||
uint32_t time_diff;
|
||||
|
||||
if (unlikely(!priv->requested))
|
||||
return -EPERM;
|
||||
|
||||
down(&master->io_sem);
|
||||
time_diff = ecrt_master_sync_monitor_process(master);
|
||||
up(&master->io_sem);
|
||||
|
||||
if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Configure a sync manager.
|
||||
*/
|
||||
int ec_cdev_ioctl_sc_sync(
|
||||
|
|
@ -3262,6 +3324,10 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
return ec_cdev_ioctl_activate(master, arg, priv);
|
||||
case EC_IOCTL_DEACTIVATE:
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
return ec_cdev_ioctl_deactivate(master, arg, priv);
|
||||
case EC_IOCTL_SEND:
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
|
|
@ -3284,6 +3350,14 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
return ec_cdev_ioctl_sync_slaves(master, arg, priv);
|
||||
case EC_IOCTL_SYNC_MON_QUEUE:
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
return ec_cdev_ioctl_sync_mon_queue(master, arg, priv);
|
||||
case EC_IOCTL_SYNC_MON_PROCESS:
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
return ec_cdev_ioctl_sync_mon_process(master, arg, priv);
|
||||
case EC_IOCTL_SC_SYNC:
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
|
|
|
|||
|
|
@ -1821,7 +1821,6 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
|
|||
uint8_t *data, mbox_prot;
|
||||
size_t rec_size, data_size;
|
||||
ec_sdo_request_t *request = fsm->request;
|
||||
uint32_t seg_size;
|
||||
unsigned int last_segment;
|
||||
|
||||
if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
|
||||
|
|
@ -1899,14 +1898,11 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
|
|||
return;
|
||||
}
|
||||
|
||||
last_segment = EC_READ_U8(data + 2) & 0x01;
|
||||
seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
|
||||
if (rec_size > 10) {
|
||||
data_size = rec_size - 3; /* Header of segment upload is smaller than
|
||||
normal upload */
|
||||
} else { // == 10
|
||||
/* seg_size contains the number of trailing bytes to ignore. */
|
||||
data_size = rec_size - seg_size;
|
||||
data_size = rec_size - 3; /* Header of segment upload is smaller than
|
||||
normal upload */
|
||||
if (rec_size == 10) {
|
||||
uint8_t seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
|
||||
data_size -= seg_size;
|
||||
}
|
||||
|
||||
if (request->data_size + data_size > fsm->complete_size) {
|
||||
|
|
@ -1920,6 +1916,7 @@ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
|
|||
memcpy(request->data + request->data_size, data + 3, data_size);
|
||||
request->data_size += data_size;
|
||||
|
||||
last_segment = EC_READ_U8(data + 2) & 0x01;
|
||||
if (!last_segment) {
|
||||
fsm->toggle = !fsm->toggle;
|
||||
ec_fsm_coe_up_prepare_segment_request(fsm);
|
||||
|
|
|
|||
|
|
@ -248,30 +248,6 @@ enum {
|
|||
#define EC_DBG(fmt, args...) \
|
||||
printk(KERN_DEBUG "EtherCAT DEBUG: " fmt, ##args)
|
||||
|
||||
/** Convenience macro for defining read-only SysFS attributes.
|
||||
*
|
||||
* This results in creating a static variable called attr_\a NAME. The SysFS
|
||||
* file will be world-readable.
|
||||
*
|
||||
* \param NAME name of the attribute to create.
|
||||
*/
|
||||
#define EC_SYSFS_READ_ATTR(NAME) \
|
||||
static struct attribute attr_##NAME = { \
|
||||
.name = EC_STR(NAME), .owner = THIS_MODULE, .mode = S_IRUGO \
|
||||
}
|
||||
|
||||
/** Convenience macro for defining read-write SysFS attributes.
|
||||
*
|
||||
* This results in creating a static variable called attr_\a NAME. The SysFS
|
||||
* file will be word-readable plus owner-writable.
|
||||
*
|
||||
* \param NAME name of the attribute to create.
|
||||
*/
|
||||
#define EC_SYSFS_READ_WRITE_ATTR(NAME) \
|
||||
static struct attribute attr_##NAME = { \
|
||||
.name = EC_STR(NAME), .owner = THIS_MODULE, .mode = S_IRUGO | S_IWUSR \
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
extern char *ec_master_version_str;
|
||||
|
|
|
|||
|
|
@ -86,40 +86,43 @@
|
|||
#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1a)
|
||||
#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x1b, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_ACTIVATE EC_IOR(0x1c, size_t)
|
||||
#define EC_IOCTL_SEND EC_IO(0x1d)
|
||||
#define EC_IOCTL_RECEIVE EC_IO(0x1e)
|
||||
#define EC_IOCTL_MASTER_STATE EC_IOR(0x1f, ec_master_state_t)
|
||||
#define EC_IOCTL_APP_TIME EC_IOW(0x20, ec_ioctl_app_time_t)
|
||||
#define EC_IOCTL_SYNC_REF EC_IO(0x21)
|
||||
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x22)
|
||||
#define EC_IOCTL_SC_SYNC EC_IOW(0x23, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x24, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x25, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x26, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x27, ec_ioctl_add_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x28, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x29, ec_ioctl_reg_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_DC EC_IOW(0x2a, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_SDO EC_IOW(0x2b, ec_ioctl_sc_sdo_t)
|
||||
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2c, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SC_VOE EC_IOWR(0x2d, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SC_STATE EC_IOWR(0x2e, ec_ioctl_sc_state_t)
|
||||
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x2f)
|
||||
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x20)
|
||||
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x31)
|
||||
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x32, ec_ioctl_domain_state_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x33, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x34, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x35, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x36, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x37, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x38, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x39, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ EC_IOW(0x3a, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3b, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3c, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x3d, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_DATA EC_IOWR(0x3e, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_DEACTIVATE EC_IO(0x1d)
|
||||
#define EC_IOCTL_SEND EC_IO(0x1e)
|
||||
#define EC_IOCTL_RECEIVE EC_IO(0x1f)
|
||||
#define EC_IOCTL_MASTER_STATE EC_IOR(0x20, ec_master_state_t)
|
||||
#define EC_IOCTL_APP_TIME EC_IOW(0x21, ec_ioctl_app_time_t)
|
||||
#define EC_IOCTL_SYNC_REF EC_IO(0x22)
|
||||
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x23)
|
||||
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x24)
|
||||
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x25, uint32_t)
|
||||
#define EC_IOCTL_SC_SYNC EC_IOW(0x26, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x27, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x28, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x29, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x2a, ec_ioctl_add_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x2b, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x2c, ec_ioctl_reg_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_DC EC_IOW(0x2d, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_SDO EC_IOW(0x2e, ec_ioctl_sc_sdo_t)
|
||||
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2f, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SC_VOE EC_IOWR(0x20, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SC_STATE EC_IOWR(0x31, ec_ioctl_sc_state_t)
|
||||
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x32)
|
||||
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x33)
|
||||
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x34)
|
||||
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x35, ec_ioctl_domain_state_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x36, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x37, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x38, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x39, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x3a, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x3b, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x3c, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ EC_IOW(0x3d, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3e, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3f, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x40, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_DATA EC_IOWR(0x41, ec_ioctl_voe_t)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
@ -135,6 +138,7 @@ typedef struct {
|
|||
uint32_t eoe_handler_count;
|
||||
#endif
|
||||
uint8_t phase;
|
||||
uint8_t active;
|
||||
uint8_t scan_busy;
|
||||
struct {
|
||||
uint8_t address[6];
|
||||
|
|
|
|||
236
master/master.c
236
master/master.c
|
|
@ -121,6 +121,7 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
init_MUTEX(&master->device_sem);
|
||||
|
||||
master->phase = EC_ORPHANED;
|
||||
master->active = 0;
|
||||
master->injection_seq_fsm = 0;
|
||||
master->injection_seq_rt = 0;
|
||||
|
||||
|
|
@ -226,12 +227,23 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
EC_ERR("Failed to allocate synchronisation datagram.\n");
|
||||
goto out_clear_ref_sync;
|
||||
}
|
||||
|
||||
// init sync monitor datagram
|
||||
ec_datagram_init(&master->sync_mon_datagram);
|
||||
snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE, "syncmon");
|
||||
ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
|
||||
if (ret < 0) {
|
||||
ec_datagram_clear(&master->sync_mon_datagram);
|
||||
EC_ERR("Failed to allocate sync monitoring datagram.\n");
|
||||
goto out_clear_sync;
|
||||
}
|
||||
|
||||
ec_master_find_dc_ref_clock(master);
|
||||
|
||||
// init character device
|
||||
ret = ec_cdev_init(&master->cdev, master, device_number);
|
||||
if (ret)
|
||||
goto out_clear_sync;
|
||||
goto out_clear_sync_mon;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
|
||||
master->class_device = device_create(class, NULL,
|
||||
|
|
@ -260,6 +272,8 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
|
||||
out_clear_cdev:
|
||||
ec_cdev_clear(&master->cdev);
|
||||
out_clear_sync_mon:
|
||||
ec_datagram_clear(&master->sync_mon_datagram);
|
||||
out_clear_sync:
|
||||
ec_datagram_clear(&master->sync_datagram);
|
||||
out_clear_ref_sync:
|
||||
|
|
@ -298,6 +312,7 @@ void ec_master_clear(
|
|||
ec_master_clear_slave_configs(master);
|
||||
ec_master_clear_slaves(master);
|
||||
|
||||
ec_datagram_clear(&master->sync_mon_datagram);
|
||||
ec_datagram_clear(&master->sync_datagram);
|
||||
ec_datagram_clear(&master->ref_sync_datagram);
|
||||
ec_fsm_master_clear(&master->fsm);
|
||||
|
|
@ -350,6 +365,69 @@ void ec_master_clear_slaves(ec_master_t *master)
|
|||
|
||||
master->dc_ref_clock = NULL;
|
||||
|
||||
// external requests are obsolete, so we wake pending waiters and remove
|
||||
// them from the list
|
||||
//
|
||||
// SII 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);
|
||||
}
|
||||
|
||||
// SDO requests
|
||||
while (1) {
|
||||
ec_master_sdo_request_t *request;
|
||||
if (list_empty(&master->slave_sdo_requests))
|
||||
break;
|
||||
// get first request
|
||||
request = list_entry(master->slave_sdo_requests.next,
|
||||
ec_master_sdo_request_t, list);
|
||||
list_del_init(&request->list); // dequeue
|
||||
EC_INFO("Discarding SDO request, slave %u does not exist anymore.\n",
|
||||
request->slave->ring_position);
|
||||
request->req.state = EC_INT_REQUEST_FAILURE;
|
||||
wake_up(&master->sdo_queue);
|
||||
}
|
||||
|
||||
// FoE requests
|
||||
while (1) {
|
||||
ec_master_foe_request_t *request;
|
||||
if (list_empty(&master->foe_requests))
|
||||
break;
|
||||
// get first request
|
||||
request = list_entry(master->foe_requests.next,
|
||||
ec_master_foe_request_t, list);
|
||||
list_del_init(&request->list); // dequeue
|
||||
EC_INFO("Discarding FOE request, slave %u does not exist anymore.\n",
|
||||
request->slave->ring_position);
|
||||
request->req.state = EC_INT_REQUEST_FAILURE;
|
||||
wake_up(&master->foe_queue);
|
||||
}
|
||||
|
||||
for (slave = master->slaves;
|
||||
slave < master->slaves + master->slave_count;
|
||||
slave++) {
|
||||
|
|
@ -592,67 +670,17 @@ out_allow:
|
|||
|
||||
/** Transition function from OPERATION to IDLE phase.
|
||||
*/
|
||||
void ec_master_leave_operation_phase(ec_master_t *master
|
||||
/**< EtherCAT master */)
|
||||
void ec_master_leave_operation_phase(
|
||||
ec_master_t *master /**< EtherCAT master */
|
||||
)
|
||||
{
|
||||
ec_slave_t *slave;
|
||||
#ifdef EC_EOE
|
||||
ec_eoe_t *eoe;
|
||||
#endif
|
||||
if (master->active)
|
||||
ecrt_master_deactivate(master);
|
||||
|
||||
if (master->debug_level)
|
||||
EC_DBG("OPERATION -> IDLE.\n");
|
||||
|
||||
master->phase = EC_IDLE;
|
||||
|
||||
#ifdef EC_EOE
|
||||
ec_master_eoe_stop(master);
|
||||
#endif
|
||||
ec_master_thread_stop(master);
|
||||
|
||||
master->send_cb = ec_master_internal_send_cb;
|
||||
master->receive_cb = ec_master_internal_receive_cb;
|
||||
master->cb_data = master;
|
||||
|
||||
down(&master->master_sem);
|
||||
ec_master_clear_domains(master);
|
||||
ec_master_clear_slave_configs(master);
|
||||
up(&master->master_sem);
|
||||
|
||||
for (slave = master->slaves;
|
||||
slave < master->slaves + master->slave_count;
|
||||
slave++) {
|
||||
|
||||
// set states for all slaves
|
||||
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
|
||||
|
||||
// mark for reconfiguration, because the master could have no
|
||||
// possibility for a reconfiguration between two sequential operation
|
||||
// phases.
|
||||
slave->force_config = 1;
|
||||
}
|
||||
|
||||
#ifdef EC_EOE
|
||||
// ... but leave EoE slaves in OP
|
||||
list_for_each_entry(eoe, &master->eoe_handlers, list) {
|
||||
if (ec_eoe_is_open(eoe))
|
||||
ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
|
||||
}
|
||||
#endif
|
||||
|
||||
master->app_time = 0ULL;
|
||||
master->app_start_time = 0ULL;
|
||||
master->has_start_time = 0;
|
||||
|
||||
if (ec_master_thread_start(master, ec_master_idle_thread,
|
||||
"EtherCAT-IDLE"))
|
||||
EC_WARN("Failed to restart master thread!\n");
|
||||
#ifdef EC_EOE
|
||||
ec_master_eoe_start(master);
|
||||
#endif
|
||||
|
||||
master->allow_scan = 1;
|
||||
master->allow_config = 1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -1635,6 +1663,11 @@ int ecrt_master_activate(ec_master_t *master)
|
|||
if (master->debug_level)
|
||||
EC_DBG("ecrt_master_activate(master = 0x%p)\n", master);
|
||||
|
||||
if (master->active) {
|
||||
EC_WARN("%s: Master already active!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
down(&master->master_sem);
|
||||
|
||||
// finish all domains
|
||||
|
|
@ -1684,11 +1717,80 @@ int ecrt_master_activate(ec_master_t *master)
|
|||
|
||||
master->allow_config = 1; // request the current configuration
|
||||
master->allow_scan = 1; // allow re-scanning on topology change
|
||||
master->active = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_deactivate(ec_master_t *master)
|
||||
{
|
||||
ec_slave_t *slave;
|
||||
#ifdef EC_EOE
|
||||
ec_eoe_t *eoe;
|
||||
#endif
|
||||
|
||||
if (master->debug_level)
|
||||
EC_DBG("ecrt_master_deactivate(master = 0x%x)\n", (u32) master);
|
||||
|
||||
if (!master->active) {
|
||||
EC_WARN("%s: Master not active.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef EC_EOE
|
||||
ec_master_eoe_stop(master);
|
||||
#endif
|
||||
ec_master_thread_stop(master);
|
||||
|
||||
master->send_cb = ec_master_internal_send_cb;
|
||||
master->receive_cb = ec_master_internal_receive_cb;
|
||||
master->cb_data = master;
|
||||
|
||||
down(&master->master_sem);
|
||||
ec_master_clear_domains(master);
|
||||
ec_master_clear_slave_configs(master);
|
||||
up(&master->master_sem);
|
||||
|
||||
for (slave = master->slaves;
|
||||
slave < master->slaves + master->slave_count;
|
||||
slave++) {
|
||||
|
||||
// set states for all slaves
|
||||
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
|
||||
|
||||
// mark for reconfiguration, because the master could have no
|
||||
// possibility for a reconfiguration between two sequential operation
|
||||
// phases.
|
||||
slave->force_config = 1;
|
||||
}
|
||||
|
||||
#ifdef EC_EOE
|
||||
// ... but leave EoE slaves in OP
|
||||
list_for_each_entry(eoe, &master->eoe_handlers, list) {
|
||||
if (ec_eoe_is_open(eoe))
|
||||
ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
|
||||
}
|
||||
#endif
|
||||
|
||||
master->app_time = 0ULL;
|
||||
master->app_start_time = 0ULL;
|
||||
master->has_start_time = 0;
|
||||
|
||||
if (ec_master_thread_start(master, ec_master_idle_thread,
|
||||
"EtherCAT-IDLE"))
|
||||
EC_WARN("Failed to restart master thread!\n");
|
||||
#ifdef EC_EOE
|
||||
ec_master_eoe_start(master);
|
||||
#endif
|
||||
|
||||
master->allow_scan = 1;
|
||||
master->allow_config = 1;
|
||||
master->active = 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_send(ec_master_t *master)
|
||||
{
|
||||
ec_datagram_t *datagram, *n;
|
||||
|
|
@ -1899,10 +2001,30 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_sync_monitor_queue(ec_master_t *master)
|
||||
{
|
||||
ec_datagram_zero(&master->sync_mon_datagram);
|
||||
ec_master_queue_datagram(master, &master->sync_mon_datagram);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
|
||||
{
|
||||
if (master->sync_mon_datagram.state == EC_DATAGRAM_RECEIVED) {
|
||||
return EC_READ_U32(master->sync_mon_datagram.data) & 0x7fffffff;
|
||||
} else {
|
||||
return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** \cond */
|
||||
|
||||
EXPORT_SYMBOL(ecrt_master_create_domain);
|
||||
EXPORT_SYMBOL(ecrt_master_activate);
|
||||
EXPORT_SYMBOL(ecrt_master_deactivate);
|
||||
EXPORT_SYMBOL(ecrt_master_send);
|
||||
EXPORT_SYMBOL(ecrt_master_send_ext);
|
||||
EXPORT_SYMBOL(ecrt_master_receive);
|
||||
|
|
@ -1912,6 +2034,8 @@ EXPORT_SYMBOL(ecrt_master_state);
|
|||
EXPORT_SYMBOL(ecrt_master_application_time);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_monitor_queue);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_monitor_process);
|
||||
|
||||
/** \endcond */
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ struct ec_master {
|
|||
ec_fsm_master_t fsm; /**< Master state machine. */
|
||||
ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
|
||||
ec_master_phase_t phase; /**< Master phase. */
|
||||
unsigned int active; /**< Master has been activated. */
|
||||
unsigned int injection_seq_fsm; /**< Datagram injection sequence number
|
||||
for the FSM side. */
|
||||
unsigned int injection_seq_rt; /**< Datagram injection sequence number
|
||||
|
|
@ -125,6 +126,8 @@ struct ec_master {
|
|||
reference clock to the master clock. */
|
||||
ec_datagram_t sync_datagram; /**< Datagram used for DC drift
|
||||
compensation. */
|
||||
ec_datagram_t sync_mon_datagram; /**< Datagram used for DC synchronisation
|
||||
monitoring. */
|
||||
ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */
|
||||
|
||||
unsigned int scan_busy; /**< Current scan state. */
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ void CommandMaster::execute(MasterDevice &m, const StringVector &args)
|
|||
}
|
||||
|
||||
cout << endl
|
||||
<< " Active: " << (data.active ? "yes" : "no") << endl
|
||||
<< " Slaves: " << data.slave_count << endl
|
||||
<< " Ethernet devices:" << endl;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue