# HG changeset patch # User Florian Pose # Date 1255950835 -7200 # Node ID cfcaf46c5b3f7379d3dd11e79ad62212e11a3a8d # Parent 373cd456dc260542eb8b0927cbca4a28daada54b# Parent 91ae9c95dd2eb9267370151db463e1525724778a Merge x86_64 fixes. diff -r 373cd456dc26 -r cfcaf46c5b3f NEWS --- a/NEWS Mon Oct 19 13:12:40 2009 +0200 +++ b/NEWS Mon Oct 19 13:13:55 2009 +0200 @@ -45,6 +45,8 @@ 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 diff -r 373cd456dc26 -r cfcaf46c5b3f TODO --- a/TODO Mon Oct 19 13:12:40 2009 +0200 +++ b/TODO Mon Oct 19 13:13:55 2009 +0200 @@ -20,7 +20,6 @@ "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 @@ * 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: diff -r 373cd456dc26 -r cfcaf46c5b3f configure.ac --- a/configure.ac Mon Oct 19 13:12:40 2009 +0200 +++ b/configure.ac Mon Oct 19 13:13:55 2009 +0200 @@ -102,6 +102,29 @@ 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 #------------------------------------------------------------------------------ diff -r 373cd456dc26 -r cfcaf46c5b3f devices/Kbuild.in --- a/devices/Kbuild.in Mon Oct 19 13:12:40 2009 +0200 +++ b/devices/Kbuild.in Mon Oct 19 13:13:55 2009 +0200 @@ -37,6 +37,13 @@ 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 diff -r 373cd456dc26 -r cfcaf46c5b3f devices/Makefile.am --- a/devices/Makefile.am Mon Oct 19 13:12:40 2009 +0200 +++ b/devices/Makefile.am Mon Oct 19 13:13:55 2009 +0200 @@ -68,6 +68,7 @@ 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_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 diff -r 373cd456dc26 -r cfcaf46c5b3f devices/generic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/generic.c Mon Oct 19 13:13:55 2009 +0200 @@ -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 +#include +#include +#include +#include /* ARPHRD_ETHER */ +#include + +#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 "); +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 */ + +/*****************************************************************************/ diff -r 373cd456dc26 -r cfcaf46c5b3f include/ecrt.h --- a/include/ecrt.h Mon Oct 19 13:12:40 2009 +0200 +++ b/include/ecrt.h Mon Oct 19 13:13:55 2009 +0200 @@ -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 @@ 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 @@ 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 @@ ); /** 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. diff -r 373cd456dc26 -r cfcaf46c5b3f lib/master.c --- a/lib/master.c Mon Oct 19 13:12:40 2009 +0200 +++ b/lib/master.c Mon Oct 19 13:13:55 2009 +0200 @@ -335,6 +335,16 @@ /*****************************************************************************/ +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_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; +} + +/*****************************************************************************/ diff -r 373cd456dc26 -r cfcaf46c5b3f master/cdev.c --- a/master/cdev.c Mon Oct 19 13:12:40 2009 +0200 +++ b/master/cdev.c Mon Oct 19 13:13:55 2009 +0200 @@ -175,6 +175,7 @@ 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 @@ /*****************************************************************************/ +/** 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 @@ /*****************************************************************************/ +/** 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 @@ 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 @@ 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; diff -r 373cd456dc26 -r cfcaf46c5b3f master/fsm_coe.c --- a/master/fsm_coe.c Mon Oct 19 13:12:40 2009 +0200 +++ b/master/fsm_coe.c Mon Oct 19 13:13:55 2009 +0200 @@ -1821,7 +1821,6 @@ 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 @@ 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 @@ 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); diff -r 373cd456dc26 -r cfcaf46c5b3f master/globals.h --- a/master/globals.h Mon Oct 19 13:12:40 2009 +0200 +++ b/master/globals.h Mon Oct 19 13:13:55 2009 +0200 @@ -248,30 +248,6 @@ #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; diff -r 373cd456dc26 -r cfcaf46c5b3f master/ioctl.h --- a/master/ioctl.h Mon Oct 19 13:12:40 2009 +0200 +++ b/master/ioctl.h Mon Oct 19 13:13:55 2009 +0200 @@ -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 @@ uint32_t eoe_handler_count; #endif uint8_t phase; + uint8_t active; uint8_t scan_busy; struct { uint8_t address[6]; diff -r 373cd456dc26 -r cfcaf46c5b3f master/master.c --- a/master/master.c Mon Oct 19 13:12:40 2009 +0200 +++ b/master/master.c Mon Oct 19 13:13:55 2009 +0200 @@ -121,6 +121,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /** Transition function from OPERATION to IDLE phase. */ -void ec_master_leave_operation_phase(ec_master_t *master - /**< EtherCAT master */) -{ - ec_slave_t *slave; -#ifdef EC_EOE - ec_eoe_t *eoe; -#endif +void ec_master_leave_operation_phase( + ec_master_t *master /**< EtherCAT master */ + ) +{ + 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 @@ 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 @@ 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_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_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 */ diff -r 373cd456dc26 -r cfcaf46c5b3f master/master.h --- a/master/master.h Mon Oct 19 13:12:40 2009 +0200 +++ b/master/master.h Mon Oct 19 13:13:55 2009 +0200 @@ -108,6 +108,7 @@ 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 @@ 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. */ diff -r 373cd456dc26 -r cfcaf46c5b3f tool/CommandMaster.cpp --- a/tool/CommandMaster.cpp Mon Oct 19 13:12:40 2009 +0200 +++ b/tool/CommandMaster.cpp Mon Oct 19 13:13:55 2009 +0200 @@ -91,6 +91,7 @@ } cout << endl + << " Active: " << (data.active ? "yes" : "no") << endl << " Slaves: " << data.slave_count << endl << " Ethernet devices:" << endl;