# HG changeset patch # User Florian Pose # Date 1148893736 0 # Node ID 0d4119024f558469672805d40c2916a804f6c578 # Parent 5cff10efb927bf1e25ac31c2a74bdbf41810c5fd MERGE -r361:425 trunk -> branches/stable-1.0: EoE, License, State machines. diff -r 5cff10efb927 -r 0d4119024f55 Makefile --- a/Makefile Mon Apr 24 10:47:03 2006 +0000 +++ b/Makefile Mon May 29 09:08:56 2006 +0000 @@ -10,7 +10,8 @@ # # The IgH EtherCAT Master is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 of the License. +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. # # The IgH EtherCAT Master is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -21,36 +22,43 @@ # 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# #------------------------------------------------------------------------------ -ifneq ($(KERNELRELEASE),) - #------------------------------------------------------------------------------ # kbuild section +ifneq ($(KERNELRELEASE),) + obj-m := master/ devices/ #------------------------------------------------------------------------------ - -else - -#------------------------------------------------------------------------------ # default section +else + ifneq ($(wildcard ethercat.conf),) include ethercat.conf else -KERNEL := `uname -r` -DEVICEINDEX := 99 +KERNEL := $(shell uname -r) endif -KERNELDIR := /lib/modules/$(KERNEL)/build +KERNEL_DIR := /lib/modules/$(KERNEL)/build +CURRENT_DIR := $(shell pwd) modules: - $(MAKE) -C $(KERNELDIR) M=`pwd` + $(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean: cleandoc - $(MAKE) -C $(KERNELDIR) M=`pwd` clean + $(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean doc: doxygen Doxyfile @@ -58,9 +66,8 @@ cleandoc: @rm -rf doc - install: - @./install.sh $(KERNEL) $(DEVICEINDEX) + @script/install.sh $(KERNEL) #------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 README --- a/README Mon Apr 24 10:47:03 2006 +0000 +++ b/README Mon May 29 09:08:56 2006 +0000 @@ -77,7 +77,8 @@ The IgH EtherCAT Master is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; version 2 of the License. +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. The IgH EtherCAT Master is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -88,4 +89,13 @@ 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 right to use EtherCAT Technology is granted and comes free of +charge under condition of compatibility of product made by +Licensee. People intending to distribute/sell products based on the +code, have to sign an agreement to guarantee that products using +software based on IgH EtherCAT master stay compatible with the actual +EtherCAT specification (which are released themselves as an open +standard) as the (only) precondition to have the right to use EtherCAT +Technology, IP and trade marks. + ------------------------------------------------------------------------------- diff -r 5cff10efb927 -r 0d4119024f55 TODO --- a/TODO Mon Apr 24 10:47:03 2006 +0000 +++ b/TODO Mon May 29 09:08:56 2006 +0000 @@ -6,10 +6,9 @@ - Mailbox-Handler - Dispatching of received mailbox data? -- Ethernet over EtherCAT (EoE) - File access over EtherCAT (FoE) -- Number of frames, the NIC can handle +- Number of frames the NIC can handle - Remove slave type information from master / read XML files from user space - eepro100 driver diff -r 5cff10efb927 -r 0d4119024f55 devices/8139too.c --- a/devices/8139too.c Mon Apr 24 10:47:03 2006 +0000 +++ b/devices/8139too.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -120,7 +130,7 @@ */ -#define DRV_NAME "8139too_ec" +#define DRV_NAME "ec_8139too" #define DRV_VERSION "0.9.27" #include @@ -329,7 +339,12 @@ {0,} }; -MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +//MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ static struct { const char str[ETH_GSTRING_LEN]; @@ -2219,8 +2234,7 @@ else { ecdev_receive(rtl_ec_dev, - &rx_ring[ring_offset + 4] + ETH_HLEN, - pkt_size - ETH_HLEN); + &rx_ring[ring_offset + 4], pkt_size); dev->last_rx = jiffies; tp->stats.rx_bytes += pkt_size; tp->stats.rx_packets++; diff -r 5cff10efb927 -r 0d4119024f55 devices/Makefile --- a/devices/Makefile Mon Apr 24 10:47:03 2006 +0000 +++ b/devices/Makefile Mon May 29 09:08:56 2006 +0000 @@ -12,7 +12,8 @@ # # The IgH EtherCAT Master is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 of the License. +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. # # The IgH EtherCAT Master is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,6 +24,15 @@ # 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# #------------------------------------------------------------------------------ ifneq ($(KERNELRELEASE),) diff -r 5cff10efb927 -r 0d4119024f55 devices/ecdev.h --- a/devices/ecdev.h Mon Apr 24 10:47:03 2006 +0000 +++ b/devices/ecdev.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 ethercat.conf.tmpl --- a/ethercat.conf.tmpl Mon Apr 24 10:47:03 2006 +0000 +++ b/ethercat.conf.tmpl Mon May 29 09:08:56 2006 +0000 @@ -9,10 +9,9 @@ # #------------------------------------------------------------------------------ +# # The kernel to compile the EtherCAT sources against +# KERNEL := `uname -r` -# PCI index of the EtherCAT device -DEVICEINDEX := 99 - #------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 ethercat.sh --- a/ethercat.sh Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -#!/bin/sh - -#------------------------------------------------------------------------------ -# -# EtherCAT rc script -# -# $Id$ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ - -CONFIGFILE=/etc/sysconfig/ethercat - -#------------------------------------------------------------------------------ - -print_usage() -{ - echo "Usage: $0 { start | stop | restart }" -} - -unload_module() -{ - if lsmod | grep ^$1 > /dev/null; then - echo " unloading module \"$1\"..." - rmmod $1 || exit 1 - fi -} - -#------------------------------------------------------------------------------ - -# Get parameters -if [ $# -eq 0 ]; then - print_usage - exit 1 -fi - -ACTION=$1 - -# Load configuration from sysconfig - -if [ -f $CONFIGFILE ]; then - . $CONFIGFILE -else - echo "ERROR: Configuration file \"$CONFIGFILE\" not found!" - exit 1 -fi - -case $ACTION in - start | restart) - echo "Starting EtherCAT master..." - - # remove modules - unload_module 8139too - unload_module 8139cp - unload_module ec_8139too - unload_module ec_master - - echo " loading master modules..." - if ! modprobe ec_8139too ec_device_index=$DEVICEINDEX; then - echo "ERROR: Failed to load module!" - exit 1 - fi - ;; - - stop) - echo "Stopping EtherCAT master..." - unload_module ec_8139too - unload_module ec_master - if ! modprobe 8139too; then - echo "Warning: Failed to restore 8139too module." - fi - ;; - - *) - print_usage - exit 1 -esac - -echo "done." -exit 0 - -#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 examples/mini/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mini/Makefile Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,70 @@ +#---------------------------------------------------------------- +# +# Makefile +# +# Minimal EtherCAT module. +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#---------------------------------------------------------------- + +ifneq ($(KERNELRELEASE),) + +#---------------------------------------------------------------- +# kbuild section +#---------------------------------------------------------------- + +obj-m := ec_mini.o + +ec_mini-objs := mini.o + +#---------------------------------------------------------------- + +else + +#---------------------------------------------------------------- +# default section +#---------------------------------------------------------------- + +ifneq ($(wildcard ethercat.conf),) +include ethercat.conf +else +KERNELDIR = /usr/src/linux +endif + +modules: + $(MAKE) -C $(KERNELDIR) M=`pwd` + +clean: + $(MAKE) -C $(KERNELDIR) M=`pwd` clean + +#---------------------------------------------------------------- + +endif diff -r 5cff10efb927 -r 0d4119024f55 examples/mini/ethercat.conf.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mini/ethercat.conf.tmpl Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,13 @@ +#------------------------------------------------------------------------------ +# +# EtherCAT Konfigurationsdatei Kernel 2.6 +# +# $Id$ +# +# This file is a versioned template configuration. Copy it to "ethercat.conf" +# (which is ignored by Subversion) and adjust it to your needs. +# +#------------------------------------------------------------------------------ + +KERNELDIR = /usr/src/linux + diff -r 5cff10efb927 -r 0d4119024f55 examples/mini/mini.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mini/mini.c Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,259 @@ +/****************************************************************************** + * + * m i n i . c + * + * Minimal module for EtherCAT. + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "../../include/ecrt.h" // EtherCAT realtime interface + +#define ASYNC +#define FREQUENCY 100 + +/*****************************************************************************/ + +struct timer_list timer; + +// EtherCAT +ec_master_t *master = NULL; +ec_domain_t *domain1 = NULL; +spinlock_t master_lock = SPIN_LOCK_UNLOCKED; + +// data fields +//void *r_ssi_input, *r_ssi_status, *r_4102[3]; + +// channels +uint32_t k_pos; +uint8_t k_stat; + +ec_field_init_t domain1_fields[] = { + {NULL, "3", "Beckhoff", "EL5001", "InputValue", 0}, + {NULL, "2", "Beckhoff", "EL4132", "OutputValue", 0}, + {} +}; + +/*****************************************************************************/ + +void run(unsigned long data) +{ + static unsigned int counter = 0; + + spin_lock(&master_lock); + +#ifdef ASYNC + // receive + ecrt_master_async_receive(master); + ecrt_domain_process(domain1); +#else + // send and receive + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_sync_io(master); + ecrt_domain_process(domain1); +#endif + + // process data + //k_pos = EC_READ_U32(r_ssi); + +#ifdef ASYNC + // send + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_async_send(master); +#endif + + spin_unlock(&master_lock); + + if (counter) { + counter--; + } + else { + counter = FREQUENCY; + //printk(KERN_INFO "k_pos = %i\n", k_pos); + //printk(KERN_INFO "k_stat = 0x%02X\n", k_stat); + } + + // restart timer + timer.expires += HZ / FREQUENCY; + add_timer(&timer); +} + +/*****************************************************************************/ + +int request_lock(void *data) +{ + spin_lock_bh(&master_lock); + return 0; // access allowed +} + +/*****************************************************************************/ + +void release_lock(void *data) +{ + spin_unlock_bh(&master_lock); +} + +/*****************************************************************************/ + +int __init init_mini_module(void) +{ + printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); + + if ((master = ecrt_request_master(0)) == NULL) { + printk(KERN_ERR "Requesting master 0 failed!\n"); + goto out_return; + } + + ecrt_master_callbacks(master, request_lock, release_lock, NULL); + + printk(KERN_INFO "Registering domain...\n"); + if (!(domain1 = ecrt_master_create_domain(master))) + { + printk(KERN_ERR "Domain creation failed!\n"); + goto out_release_master; + } + + printk(KERN_INFO "Registering domain fields...\n"); + if (ecrt_domain_register_field_list(domain1, domain1_fields)) { + printk(KERN_ERR "Field registration failed!\n"); + goto out_release_master; + } + + printk(KERN_INFO "Activating master...\n"); + if (ecrt_master_activate(master)) { + printk(KERN_ERR "Failed to activate master!\n"); + goto out_release_master; + } + +#if 0 + if (ecrt_master_fetch_sdo_lists(master)) { + printk(KERN_ERR "Failed to fetch SDO lists!\n"); + goto out_deactivate; + } + ecrt_master_print(master, 2); +#else + ecrt_master_print(master, 0); +#endif + +#if 0 + if (!(slave = ecrt_master_get_slave(master, "5"))) { + printk(KERN_ERR "Failed to get slave 5!\n"); + goto out_deactivate; + } + + if (ecrt_slave_sdo_write_exp8(slave, 0x4061, 1, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4061, 2, 1) || + ecrt_slave_sdo_write_exp8(slave, 0x4061, 3, 1) || + ecrt_slave_sdo_write_exp8(slave, 0x4066, 0, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4067, 0, 4) || + ecrt_slave_sdo_write_exp8(slave, 0x4068, 0, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4069, 0, 25) || + ecrt_slave_sdo_write_exp8(slave, 0x406A, 0, 25) || + ecrt_slave_sdo_write_exp8(slave, 0x406B, 0, 50)) { + printk(KERN_ERR "Failed to configure SSI slave!\n"); + goto out_deactivate; + } +#endif + +#if 0 + printk(KERN_INFO "Writing alias...\n"); + if (ecrt_slave_sdo_write_exp16(slave, 0xBEEF)) { + printk(KERN_ERR "Failed to write alias!\n"); + goto out_deactivate; + } +#endif + +#ifdef ASYNC + // send once and wait... + ecrt_master_prepare_async_io(master); +#endif + +#if 1 + if (ecrt_master_start_eoe(master)) { + printk(KERN_ERR "Failed to start EoE processing!\n"); + goto out_deactivate; + } +#endif + + printk("Starting cyclic sample thread.\n"); + init_timer(&timer); + timer.function = run; + timer.expires = jiffies + 10; + add_timer(&timer); + + printk(KERN_INFO "=== Minimal EtherCAT environment started. ===\n"); + return 0; + +#if 1 + out_deactivate: + ecrt_master_deactivate(master); +#endif + out_release_master: + ecrt_release_master(master); + out_return: + return -1; +} + +/*****************************************************************************/ + +void __exit cleanup_mini_module(void) +{ + printk(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n"); + + if (master) { + del_timer_sync(&timer); + printk(KERN_INFO "Deactivating master...\n"); + ecrt_master_deactivate(master); + ecrt_release_master(master); + } + + printk(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n"); +} + +/*****************************************************************************/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Florian Pose "); +MODULE_DESCRIPTION ("EtherCAT minimal test environment"); + +module_init(init_mini_module); +module_exit(cleanup_mini_module); + +/*****************************************************************************/ + diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/Makefile Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,91 @@ +#------------------------------------------------------------------------------ +# +# Makefile +# +# Sample module for use with IgH MSR library. +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +MODULE := ec_msr_sample + +#------------------------------------------------------------------------------ +# kbuild section +#------------------------------------------------------------------------------ + +ifneq ($(KERNELRELEASE),) + +obj-m := $(MODULE).o + +$(MODULE)-objs := msr_sample.o \ + rt_lib/msr-core/msr_lists.o \ + rt_lib/msr-core/msr_main.o \ + rt_lib/msr-core/msr_charbuf.o \ + rt_lib/msr-core/msr_reg.o \ + rt_lib/msr-core/msr_interpreter.o \ + rt_lib/msr-core/msr_messages.o \ + rt_lib/msr-core/msr_proc.o \ + rt_lib/msr-core/msr_error_reg.o \ + rt_lib/msr-utils/msr_utils.o \ + rt_lib/msr-utils/msr_time.o \ + rt_lib/msr-math/msr_base64.o \ + rt_lib/msr-math/msr_hex_bin.o \ + libm.o + +EXTRA_CFLAGS := -I$(src)/rt_lib/msr-include -I/usr/realtime/include \ + -D_SIMULATION -mhard-float + +#------------------------------------------------------------------------------ +# default section +#------------------------------------------------------------------------------ + +else + +ifneq ($(wildcard kernel.conf),) +include kernel.conf +else +KERNEL := $(shell uname -r) +endif + +KERNELDIR := /lib/modules/$(KERNEL)/build + +modules: + $(MAKE) -C $(KERNELDIR) M=`pwd` + +clean: + $(MAKE) -C $(KERNELDIR) M=`pwd` clean + +install: + @./install.sh $(MODULE) $(KERNEL) + +endif + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/install.sh Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,76 @@ +#!/bin/sh + +#------------------------------------------------------------------------------ +# +# Realtime module install script +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +# Fetch parameters + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +MODULENAME=$1 +KERNEL=$2 + +MODULESDIR=/lib/modules/$KERNEL/kernel/drivers/rt + +echo "Realtime installer" +echo " target: $MODULENAME" +echo " kernel: $KERNEL" + +# Create target directory + +if [ ! -d $MODULESDIR ]; then + echo " creating $MODULESDIR..." + mkdir $MODULESDIR || exit 1 +fi + +# Install files + +echo " installing $MODULENAME..." +if ! cp $MODULENAME.ko $MODULESDIR/$MODULENAME.ko; then exit 1; fi + +# Calculate dependencies + +echo " building module dependencies..." +depmod + +# Finish + +echo "done." +exit 0 + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/kernel.conf.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/kernel.conf.tmpl Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------------ +# +# Configuration file for MSR realtime modules +# +# $Id$ +# +# This file is a versioned template configuration. Copy it to "kernel.conf" +# (which is ignored by Subversion) and adjust it to your needs. +# +#------------------------------------------------------------------------------ + +# Kernel sources for module compilation +KERNEL := `uname -r` + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/libm.o_shipped --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/libm.o_shipped Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,1 @@ +/usr/lib/libm.a \ No newline at end of file diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/msr_load --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/msr_load Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh +module="ec_msr_sample" +device="msr" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"` + +echo $major +# Remove stale nodes and replace them, then give gid and perms +# Usually the script is shorter, it's scull that has several devices in it. + +rm -f /dev/${device} +mknod /dev/${device} c $major 0 +# ln -sf ${device}0 /dev/${device} +chgrp users /dev/${device} +chmod $mode /dev/${device} + + + + + + + + + diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/msr_param.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/msr_param.h Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +#ifndef _MSR_PARAM_H_ +#define _MSR_PARAM_H_ + +#define MSR_ABTASTFREQUENZ 1000 + +#endif + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/msr_sample.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/msr_sample.c Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,307 @@ +/****************************************************************************** + * + * Sample module for use with IgH MSR library. + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +// Linux +#include + +// RTAI +#include "rtai_sched.h" +#include "rtai_sem.h" + +// RT_lib +#include +#include +#include +#include "msr_param.h" + +// EtherCAT +#include "../../include/ecrt.h" + +#define ASYNC + +#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ) +#define TIMERTICKS (1000000000 / MSR_ABTASTFREQUENZ) + +/*****************************************************************************/ + +// RTAI +RT_TASK task; +SEM master_sem; +cycles_t t_start = 0, t_critical; + +// EtherCAT +ec_master_t *master = NULL; +ec_domain_t *domain1 = NULL; + +// raw process data +void *r_ssi; +void *r_ssi_st; + +// Channels +uint32_t k_ssi; +uint32_t k_ssi_st; + +ec_field_init_t domain1_fields[] = { + {&r_ssi, "0:3", "Beckhoff", "EL5001", "InputValue", 0}, + {&r_ssi_st, "0:3", "Beckhoff", "EL5001", "Status", 0}, + {} +}; + +/*****************************************************************************/ + +void msr_controller_run(void) +{ + rt_sem_wait(&master_sem); + +#ifdef ASYNC + // Empfangen + ecrt_master_async_receive(master); + ecrt_domain_process(domain1); +#else + // Senden und empfangen + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_sync_io(master); + ecrt_domain_process(domain1); +#endif + + // Prozessdaten verarbeiten + k_ssi = EC_READ_U32(r_ssi); + k_ssi_st = EC_READ_U8 (r_ssi_st); + +#ifdef ASYNC + // Senden + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_async_send(master); +#endif + + rt_sem_signal(&master_sem); + + msr_write_kanal_list(); +} + +/*****************************************************************************/ + +void msr_run(long data) +{ + while (1) { + t_start = get_cycles(); + MSR_RTAITHREAD_CODE(msr_controller_run();); + rt_task_wait_period(); + } +} + +/*****************************************************************************/ + +int msr_reg(void) +{ + msr_reg_kanal("/ssi_position", "", &k_ssi, TUINT); + msr_reg_kanal("/ssi_status", "", &k_ssi_st, TUINT); + return 0; +} + +/*****************************************************************************/ + +int request_lock(void *data) +{ + // too close to the next RT cycle: deny access... + if (get_cycles() - t_start > t_critical) return -1; + + // allow access + rt_sem_wait(&master_sem); + return 0; +} + +/*****************************************************************************/ + +void release_lock(void *data) +{ + rt_sem_signal(&master_sem); +} + +/*****************************************************************************/ + +int __init init_mod(void) +{ + RTIME ticks; +#if 0 + ec_slave_t *slave; +#endif + + printk(KERN_INFO "=== Starting EtherCAT RTAI MSR sample module... ===\n"); + + rt_sem_init(&master_sem, 1); + t_critical = cpu_khz * 800 / MSR_ABTASTFREQUENZ; // ticks for 80% + + if (msr_rtlib_init(1, MSR_ABTASTFREQUENZ, 10, &msr_reg) < 0) { + printk(KERN_ERR "Failed to initialize rtlib!\n"); + goto out_return; + } + + if ((master = ecrt_request_master(0)) == NULL) { + printk(KERN_ERR "Failed to request master 0!\n"); + goto out_msr_cleanup; + } + + ecrt_master_callbacks(master, request_lock, release_lock, NULL); + + printk(KERN_INFO "Creating domains...\n"); + if (!(domain1 = ecrt_master_create_domain(master))) { + printk(KERN_ERR "Failed to create domains!\n"); + goto out_release_master; + } + + printk(KERN_INFO "Registering domain fields...\n"); + if (ecrt_domain_register_field_list(domain1, domain1_fields)) { + printk(KERN_ERR "Failed to register domain fields.\n"); + goto out_release_master; + } + + printk(KERN_INFO "Activating master...\n"); + if (ecrt_master_activate(master)) { + printk(KERN_ERR "Could not activate master!\n"); + goto out_release_master; + } + +#if 0 + if (ecrt_master_fetch_sdo_lists(master)) { + printk(KERN_ERR "Failed to fetch SDO lists!\n"); + goto out_deactivate; + } + ecrt_master_print(master, 2); +#else + ecrt_master_print(master, 0); +#endif + +#if 0 + if (!(slave = ecrt_master_get_slave(master, "0:3"))) { + printk(KERN_ERR "Failed to get slave!\n"); + goto out_deactivate; + } + + if ( + ecrt_slave_sdo_write_exp8(slave, 0x4061, 1, 1) || // disable frame error bit + ecrt_slave_sdo_write_exp8(slave, 0x4061, 2, 0) || // power failure bit + ecrt_slave_sdo_write_exp8(slave, 0x4061, 3, 1) || // inhibit time + ecrt_slave_sdo_write_exp8(slave, 0x4061, 4, 0) || // test mode + ecrt_slave_sdo_write_exp8(slave, 0x4066, 0, 0) || // graycode + ecrt_slave_sdo_write_exp8(slave, 0x4067, 0, 5) || // 125kbaud + ecrt_slave_sdo_write_exp8(slave, 0x4068, 0, 0) || // single-turn + ecrt_slave_sdo_write_exp8(slave, 0x4069, 0, 25) || // frame size + ecrt_slave_sdo_write_exp8(slave, 0x406A, 0, 25) || // data length + ecrt_slave_sdo_write_exp16(slave, 0x406B, 0, 50) // inhibit time in us + ) { + printk(KERN_ERR "Failed to configure SSI slave!\n"); + goto out_deactivate; + } +#endif + +#if 0 + if (!(slave = ecrt_master_get_slave(master, "1:0"))) { + printk(KERN_ERR "Failed to get slave!\n"); + goto out_deactivate; + } + if (ecrt_slave_write_alias(slave, 0x5678)) { + printk(KERN_ERR "Failed to write alias!\n"); + goto out_deactivate; + } +#endif + +#ifdef ASYNC + // Einmal senden und warten... + ecrt_master_prepare_async_io(master); +#endif + + if (ecrt_master_start_eoe(master)) { + printk(KERN_ERR "Failed to start EoE processing!\n"); + goto out_deactivate; + } + + printk("Starting cyclic sample thread...\n"); + ticks = start_rt_timer(nano2count(TIMERTICKS)); + if (rt_task_init(&task, msr_run, 0, 2000, 0, 1, NULL)) { + printk(KERN_ERR "Failed to init RTAI task!\n"); + goto out_stop_timer; + } + if (rt_task_make_periodic(&task, rt_get_time() + ticks, ticks)) { + printk(KERN_ERR "Failed to run RTAI task!\n"); + goto out_stop_task; + } + + printk(KERN_INFO "=== EtherCAT RTAI MSR sample module started. ===\n"); + return 0; + + out_stop_task: + rt_task_delete(&task); + out_stop_timer: + stop_rt_timer(); + out_deactivate: + ecrt_master_deactivate(master); + out_release_master: + ecrt_release_master(master); + out_msr_cleanup: + msr_rtlib_cleanup(); + out_return: + rt_sem_delete(&master_sem); + return -1; +} + +/*****************************************************************************/ + +void __exit cleanup_mod(void) +{ + printk(KERN_INFO "=== Unloading EtherCAT RTAI MSR sample module... ===\n"); + + rt_task_delete(&task); + stop_rt_timer(); + ecrt_master_deactivate(master); + ecrt_release_master(master); + rt_sem_delete(&master_sem); + msr_rtlib_cleanup(); + + printk(KERN_INFO "=== EtherCAT RTAI MSR sample module unloaded. ===\n"); +} + +/*****************************************************************************/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Florian Pose "); +MODULE_DESCRIPTION ("EtherCAT RTAI MSR sample module"); + +module_init(init_mod); +module_exit(cleanup_mod); + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/msr_unload --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/msr_unload Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,9 @@ +#!/bin/sh +module="ec_msr_sample" +device="msr" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes +rm -f /dev/${device} /dev/${device}0 diff -r 5cff10efb927 -r 0d4119024f55 examples/msr/msrserv.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/msr/msrserv.pl Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,267 @@ +#!/usr/bin/perl -w + +#------------------------------------------------------------------------------ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ +# +# Multithreaded Server +# according to the example from "Programming Perl" +# this code is improved according to the example from +# perldoc perlipc, so now safely being usable under Perl 5.8 +# (see note (*)) +# +# works with read/write on a device-file +# +#------------------------------------------------------------------------------ + +require 5.002; +use strict; +BEGIN { $ENV{PATH} = '/opt/msr/bin:/usr/bin:/bin' } +use Socket; +use Carp; +use FileHandle; +use Getopt::Std; + +use Sys::Syslog qw(:DEFAULT setlogsock); + +use vars qw ( + $self $pid $dolog $port $dev %opts $selfbase + $len $offset $stream $written $read $log $blksize + $instdir + $authfile %authhosts + ); + + +# Do logging to local syslogd by unix-domain socket instead of inetd +setlogsock("unix"); + +# Prototypes and some little Tools +sub spawn; +sub logmsg { + my ($level, $debug, @text) = @_; + syslog("daemon|$level", @text) if $debug > $dolog; +# print STDERR "daemon|$level", @text, "\n" if $dolog; +} +sub out { + my $waitpid = wait; + logmsg("notice", 2, "$waitpid exited"); + unlink "$selfbase.pid"; + exit 0; +} + +sub help { + print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n"; + exit; +} + +# Process Options +%opts = ( + "l" => 1, + "h" => 0, + "p" => 2345, + "d" => "/dev/msr" + ); + +getopts("lhp:d:", \%opts); + +help if $opts{"h"}; + +( $self = $0 ) =~ s+.*/++ ; +( $selfbase = $self ) =~ s/\..*//; +$log = "$selfbase.log"; +$dolog = $opts{"l"}; +$port = $opts{"p"}; +$dev = $opts{"d"}; +$blksize = 1024; # try to write as much bytes +$instdir = "/opt/msr"; +$authfile = "$instdir/etc/hosts.auth"; + +# Start logging +openlog($self, 'pid'); + +# Flush Output, dont buffer +$| = 1; + +# first fork and run in background +if ($pid = fork) { +# open LOG, ">$log" if $dolog; +# close LOG; + logmsg("notice", 2, "forked process: $pid\n"); + exit 0; +} + +# Server tells about startup success +open (PID, ">/$instdir/var/run/$selfbase.pid"); +print PID "$$\n"; +close PID; + +# Cleanup on exit (due to kill -TERM signal) +$SIG{TERM} = \&out; + +# We use streams +my $proto = getprotobyname('tcp'); + +# Open Server socket +socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; +setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) + or die "setsocketopt: $!"; +bind (Server, sockaddr_in($port, INADDR_ANY)) + or die "bind: $!"; +listen (Server, SOMAXCONN) + or die "listen: $!"; + +%authhosts = (); +# get authorized hosts +open (AUTH, $authfile) + or logmsg ("notice", 2, "Could not read allowed hosts file: $authfile"); +while () { + chomp; + my $host = lc $_; + if ($host =~ /^[\d\w]/) { + $authhosts{$_} = 1; + logmsg ("notice", 2, "Authorized host: >$host<"); + } +} +close (AUTH); + +# tell about open server socket +logmsg ("notice", 2, "Server started at port $port"); + +my $waitedpid = 0; +my $paddr; + +# wait for children to return, thus avoiding zombies +# improvement (*) +use POSIX ":sys_wait_h"; +sub REAPER { + my $child; + while (($waitedpid = waitpid(-1,WNOHANG)) > 0) { + logmsg ("notice", 2, "reaped $waitedpid", ($? ? " with exit $?" : "")); + } + $SIG{CHLD} = \&REAPER; # loathe sysV +} + +# also all sub-processes should wait for their children +$SIG{CHLD} = \&REAPER; + +# start a new server for every incoming request +# improvement (*) -- loop forever + +while ( 1 ) { + for ( $waitedpid = 0; + ($paddr = accept(Client,Server)) || $waitedpid; + $waitedpid = 0, close Client ) { + next if $waitedpid and not $paddr; + my ($port, $iaddr) = sockaddr_in($paddr); + my $name = lc gethostbyaddr($iaddr, AF_INET); + my $ipaddr = inet_ntoa($iaddr); + my $n = 0; + +# tell about the requesting client + logmsg ("info", 2, "Connection from >$ipaddr< ($name) at port $port"); + + spawn sub { + my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen); + my ($watchpegel, $shmpegel); + my ($rin, $rout, $in, $line, $data_requested, $oversample); + my (@channels); + +# to use stdio on writing to Client + Client->autoflush(); + +# Open Device + sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev"); + +# Bitmask to check for input on stdin + $rin = ""; + vec($rin, fileno(Client), 1) = 1; + +# check for authorized hosts + my $access = 'deny'; + $access = 'allow' if $authhosts{$ipaddr}; + $line = "\n"; + logmsg ("info", 2, $line); + $len = length $line; + $offset = 0; + while ($len) { + $written = syswrite (DEV, $line, $len, $offset); + $len -= $written; + $offset += $written; + } + + while ( 1 ) { + $in = select ($rout=$rin, undef, undef, 0.0); # poll client +# look for any Input from Client + if ($in) { +# exit on EOF + $len = sysread (Client, $line, $blksize) or exit; + logmsg("info", 0, "got $len bytes: \"$line\""); + $offset = 0; +# copy request to device + while ($len) { + $written = syswrite (DEV, $line, $len, $offset); + $len -= $written; + $offset += $written; + } + } +# look for some output from device + if ($len = sysread DEV, $stream, $blksize) { + print Client $stream; + } else { + select undef, undef, undef, 0.1; # calm down if nothing on device + } + } + }; + logmsg("info", 2, "spawned\n"); + } + logmsg("info", 2, "server loop\n"); +} + +sub spawn { + my $coderef = shift; + + unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') { + confess "usage: spawn CODEREF"; + } + my $pid; + if (!defined($pid = fork)) { + logmsg ("notice", 2, "fork failed: $!"); + return; + } elsif ($pid) { + logmsg ("notice", 2, "Request $pid"); + return; # Parent + } + +# do not use fdup as in the original example +# open (STDIN, "<&Client") or die "Can't dup client to stdin"; +# open (STDOUT, ">&Client") or die "Can't dup client to stdout"; +# STDOUT->autoflush(); + exit &$coderef(); +} diff -r 5cff10efb927 -r 0d4119024f55 examples/rtai/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai/Makefile Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,72 @@ +#------------------------------------------------------------------------------ +# +# Makefile +# +# RTAI sample module for the IgH EtherCAT master. +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +ifneq ($(KERNELRELEASE),) + +#------------------------------------------------------------------------------ +# kbuild section +#------------------------------------------------------------------------------ + +obj-m := ec_rtai_sample.o + +ec_rtai_sample-objs := rtai_sample.o + +EXTRA_CFLAGS := -I/usr/realtime/include -mhard-float + +#------------------------------------------------------------------------------ + +else + +#------------------------------------------------------------------------------ +# default section +#------------------------------------------------------------------------------ + +ifneq ($(wildcard kernel.conf),) +include kernel.conf +else +KERNELDIR = /usr/src/linux +endif + +modules: + $(MAKE) -C $(KERNELDIR) M=`pwd` + +clean: + $(MAKE) -C $(KERNELDIR) M=`pwd` clean + +#------------------------------------------------------------------------------ + +endif diff -r 5cff10efb927 -r 0d4119024f55 examples/rtai/kernel.conf.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai/kernel.conf.tmpl Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,13 @@ +#------------------------------------------------------------------------------ +# +# Kernel configuration file for RTAI sample. +# +# $Id$ +# +# This file is a versioned template configuration. Copy it to "kernel.conf" +# (which is ignored by Subversion) and adjust it to your needs. +# +#------------------------------------------------------------------------------ + +KERNELDIR = /usr/src/linux + diff -r 5cff10efb927 -r 0d4119024f55 examples/rtai/rtai_sample.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai/rtai_sample.c Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,281 @@ +/****************************************************************************** + * + * RTAI sample for the IgH EtherCAT master. + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +// Linux +#include + +// RTAI +#include "rtai_sched.h" +#include "rtai_sem.h" + +// EtherCAT +#include "../../include/ecrt.h" + +/*****************************************************************************/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT RTAI sample module"); + +/*****************************************************************************/ + +// comment this for synchronous IO +#define ASYNC + +// RTAI task frequency in Hz +#define FREQUENCY 10000 + +#define TIMERTICKS (1000000000 / FREQUENCY) + +/*****************************************************************************/ + +// RTAI +RT_TASK task; +SEM master_sem; +cycles_t t_last_start = 0; +cycles_t t_critical; + +// EtherCAT +ec_master_t *master = NULL; +ec_domain_t *domain1 = NULL; + +// data fields +void *r_ssi_input; + +// channels +uint32_t k_pos; + +ec_field_init_t domain1_fields[] = { + {&r_ssi_input, "3", "Beckhoff", "EL5001", "InputValue", 0}, + {NULL, "2", "Beckhoff", "EL4132", "OutputValue", 0}, + {} +}; + +/*****************************************************************************/ + +void run(long data) +{ + while (1) + { + t_last_start = get_cycles(); + rt_sem_wait(&master_sem); + +#ifdef ASYNC + // receive + ecrt_master_async_receive(master); + ecrt_domain_process(domain1); +#else + // send and receive + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_sync_io(master); + ecrt_domain_process(domain1); +#endif + + // process data + k_pos = EC_READ_U32(r_ssi_input); + +#ifdef ASYNC + // send + ecrt_domain_queue(domain1); + ecrt_master_run(master); + ecrt_master_async_send(master); +#endif + + rt_sem_signal(&master_sem); + rt_task_wait_period(); + } +} + +/*****************************************************************************/ + +int request_lock(void *data) +{ + // too close to the next RT cycle: deny access... + if (get_cycles() - t_last_start > t_critical) return -1; + + // allow access + rt_sem_wait(&master_sem); + return 0; +} + +/*****************************************************************************/ + +void release_lock(void *data) +{ + rt_sem_signal(&master_sem); +} + +/*****************************************************************************/ + +int __init init_mod(void) +{ + RTIME tick_period, requested_ticks, now; + + printk(KERN_INFO "=== Starting EtherCAT RTAI sample module... ===\n"); + + rt_sem_init(&master_sem, 1); + + t_critical = cpu_khz * 800 / FREQUENCY; // ticks for 80% + + if ((master = ecrt_request_master(0)) == NULL) { + printk(KERN_ERR "Requesting master 0 failed!\n"); + goto out_return; + } + + ecrt_master_callbacks(master, request_lock, release_lock, NULL); + + printk(KERN_INFO "Registering domain...\n"); + if (!(domain1 = ecrt_master_create_domain(master))) + { + printk(KERN_ERR "Domain creation failed!\n"); + goto out_release_master; + } + + printk(KERN_INFO "Registering domain fields...\n"); + if (ecrt_domain_register_field_list(domain1, domain1_fields)) { + printk(KERN_ERR "Field registration failed!\n"); + goto out_release_master; + } + + printk(KERN_INFO "Activating master...\n"); + if (ecrt_master_activate(master)) { + printk(KERN_ERR "Failed to activate master!\n"); + goto out_release_master; + } + +#if 0 + if (ecrt_master_fetch_sdo_lists(master)) { + printk(KERN_ERR "Failed to fetch SDO lists!\n"); + goto out_deactivate; + } + ecrt_master_print(master, 2); +#else + ecrt_master_print(master, 0); +#endif + +#if 0 + if (!(slave = ecrt_master_get_slave(master, "5"))) { + printk(KERN_ERR "Failed to get slave 5!\n"); + goto out_deactivate; + } + + if (ecrt_slave_sdo_write_exp8(slave, 0x4061, 1, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4061, 2, 1) || + ecrt_slave_sdo_write_exp8(slave, 0x4061, 3, 1) || + ecrt_slave_sdo_write_exp8(slave, 0x4066, 0, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4067, 0, 4) || + ecrt_slave_sdo_write_exp8(slave, 0x4068, 0, 0) || + ecrt_slave_sdo_write_exp8(slave, 0x4069, 0, 25) || + ecrt_slave_sdo_write_exp8(slave, 0x406A, 0, 25) || + ecrt_slave_sdo_write_exp8(slave, 0x406B, 0, 50)) { + printk(KERN_ERR "Failed to configure SSI slave!\n"); + goto out_deactivate; + } +#endif + +#if 0 + printk(KERN_INFO "Writing alias...\n"); + if (ecrt_slave_sdo_write_exp16(slave, 0xBEEF)) { + printk(KERN_ERR "Failed to write alias!\n"); + goto out_deactivate; + } +#endif + +#ifdef ASYNC + // send once and wait... + ecrt_master_prepare_async_io(master); +#endif + + if (ecrt_master_start_eoe(master)) { + printk(KERN_ERR "Failed to start EoE processing!\n"); + goto out_deactivate; + } + + printk("Starting cyclic sample thread...\n"); + requested_ticks = nano2count(TIMERTICKS); + tick_period = start_rt_timer(requested_ticks); + printk(KERN_INFO "RT timer started with %i/%i ticks.\n", + (int) tick_period, (int) requested_ticks); + + if (rt_task_init(&task, run, 0, 2000, 0, 1, NULL)) { + printk(KERN_ERR "Failed to init RTAI task!\n"); + goto out_stop_timer; + } + + now = rt_get_time(); + if (rt_task_make_periodic(&task, now + tick_period, tick_period)) { + printk(KERN_ERR "Failed to run RTAI task!\n"); + goto out_stop_task; + } + + printk(KERN_INFO "=== EtherCAT RTAI sample module started. ===\n"); + return 0; + + out_stop_task: + rt_task_delete(&task); + out_stop_timer: + stop_rt_timer(); + out_deactivate: + ecrt_master_deactivate(master); + out_release_master: + ecrt_release_master(master); + out_return: + rt_sem_delete(&master_sem); + return -1; +} + +/*****************************************************************************/ + +void __exit cleanup_mod(void) +{ + printk(KERN_INFO "=== Stopping EtherCAT RTAI sample module... ===\n"); + + rt_task_delete(&task); + stop_rt_timer(); + ecrt_master_deactivate(master); + ecrt_release_master(master); + rt_sem_delete(&master_sem); + + printk(KERN_INFO "=== EtherCAT RTAI sample module stopped. ===\n"); +} + +/*****************************************************************************/ + +module_init(init_mod); +module_exit(cleanup_mod); + +/*****************************************************************************/ + diff -r 5cff10efb927 -r 0d4119024f55 include/ecrt.h --- a/include/ecrt.h Mon Apr 24 10:47:03 2006 +0000 +++ b/include/ecrt.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -88,6 +98,9 @@ * Master methods *****************************************************************************/ +void ecrt_master_callbacks(ec_master_t *master, int (*request_cb)(void *), + void (*release_cb)(void *), void *cb_data); + ec_domain_t *ecrt_master_create_domain(ec_master_t *master); int ecrt_master_activate(ec_master_t *master); @@ -102,6 +115,8 @@ void ecrt_master_run(ec_master_t *master); +int ecrt_master_start_eoe(ec_master_t *master); + void ecrt_master_debug(ec_master_t *master, int level); void ecrt_master_print(const ec_master_t *master, unsigned int verbosity); diff -r 5cff10efb927 -r 0d4119024f55 install.sh --- a/install.sh Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -#!/bin/sh - -#------------------------------------------------------------------------------ -# -# EtherCAT install script -# -# $Id$ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ - -CONFIGFILE=/etc/sysconfig/ethercat - -#------------------------------------------------------------------------------ - -# install function - -install() -{ - echo " $1" - if ! cp $1 $INSTALLDIR; then exit 1; fi -} - -#------------------------------------------------------------------------------ - -# Fetch parameter - -if [ $# -ne 2 ]; then - echo "This script is called by \"make\". Run \"make install\" instead." - exit 1 -fi - -KERNEL=$1 -DEVICEINDEX=$2 - -INSTALLDIR=/lib/modules/$KERNEL/kernel/drivers/net -echo "EtherCAT installer - Kernel: $KERNEL" - -# Copy files - -echo " installing modules..." -install master/ec_master.ko -install devices/ec_8139too.ko - -# Update dependencies - -echo " building module dependencies..." -depmod - -# Create configuration file - -if [ -f $CONFIGFILE ]; then - echo " notice: using existing configuration file." -else - echo " creating $CONFIGFILE..." - echo "DEVICEINDEX=$DEVICEINDEX" > $CONFIGFILE || exit 1 -fi - -# Install rc script - -echo " installing startup script..." -cp ethercat.sh /etc/init.d/ethercat || exit 1 -if [ ! -L /usr/sbin/rcethercat ]; then - ln -s /etc/init.d/ethercat /usr/sbin/rcethercat || exit 1 -fi - -# Install tools - -echo " installing tools..." -cp tools/ec_list.pl /usr/local/bin/ec_list || exit 1 - -# Finish - -echo "EtherCAT installer done." -exit 0 - -#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 master/Makefile --- a/master/Makefile Mon Apr 24 10:47:03 2006 +0000 +++ b/master/Makefile Mon May 29 09:08:56 2006 +0000 @@ -12,7 +12,8 @@ # # The IgH EtherCAT Master is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 of the License. +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. # # The IgH EtherCAT Master is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,6 +24,15 @@ # 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# #------------------------------------------------------------------------------ ifneq ($(KERNELRELEASE),) @@ -33,7 +43,7 @@ obj-m := ec_master.o ec_master-objs := module.o master.o device.o slave.o command.o types.o \ - domain.o mailbox.o canopen.o ethernet.o + domain.o mailbox.o canopen.o ethernet.o debug.o fsm.o REV := $(shell svnversion $(src) 2>/dev/null) diff -r 5cff10efb927 -r 0d4119024f55 master/canopen.c --- a/master/canopen.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/canopen.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/command.c --- a/master/command.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/command.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -29,7 +39,6 @@ /*****************************************************************************/ #include -#include #include "command.h" #include "master.h" @@ -68,6 +77,7 @@ command->index = 0x00; command->working_counter = 0x00; command->state = EC_CMD_INIT; + command->t_sent = 0; } /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/command.h --- a/master/command.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/command.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -32,6 +42,7 @@ #define _EC_COMMAND_H_ #include +#include #include "globals.h" @@ -106,6 +117,7 @@ uint8_t index; /**< command index (set by master) */ uint16_t working_counter; /**< working counter */ ec_command_state_t state; /**< command state */ + cycles_t t_sent; /**< time, the commands was sent */ } ec_command_t; @@ -113,6 +125,7 @@ void ec_command_init(ec_command_t *); void ec_command_clear(ec_command_t *); +int ec_command_prealloc(ec_command_t *, size_t); int ec_command_nprd(ec_command_t *, uint16_t, uint16_t, size_t); int ec_command_npwr(ec_command_t *, uint16_t, uint16_t, size_t); diff -r 5cff10efb927 -r 0d4119024f55 master/debug.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/debug.c Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,189 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +/** + \file + Ethernet interface for debugging purposes. +*/ + +/*****************************************************************************/ + +#include + +#include "globals.h" +#include "debug.h" + +/*****************************************************************************/ + +// net_device functions +int ec_dbgdev_open(struct net_device *); +int ec_dbgdev_stop(struct net_device *); +struct net_device_stats *ec_dbgdev_stats(struct net_device *); + +/*****************************************************************************/ + +/** + Debug constructor. + Initializes the debug object, creates a net_device and registeres it. +*/ + +int ec_debug_init(ec_debug_t *dbg /**< debug object */) +{ + int result; + + dbg->opened = 0; + memset(&dbg->stats, 0, sizeof(struct net_device_stats)); + + if (!(dbg->dev = + alloc_netdev(sizeof(ec_debug_t *), "ec%d", ether_setup))) { + EC_ERR("Unable to allocate net_device for debug object!\n"); + goto out_return; + } + + // initialize net_device + dbg->dev->open = ec_dbgdev_open; + dbg->dev->stop = ec_dbgdev_stop; + dbg->dev->get_stats = ec_dbgdev_stats; + + // initialize private data + *((ec_debug_t **) netdev_priv(dbg->dev)) = dbg; + + // connect the net_device to the kernel + if ((result = register_netdev(dbg->dev))) { + EC_ERR("Unable to register net_device: error %i\n", result); + goto out_free; + } + + return 0; + + out_free: + free_netdev(dbg->dev); + dbg->dev = NULL; + out_return: + return -1; +} + +/*****************************************************************************/ + +/** + Debug destructor. + Unregisteres the net_device and frees allocated memory. +*/ + +void ec_debug_clear(ec_debug_t *dbg /**< debug object */) +{ + if (dbg->dev) { + unregister_netdev(dbg->dev); + free_netdev(dbg->dev); + } +} + +/*****************************************************************************/ + +/** + Sends frame data to the interface. +*/ + +void ec_debug_send(ec_debug_t *dbg, /**< debug object */ + const uint8_t *data, /**< frame data */ + size_t size /**< size of the frame data */ + ) +{ + struct sk_buff *skb; + + if (!dbg->opened) return; + + // allocate socket buffer + if (!(skb = dev_alloc_skb(size))) { + dbg->stats.rx_dropped++; + return; + } + + // copy frame contents into socket buffer + memcpy(skb_put(skb, size), data, size); + + // update device statistics + dbg->stats.rx_packets++; + dbg->stats.rx_bytes += size; + + // pass socket buffer to network stack + skb->dev = dbg->dev; + skb->protocol = eth_type_trans(skb, dbg->dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); +} + +/****************************************************************************** + * NET_DEVICE functions + *****************************************************************************/ + +/** + Opens the virtual network device. +*/ + +int ec_dbgdev_open(struct net_device *dev /**< debug net_device */) +{ + ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev)); + dbg->opened = 1; + EC_INFO("debug interface %s opened.\n", dev->name); + return 0; +} + +/*****************************************************************************/ + +/** + Stops the virtual network device. +*/ + +int ec_dbgdev_stop(struct net_device *dev /**< debug net_device */) +{ + ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev)); + dbg->opened = 0; + EC_INFO("debug interface %s stopped.\n", dev->name); + return 0; +} + +/*****************************************************************************/ + +/** + Gets statistics about the virtual network device. +*/ + +struct net_device_stats *ec_dbgdev_stats(struct net_device *dev + /**< debug net_device */) +{ + ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev)); + return &dbg->stats; +} + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/debug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/debug.h Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +/** + \file + Network interface for debugging purposes. +*/ + +/*****************************************************************************/ + +#include + +/*****************************************************************************/ + +/** + Debugging network interface. +*/ + +typedef struct +{ + struct net_device *dev; /**< net_device for virtual ethernet device */ + struct net_device_stats stats; /**< device statistics */ + uint8_t opened; /**< net_device is opened */ +} +ec_debug_t; + +/*****************************************************************************/ + +int ec_debug_init(ec_debug_t *); +void ec_debug_clear(ec_debug_t *); +void ec_debug_send(ec_debug_t *, const uint8_t *, size_t); + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/device.c --- a/master/device.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/device.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -61,9 +71,14 @@ device->open = 0; device->link_state = 0; // down - if (!(device->tx_skb = dev_alloc_skb(ETH_HLEN + EC_MAX_FRAME_SIZE))) { + if (ec_debug_init(&device->dbg)) { + EC_ERR("Failed to init debug device!\n"); + goto out_return; + } + + if (!(device->tx_skb = dev_alloc_skb(ETH_FRAME_LEN))) { EC_ERR("Error allocating device socket buffer!\n"); - return -1; + goto out_debug; } device->tx_skb->dev = net_dev; @@ -76,6 +91,11 @@ memset(eth->h_dest, 0xFF, net_dev->addr_len); return 0; + + out_debug: + ec_debug_clear(&device->dbg); + out_return: + return -1; } /*****************************************************************************/ @@ -88,6 +108,7 @@ { if (device->open) ec_device_close(device); if (device->tx_skb) dev_kfree_skb(device->tx_skb); + ec_debug_clear(&device->dbg); } /*****************************************************************************/ @@ -180,6 +201,8 @@ ec_print_data(device->tx_skb->data + ETH_HLEN, size); } + ec_debug_send(&device->dbg, device->tx_skb->data, ETH_HLEN + size); + // start sending device->dev->hard_start_xmit(device->tx_skb, device->dev); } @@ -210,16 +233,19 @@ */ void ecdev_receive(ec_device_t *device, /**< EtherCAT device */ - const void *data, /**< pointer to receibed data */ + const void *data, /**< pointer to received data */ size_t size /**< number of bytes received */ ) { if (unlikely(device->master->debug_level > 1)) { EC_DBG("Received frame:\n"); - ec_print_data_diff(device->tx_skb->data + ETH_HLEN, data, size); - } - - ec_master_receive(device->master, data, size); + ec_print_data_diff(device->tx_skb->data + ETH_HLEN, + data + ETH_HLEN, size - ETH_HLEN); + } + + ec_debug_send(&device->dbg, data, size); + + ec_master_receive(device->master, data + ETH_HLEN, size - ETH_HLEN); } /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/device.h --- a/master/device.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/device.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -36,6 +46,7 @@ #include "../include/ecrt.h" #include "../devices/ecdev.h" #include "globals.h" +#include "debug.h" /*****************************************************************************/ @@ -54,6 +65,7 @@ ec_isr_t isr; /**< pointer to the device's interrupt service routine */ struct module *module; /**< pointer to the device's owning module */ uint8_t link_state; /**< device link state */ + ec_debug_t dbg; /**< debug device */ }; /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/domain.c --- a/master/domain.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/domain.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/domain.h --- a/master/domain.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/domain.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/doxygen.c --- a/master/doxygen.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/doxygen.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,25 +20,33 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ -/** - \file - Just for the doxygen mainpage. -*/ +// This file only contains the doxygen mainpage. /*****************************************************************************/ /** \mainpage The IgH EtherCAT master - \section sec_docs Documentation + \section sec_general General information - You will find the code documentation in the modules section. + This HTML contains the complete code documentation. - There is an external API documentation for the realtime interface. + The API documentations are in the modules + section. - For information how to build and install, see the README file. + For information how to build and install, see the INSTALL file in the source + root. \section sec_contact Contact @@ -58,7 +67,8 @@ The IgH EtherCAT Master is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; version 2 of the License. + as published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. The IgH EtherCAT Master is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -68,6 +78,15 @@ 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 right to use EtherCAT Technology is granted and comes free of + charge under condition of compatibility of product made by + Licensee. People intending to distribute/sell products based on the + code, have to sign an agreement to guarantee that products using + software based on IgH EtherCAT master stay compatible with the actual + EtherCAT specification (which are released themselves as an open + standard) as the (only) precondition to have the right to use EtherCAT + Technology, IP and trade marks. \endverbatim */ diff -r 5cff10efb927 -r 0d4119024f55 master/ethernet.c --- a/master/ethernet.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/ethernet.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -28,6 +38,8 @@ /*****************************************************************************/ +#include + #include "../include/ecrt.h" #include "globals.h" #include "master.h" @@ -35,26 +47,210 @@ #include "mailbox.h" #include "ethernet.h" +#define EOE_DEBUG_LEVEL 0 + +/*****************************************************************************/ + +void ec_eoe_flush(ec_eoe_t *); + +// state functions +void ec_eoe_state_rx_start(ec_eoe_t *); +void ec_eoe_state_rx_check(ec_eoe_t *); +void ec_eoe_state_rx_fetch(ec_eoe_t *); +void ec_eoe_state_tx_start(ec_eoe_t *); +void ec_eoe_state_tx_sent(ec_eoe_t *); + +// net_device functions +int ec_eoedev_open(struct net_device *); +int ec_eoedev_stop(struct net_device *); +int ec_eoedev_tx(struct sk_buff *, struct net_device *); +struct net_device_stats *ec_eoedev_stats(struct net_device *); + /*****************************************************************************/ /** EoE constructor. -*/ - -void ec_eoe_init(ec_eoe_t *eoe, ec_slave_t *slave) -{ - eoe->slave = slave; - eoe->rx_state = EC_EOE_IDLE; + Initializes the EoE handler, creates a net_device and registeres it. +*/ + +int ec_eoe_init(ec_eoe_t *eoe /**< EoE handler */) +{ + ec_eoe_t **priv; + int result, i; + + eoe->slave = NULL; + eoe->state = ec_eoe_state_rx_start; + eoe->opened = 0; + eoe->rx_skb = NULL; + eoe->rx_expected_fragment = 0; + INIT_LIST_HEAD(&eoe->tx_queue); + eoe->tx_frame = NULL; + eoe->tx_queue_active = 0; + eoe->tx_queued_frames = 0; + eoe->tx_queue_lock = SPIN_LOCK_UNLOCKED; + eoe->tx_frame_number = 0xFF; + memset(&eoe->stats, 0, sizeof(struct net_device_stats)); + + if (!(eoe->dev = + alloc_netdev(sizeof(ec_eoe_t *), "eoe%d", ether_setup))) { + EC_ERR("Unable to allocate net_device for EoE handler!\n"); + goto out_return; + } + + // initialize net_device + eoe->dev->open = ec_eoedev_open; + eoe->dev->stop = ec_eoedev_stop; + eoe->dev->hard_start_xmit = ec_eoedev_tx; + eoe->dev->get_stats = ec_eoedev_stats; + + for (i = 0; i < ETH_ALEN; i++) + eoe->dev->dev_addr[i] = i | (i << 4); + + // initialize private data + priv = netdev_priv(eoe->dev); + *priv = eoe; + + // Usually setting the MTU appropriately makes the upper layers + // do the frame fragmenting. In some cases this doesn't work + // so the MTU is left on the Ethernet standard value and fragmenting + // is done "manually". +#if 0 + eoe->dev->mtu = slave->sii_rx_mailbox_size - ETH_HLEN - 10; +#endif + + // connect the net_device to the kernel + if ((result = register_netdev(eoe->dev))) { + EC_ERR("Unable to register net_device: error %i\n", result); + goto out_free; + } + + // make the last address octet unique + eoe->dev->dev_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex; + + return 0; + + out_free: + free_netdev(eoe->dev); + eoe->dev = NULL; + out_return: + return -1; } /*****************************************************************************/ /** EoE destructor. -*/ - -void ec_eoe_clear(ec_eoe_t *eoe) -{ + Unregisteres the net_device and frees allocated memory. +*/ + +void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */) +{ + if (eoe->dev) { + unregister_netdev(eoe->dev); + free_netdev(eoe->dev); + } + + // empty transmit queue + ec_eoe_flush(eoe); + + if (eoe->tx_frame) { + dev_kfree_skb(eoe->tx_frame->skb); + kfree(eoe->tx_frame); + } + + if (eoe->rx_skb) dev_kfree_skb(eoe->rx_skb); +} + +/*****************************************************************************/ + +/** + Empties the transmit queue. +*/ + +void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */) +{ + ec_eoe_frame_t *frame, *next; + + spin_lock_bh(&eoe->tx_queue_lock); + + list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) { + list_del(&frame->queue); + dev_kfree_skb(frame->skb); + kfree(frame); + } + eoe->tx_queued_frames = 0; + + spin_unlock_bh(&eoe->tx_queue_lock); +} + +/*****************************************************************************/ + +/** + Sends a frame or the next fragment. +*/ + +int ec_eoe_send(ec_eoe_t *eoe /**< EoE handler */) +{ + size_t remaining_size, current_size, complete_offset; + unsigned int last_fragment; + uint8_t *data; +#if EOE_DEBUG_LEVEL > 1 + unsigned int i; +#endif + + remaining_size = eoe->tx_frame->skb->len - eoe->tx_offset; + + if (remaining_size <= eoe->slave->sii_tx_mailbox_size - 10) { + current_size = remaining_size; + last_fragment = 1; + } + else { + current_size = ((eoe->slave->sii_tx_mailbox_size - 10) / 32) * 32; + last_fragment = 0; + } + + if (eoe->tx_fragment_number) { + complete_offset = eoe->tx_offset / 32; + } + else { + complete_offset = remaining_size / 32 + 1; + } + +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("EoE TX sending %sfragment %i with %i octets (%i)." + " %i frames queued.\n", last_fragment ? "last " : "", + eoe->tx_fragment_number, current_size, complete_offset, + eoe->tx_queued_frames); +#endif + +#if EOE_DEBUG_LEVEL > 1 + EC_DBG(""); + for (i = 0; i < current_size; i++) { + printk("%02X ", frame->skb->data[eoe->tx_offset + i]); + if ((i + 1) % 16 == 0) { + printk("\n"); + EC_DBG(""); + } + } + printk("\n"); +#endif + + if (!(data = ec_slave_mbox_prepare_send(eoe->slave, 0x02, + current_size + 4))) + return -1; + + EC_WRITE_U8 (data, 0x00); // eoe fragment req. + EC_WRITE_U8 (data + 1, last_fragment); + EC_WRITE_U16(data + 2, ((eoe->tx_fragment_number & 0x3F) | + (complete_offset & 0x3F) << 6 | + (eoe->tx_frame_number & 0x0F) << 12)); + + memcpy(data + 4, eoe->tx_frame->skb->data + eoe->tx_offset, current_size); + ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command); + + eoe->tx_offset += current_size; + eoe->tx_fragment_number++; + return 0; } /*****************************************************************************/ @@ -63,70 +259,137 @@ Runs the EoE state machine. */ -void ec_eoe_run(ec_eoe_t *eoe) -{ - uint8_t *data; - ec_master_t *master; - size_t rec_size; -#if 0 - unsigned int i; - uint8_t fragment_number; - uint8_t complete_size; - uint8_t frame_number; - uint8_t last_fragment; -#endif - - master = eoe->slave->master; - - if (eoe->rx_state == EC_EOE_IDLE) { - ec_slave_mbox_prepare_check(eoe->slave); - ec_master_queue_command(master, &eoe->slave->mbox_command); - eoe->rx_state = EC_EOE_CHECKING; - return; - } - - if (eoe->rx_state == EC_EOE_CHECKING) { - if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) { - master->stats.eoe_errors++; - eoe->rx_state = EC_EOE_IDLE; - return; - } - if (!ec_slave_mbox_check(eoe->slave)) { - eoe->rx_state = EC_EOE_IDLE; - return; - } - ec_slave_mbox_prepare_fetch(eoe->slave); - ec_master_queue_command(master, &eoe->slave->mbox_command); - eoe->rx_state = EC_EOE_FETCHING; - return; - } - - if (eoe->rx_state == EC_EOE_FETCHING) { - if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) { - master->stats.eoe_errors++; - eoe->rx_state = EC_EOE_IDLE; - return; - } - if (!(data = ec_slave_mbox_fetch(eoe->slave, 0x02, &rec_size))) { - master->stats.eoe_errors++; - eoe->rx_state = EC_EOE_IDLE; - return; - } - -#if 0 +void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */) +{ + if (!eoe->opened) return; + + // call state function + eoe->state(eoe); +} + +/*****************************************************************************/ + +/** + Returns the state of the device. + \return 1 if the device is "up", 0 if it is "down" +*/ + +unsigned int ec_eoe_active(const ec_eoe_t *eoe /**< EoE handler */) +{ + return eoe->slave && eoe->opened; +} + +/*****************************************************************************/ + +/** + Prints EoE handler information. +*/ + +void ec_eoe_print(const ec_eoe_t *eoe /**< EoE handler */) +{ + EC_INFO(" EoE handler %s\n", eoe->dev->name); + EC_INFO(" State: %s\n", eoe->opened ? "opened" : "closed"); + if (eoe->slave) + EC_INFO(" Coupled to slave %i.\n", eoe->slave->ring_position); + else + EC_INFO(" Not coupled.\n"); +} + +/****************************************************************************** + * STATE PROCESSING FUNCTIONS + *****************************************************************************/ + +/** + State: RX_START. + Starts a new receiving sequence by queueing a command that checks the + slave's mailbox for a new EoE command. +*/ + +void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */) +{ + if (!eoe->slave->online || !eoe->slave->master->device->link_state) + return; + + ec_slave_mbox_prepare_check(eoe->slave); + ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command); + eoe->state = ec_eoe_state_rx_check; +} + +/*****************************************************************************/ + +/** + State: RX_CHECK. + Processes the checking command sent in RX_START and issues a receive + command, if new data is available. +*/ + +void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */) +{ + if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) { + eoe->stats.rx_errors++; + eoe->state = ec_eoe_state_tx_start; + return; + } + + if (!ec_slave_mbox_check(eoe->slave)) { + eoe->state = ec_eoe_state_tx_start; + return; + } + + ec_slave_mbox_prepare_fetch(eoe->slave); + ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command); + eoe->state = ec_eoe_state_rx_fetch; +} + +/*****************************************************************************/ + +/** + State: RX_FETCH. + Checks if the requested data of RX_CHECK was received and processes the + EoE command. +*/ + +void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */) +{ + size_t rec_size, data_size; + uint8_t *data, frame_type, last_fragment, time_appended; + uint8_t frame_number, fragment_offset, fragment_number; + off_t offset; + + if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) { + eoe->stats.rx_errors++; + eoe->state = ec_eoe_state_tx_start; + return; + } + + if (!(data = ec_slave_mbox_fetch(eoe->slave, 0x02, &rec_size))) { + eoe->stats.rx_errors++; + eoe->state = ec_eoe_state_tx_start; + return; + } + + frame_type = EC_READ_U16(data) & 0x000F; + + if (frame_type == 0x00) { // EoE Fragment Request + last_fragment = (EC_READ_U16(data) >> 8) & 0x0001; + time_appended = (EC_READ_U16(data) >> 9) & 0x0001; fragment_number = EC_READ_U16(data + 2) & 0x003F; - complete_size = (EC_READ_U16(data + 2) >> 6) & 0x003F; - frame_number = (EC_READ_U16(data + 2) >> 12) & 0x0003; - last_fragment = (EC_READ_U16(data + 2) >> 15) & 0x0001; - - EC_DBG("EOE %s received, fragment: %i, complete size: %i (0x%02X)," - " frame %i%s\n", - fragment_number ? "fragment" : "initiate", fragment_number, - (complete_size - 31) / 32, complete_size, frame_number, - last_fragment ? ", last fragment" : ""); + fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F; + frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F; + +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("EoE RX fragment %i, offset %i, frame %i%s%s," + " %i octets\n", fragment_number, fragment_offset, + frame_number, + last_fragment ? ", last fragment" : "", + time_appended ? ", + timestamp" : "", + time_appended ? rec_size - 8 : rec_size - 4); +#endif + +#if EOE_DEBUG_LEVEL > 1 EC_DBG(""); - for (i = 0; i < rec_size - 2; i++) { - printk("%02X ", data[i + 2]); + for (i = 0; i < rec_size - 4; i++) { + printk("%02X ", data[i + 4]); if ((i + 1) % 16 == 0) { printk("\n"); EC_DBG(""); @@ -135,21 +398,303 @@ printk("\n"); #endif - eoe->rx_state = EC_EOE_IDLE; - return; - } -} - -/*****************************************************************************/ - -/** - Prints EoE object information. -*/ - -void ec_eoe_print(const ec_eoe_t *eoe) -{ - EC_INFO(" EoE slave %i\n", eoe->slave->ring_position); - EC_INFO(" RX State %i\n", eoe->rx_state); -} - -/*****************************************************************************/ + data_size = time_appended ? rec_size - 8 : rec_size - 4; + + if (!fragment_number) { + if (eoe->rx_skb) { + EC_WARN("EoE RX freeing old socket buffer...\n"); + dev_kfree_skb(eoe->rx_skb); + } + + // new socket buffer + if (!(eoe->rx_skb = dev_alloc_skb(fragment_offset * 32))) { + if (printk_ratelimit()) + EC_WARN("EoE RX low on mem. frame dropped.\n"); + eoe->stats.rx_dropped++; + eoe->state = ec_eoe_state_tx_start; + return; + } + + eoe->rx_skb_offset = 0; + eoe->rx_skb_size = fragment_offset * 32; + eoe->rx_expected_fragment = 0; + } + else { + if (!eoe->rx_skb) { + eoe->stats.rx_dropped++; + eoe->state = ec_eoe_state_tx_start; + return; + } + + offset = fragment_offset * 32; + if (offset != eoe->rx_skb_offset || + offset + data_size > eoe->rx_skb_size || + fragment_number != eoe->rx_expected_fragment) { + eoe->stats.rx_errors++; + eoe->state = ec_eoe_state_tx_start; + dev_kfree_skb(eoe->rx_skb); + eoe->rx_skb = NULL; + return; + } + } + + // copy fragment into socket buffer + memcpy(skb_put(eoe->rx_skb, data_size), data + 4, data_size); + eoe->rx_skb_offset += data_size; + + if (last_fragment) { + // update statistics + eoe->stats.rx_packets++; + eoe->stats.rx_bytes += eoe->rx_skb->len; + +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("EoE RX frame completed with %u octets.\n", + eoe->rx_skb->len); +#endif + + // pass socket buffer to network stack + eoe->rx_skb->dev = eoe->dev; + eoe->rx_skb->protocol = eth_type_trans(eoe->rx_skb, eoe->dev); + eoe->rx_skb->ip_summed = CHECKSUM_UNNECESSARY; + if (netif_rx(eoe->rx_skb)) { + EC_WARN("EoE RX netif_rx failed.\n"); + } + eoe->rx_skb = NULL; + + eoe->state = ec_eoe_state_tx_start; + } + else { + eoe->rx_expected_fragment++; +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("EoE RX expecting fragment %i\n", + eoe->rx_expected_fragment); +#endif + eoe->state = ec_eoe_state_rx_start; + } + } + else { +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("other frame received.\n"); +#endif + eoe->stats.rx_dropped++; + eoe->state = ec_eoe_state_tx_start; + } +} + +/*****************************************************************************/ + +/** + State: TX START. + Starts a new transmit sequence. If no data is available, a new receive + sequence is started instead. +*/ + +void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */) +{ +#if EOE_DEBUG_LEVEL > 0 + unsigned int wakeup; +#endif + + if (!eoe->slave->online || !eoe->slave->master->device->link_state) + return; + + spin_lock_bh(&eoe->tx_queue_lock); + + if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) { + spin_unlock_bh(&eoe->tx_queue_lock); + // no data available. + // start a new receive immediately. + ec_eoe_state_rx_start(eoe); + return; + } + + // take the first frame out of the queue + eoe->tx_frame = list_entry(eoe->tx_queue.next, ec_eoe_frame_t, queue); + list_del(&eoe->tx_frame->queue); + if (!eoe->tx_queue_active && + eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE / 2) { + netif_wake_queue(eoe->dev); + eoe->tx_queue_active = 1; +#if EOE_DEBUG_LEVEL > 0 + wakeup = 1; +#endif + } + + eoe->tx_queued_frames--; + spin_unlock_bh(&eoe->tx_queue_lock); + + eoe->tx_frame_number++; + eoe->tx_frame_number %= 16; + eoe->tx_fragment_number = 0; + eoe->tx_offset = 0; + + if (ec_eoe_send(eoe)) { + dev_kfree_skb(eoe->tx_frame->skb); + kfree(eoe->tx_frame); + eoe->tx_frame = NULL; + eoe->stats.tx_errors++; + eoe->state = ec_eoe_state_rx_start; + return; + } + +#if EOE_DEBUG_LEVEL > 0 + if (wakeup) EC_DBG("waking up TX queue...\n"); +#endif + + eoe->state = ec_eoe_state_tx_sent; +} + +/*****************************************************************************/ + +/** + State: TX SENT. + Checks is the previous transmit command succeded and sends the next + fragment, if necessary. +*/ + +void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */) +{ + if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) { + eoe->stats.tx_errors++; + eoe->state = ec_eoe_state_rx_start; + return; + } + + if (eoe->slave->mbox_command.working_counter != 1) { + eoe->stats.tx_errors++; + eoe->state = ec_eoe_state_rx_start; + return; + } + + // frame completely sent + if (eoe->tx_offset >= eoe->tx_frame->skb->len) { + eoe->stats.tx_packets++; + eoe->stats.tx_bytes += eoe->tx_frame->skb->len; + dev_kfree_skb(eoe->tx_frame->skb); + kfree(eoe->tx_frame); + eoe->tx_frame = NULL; + eoe->state = ec_eoe_state_rx_start; + } + else { // send next fragment + if (ec_eoe_send(eoe)) { + dev_kfree_skb(eoe->tx_frame->skb); + kfree(eoe->tx_frame); + eoe->tx_frame = NULL; + eoe->stats.tx_errors++; + eoe->state = ec_eoe_state_rx_start; + } + } +} + +/****************************************************************************** + * NET_DEVICE functions + *****************************************************************************/ + +/** + Opens the virtual network device. +*/ + +int ec_eoedev_open(struct net_device *dev /**< EoE net_device */) +{ + ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev)); + ec_eoe_flush(eoe); + eoe->opened = 1; + netif_start_queue(dev); + eoe->tx_queue_active = 1; + EC_INFO("%s opened.\n", dev->name); + if (!eoe->slave) + EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name); + else { + eoe->slave->requested_state = EC_SLAVE_STATE_OP; + eoe->slave->state_error = 0; + } + return 0; +} + +/*****************************************************************************/ + +/** + Stops the virtual network device. +*/ + +int ec_eoedev_stop(struct net_device *dev /**< EoE net_device */) +{ + ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev)); + netif_stop_queue(dev); + eoe->tx_queue_active = 0; + eoe->opened = 0; + ec_eoe_flush(eoe); + EC_INFO("%s stopped.\n", dev->name); + if (!eoe->slave) + EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name); + else { + eoe->slave->requested_state = EC_SLAVE_STATE_INIT; + eoe->slave->state_error = 0; + } + return 0; +} + +/*****************************************************************************/ + +/** + Transmits data via the virtual network device. +*/ + +int ec_eoedev_tx(struct sk_buff *skb, /**< transmit socket buffer */ + struct net_device *dev /**< EoE net_device */ + ) +{ + ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev)); + ec_eoe_frame_t *frame; + +#if 0 + if (skb->len > eoe->slave->sii_tx_mailbox_size - 10) { + EC_WARN("EoE TX frame (%i octets) exceeds MTU. dropping.\n", skb->len); + dev_kfree_skb(skb); + eoe->stats.tx_dropped++; + return 0; + } +#endif + + if (!(frame = + (ec_eoe_frame_t *) kmalloc(sizeof(ec_eoe_frame_t), GFP_ATOMIC))) { + if (printk_ratelimit()) + EC_WARN("EoE TX: low on mem. frame dropped.\n"); + return 1; + } + + frame->skb = skb; + + spin_lock_bh(&eoe->tx_queue_lock); + list_add_tail(&frame->queue, &eoe->tx_queue); + eoe->tx_queued_frames++; + if (eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE) { + netif_stop_queue(dev); + eoe->tx_queue_active = 0; + } + spin_unlock_bh(&eoe->tx_queue_lock); + +#if EOE_DEBUG_LEVEL > 0 + EC_DBG("EoE TX queued frame with %i octets (%i frames queued).\n", + skb->len, eoe->tx_queued_frames); + if (!eoe->tx_queue_active) + EC_WARN("EoE TX queue is now full.\n"); +#endif + + return 0; +} + +/*****************************************************************************/ + +/** + Gets statistics about the virtual network device. +*/ + +struct net_device_stats *ec_eoedev_stats(struct net_device *dev + /**< EoE net_device */) +{ + ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev)); + return &eoe->stats; +} + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/ethernet.h --- a/master/ethernet.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/ethernet.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -29,6 +39,7 @@ /*****************************************************************************/ #include +#include #include "../include/ecrt.h" #include "globals.h" @@ -38,38 +49,54 @@ /*****************************************************************************/ /** - State of an EoE object. + Queued frame structure. */ -typedef enum +typedef struct { - EC_EOE_IDLE, /**< Idle. The next step ist to check for data. */ - EC_EOE_CHECKING, /**< Checking frame was sent. */ - EC_EOE_FETCHING /**< There is new data. Fetching frame was sent. */ + struct list_head queue; /**< list item */ + struct sk_buff *skb; /**< socket buffer */ } -ec_eoe_state_t; +ec_eoe_frame_t; /*****************************************************************************/ +typedef struct ec_eoe ec_eoe_t; + /** - Ethernet-over-EtherCAT (EoE) Object. + Ethernet-over-EtherCAT (EoE) handler. The master creates one of these objects for each slave that supports the EoE protocol. */ -typedef struct +struct ec_eoe { struct list_head list; /**< list item */ ec_slave_t *slave; /**< pointer to the corresponding slave */ - ec_eoe_state_t rx_state; /**< state of the state machine */ -} -ec_eoe_t; + void (*state)(ec_eoe_t *); /**< state function for the state machine */ + struct net_device *dev; /**< net_device for virtual ethernet device */ + struct net_device_stats stats; /**< device statistics */ + unsigned int opened; /**< net_device is opened */ + struct sk_buff *rx_skb; /**< current rx socket buffer */ + off_t rx_skb_offset; /**< current write pointer in the socket buffer */ + size_t rx_skb_size; /**< size of the allocated socket buffer memory */ + uint8_t rx_expected_fragment; /**< next expected fragment number */ + struct list_head tx_queue; /**< queue for frames to send */ + unsigned int tx_queue_active; /**< kernel netif queue started */ + unsigned int tx_queued_frames; /**< number of frames in the queue */ + spinlock_t tx_queue_lock; /**< spinlock for the send queue */ + ec_eoe_frame_t *tx_frame; /**< current TX frame */ + uint8_t tx_frame_number; /**< number of the transmitted frame */ + uint8_t tx_fragment_number; /**< number of the fragment */ + size_t tx_offset; /**< number of octets sent */ +}; /*****************************************************************************/ -void ec_eoe_init(ec_eoe_t *, ec_slave_t *); +int ec_eoe_init(ec_eoe_t *); void ec_eoe_clear(ec_eoe_t *); void ec_eoe_run(ec_eoe_t *); +unsigned int ec_eoe_active(const ec_eoe_t *); void ec_eoe_print(const ec_eoe_t *); /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/fsm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm.c Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,1758 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +/** + \file + EtherCAT finite state machines. +*/ + +/*****************************************************************************/ + +#include "globals.h" +#include "fsm.h" +#include "master.h" + +/*****************************************************************************/ + +/** + Size of memory to allocate while reading categories. +*/ + +#define EC_CAT_MEM 0x100 + +/*****************************************************************************/ + +const ec_code_msg_t al_status_messages[]; + +/*****************************************************************************/ + +void ec_fsm_master_start(ec_fsm_t *); +void ec_fsm_master_broadcast(ec_fsm_t *); +void ec_fsm_master_proc_states(ec_fsm_t *); +void ec_fsm_master_scan(ec_fsm_t *); +void ec_fsm_master_states(ec_fsm_t *); +void ec_fsm_master_validate_vendor(ec_fsm_t *); +void ec_fsm_master_validate_product(ec_fsm_t *); +void ec_fsm_master_reconfigure(ec_fsm_t *); +void ec_fsm_master_address(ec_fsm_t *); +void ec_fsm_master_conf(ec_fsm_t *); + +void ec_fsm_slave_start_reading(ec_fsm_t *); +void ec_fsm_slave_read_status(ec_fsm_t *); +void ec_fsm_slave_read_base(ec_fsm_t *); +void ec_fsm_slave_read_dl(ec_fsm_t *); +void ec_fsm_slave_prepare_sii(ec_fsm_t *); +void ec_fsm_slave_read_sii(ec_fsm_t *); +void ec_fsm_slave_category_header(ec_fsm_t *); +void ec_fsm_slave_category_data(ec_fsm_t *); +void ec_fsm_slave_end(ec_fsm_t *); + +void ec_fsm_slave_conf(ec_fsm_t *); +void ec_fsm_slave_sync(ec_fsm_t *); +void ec_fsm_slave_preop(ec_fsm_t *); +void ec_fsm_slave_fmmu(ec_fsm_t *); +void ec_fsm_slave_saveop(ec_fsm_t *); +void ec_fsm_slave_op(ec_fsm_t *); +void ec_fsm_slave_op2(ec_fsm_t *); + +void ec_fsm_sii_start_reading(ec_fsm_t *); +void ec_fsm_sii_check(ec_fsm_t *); +void ec_fsm_sii_fetch(ec_fsm_t *); +void ec_fsm_sii_end(ec_fsm_t *); +void ec_fsm_sii_error(ec_fsm_t *); + +void ec_fsm_change_start(ec_fsm_t *); +void ec_fsm_change_check(ec_fsm_t *); +void ec_fsm_change_status(ec_fsm_t *); +void ec_fsm_change_code(ec_fsm_t *); +void ec_fsm_change_ack(ec_fsm_t *); +void ec_fsm_change_ack2(ec_fsm_t *); +void ec_fsm_change_end(ec_fsm_t *); +void ec_fsm_change_error(ec_fsm_t *); + +/*****************************************************************************/ + +/** + Constructor. +*/ + +int ec_fsm_init(ec_fsm_t *fsm, /**< finite state machine */ + ec_master_t *master /**< EtherCAT master */ + ) +{ + fsm->master = master; + fsm->master_state = ec_fsm_master_start; + fsm->master_slaves_responding = 0; + fsm->master_slave_states = EC_SLAVE_STATE_UNKNOWN; + fsm->master_validation = 0; + fsm->slave_cat_data = NULL; + + ec_command_init(&fsm->command); + if (ec_command_prealloc(&fsm->command, EC_MAX_DATA_SIZE)) { + EC_ERR("FSM failed to allocate FSM command.\n"); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + Destructor. +*/ + +void ec_fsm_clear(ec_fsm_t *fsm /**< finite state machine */) +{ + if (fsm->slave_cat_data) kfree(fsm->slave_cat_data); + ec_command_clear(&fsm->command); +} + +/*****************************************************************************/ + +/** + Resets the state machine. +*/ + +void ec_fsm_reset(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->master_state = ec_fsm_master_start; + fsm->master_slaves_responding = 0; + fsm->master_slave_states = EC_SLAVE_STATE_UNKNOWN; + + if (fsm->slave_cat_data) { + kfree(fsm->slave_cat_data); + fsm->slave_cat_data = NULL; + } +} + +/*****************************************************************************/ + +/** + Executes the current state of the state machine. +*/ + +void ec_fsm_execute(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->master_state(fsm); +} + +/****************************************************************************** + * master state machine + *****************************************************************************/ + +/** + Master state: START. + Starts with getting slave count and slave states. +*/ + +void ec_fsm_master_start(ec_fsm_t *fsm) +{ + ec_command_brd(&fsm->command, 0x0130, 2); + ec_master_queue_command(fsm->master, &fsm->command); + fsm->master_state = ec_fsm_master_broadcast; +} + +/*****************************************************************************/ + +/** + Master state: BROADCAST. + Processes the broadcast read slave count and slaves states. +*/ + +void ec_fsm_master_broadcast(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + unsigned int topology_change, states_change, i; + ec_slave_t *slave; + ec_master_t *master = fsm->master; + + if (command->state != EC_CMD_RECEIVED) { + if (!master->device->link_state) { + fsm->master_slaves_responding = 0; + list_for_each_entry(slave, &master->slaves, list) { + slave->online = 0; + } + } + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + topology_change = (command->working_counter != + fsm->master_slaves_responding); + states_change = (EC_READ_U8(command->data) != fsm->master_slave_states); + + fsm->master_slave_states = EC_READ_U8(command->data); + fsm->master_slaves_responding = command->working_counter; + + if (topology_change) { + EC_INFO("%i slave%s responding.\n", + fsm->master_slaves_responding, + fsm->master_slaves_responding == 1 ? "" : "s"); + + if (master->mode == EC_MASTER_MODE_RUNNING) { + if (fsm->master_slaves_responding == master->slave_count) { + fsm->master_validation = 1; // start validation later + } + else { + EC_WARN("Invalid slave count. Bus in tainted state.\n"); + } + } + } + + if (states_change) { + EC_INFO("Slave states: "); + ec_print_states(fsm->master_slave_states); + printk(".\n"); + } + + // topology change in free-run mode: clear all slaves and scan the bus + if (topology_change && master->mode == EC_MASTER_MODE_FREERUN) { + EC_INFO("Scanning bus.\n"); + + ec_master_eoe_stop(master); + ec_master_clear_slaves(master); + + if (!fsm->master_slaves_responding) { + // no slaves present -> finish state machine. + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // init slaves + for (i = 0; i < fsm->master_slaves_responding; i++) { + if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t), + GFP_ATOMIC))) { + EC_ERR("Failed to allocate slave %i!\n", i); + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + if (ec_slave_init(slave, master, i, i + 1)) { + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + if (kobject_add(&slave->kobj)) { + EC_ERR("Failed to add kobject.\n"); + kobject_put(&slave->kobj); // free + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + list_add_tail(&slave->list, &master->slaves); + } + + // begin scanning of slaves + fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); + fsm->slave_state = ec_fsm_slave_start_reading; + fsm->master_state = ec_fsm_master_scan; + fsm->master_state(fsm); // execute immediately + return; + } + + // fetch state from each slave + fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); + ec_command_nprd(&fsm->command, fsm->slave->station_address, 0x0130, 2); + ec_master_queue_command(master, &fsm->command); + fsm->master_state = ec_fsm_master_states; +} + +/*****************************************************************************/ + +/** + Master action: Get state of next slave. +*/ + +void ec_fsm_master_action_next_slave_state(ec_fsm_t *fsm + /**< finite state machine */) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave = fsm->slave; + + // have all states been read? + if (slave->list.next == &master->slaves) { + + // check, if a bus validation has to be done + if (fsm->master_validation) { + fsm->master_validation = 0; + list_for_each_entry(slave, &master->slaves, list) { + if (slave->online) continue; + + // At least one slave is offline. validate! + EC_INFO("Validating bus.\n"); + fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); + fsm->master_state = ec_fsm_master_validate_vendor; + fsm->sii_offset = 0x0008; // vendor ID + fsm->sii_mode = 0; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->sii_state(fsm); // execute immediately + return; + } + } + + fsm->master_state = ec_fsm_master_proc_states; + fsm->master_state(fsm); // execute immediately + return; + } + + // process next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + ec_command_nprd(&fsm->command, fsm->slave->station_address, 0x0130, 2); + ec_master_queue_command(master, &fsm->command); + fsm->master_state = ec_fsm_master_states; +} + +/*****************************************************************************/ + +/** + Master state: STATES. + Fetches the AL- and online state of a slave. +*/ + +void ec_fsm_master_states(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_command_t *command = &fsm->command; + uint8_t new_state; + + if (command->state != EC_CMD_RECEIVED) { + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // did the slave not respond to its station address? + if (command->working_counter != 1) { + if (slave->online) { + slave->online = 0; + EC_INFO("Slave %i: offline.\n", slave->ring_position); + } + ec_fsm_master_action_next_slave_state(fsm); + return; + } + + // slave responded + new_state = EC_READ_U8(command->data); + if (!slave->online) { // slave was offline before + slave->online = 1; + slave->state_error = 0; + slave->current_state = new_state; + EC_INFO("Slave %i: online (", slave->ring_position); + ec_print_states(new_state); + printk(").\n"); + } + else if (new_state != slave->current_state) { + EC_INFO("Slave %i: ", slave->ring_position); + ec_print_states(slave->current_state); + printk(" -> "); + ec_print_states(new_state); + printk(".\n"); + slave->current_state = new_state; + } + + ec_fsm_master_action_next_slave_state(fsm); +} + +/*****************************************************************************/ + +/** + Master state: PROC_STATES. + Processes the slave states. +*/ + +void ec_fsm_master_proc_states(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave; + + // check if any slaves are not in the state, they're supposed to be + list_for_each_entry(slave, &master->slaves, list) { + if (slave->state_error || !slave->online || + slave->requested_state == EC_SLAVE_STATE_UNKNOWN || + slave->current_state == slave->requested_state) continue; + + EC_INFO("Changing state of slave %i from ", slave->ring_position); + ec_print_states(slave->current_state); + printk(" to "); + ec_print_states(slave->requested_state); + printk(".\n"); + + fsm->slave = slave; + fsm->slave_state = ec_fsm_slave_conf; + fsm->change_new = EC_SLAVE_STATE_INIT; + fsm->change_state = ec_fsm_change_start; + fsm->master_state = ec_fsm_master_conf; + fsm->master_state(fsm); // execute immediately + return; + } + + // nothing to configure. restart master state machine. + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: VALIDATE_VENDOR. + Validates the vendor ID of a slave. +*/ + +void ec_fsm_master_validate_vendor(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + + fsm->sii_state(fsm); // execute SII state machine + + if (fsm->sii_state == ec_fsm_sii_error) { + EC_ERR("Failed to validate vendor ID of slave %i.\n", + slave->ring_position); + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + if (fsm->sii_state != ec_fsm_sii_end) return; + + if (fsm->sii_result != slave->sii_vendor_id) { + EC_ERR("Slave %i: invalid vendor ID!\n", slave->ring_position); + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // vendor ID is ok. check product code. + fsm->master_state = ec_fsm_master_validate_product; + fsm->sii_offset = 0x000A; // product code + fsm->sii_mode = 0; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->sii_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: VALIDATE_PRODUCT. + Validates the product ID of a slave. +*/ + +void ec_fsm_master_validate_product(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + + fsm->sii_state(fsm); // execute SII state machine + + if (fsm->sii_state == ec_fsm_sii_error) { + EC_ERR("Failed to validate product code of slave %i.\n", + slave->ring_position); + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + if (fsm->sii_state != ec_fsm_sii_end) return; + + if (fsm->sii_result != slave->sii_product_code) { + EC_ERR("Slave %i: invalid product code!\n", slave->ring_position); + EC_ERR("expected 0x%08X, got 0x%08X.\n", slave->sii_product_code, + fsm->sii_result); + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // have all states been validated? + if (slave->list.next == &fsm->master->slaves) { + fsm->slave = list_entry(fsm->master->slaves.next, ec_slave_t, list); + fsm->master_state = ec_fsm_master_reconfigure; + return; + } + + // validate next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + fsm->master_state = ec_fsm_master_validate_vendor; + fsm->sii_offset = 0x0008; // vendor ID + fsm->sii_mode = 0; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->sii_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: RECONFIGURE. + Looks for slave, that have lost their configuration and writes + their station address, so that they can be reconfigured later. +*/ + +void ec_fsm_master_reconfigure(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + while (fsm->slave->online) { + if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + // check next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + } + + EC_INFO("Reinitializing slave %i.\n", fsm->slave->ring_position); + + // write station address + ec_command_apwr(command, fsm->slave->ring_position, 0x0010, 2); + EC_WRITE_U16(command->data, fsm->slave->station_address); + ec_master_queue_command(fsm->master, command); + fsm->master_state = ec_fsm_master_address; +} + +/*****************************************************************************/ + +/** + Master state: ADDRESS. + Checks, if the new station address has been written to the slave. +*/ + +void ec_fsm_master_address(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_command_t *command = &fsm->command; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Failed to write station address on slave %i.\n", + slave->ring_position); + } + + if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // check next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + fsm->master_state = ec_fsm_master_reconfigure; + fsm->master_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: SCAN. + Executes the sub-statemachine for the scanning of a slave. +*/ + +void ec_fsm_master_scan(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave = fsm->slave; + uint16_t coupler_index, coupler_subindex; + uint16_t reverse_coupler_index, current_coupler_index; + ec_slave_ident_t *ident; + + fsm->slave_state(fsm); // execute slave state machine + + if (fsm->slave_state != ec_fsm_slave_end) return; + + // have all slaves been fetched? + if (slave->list.next == &master->slaves) { + EC_INFO("Bus scanning completed.\n"); + + // identify all slaves and calculate coupler addressing + + coupler_index = 0; + reverse_coupler_index = 0xFFFF; + current_coupler_index = 0x3FFF; + coupler_subindex = 0; + + list_for_each_entry(slave, &master->slaves, list) + { + // search for identification in "database" + ident = slave_idents; + while (ident->type) { + if (ident->vendor_id == slave->sii_vendor_id + && ident->product_code == slave->sii_product_code) { + slave->type = ident->type; + break; + } + ident++; + } + + if (!slave->type) { + EC_WARN("FSM: Unknown slave device (vendor 0x%08X," + " code 0x%08X) at position %i.\n", + slave->sii_vendor_id, slave->sii_product_code, + slave->ring_position); + } + else { + // if the slave is a bus coupler, change adressing base + if (slave->type->special == EC_TYPE_BUS_COUPLER) { + if (slave->sii_alias) + current_coupler_index = reverse_coupler_index--; + else + current_coupler_index = coupler_index++; + coupler_subindex = 0; + } + } + + // determine initial state. + if ((slave->type && slave->type->special == EC_TYPE_BUS_COUPLER)) { + slave->requested_state = EC_SLAVE_STATE_OP; + } + else { + if (master->mode == EC_MASTER_MODE_RUNNING) + slave->requested_state = EC_SLAVE_STATE_PREOP; + else + slave->requested_state = EC_SLAVE_STATE_INIT; + } + slave->state_error = 0; + + // calculate coupler-based slave address + slave->coupler_index = current_coupler_index; + slave->coupler_subindex = coupler_subindex; + coupler_subindex++; + } + + if (master->mode == EC_MASTER_MODE_FREERUN) { + // start EoE processing + ec_master_eoe_start(master); + } + + fsm->master_state = ec_fsm_master_start; + fsm->master_state(fsm); // execute immediately + return; + } + + // process next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + fsm->slave_state = ec_fsm_slave_start_reading; + fsm->slave_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: CONF. + Starts configuring a slave. +*/ + +void ec_fsm_master_conf(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->slave_state(fsm); // execute slave's state machine + if (fsm->slave_state != ec_fsm_slave_end) return; + fsm->master_state = ec_fsm_master_proc_states; + fsm->master_state(fsm); // execute immediately +} + +/****************************************************************************** + * slave state machine + *****************************************************************************/ + +/** + Slave state: START_READING. + First state of the slave state machine. Writes the station address to the + slave, according to its ring position. +*/ + +void ec_fsm_slave_start_reading(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + // write station address + ec_command_apwr(command, fsm->slave->ring_position, 0x0010, 2); + EC_WRITE_U16(command->data, fsm->slave->station_address); + ec_master_queue_command(fsm->master, command); + fsm->slave_state = ec_fsm_slave_read_status; +} + +/*****************************************************************************/ + +/** + Slave state: READ_STATUS. +*/ + +void ec_fsm_slave_read_status(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("FSM failed to write station address of slave %i.\n", + fsm->slave->ring_position); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // read AL status + ec_command_nprd(command, fsm->slave->station_address, 0x0130, 2); + ec_master_queue_command(fsm->master, command); + fsm->slave_state = ec_fsm_slave_read_base; +} + +/*****************************************************************************/ + +/** + Slave state: READ_BASE. +*/ + +void ec_fsm_slave_read_base(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("FSM failed to read AL status of slave %i.\n", + fsm->slave->ring_position); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + slave->current_state = EC_READ_U8(command->data); + if (slave->current_state & EC_ACK) { + EC_WARN("Slave %i has status error bit set (0x%02X)!\n", + slave->ring_position, slave->current_state); + slave->current_state &= 0x0F; + } + + // read base data + ec_command_nprd(command, fsm->slave->station_address, 0x0000, 6); + ec_master_queue_command(fsm->master, command); + fsm->slave_state = ec_fsm_slave_read_dl; +} + +/*****************************************************************************/ + +/** + Slave state: READ_DL. +*/ + +void ec_fsm_slave_read_dl(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("FSM failed to read base data of slave %i.\n", + slave->ring_position); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + slave->base_type = EC_READ_U8 (command->data); + slave->base_revision = EC_READ_U8 (command->data + 1); + slave->base_build = EC_READ_U16(command->data + 2); + slave->base_fmmu_count = EC_READ_U8 (command->data + 4); + slave->base_sync_count = EC_READ_U8 (command->data + 5); + + if (slave->base_fmmu_count > EC_MAX_FMMUS) + slave->base_fmmu_count = EC_MAX_FMMUS; + + // read data link status + ec_command_nprd(command, slave->station_address, 0x0110, 2); + ec_master_queue_command(slave->master, command); + fsm->slave_state = ec_fsm_slave_prepare_sii; +} + +/*****************************************************************************/ + +/** + Slave state: PREPARE_SII. +*/ + +void ec_fsm_slave_prepare_sii(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + uint16_t dl_status; + unsigned int i; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("FSM failed to read DL status of slave %i.\n", + slave->ring_position); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + dl_status = EC_READ_U16(command->data); + + for (i = 0; i < 4; i++) { + slave->dl_link[i] = dl_status & (1 << (4 + i)) ? 1 : 0; + slave->dl_loop[i] = dl_status & (1 << (8 + i * 2)) ? 1 : 0; + slave->dl_signal[i] = dl_status & (1 << (9 + i * 2)) ? 1 : 0; + } + + fsm->sii_offset = 0x0004; + fsm->sii_mode = 1; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->slave_sii_num = 0; + fsm->slave_state = ec_fsm_slave_read_sii; + fsm->slave_state(fsm); // execute state immediately +} + +/*****************************************************************************/ + +/** + Slave state: READ_SII. +*/ + +void ec_fsm_slave_read_sii(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + + // execute SII state machine + fsm->sii_state(fsm); + + if (fsm->sii_state == ec_fsm_sii_error) { + fsm->slave_state = ec_fsm_slave_end; + EC_ERR("FSM failed to read SII data at 0x%04X on slave %i.\n", + fsm->sii_offset, slave->ring_position); + return; + } + + if (fsm->sii_state != ec_fsm_sii_end) return; + + switch (fsm->slave_sii_num) { + case 0: + slave->sii_alias = fsm->sii_result & 0xFFFF; + fsm->sii_offset = 0x0008; + break; + case 1: + slave->sii_vendor_id = fsm->sii_result; + fsm->sii_offset = 0x000A; + break; + case 2: + slave->sii_product_code = fsm->sii_result; + fsm->sii_offset = 0x000C; + break; + case 3: + slave->sii_revision_number = fsm->sii_result; + fsm->sii_offset = 0x000E; + break; + case 4: + slave->sii_serial_number = fsm->sii_result; + fsm->sii_offset = 0x0018; + break; + case 5: + slave->sii_rx_mailbox_offset = fsm->sii_result & 0xFFFF; + slave->sii_rx_mailbox_size = fsm->sii_result >> 16; + fsm->sii_offset = 0x001A; + break; + case 6: + slave->sii_tx_mailbox_offset = fsm->sii_result & 0xFFFF; + slave->sii_tx_mailbox_size = fsm->sii_result >> 16; + fsm->sii_offset = 0x001C; + break; + case 7: + slave->sii_mailbox_protocols = fsm->sii_result & 0xFFFF; + + fsm->slave_cat_offset = 0x0040; + + if (fsm->slave_cat_data) { + EC_INFO("FSM freeing old category data on slave %i...\n", + fsm->slave->ring_position); + kfree(fsm->slave_cat_data); + } + + if (!(fsm->slave_cat_data = + (uint8_t *) kmalloc(EC_CAT_MEM, GFP_ATOMIC))) { + EC_ERR("FSM Failed to allocate category data.\n"); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // start reading first category header + fsm->sii_offset = fsm->slave_cat_offset; + fsm->sii_state = ec_fsm_sii_start_reading; + + fsm->slave_state = ec_fsm_slave_category_header; + fsm->slave_state(fsm); // execute state immediately + return; + } + + fsm->slave_sii_num++; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->slave_state(fsm); // execute state immediately +} + +/*****************************************************************************/ + +/** + Slave state: CATEGORY_HEADER. + Start reading categories. +*/ + +void ec_fsm_slave_category_header(ec_fsm_t *fsm /**< finite state machine */) +{ + // execute SII state machine + fsm->sii_state(fsm); + + if (fsm->sii_state == ec_fsm_sii_error) { + kfree(fsm->slave_cat_data); + fsm->slave_cat_data = NULL; + fsm->slave_state = ec_fsm_slave_end; + EC_ERR("FSM failed to read category header at 0x%04X on slave %i.\n", + fsm->slave_cat_offset, fsm->slave->ring_position); + return; + } + + if (fsm->sii_state != ec_fsm_sii_end) return; + + // last category? + if ((fsm->sii_result & 0xFFFF) == 0xFFFF) { + kfree(fsm->slave_cat_data); + fsm->slave_cat_data = NULL; + fsm->slave_state = ec_fsm_slave_end; + return; + } + + fsm->slave_cat_type = fsm->sii_result & 0x7FFF; + fsm->slave_cat_words = (fsm->sii_result >> 16) & 0xFFFF; + + if (fsm->slave_cat_words > EC_CAT_MEM * 2) { + EC_ERR("FSM category memory too small! %i words needed.\n", + fsm->slave_cat_words); + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // start reading category data + fsm->slave_cat_data_offset = 0; + fsm->sii_offset = (fsm->slave_cat_offset + 2 + + fsm->slave_cat_data_offset); + fsm->sii_mode = 1; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->slave_state = ec_fsm_slave_category_data; + fsm->slave_state(fsm); // execute state immediately +} + +/*****************************************************************************/ + +/** + Slave state: CATEGORY_DATA. + Reads category data. +*/ + +void ec_fsm_slave_category_data(ec_fsm_t *fsm /**< finite state machine */) +{ + // execute SII state machine + fsm->sii_state(fsm); + + if (fsm->sii_state == ec_fsm_sii_error) { + kfree(fsm->slave_cat_data); + fsm->slave_cat_data = NULL; + fsm->slave_state = ec_fsm_slave_end; + EC_ERR("FSM failed to read category 0x%02X data at 0x%04X" + " on slave %i.\n", fsm->slave_cat_type, fsm->sii_offset, + fsm->slave->ring_position); + return; + } + + if (fsm->sii_state != ec_fsm_sii_end) return; + + fsm->slave_cat_data[fsm->slave_cat_data_offset * 2] = + fsm->sii_result & 0xFF; + fsm->slave_cat_data[fsm->slave_cat_data_offset * 2 + 1] = + (fsm->sii_result >> 8) & 0xFF; + + // read second word "on the fly" + if (fsm->slave_cat_data_offset + 1 < fsm->slave_cat_words) { + fsm->slave_cat_data_offset++; + fsm->slave_cat_data[fsm->slave_cat_data_offset * 2] = + (fsm->sii_result >> 16) & 0xFF; + fsm->slave_cat_data[fsm->slave_cat_data_offset * 2 + 1] = + (fsm->sii_result >> 24) & 0xFF; + } + + fsm->slave_cat_data_offset++; + + if (fsm->slave_cat_data_offset < fsm->slave_cat_words) { + fsm->sii_offset = (fsm->slave_cat_offset + 2 + + fsm->slave_cat_data_offset); + fsm->sii_mode = 1; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->slave_state = ec_fsm_slave_category_data; + fsm->slave_state(fsm); // execute state immediately + return; + } + + // category data complete + switch (fsm->slave_cat_type) + { + case 0x000A: + if (ec_slave_fetch_strings(fsm->slave, fsm->slave_cat_data)) + goto out_free; + break; + case 0x001E: + if (ec_slave_fetch_general(fsm->slave, fsm->slave_cat_data)) + goto out_free; + break; + case 0x0028: + break; + case 0x0029: + if (ec_slave_fetch_sync(fsm->slave, fsm->slave_cat_data, + fsm->slave_cat_words)) + goto out_free; + break; + case 0x0032: + if (ec_slave_fetch_pdo(fsm->slave, fsm->slave_cat_data, + fsm->slave_cat_words, + EC_TX_PDO)) + goto out_free; + break; + case 0x0033: + if (ec_slave_fetch_pdo(fsm->slave, fsm->slave_cat_data, + fsm->slave_cat_words, + EC_RX_PDO)) + goto out_free; + break; + default: + EC_WARN("FSM: Unknown category type 0x%04X in slave %i.\n", + fsm->slave_cat_type, fsm->slave->ring_position); + } + + // start reading next category header + fsm->slave_cat_offset += 2 + fsm->slave_cat_words; + fsm->sii_offset = fsm->slave_cat_offset; + fsm->sii_mode = 1; + fsm->sii_state = ec_fsm_sii_start_reading; + fsm->slave_state = ec_fsm_slave_category_header; + fsm->slave_state(fsm); // execute state immediately + return; + + out_free: + kfree(fsm->slave_cat_data); + fsm->slave_cat_data = NULL; + fsm->slave_state = ec_fsm_slave_end; +} + +/*****************************************************************************/ + +/** + Slave state: CONF. +*/ + +void ec_fsm_slave_conf(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_master_t *master = fsm->master; + ec_command_t *command = &fsm->command; + + fsm->change_state(fsm); // execute state change state machine + + if (fsm->change_state == ec_fsm_change_error) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (fsm->change_state != ec_fsm_change_end) return; + + // slave is now in INIT + if (slave->current_state == slave->requested_state) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // check for slave registration + if (!slave->type) { + EC_WARN("Slave %i has unknown type!\n", slave->ring_position); + } + + // check and reset CRC fault counters + //ec_slave_check_crc(slave); + + if (!slave->base_fmmu_count) { // no fmmus + fsm->slave_state = ec_fsm_slave_sync; + fsm->slave_state(fsm); // execute immediately + return; + } + + // reset FMMUs + ec_command_npwr(command, slave->station_address, 0x0600, + EC_FMMU_SIZE * slave->base_fmmu_count); + memset(command->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count); + ec_master_queue_command(master, command); + fsm->slave_state = ec_fsm_slave_sync; +} + +/*****************************************************************************/ + +/** + Slave state: SYNC. + Configure sync managers. +*/ + +void ec_fsm_slave_sync(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + unsigned int j; + const ec_sync_t *sync; + ec_eeprom_sync_t *eeprom_sync, mbox_sync; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Failed to reset FMMUs of slave %i.\n", + slave->ring_position); + slave->state_error = 1; + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (!slave->base_sync_count) { // no sync managers + fsm->slave_state = ec_fsm_slave_preop; + fsm->slave_state(fsm); // execute immediately + return; + } + + // configure sync managers + ec_command_npwr(command, slave->station_address, 0x0800, + EC_SYNC_SIZE * slave->base_sync_count); + memset(command->data, 0x00, EC_SYNC_SIZE * slave->base_sync_count); + + // known slave type, take type's SM information + if (slave->type) { + for (j = 0; slave->type->sync_managers[j] && j < EC_MAX_SYNC; j++) { + sync = slave->type->sync_managers[j]; + ec_sync_config(sync, command->data + EC_SYNC_SIZE * j); + } + } + + // unknown type, but slave has mailbox + else if (slave->sii_mailbox_protocols) + { + // does it supply sync manager configurations in its EEPROM? + if (!list_empty(&slave->eeprom_syncs)) { + list_for_each_entry(eeprom_sync, &slave->eeprom_syncs, list) { + if (eeprom_sync->index >= slave->base_sync_count) { + EC_ERR("Invalid sync manager configuration found!"); + fsm->slave_state = ec_fsm_slave_end; + return; + } + ec_eeprom_sync_config(eeprom_sync, + command->data + EC_SYNC_SIZE + * eeprom_sync->index); + } + } + + // no sync manager information; guess mailbox settings + else { + mbox_sync.physical_start_address = + slave->sii_rx_mailbox_offset; + mbox_sync.length = slave->sii_rx_mailbox_size; + mbox_sync.control_register = 0x26; + mbox_sync.enable = 1; + ec_eeprom_sync_config(&mbox_sync, command->data); + + mbox_sync.physical_start_address = + slave->sii_tx_mailbox_offset; + mbox_sync.length = slave->sii_tx_mailbox_size; + mbox_sync.control_register = 0x22; + mbox_sync.enable = 1; + ec_eeprom_sync_config(&mbox_sync, + command->data + EC_SYNC_SIZE); + } + + EC_INFO("Mailbox configured for unknown slave %i\n", + slave->ring_position); + } + + ec_master_queue_command(fsm->master, command); + fsm->slave_state = ec_fsm_slave_preop; +} + +/*****************************************************************************/ + +/** + Slave state: PREOP. + Change slave state to PREOP. +*/ + +void ec_fsm_slave_preop(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Failed to set sync managers on slave %i.\n", + slave->ring_position); + slave->state_error = 1; + fsm->slave_state = ec_fsm_slave_end; + return; + } + + fsm->change_new = EC_SLAVE_STATE_PREOP; + fsm->change_state = ec_fsm_change_start; + fsm->slave_state = ec_fsm_slave_fmmu; + fsm->change_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Slave state: FMMU. + Configure FMMUs. +*/ + +void ec_fsm_slave_fmmu(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_master_t *master = fsm->master; + ec_command_t *command = &fsm->command; + unsigned int j; + + fsm->change_state(fsm); // execute state change state machine + + if (fsm->change_state == ec_fsm_change_error) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (fsm->change_state != ec_fsm_change_end) return; + + // slave is now in PREOP + if (slave->current_state == slave->requested_state) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // stop activation here for slaves without type + if (!slave->type) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (!slave->base_fmmu_count) { + fsm->slave_state = ec_fsm_slave_saveop; + fsm->slave_state(fsm); // execute immediately + return; + } + + // configure FMMUs + ec_command_npwr(command, slave->station_address, + 0x0600, EC_FMMU_SIZE * slave->base_fmmu_count); + memset(command->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count); + for (j = 0; j < slave->fmmu_count; j++) { + ec_fmmu_config(&slave->fmmus[j], command->data + EC_FMMU_SIZE * j); + } + + ec_master_queue_command(master, command); + fsm->slave_state = ec_fsm_slave_saveop; +} + +/*****************************************************************************/ + +/** + Slave state: SAVEOP. + Set slave state to SAVEOP. +*/ + +void ec_fsm_slave_saveop(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + if (fsm->slave->base_fmmu_count && (command->state != EC_CMD_RECEIVED || + command->working_counter != 1)) { + EC_ERR("FSM failed to set FMMUs on slave %i.\n", + fsm->slave->ring_position); + fsm->slave->state_error = 1; + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // set state to SAVEOP + fsm->slave_state = ec_fsm_slave_op; + fsm->change_new = EC_SLAVE_STATE_SAVEOP; + fsm->change_state = ec_fsm_change_start; + fsm->change_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Slave state: OP. + Set slave state to OP. +*/ + +void ec_fsm_slave_op(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->change_state(fsm); // execute state change state machine + + if (fsm->change_state == ec_fsm_change_error) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (fsm->change_state != ec_fsm_change_end) return; + + // slave is now in SAVEOP + if (fsm->slave->current_state == fsm->slave->requested_state) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + // set state to OP + fsm->slave_state = ec_fsm_slave_op2; + fsm->change_new = EC_SLAVE_STATE_OP; + fsm->change_state = ec_fsm_change_start; + fsm->change_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Slave state: OP2 + Executes the change state machine, until the OP state is set. +*/ + +void ec_fsm_slave_op2(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->change_state(fsm); // execute state change state machine + + if (fsm->change_state == ec_fsm_change_error) { + fsm->slave_state = ec_fsm_slave_end; + return; + } + + if (fsm->change_state != ec_fsm_change_end) return; + + // slave is now in OP + fsm->slave_state = ec_fsm_slave_end; +} + +/*****************************************************************************/ + +/** + Slave state: END. + End state of the slave state machine. +*/ + +void ec_fsm_slave_end(ec_fsm_t *fsm /**< finite state machine */) +{ +} + +/****************************************************************************** + * SII state machine + *****************************************************************************/ + +/** + SII state: START_READING. + Starts reading the slave information interface. +*/ + +void ec_fsm_sii_start_reading(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + // initiate read operation + if (fsm->sii_mode) { + ec_command_npwr(command, fsm->slave->station_address, 0x502, 4); + } + else { + ec_command_apwr(command, fsm->slave->ring_position, 0x502, 4); + } + + EC_WRITE_U8 (command->data, 0x00); // read-only access + EC_WRITE_U8 (command->data + 1, 0x01); // request read operation + EC_WRITE_U16(command->data + 2, fsm->sii_offset); + ec_master_queue_command(fsm->master, command); + fsm->sii_state = ec_fsm_sii_check; +} + +/*****************************************************************************/ + +/** + SII state: CHECK. + Checks, if the SII-read-command has been sent and issues a fetch command. +*/ + +void ec_fsm_sii_check(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("SII: Reception of read command failed.\n"); + fsm->sii_state = ec_fsm_sii_error; + return; + } + + fsm->sii_start = get_cycles(); + + // issue check/fetch command + if (fsm->sii_mode) { + ec_command_nprd(command, fsm->slave->station_address, 0x502, 10); + } + else { + ec_command_aprd(command, fsm->slave->ring_position, 0x502, 10); + } + + ec_master_queue_command(fsm->master, command); + fsm->sii_state = ec_fsm_sii_fetch; +} + +/*****************************************************************************/ + +/** + SII state: FETCH. + Fetches the result of an SII-read command. +*/ + +void ec_fsm_sii_fetch(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("SII: Reception of check/fetch command failed.\n"); + fsm->sii_state = ec_fsm_sii_error; + return; + } + + // check "busy bit" + if (EC_READ_U8(command->data + 1) & 0x81) { + // still busy... timeout? + if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) { + EC_ERR("SII: Timeout.\n"); + fsm->sii_state = ec_fsm_sii_error; +#if 0 + EC_DBG("SII busy: %02X %02X %02X %02X\n", + EC_READ_U8(command->data + 0), + EC_READ_U8(command->data + 1), + EC_READ_U8(command->data + 2), + EC_READ_U8(command->data + 3)); +#endif + } + + // issue check/fetch command again + if (fsm->sii_mode) { + ec_command_nprd(command, fsm->slave->station_address, 0x502, 10); + } + else { + ec_command_aprd(command, fsm->slave->ring_position, 0x502, 10); + } + ec_master_queue_command(fsm->master, command); + return; + } + +#if 0 + EC_DBG("SII rec: %02X %02X %02X %02X - %02X %02X %02X %02X\n", + EC_READ_U8(command->data + 0), EC_READ_U8(command->data + 1), + EC_READ_U8(command->data + 2), EC_READ_U8(command->data + 3), + EC_READ_U8(command->data + 6), EC_READ_U8(command->data + 7), + EC_READ_U8(command->data + 8), EC_READ_U8(command->data + 9)); +#endif + + // SII value received. + fsm->sii_result = EC_READ_U32(command->data + 6); + fsm->sii_state = ec_fsm_sii_end; +} + +/*****************************************************************************/ + +/** + SII state: END. + End state of the slave SII state machine. +*/ + +void ec_fsm_sii_end(ec_fsm_t *fsm /**< finite state machine */) +{ +} + +/*****************************************************************************/ + +/** + SII state: ERROR. + End state of the slave SII state machine. +*/ + +void ec_fsm_sii_error(ec_fsm_t *fsm /**< finite state machine */) +{ +} + +/****************************************************************************** + * state change state machine + *****************************************************************************/ + +/** + Change state: START. +*/ + +void ec_fsm_change_start(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + // write new state to slave + ec_command_npwr(command, slave->station_address, 0x0120, 2); + EC_WRITE_U16(command->data, fsm->change_new); + ec_master_queue_command(fsm->master, command); + fsm->change_state = ec_fsm_change_check; +} + +/*****************************************************************************/ + +/** + Change state: CHECK. +*/ + +void ec_fsm_change_check(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED) { + EC_ERR("Failed to send state command to slave %i!\n", + fsm->slave->ring_position); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + if (command->working_counter != 1) { + EC_ERR("Failed to set state 0x%02X on slave %i: Slave did not" + " respond.\n", fsm->change_new, fsm->slave->ring_position); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + fsm->change_start = get_cycles(); + + // read AL status from slave + ec_command_nprd(command, slave->station_address, 0x0130, 2); + ec_master_queue_command(fsm->master, command); + fsm->change_state = ec_fsm_change_status; +} + +/*****************************************************************************/ + +/** + Change state: STATUS. +*/ + +void ec_fsm_change_status(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Failed to check state 0x%02X on slave %i.\n", + fsm->change_new, slave->ring_position); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + slave->current_state = EC_READ_U8(command->data); + + if (slave->current_state == fsm->change_new) { + // state has been set successfully + fsm->change_state = ec_fsm_change_end; + return; + } + + if (slave->current_state & 0x10) { + // state change error + fsm->change_new = slave->current_state & 0x0F; + EC_ERR("Failed to set state 0x%02X - Slave %i refused state change" + " (code 0x%02X)!\n", fsm->change_new, slave->ring_position, + slave->current_state); + // fetch AL status error code + ec_command_nprd(command, slave->station_address, 0x0134, 2); + ec_master_queue_command(fsm->master, command); + fsm->change_state = ec_fsm_change_code; + return; + } + + if (get_cycles() - fsm->change_start >= (cycles_t) 10 * cpu_khz) { + // timeout while checking + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + EC_ERR("Timeout while setting state 0x%02X on slave %i.\n", + fsm->change_new, slave->ring_position); + return; + } + + // still old state: check again + ec_command_nprd(command, slave->station_address, 0x0130, 2); + ec_master_queue_command(fsm->master, command); +} + +/*****************************************************************************/ + +/** + Change state: CODE. +*/ + +void ec_fsm_change_code(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + uint32_t code; + const ec_code_msg_t *al_msg; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Reception of AL status code command failed.\n"); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + if ((code = EC_READ_U16(command->data))) { + for (al_msg = al_status_messages; al_msg->code; al_msg++) { + if (al_msg->code != code) continue; + EC_ERR("AL status message 0x%04X: \"%s\".\n", + al_msg->code, al_msg->message); + break; + } + if (!al_msg->code) + EC_ERR("Unknown AL status code 0x%04X.\n", code); + } + + // acknowledge "old" slave state + ec_command_npwr(command, slave->station_address, 0x0120, 2); + EC_WRITE_U16(command->data, slave->current_state); + ec_master_queue_command(fsm->master, command); + fsm->change_state = ec_fsm_change_ack; +} + +/*****************************************************************************/ + +/** + Change state: ACK. +*/ + +void ec_fsm_change_ack(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Reception of state ack command failed.\n"); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + // read new AL status + ec_command_nprd(command, slave->station_address, 0x0130, 2); + ec_master_queue_command(fsm->master, command); + fsm->change_state = ec_fsm_change_ack2; +} + +/*****************************************************************************/ + +/** + Change state: ACK. + Acknowledge 2. +*/ + +void ec_fsm_change_ack2(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_command_t *command = &fsm->command; + ec_slave_t *slave = fsm->slave; + + if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) { + EC_ERR("Reception of state ack check command failed.\n"); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + slave->current_state = EC_READ_U8(command->data); + + if (slave->current_state == fsm->change_new) { + EC_INFO("Acknowleged state 0x%02X on slave %i.\n", + slave->current_state, slave->ring_position); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; + return; + } + + EC_WARN("Failed to acknowledge state 0x%02X on slave %i" + " - Timeout!\n", fsm->change_new, slave->ring_position); + slave->state_error = 1; + fsm->change_state = ec_fsm_change_error; +} + +/*****************************************************************************/ + +/** + Change state: END. +*/ + +void ec_fsm_change_end(ec_fsm_t *fsm /**< finite state machine */) +{ +} + +/*****************************************************************************/ + +/** + Change state: ERROR. +*/ + +void ec_fsm_change_error(ec_fsm_t *fsm /**< finite state machine */) +{ +} + +/*****************************************************************************/ + +/** + Application layer status messages. +*/ + +const ec_code_msg_t al_status_messages[] = { + {0x0001, "Unspecified error"}, + {0x0011, "Invalud requested state change"}, + {0x0012, "Unknown requested state"}, + {0x0013, "Bootstrap not supported"}, + {0x0014, "No valid firmware"}, + {0x0015, "Invalid mailbox configuration"}, + {0x0016, "Invalid mailbox configuration"}, + {0x0017, "Invalid sync manager configuration"}, + {0x0018, "No valid inputs available"}, + {0x0019, "No valid outputs"}, + {0x001A, "Synchronisation error"}, + {0x001B, "Sync manager watchdog"}, + {0x0020, "Slave needs cold start"}, + {0x0021, "Slave needs INIT"}, + {0x0022, "Slave needs PREOP"}, + {0x0023, "Slave needs SAVEOP"}, + {} +}; + +/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/fsm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm.h Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006 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 + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * + *****************************************************************************/ + +/** + \file + EtherCAT finite state machines. +*/ + +/*****************************************************************************/ + +#ifndef __EC_STATES__ +#define __EC_STATES__ + +#include "../include/ecrt.h" +#include "command.h" +#include "slave.h" + +/*****************************************************************************/ + +typedef struct ec_fsm ec_fsm_t; + +/*****************************************************************************/ + +/** + Finite state machine of an EtherCAT master. +*/ + +struct ec_fsm +{ + ec_master_t *master; /**< master the FSM runs on */ + ec_slave_t *slave; /**< slave the FSM runs on */ + ec_command_t command; /**< command used in the state machine */ + + void (*master_state)(ec_fsm_t *); /**< master state function */ + unsigned int master_slaves_responding; /**< number of responding slaves */ + ec_slave_state_t master_slave_states; /**< states of responding slaves */ + unsigned int master_validation; /**< non-zero, if validation to do */ + + void (*slave_state)(ec_fsm_t *); /**< slave state function */ + uint8_t slave_sii_num; /**< SII value iteration counter */ + uint8_t *slave_cat_data; /**< temporary memory for category data */ + uint16_t slave_cat_offset; /**< current category word offset in EEPROM */ + uint16_t slave_cat_data_offset; /**< current offset in category data */ + uint16_t slave_cat_type; /**< type of current category */ + uint16_t slave_cat_words; /**< number of words of current category */ + + void (*sii_state)(ec_fsm_t *); /**< SII state function */ + uint16_t sii_offset; /**< input: offset in SII */ + unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */ + uint32_t sii_result; /**< output: read SII value (32bit) */ + cycles_t sii_start; /**< sii start */ + + void (*change_state)(ec_fsm_t *); /**< slave state change state function */ + uint8_t change_new; /**< input: new state */ + cycles_t change_start; /**< change start */ +}; + +/*****************************************************************************/ + +int ec_fsm_init(ec_fsm_t *, ec_master_t *); +void ec_fsm_clear(ec_fsm_t *); +void ec_fsm_reset(ec_fsm_t *); +void ec_fsm_execute(ec_fsm_t *); + +/*****************************************************************************/ + +#endif diff -r 5cff10efb927 -r 0d4119024f55 master/globals.h --- a/master/globals.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/globals.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -49,16 +59,16 @@ /** maximum number of FMMUs per slave */ #define EC_MAX_FMMUS 16 +/** size of the EoE tx queue */ +#define EC_EOE_TX_QUEUE_SIZE 100 + +/** clock frequency for the EoE state machines */ +#define EC_EOE_FREQUENCY 1000 + /****************************************************************************** * EtherCAT protocol *****************************************************************************/ -/** maximum size of an EtherCAT frame (without header and CRC) */ -#define EC_MAX_FRAME_SIZE 1500 - -/** minimum size of an EtherCAT frame (without header and CRC) */ -#define EC_MIN_FRAME_SIZE 46 - /** size of an EtherCAT frame header */ #define EC_FRAME_HEADER_SIZE 2 @@ -75,25 +85,70 @@ #define EC_FMMU_SIZE 16 /** resulting maximum data size of a single command in a frame */ -#define EC_MAX_DATA_SIZE (EC_MAX_FRAME_SIZE - EC_FRAME_HEADER_SIZE \ +#define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE \ - EC_COMMAND_HEADER_SIZE - EC_COMMAND_FOOTER_SIZE) /*****************************************************************************/ +/** + Convenience macro for printing EtherCAT-specific information to syslog. + This will print the message in \a fmt with a prefixed "EtherCAT: ". + \param fmt format string (like in printf()) + \param args arguments (optional) +*/ + #define EC_INFO(fmt, args...) \ printk(KERN_INFO "EtherCAT: " fmt, ##args) + +/** + Convenience macro for printing EtherCAT-specific errors to syslog. + This will print the message in \a fmt with a prefixed "EtherCAT ERROR: ". + \param fmt format string (like in printf()) + \param args arguments (optional) +*/ + #define EC_ERR(fmt, args...) \ printk(KERN_ERR "EtherCAT ERROR: " fmt, ##args) + +/** + Convenience macro for printing EtherCAT-specific warnings to syslog. + This will print the message in \a fmt with a prefixed "EtherCAT WARNING: ". + \param fmt format string (like in printf()) + \param args arguments (optional) +*/ + #define EC_WARN(fmt, args...) \ printk(KERN_WARNING "EtherCAT WARNING: " fmt, ##args) + +/** + Convenience macro for printing EtherCAT debug messages to syslog. + This will print the message in \a fmt with a prefixed "EtherCAT DEBUG: ". + \param fmt format string (like in printf()) + \param args arguments (optional) +*/ + #define EC_DBG(fmt, args...) \ printk(KERN_DEBUG "EtherCAT DEBUG: " fmt, ##args) +/** + Helper macro for EC_STR(), literates a macro argument. + \param X argument to literate. +*/ + #define EC_LIT(X) #X + +/** + Converts a macro argument to a string. + \param X argument to stringify. +*/ + #define EC_STR(X) EC_LIT(X) /** - Convenience macro for defining SysFS attributes. + 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) \ @@ -101,10 +156,23 @@ .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 void ec_print_data(const uint8_t *, size_t); extern void ec_print_data_diff(const uint8_t *, const uint8_t *, size_t); +extern void ec_print_states(uint8_t); /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/mailbox.c --- a/master/mailbox.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/mailbox.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/mailbox.h --- a/master/mailbox.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/mailbox.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/master.c --- a/master/master.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/master.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -45,9 +55,9 @@ /*****************************************************************************/ -void ec_master_freerun(unsigned long); +void ec_master_freerun(void *); +void ec_master_eoe_run(unsigned long); ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *); -void ec_master_process_watch_command(ec_master_t *); /*****************************************************************************/ @@ -83,19 +93,51 @@ */ int ec_master_init(ec_master_t *master, /**< EtherCAT master */ - unsigned int index /**< master index */ + unsigned int index, /**< master index */ + unsigned int eoe_devices /**< number of EoE devices */ ) { + ec_eoe_t *eoe, *next_eoe; + unsigned int i; + EC_INFO("Initializing master %i.\n", index); master->index = index; master->device = NULL; master->reserved = 0; - INIT_LIST_HEAD(&master->slaves); INIT_LIST_HEAD(&master->command_queue); INIT_LIST_HEAD(&master->domains); - INIT_LIST_HEAD(&master->eoe_slaves); + INIT_LIST_HEAD(&master->eoe_handlers); + ec_command_init(&master->simple_command); + INIT_WORK(&master->freerun_work, ec_master_freerun, (void *) master); + init_timer(&master->eoe_timer); + master->eoe_timer.function = ec_master_eoe_run; + master->eoe_timer.data = (unsigned long) master; + master->internal_lock = SPIN_LOCK_UNLOCKED; + master->eoe_running = 0; + + // create workqueue + if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) { + EC_ERR("Failed to create master workqueue.\n"); + goto out_return; + } + + // create EoE handlers + for (i = 0; i < eoe_devices; i++) { + if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) { + EC_ERR("Failed to allocate EoE-Object.\n"); + goto out_clear_eoe; + } + if (ec_eoe_init(eoe)) { + kfree(eoe); + goto out_clear_eoe; + } + list_add_tail(&eoe->list, &master->eoe_handlers); + } + + // create state machine object + if (ec_fsm_init(&master->fsm, master)) goto out_clear_eoe; // init kobject and add it to the hierarchy memset(&master->kobj, 0x00, sizeof(struct kobject)); @@ -107,16 +149,18 @@ return -1; } - // init freerun timer - init_timer(&master->freerun_timer); - master->freerun_timer.function = ec_master_freerun; - master->freerun_timer.data = (unsigned long) master; - - ec_command_init(&master->simple_command); - ec_command_init(&master->watch_command); - ec_master_reset(master); return 0; + + out_clear_eoe: + list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { + list_del(&eoe->list); + ec_eoe_clear(eoe); + kfree(eoe); + } + destroy_workqueue(master->workqueue); + out_return: + return -1; } /*****************************************************************************/ @@ -130,21 +174,27 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */) { ec_master_t *master = container_of(kobj, ec_master_t, kobj); + ec_eoe_t *eoe, *next_eoe; EC_INFO("Clearing master %i...\n", master->index); - del_timer_sync(&master->freerun_timer); - ec_master_reset(master); + ec_fsm_clear(&master->fsm); + ec_command_clear(&master->simple_command); + destroy_workqueue(master->workqueue); + + // clear EoE objects + list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { + list_del(&eoe->list); + ec_eoe_clear(eoe); + kfree(eoe); + } if (master->device) { ec_device_clear(master->device); kfree(master->device); } - ec_command_clear(&master->simple_command); - ec_command_clear(&master->watch_command); - EC_INFO("Master %i cleared.\n", master->index); } @@ -158,20 +208,12 @@ void ec_master_reset(ec_master_t *master /**< EtherCAT master */) { - ec_slave_t *slave, *next_s; ec_command_t *command, *next_c; ec_domain_t *domain, *next_d; - ec_eoe_t *eoe, *next_eoe; - + + ec_master_eoe_stop(master); ec_master_freerun_stop(master); - - // remove all slaves - list_for_each_entry_safe(slave, next_s, &master->slaves, list) { - list_del(&slave->list); - kobject_del(&slave->kobj); - kobject_put(&slave->kobj); - } - master->slave_count = 0; + ec_master_clear_slaves(master); // empty command queue list_for_each_entry_safe(command, next_c, &master->command_queue, queue) { @@ -186,28 +228,41 @@ kobject_put(&domain->kobj); } - // clear EoE objects - list_for_each_entry_safe(eoe, next_eoe, &master->eoe_slaves, list) { - list_del(&eoe->list); - ec_eoe_clear(eoe); - kfree(eoe); - } - master->command_index = 0; master->debug_level = 0; master->timeout = 500; // 500us - master->slaves_responding = 0; - master->slave_states = EC_SLAVE_STATE_UNKNOWN; - master->stats.timeouts = 0; master->stats.delayed = 0; master->stats.corrupted = 0; master->stats.unmatched = 0; - master->stats.eoe_errors = 0; master->stats.t_last = 0; master->mode = EC_MASTER_MODE_IDLE; + + master->request_cb = NULL; + master->release_cb = NULL; + master->cb_data = NULL; + + ec_fsm_reset(&master->fsm); +} + +/*****************************************************************************/ + +/** + Clears all slaves. +*/ + +void ec_master_clear_slaves(ec_master_t *master) +{ + ec_slave_t *slave, *next_slave; + + list_for_each_entry_safe(slave, next_slave, &master->slaves, list) { + list_del(&slave->list); + kobject_del(&slave->kobj); + kobject_put(&slave->kobj); + } + master->slave_count = 0; } /*****************************************************************************/ @@ -249,15 +304,14 @@ size_t command_size; uint8_t *frame_data, *cur_data; void *follows_word; - cycles_t start = 0, end; + cycles_t t_start, t_end; unsigned int frame_count, more_commands_waiting; frame_count = 0; - - if (unlikely(master->debug_level > 0)) { + t_start = get_cycles(); + + if (unlikely(master->debug_level > 0)) EC_DBG("ec_master_send_commands\n"); - start = get_cycles(); - } do { // fetch pointer to transmit socket buffer @@ -273,12 +327,13 @@ // does the current command fit in the frame? command_size = EC_COMMAND_HEADER_SIZE + command->data_size + EC_COMMAND_FOOTER_SIZE; - if (cur_data - frame_data + command_size > EC_MAX_FRAME_SIZE) { + if (cur_data - frame_data + command_size > ETH_DATA_LEN) { more_commands_waiting = 1; break; } command->state = EC_CMD_SENT; + command->t_sent = t_start; command->index = master->command_index++; if (unlikely(master->debug_level > 0)) @@ -317,7 +372,7 @@ - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000); // pad frame - while (cur_data - frame_data < EC_MIN_FRAME_SIZE) + while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN) EC_WRITE_U8(cur_data++, 0x00); if (unlikely(master->debug_level > 0)) @@ -330,9 +385,9 @@ while (more_commands_waiting); if (unlikely(master->debug_level > 0)) { - end = get_cycles(); + t_end = get_cycles(); EC_DBG("ec_master_send_commands sent %i frames in %ius.\n", - frame_count, (u32) (end - start) * 1000 / cpu_khz); + frame_count, (u32) (t_end - t_start) * 1000 / cpu_khz); } } @@ -477,11 +532,10 @@ int ec_master_bus_scan(ec_master_t *master /**< EtherCAT master */) { - ec_slave_t *slave, *next; + ec_slave_t *slave; ec_slave_ident_t *ident; + ec_command_t *command; unsigned int i; - ec_command_t *command; - ec_eoe_t *eoe; uint16_t coupler_index, coupler_subindex; uint16_t reverse_coupler_index, current_coupler_index; @@ -502,7 +556,8 @@ // init slaves for (i = 0; i < master->slave_count; i++) { - if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t), GFP_KERNEL))) { + if (!(slave = + (ec_slave_t *) kmalloc(sizeof(ec_slave_t), GFP_KERNEL))) { EC_ERR("Failed to allocate slave %i!\n", i); goto out_free; } @@ -553,7 +608,7 @@ if (!slave->type) { EC_WARN("Unknown slave device (vendor 0x%08X, code 0x%08X) at" " position %i.\n", slave->sii_vendor_id, - slave->sii_product_code, i); + slave->sii_product_code, slave->ring_position); } else if (slave->type->special == EC_TYPE_BUS_COUPLER) { if (slave->sii_alias) @@ -566,27 +621,12 @@ slave->coupler_index = current_coupler_index; slave->coupler_subindex = coupler_subindex; coupler_subindex++; - - // does the slave support EoE? - if (slave->sii_mailbox_protocols & EC_MBOX_EOE) { - if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) { - EC_ERR("Failed to allocate EoE-Object.\n"); - goto out_free; - } - - ec_eoe_init(eoe, slave); - list_add_tail(&eoe->list, &master->eoe_slaves); - } } return 0; out_free: - list_for_each_entry_safe(slave, next, &master->slaves, list) { - list_del(&slave->list); - kobject_del(&slave->kobj); - kobject_put(&slave->kobj); - } + ec_master_clear_slaves(master); return -1; } @@ -619,10 +659,6 @@ EC_WARN("%i command(s) UNMATCHED!\n", master->stats.unmatched); master->stats.unmatched = 0; } - if (master->stats.eoe_errors) { - EC_WARN("%i EOE ERROR(S)!\n", master->stats.eoe_errors); - master->stats.eoe_errors = 0; - } master->stats.t_last = t_now; } } @@ -645,18 +681,8 @@ EC_INFO("Starting Free-Run mode.\n"); master->mode = EC_MASTER_MODE_FREERUN; - - if (master->watch_command.state == EC_CMD_INIT) { - if (ec_command_brd(&master->watch_command, 0x130, 2)) { - EC_ERR("Failed to allocate watchdog command!\n"); - return; - } - } - - ecrt_master_prepare_async_io(master); - - master->freerun_timer.expires = jiffies + 10; - add_timer(&master->freerun_timer); + ec_fsm_reset(&master->fsm); + queue_delayed_work(master->workqueue, &master->freerun_work, 1); } /*****************************************************************************/ @@ -669,9 +695,15 @@ { if (master->mode != EC_MASTER_MODE_FREERUN) return; + ec_master_eoe_stop(master); + EC_INFO("Stopping Free-Run mode.\n"); - del_timer_sync(&master->freerun_timer); + if (!cancel_delayed_work(&master->freerun_work)) { + flush_workqueue(master->workqueue); + } + + ec_master_clear_slaves(master); master->mode = EC_MASTER_MODE_IDLE; } @@ -681,22 +713,24 @@ Free-Run mode function. */ -void ec_master_freerun(unsigned long data /**< private timer data = master */) +void ec_master_freerun(void *data /**< master pointer */) { ec_master_t *master = (ec_master_t *) data; + // aquire master lock + spin_lock_bh(&master->internal_lock); + ecrt_master_async_receive(master); - // watchdog command - ec_master_process_watch_command(master); - ec_master_queue_command(master, &master->watch_command); - - master->slave_count = master->watch_command.working_counter; + // execute master state machine + ec_fsm_execute(&master->fsm); ecrt_master_async_send(master); - master->freerun_timer.expires += HZ; - add_timer(&master->freerun_timer); + // release master lock + spin_unlock_bh(&master->internal_lock); + + queue_delayed_work(master->workqueue, &master->freerun_work, 1); } /*****************************************************************************/ @@ -791,63 +825,139 @@ /*****************************************************************************/ /** - Processes the watchdog command. -*/ - -void ec_master_process_watch_command(ec_master_t *master - /**< EtherCAT master */ - ) -{ - unsigned int first; - - if (unlikely(master->watch_command.state == EC_CMD_INIT)) return; - - first = 1; - - if (master->watch_command.working_counter != master->slaves_responding || - master->watch_command.data[0] != master->slave_states) - { - master->slaves_responding = master->watch_command.working_counter; - master->slave_states = master->watch_command.data[0]; - - EC_INFO("%i slave%s responding (", master->slaves_responding, - master->slaves_responding == 1 ? "" : "s"); - - if (master->slave_states & EC_SLAVE_STATE_INIT) { - printk("INIT"); - first = 0; - } - if (master->slave_states & EC_SLAVE_STATE_PREOP) { - if (!first) printk(", "); - printk("PREOP"); - first = 0; - } - if (master->slave_states & EC_SLAVE_STATE_SAVEOP) { - if (!first) printk(", "); - printk("SAVEOP"); - first = 0; - } - if (master->slave_states & EC_SLAVE_STATE_OP) { - if (!first) printk(", "); - printk("OP"); - } - printk(")\n"); - } -} - -/*****************************************************************************/ - + Starts Ethernet-over-EtherCAT processing for all EoE-capable slaves. +*/ + +void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */) +{ + ec_eoe_t *eoe; + ec_slave_t *slave; + unsigned int coupled, found; + + if (master->eoe_running) return; + + // decouple all EoE handlers + list_for_each_entry(eoe, &master->eoe_handlers, list) + eoe->slave = NULL; + coupled = 0; + + // couple a free EoE handler to every EoE-capable slave + list_for_each_entry(slave, &master->slaves, list) { + if (!(slave->sii_mailbox_protocols & EC_MBOX_EOE)) continue; + + found = 0; + list_for_each_entry(eoe, &master->eoe_handlers, list) { + if (eoe->slave) continue; + eoe->slave = slave; + found = 1; + coupled++; + EC_INFO("Coupling device %s to slave %i.\n", + eoe->dev->name, slave->ring_position); + if (eoe->opened) { + slave->requested_state = EC_SLAVE_STATE_OP; + } + else { + slave->requested_state = EC_SLAVE_STATE_INIT; + } + slave->state_error = 0; + break; + } + + if (!found) { + EC_WARN("No EoE handler for slave %i!\n", slave->ring_position); + slave->requested_state = EC_SLAVE_STATE_INIT; + slave->state_error = 0; + } + } + + if (!coupled) { + EC_INFO("No EoE handlers coupled.\n"); + return; + } + + EC_INFO("Starting EoE processing.\n"); + master->eoe_running = 1; + + // start EoE processing + master->eoe_timer.expires = jiffies + 10; + add_timer(&master->eoe_timer); + return; +} + +/*****************************************************************************/ + +/** + Stops the Ethernet-over-EtherCAT processing. +*/ + +void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */) +{ + ec_eoe_t *eoe; + + if (!master->eoe_running) return; + + EC_INFO("Stopping EoE processing.\n"); + + del_timer_sync(&master->eoe_timer); + + // decouple all EoE handlers + list_for_each_entry(eoe, &master->eoe_handlers, list) { + if (eoe->slave) { + eoe->slave->requested_state = EC_SLAVE_STATE_INIT; + eoe->slave->state_error = 0; + eoe->slave = NULL; + } + } + + master->eoe_running = 0; +} + +/*****************************************************************************/ /** Does the Ethernet-over-EtherCAT processing. */ -void ec_master_run_eoe(ec_master_t *master /**< EtherCAT master */) -{ +void ec_master_eoe_run(unsigned long data /**< master pointer */) +{ + ec_master_t *master = (ec_master_t *) data; ec_eoe_t *eoe; - - list_for_each_entry(eoe, &master->eoe_slaves, list) { + unsigned int active = 0; + + list_for_each_entry(eoe, &master->eoe_handlers, list) { + if (ec_eoe_active(eoe)) active++; + } + if (!active) goto queue_timer; + + // aquire master lock... + if (master->mode == EC_MASTER_MODE_RUNNING) { + // request_cb must return 0, if the lock has been aquired! + if (master->request_cb(master->cb_data)) + goto queue_timer; + } + else if (master->mode == EC_MASTER_MODE_FREERUN) { + spin_lock(&master->internal_lock); + } + else + goto queue_timer; + + // actual EoE stuff + ecrt_master_async_receive(master); + list_for_each_entry(eoe, &master->eoe_handlers, list) { ec_eoe_run(eoe); } + ecrt_master_async_send(master); + + // release lock... + if (master->mode == EC_MASTER_MODE_RUNNING) { + master->release_cb(master->cb_data); + } + else if (master->mode == EC_MASTER_MODE_FREERUN) { + spin_unlock(&master->internal_lock); + } + + queue_timer: + master->eoe_timer.expires += HZ / EC_EOE_FREQUENCY; + add_timer(&master->eoe_timer); } /****************************************************************************** @@ -920,13 +1030,6 @@ command = &master->simple_command; - if (master->watch_command.state == EC_CMD_INIT) { - if (ec_command_brd(&master->watch_command, 0x130, 2)) { - EC_ERR("Failed to allocate watchdog command!\n"); - return -1; - } - } - // allocate all domains domain_offset = 0; list_for_each_entry(domain, &master->domains, list) { @@ -1080,9 +1183,6 @@ return -1; } - master->slaves_responding = 0; - master->slave_states = EC_SLAVE_STATE_INIT; - return 0; } @@ -1223,26 +1323,32 @@ void ecrt_master_async_receive(ec_master_t *master /**< EtherCAT master */) { ec_command_t *command, *next; + cycles_t t_received, t_timeout; ec_device_call_isr(master->device); + t_received = get_cycles(); + t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); + // dequeue all received commands list_for_each_entry_safe(command, next, &master->command_queue, queue) if (command->state == EC_CMD_RECEIVED) list_del_init(&command->queue); - // dequeue all remaining commands + // dequeue all commands that timed out list_for_each_entry_safe(command, next, &master->command_queue, queue) { switch (command->state) { case EC_CMD_SENT: case EC_CMD_QUEUED: - command->state = EC_CMD_TIMEOUT; - master->stats.timeouts++; - ec_master_output_stats(master); + if (t_received - command->t_sent > t_timeout) { + list_del_init(&command->queue); + command->state = EC_CMD_TIMEOUT; + master->stats.timeouts++; + ec_master_output_stats(master); + } break; default: break; } - list_del_init(&command->queue); } } @@ -1288,12 +1394,8 @@ // output statistics ec_master_output_stats(master); - // watchdog command - ec_master_process_watch_command(master); - ec_master_queue_command(master, &master->watch_command); - - // Ethernet-over-EtherCAT - ec_master_run_eoe(master); + // execute master state machine + ec_fsm_execute(&master->fsm); } /*****************************************************************************/ @@ -1407,6 +1509,45 @@ /*****************************************************************************/ /** + Sets the locking callbacks. + The request_cb function must return zero, to allow another instance + (the EoE process for example) to access the master. Non-zero means, + that access is forbidden at this time. + \ingroup RealtimeInterface +*/ + +void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */ + int (*request_cb)(void *), /**< request lock CB */ + void (*release_cb)(void *), /**< release lock CB */ + void *cb_data /**< data parameter */ + ) +{ + master->request_cb = request_cb; + master->release_cb = release_cb; + master->cb_data = cb_data; +} + +/*****************************************************************************/ + +/** + Starts Ethernet-over-EtherCAT processing for all EoE-capable slaves. + \ingroup RealtimeInterface +*/ + +int ecrt_master_start_eoe(ec_master_t *master /**< EtherCAT master */) +{ + if (!master->request_cb || !master->release_cb) { + EC_ERR("EoE requires master callbacks to be set!\n"); + return -1; + } + + ec_master_eoe_start(master); + return 0; +} + +/*****************************************************************************/ + +/** Sets the debug level of the master. The following levels are valid: - 1: only output positions marks and basic data @@ -1448,9 +1589,9 @@ list_for_each_entry(slave, &master->slaves, list) ec_slave_print(slave, verbosity); } - if (!list_empty(&master->eoe_slaves)) { + if (!list_empty(&master->eoe_handlers)) { EC_INFO("Ethernet-over-EtherCAT (EoE) objects:\n"); - list_for_each_entry(eoe, &master->eoe_slaves, list) { + list_for_each_entry(eoe, &master->eoe_handlers, list) { ec_eoe_print(eoe); } } @@ -1470,6 +1611,8 @@ EXPORT_SYMBOL(ecrt_master_async_send); EXPORT_SYMBOL(ecrt_master_async_receive); EXPORT_SYMBOL(ecrt_master_run); +EXPORT_SYMBOL(ecrt_master_callbacks); +EXPORT_SYMBOL(ecrt_master_start_eoe); EXPORT_SYMBOL(ecrt_master_debug); EXPORT_SYMBOL(ecrt_master_print); EXPORT_SYMBOL(ecrt_master_get_slave); diff -r 5cff10efb927 -r 0d4119024f55 master/master.h --- a/master/master.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/master.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -37,6 +47,7 @@ #include "device.h" #include "domain.h" +#include "fsm.h" /*****************************************************************************/ @@ -64,7 +75,6 @@ unsigned int delayed; /**< delayed commands */ unsigned int corrupted; /**< corrupted frames */ unsigned int unmatched; /**< unmatched commands */ - unsigned int eoe_errors; /**< Ethernet-over-EtherCAT errors */ cycles_t t_last; /**< time of last output */ } ec_stats_t; @@ -78,32 +88,46 @@ struct ec_master { - struct list_head list; /**< list item */ + struct list_head list; /**< list item for module's master list */ + unsigned int reserved; /**< non-zero, if the master is reserved for RT */ + unsigned int index; /**< master index */ + struct kobject kobj; /**< kobject */ - unsigned int index; /**< master index */ + struct list_head slaves; /**< list of slaves on the bus */ unsigned int slave_count; /**< number of slaves on the bus */ + ec_device_t *device; /**< EtherCAT device */ + struct list_head command_queue; /**< command queue */ uint8_t command_index; /**< current command index */ + struct list_head domains; /**< list of domains */ + ec_command_t simple_command; /**< command structure for initialization */ - ec_command_t watch_command; /**< command for watching the slaves */ - unsigned int slaves_responding; /**< number of responding slaves */ - ec_slave_state_t slave_states; /**< states of the responding slaves */ + unsigned int timeout; /**< timeout in synchronous IO */ + int debug_level; /**< master debug level */ ec_stats_t stats; /**< cyclic statistics */ - unsigned int timeout; /**< timeout in synchronous IO */ - struct list_head eoe_slaves; /**< Ethernet-over-EtherCAT slaves */ - unsigned int reserved; /**< true, if the master is reserved for RT */ - struct timer_list freerun_timer; /**< timer object for free run mode */ + + struct workqueue_struct *workqueue; /**< master workqueue */ + struct work_struct freerun_work; /**< free run work object */ + ec_fsm_t fsm; /**< master state machine */ ec_master_mode_t mode; /**< master mode */ + + struct timer_list eoe_timer; /** EoE timer object */ + unsigned int eoe_running; /**< non-zero, if EoE processing is active. */ + struct list_head eoe_handlers; /**< Ethernet-over-EtherCAT handlers */ + spinlock_t internal_lock; /**< spinlock used in freerun mode */ + int (*request_cb)(void *); /**< lock request callback */ + void (*release_cb)(void *); /**< lock release callback */ + void *cb_data; /**< data parameter of locking callbacks */ }; /*****************************************************************************/ // master creation and deletion -int ec_master_init(ec_master_t *, unsigned int); +int ec_master_init(ec_master_t *, unsigned int, unsigned int); void ec_master_clear(struct kobject *); void ec_master_reset(ec_master_t *); @@ -111,6 +135,10 @@ void ec_master_freerun_start(ec_master_t *); void ec_master_freerun_stop(ec_master_t *); +// EoE +void ec_master_eoe_start(ec_master_t *); +void ec_master_eoe_stop(ec_master_t *); + // IO void ec_master_receive(ec_master_t *, const uint8_t *, size_t); void ec_master_queue_command(ec_master_t *, ec_command_t *); @@ -120,8 +148,11 @@ int ec_master_bus_scan(ec_master_t *); // misc. +void ec_master_clear_slaves(ec_master_t *); +void ec_sync_config(const ec_sync_t *, uint8_t *); +void ec_eeprom_sync_config(const ec_eeprom_sync_t *, uint8_t *); +void ec_fmmu_config(const ec_fmmu_t *, uint8_t *); void ec_master_output_stats(ec_master_t *); -void ec_master_run_eoe(ec_master_t *); /*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 master/module.c --- a/master/module.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/module.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -57,19 +67,22 @@ /*****************************************************************************/ static int ec_master_count = 1; +static int ec_eoe_devices = 0; static struct list_head ec_masters; /*****************************************************************************/ /** \cond */ -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT master driver module"); +module_param(ec_master_count, int, S_IRUGO); +module_param(ec_eoe_devices, int, S_IRUGO); + +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT master driver module"); MODULE_LICENSE("GPL"); MODULE_VERSION(COMPILE_INFO); - -module_param(ec_master_count, int, 1); MODULE_PARM_DESC(ec_master_count, "number of EtherCAT masters to initialize"); +MODULE_PARM_DESC(ec_eoe_devices, "number of EoE devices per master"); /** \endcond */ @@ -104,7 +117,7 @@ goto out_free; } - if (ec_master_init(master, i)) // kobject_put is done inside... + if (ec_master_init(master, i, ec_eoe_devices)) goto out_free; if (kobject_add(&master->kobj)) { @@ -218,6 +231,41 @@ printk("\n"); } +/*****************************************************************************/ + +/** + Prints slave states in clear text. +*/ + +void ec_print_states(const uint8_t states /**< slave states */) +{ + unsigned int first = 1; + + if (!states) { + printk("(unknown)"); + return; + } + + if (states & EC_SLAVE_STATE_INIT) { + printk("INIT"); + first = 0; + } + if (states & EC_SLAVE_STATE_PREOP) { + if (!first) printk(", "); + printk("PREOP"); + first = 0; + } + if (states & EC_SLAVE_STATE_SAVEOP) { + if (!first) printk(", "); + printk("SAVEOP"); + first = 0; + } + if (states & EC_SLAVE_STATE_OP) { + if (!first) printk(", "); + printk("OP"); + } +} + /****************************************************************************** * Device interface *****************************************************************************/ @@ -240,27 +288,18 @@ { ec_master_t *master; - if (!net_dev) { - EC_WARN("Device is NULL!\n"); - goto out_return; - } - if (!(master = ec_find_master(master_index))) return NULL; - // critical section start if (master->device) { EC_ERR("Master %i already has a device!\n", master_index); - // critical section leave goto out_return; } if (!(master->device = (ec_device_t *) kmalloc(sizeof(ec_device_t), GFP_KERNEL))) { EC_ERR("Failed to allocate device!\n"); - // critical section leave goto out_return; } - // critical section end if (ec_device_init(master->device, master, net_dev, isr, module)) { EC_ERR("Failed to init device!\n"); @@ -369,13 +408,11 @@ if (!(master = ec_find_master(master_index))) goto out_return; - // begin critical section if (master->reserved) { EC_ERR("Master %i is already in use!\n", master_index); goto out_return; } master->reserved = 1; - // end critical section if (!master->device) { EC_ERR("Master %i has no assigned device!\n", master_index); @@ -404,6 +441,7 @@ out_module_put: module_put(master->device->module); ec_master_reset(master); + ec_master_freerun_start(master); out_release: master->reserved = 0; out_return: diff -r 5cff10efb927 -r 0d4119024f55 master/slave.c --- a/master/slave.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/slave.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -38,13 +48,14 @@ /*****************************************************************************/ +extern const ec_code_msg_t al_status_messages[]; + +/*****************************************************************************/ + int ec_slave_fetch_categories(ec_slave_t *); -int ec_slave_fetch_strings(ec_slave_t *, const uint8_t *); -int ec_slave_fetch_general(ec_slave_t *, const uint8_t *); -int ec_slave_fetch_sync(ec_slave_t *, const uint8_t *, size_t); -int ec_slave_fetch_pdo(ec_slave_t *, const uint8_t *, size_t, ec_pdo_type_t); -int ec_slave_locate_string(ec_slave_t *, unsigned int, char **); ssize_t ec_show_slave_attribute(struct kobject *, struct attribute *, char *); +ssize_t ec_store_slave_attribute(struct kobject *, struct attribute *, + const char *, size_t); /*****************************************************************************/ @@ -55,7 +66,9 @@ EC_SYSFS_READ_ATTR(vendor_name); EC_SYSFS_READ_ATTR(product_name); EC_SYSFS_READ_ATTR(product_desc); +EC_SYSFS_READ_ATTR(sii_name); EC_SYSFS_READ_ATTR(type); +EC_SYSFS_READ_WRITE_ATTR(state); static struct attribute *def_attrs[] = { &attr_ring_position, @@ -63,13 +76,15 @@ &attr_vendor_name, &attr_product_name, &attr_product_desc, + &attr_sii_name, &attr_type, + &attr_state, NULL, }; static struct sysfs_ops sysfs_ops = { - .show = &ec_show_slave_attribute, - .store = NULL + .show = ec_show_slave_attribute, + .store = ec_store_slave_attribute }; static struct kobj_type ktype_ec_slave = { @@ -82,10 +97,6 @@ /*****************************************************************************/ -const ec_code_msg_t al_status_messages[]; - -/*****************************************************************************/ - /** Slave constructor. \return 0 in case of success, else < 0 @@ -134,9 +145,14 @@ slave->type = NULL; slave->registered = 0; slave->fmmu_count = 0; + slave->eeprom_group = NULL; + slave->eeprom_image = NULL; + slave->eeprom_order = NULL; slave->eeprom_name = NULL; - slave->eeprom_group = NULL; - slave->eeprom_desc = NULL; + slave->requested_state = EC_SLAVE_STATE_UNKNOWN; + slave->current_state = EC_SLAVE_STATE_UNKNOWN; + slave->state_error = 0; + slave->online = 1; ec_command_init(&slave->mbox_command); @@ -200,9 +216,10 @@ kfree(pdo); } + if (slave->eeprom_group) kfree(slave->eeprom_group); + if (slave->eeprom_image) kfree(slave->eeprom_image); + if (slave->eeprom_order) kfree(slave->eeprom_order); if (slave->eeprom_name) kfree(slave->eeprom_name); - if (slave->eeprom_group) kfree(slave->eeprom_group); - if (slave->eeprom_desc) kfree(slave->eeprom_desc); // free all SDOs list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) { @@ -492,6 +509,7 @@ /** Fetches data from slave's EEPROM. \return 0 in case of success, else < 0 + \todo memory allocation */ int ec_slave_fetch_categories(ec_slave_t *slave /**< EtherCAT slave */) @@ -632,9 +650,11 @@ if (ec_slave_locate_string(slave, data[0], &slave->eeprom_group)) return -1; - if (ec_slave_locate_string(slave, data[1], &slave->eeprom_name)) - return -1; - if (ec_slave_locate_string(slave, data[3], &slave->eeprom_desc)) + if (ec_slave_locate_string(slave, data[1], &slave->eeprom_image)) + return -1; + if (ec_slave_locate_string(slave, data[2], &slave->eeprom_order)) + return -1; + if (ec_slave_locate_string(slave, data[3], &slave->eeprom_name)) return -1; for (i = 0; i < 4; i++) @@ -824,6 +844,7 @@ if (ec_command_nprd(command, slave->station_address, 0x0130, 2)) return; if (unlikely(ec_master_simple_io(slave->master, command))) { + slave->current_state = EC_SLAVE_STATE_UNKNOWN; EC_WARN("Acknowledge checking failed on slave %i!\n", slave->ring_position); return; @@ -832,12 +853,14 @@ end = get_cycles(); if (likely(EC_READ_U8(command->data) == state)) { + slave->current_state = state; EC_INFO("Acknowleged state 0x%02X on slave %i.\n", state, slave->ring_position); return; } if (unlikely((end - start) >= timeout)) { + slave->current_state = EC_SLAVE_STATE_UNKNOWN; EC_WARN("Failed to acknowledge state 0x%02X on slave %i" " - Timeout!\n", state, slave->ring_position); return; @@ -897,6 +920,8 @@ command = &slave->master->simple_command; + slave->requested_state = state; + if (ec_command_npwr(command, slave->station_address, 0x0120, 2)) return -1; EC_WRITE_U16(command->data, state); if (unlikely(ec_master_simple_io(slave->master, command))) { @@ -915,6 +940,7 @@ if (ec_command_nprd(command, slave->station_address, 0x0130, 2)) return -1; if (unlikely(ec_master_simple_io(slave->master, command))) { + slave->current_state = EC_SLAVE_STATE_UNKNOWN; EC_ERR("Failed to check state 0x%02X on slave %i!\n", state, slave->ring_position); return -1; @@ -926,16 +952,20 @@ EC_ERR("Failed to set state 0x%02X - Slave %i refused state change" " (code 0x%02X)!\n", state, slave->ring_position, EC_READ_U8(command->data)); - state = EC_READ_U8(command->data) & 0x0F; + slave->current_state = EC_READ_U8(command->data); + state = slave->current_state & 0x0F; ec_slave_read_al_status_code(slave); ec_slave_state_ack(slave, state); return -1; } - if (likely(EC_READ_U8(command->data) == (state & 0x0F))) + if (likely(EC_READ_U8(command->data) == (state & 0x0F))) { + slave->current_state = state; return 0; // state change successful + } if (unlikely((end - start) >= timeout)) { + slave->current_state = EC_SLAVE_STATE_UNKNOWN; EC_ERR("Failed to check state 0x%02X of slave %i - Timeout!\n", state, slave->ring_position); return -1; @@ -1100,12 +1130,14 @@ EC_INFO(" Revision number: 0x%08X, Serial number: 0x%08X\n", slave->sii_revision_number, slave->sii_serial_number); + if (slave->eeprom_group) + EC_INFO(" Group: %s\n", slave->eeprom_group); + if (slave->eeprom_image) + EC_INFO(" Image: %s\n", slave->eeprom_image); + if (slave->eeprom_order) + EC_INFO(" Order#: %s\n", slave->eeprom_order); if (slave->eeprom_name) EC_INFO(" Name: %s\n", slave->eeprom_name); - if (slave->eeprom_group) - EC_INFO(" Group: %s\n", slave->eeprom_group); - if (slave->eeprom_desc) - EC_INFO(" Description: %s\n", slave->eeprom_desc); if (!list_empty(&slave->eeprom_syncs)) { EC_INFO(" Sync-Managers:\n"); @@ -1209,7 +1241,6 @@ /** Formats attribute data for SysFS read access. \return number of bytes to read - \ingroup RealTimeInterface */ ssize_t ec_show_slave_attribute(struct kobject *kobj, /**< slave's kobject */ @@ -1238,6 +1269,10 @@ if (slave->type) return sprintf(buffer, "%s\n", slave->type->description); } + else if (attr == &attr_sii_name) { + if (slave->eeprom_name) + return sprintf(buffer, "%s\n", slave->eeprom_name); + } else if (attr == &attr_type) { if (slave->type) { if (slave->type->special == EC_TYPE_BUS_COUPLER) @@ -1246,6 +1281,20 @@ return sprintf(buffer, "normal\n"); } } + else if (attr == &attr_state) { + switch (slave->current_state) { + case EC_SLAVE_STATE_INIT: + return sprintf(buffer, "INIT\n"); + case EC_SLAVE_STATE_PREOP: + return sprintf(buffer, "PREOP\n"); + case EC_SLAVE_STATE_SAVEOP: + return sprintf(buffer, "SAVEOP\n"); + case EC_SLAVE_STATE_OP: + return sprintf(buffer, "OP\n"); + default: + return sprintf(buffer, "UNKNOWN\n"); + } + } return 0; } @@ -1253,28 +1302,45 @@ /*****************************************************************************/ /** - Application layer status messages. -*/ - -const ec_code_msg_t al_status_messages[] = { - {0x0001, "Unspecified error"}, - {0x0011, "Invalud requested state change"}, - {0x0012, "Unknown requested state"}, - {0x0013, "Bootstrap not supported"}, - {0x0014, "No valid firmware"}, - {0x0015, "Invalid mailbox configuration"}, - {0x0016, "Invalid mailbox configuration"}, - {0x0017, "Invalid sync manager configuration"}, - {0x0018, "No valid inputs available"}, - {0x0019, "No valid outputs"}, - {0x001A, "Synchronisation error"}, - {0x001B, "Sync manager watchdog"}, - {0x0020, "Slave needs cold start"}, - {0x0021, "Slave needs INIT"}, - {0x0022, "Slave needs PREOP"}, - {0x0023, "Slave needs SAVEOP"}, - {} -}; + Formats attribute data for SysFS write access. + \return number of bytes processed, or negative error code +*/ + +ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */ + struct attribute *attr, /**< attribute */ + const char *buffer, /**< memory with data */ + size_t size /**< size of data to store */ + ) +{ + ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj); + + if (attr == &attr_state) { + if (!strcmp(buffer, "INIT\n")) { + slave->requested_state = EC_SLAVE_STATE_INIT; + slave->state_error = 0; + return size; + } + else if (!strcmp(buffer, "PREOP\n")) { + slave->requested_state = EC_SLAVE_STATE_PREOP; + slave->state_error = 0; + return size; + } + else if (!strcmp(buffer, "SAVEOP\n")) { + slave->requested_state = EC_SLAVE_STATE_SAVEOP; + slave->state_error = 0; + return size; + } + else if (!strcmp(buffer, "OP\n")) { + slave->requested_state = EC_SLAVE_STATE_OP; + slave->state_error = 0; + return size; + } + + EC_ERR("Failed to set slave state!\n"); + } + + return -EINVAL; +} /****************************************************************************** * Realtime interface diff -r 5cff10efb927 -r 0d4119024f55 master/slave.h --- a/master/slave.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/slave.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** @@ -255,13 +265,19 @@ struct list_head eeprom_syncs; /**< EEPROM SYNC MANAGER categories */ struct list_head eeprom_pdos; /**< EEPROM [RT]XPDO categories */ + char *eeprom_group; /**< slave group acc. to EEPROM */ + char *eeprom_image; /**< slave image name acc. to EEPROM */ + char *eeprom_order; /**< slave order number acc. to EEPROM */ char *eeprom_name; /**< slave name acc. to EEPROM */ - char *eeprom_group; /**< slave group acc. to EEPROM */ - char *eeprom_desc; /**< slave description acc. to EEPROM */ struct list_head sdo_dictionary; /**< SDO directory list */ ec_command_t mbox_command; /**< mailbox command */ + + ec_slave_state_t requested_state; /**< requested slave state */ + ec_slave_state_t current_state; /**< current slave state */ + unsigned int state_error; /**< a state error has happened */ + unsigned int online; /**< non-zero, if the slave responds. */ }; /*****************************************************************************/ @@ -282,6 +298,13 @@ // CoE int ec_slave_fetch_sdo_list(ec_slave_t *); +// state machine +int ec_slave_fetch_strings(ec_slave_t *, const uint8_t *); +int ec_slave_fetch_general(ec_slave_t *, const uint8_t *); +int ec_slave_fetch_sync(ec_slave_t *, const uint8_t *, size_t); +int ec_slave_fetch_pdo(ec_slave_t *, const uint8_t *, size_t, ec_pdo_type_t); +int ec_slave_locate_string(ec_slave_t *, unsigned int, char **); + // misc. void ec_slave_print(const ec_slave_t *, unsigned int); int ec_slave_check_crc(ec_slave_t *); diff -r 5cff10efb927 -r 0d4119024f55 master/types.c --- a/master/types.c Mon Apr 24 10:47:03 2006 +0000 +++ b/master/types.c Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 master/types.h --- a/master/types.h Mon Apr 24 10:47:03 2006 +0000 +++ b/master/types.h Mon May 29 09:08:56 2006 +0000 @@ -8,7 +8,8 @@ * * The IgH EtherCAT Master is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the License. + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * The IgH EtherCAT Master is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,15 @@ * 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 right to use EtherCAT Technology is granted and comes free of + * charge under condition of compatibility of product made by + * Licensee. People intending to distribute/sell products based on the + * code, have to sign an agreement to guarantee that products using + * software based on IgH EtherCAT master stay compatible with the actual + * EtherCAT specification (which are released themselves as an open + * standard) as the (only) precondition to have the right to use EtherCAT + * Technology, IP and trade marks. + * *****************************************************************************/ /** diff -r 5cff10efb927 -r 0d4119024f55 mini/Makefile --- a/mini/Makefile Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -#---------------------------------------------------------------- -# -# Makefile -# -# Minimal EtherCAT module. -# -# $Id$ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#---------------------------------------------------------------- - -ifneq ($(KERNELRELEASE),) - -#---------------------------------------------------------------- -# kbuild section -#---------------------------------------------------------------- - -obj-m := ec_mini.o - -ec_mini-objs := mini.o - -#---------------------------------------------------------------- - -else - -#---------------------------------------------------------------- -# default section -#---------------------------------------------------------------- - -ifneq ($(wildcard ethercat.conf),) -include ethercat.conf -else -KERNELDIR = /usr/src/linux -endif - -modules: - $(MAKE) -C $(KERNELDIR) M=`pwd` - -clean: - $(MAKE) -C $(KERNELDIR) M=`pwd` clean - -#---------------------------------------------------------------- - -endif diff -r 5cff10efb927 -r 0d4119024f55 mini/ethercat.conf.tmpl --- a/mini/ethercat.conf.tmpl Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -#------------------------------------------------------------------------------ -# -# EtherCAT Konfigurationsdatei Kernel 2.6 -# -# $Id$ -# -# This file is a versioned template configuration. Copy it to "ethercat.conf" -# (which is ignored by Subversion) and adjust it to your needs. -# -#------------------------------------------------------------------------------ - -KERNELDIR = /usr/src/linux - diff -r 5cff10efb927 -r 0d4119024f55 mini/mini.c --- a/mini/mini.c Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,219 +0,0 @@ -/****************************************************************************** - * - * m i n i . c - * - * Minimal module for EtherCAT. - * - * $Id$ - * - * Copyright (C) 2006 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 - * as published by the Free Software Foundation; version 2 of the License. - * - * 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 - * - *****************************************************************************/ - -#include -#include -#include - -#include "../include/ecrt.h" // EtherCAT realtime interface - -#define ASYNC -#define FREQUENCY 100 - -/*****************************************************************************/ - -struct timer_list timer; - -// EtherCAT -ec_master_t *master = NULL; -ec_domain_t *domain1 = NULL; - -// data fields -//void *r_ssi_input, *r_ssi_status, *r_4102[3]; - -// channels -uint32_t k_pos; -uint8_t k_stat; - -ec_field_init_t domain1_fields[] = { - {NULL, "1", "Beckhoff", "EL5001", "InputValue", 0}, - {NULL, "2", "Beckhoff", "EL4132", "OutputValue", 0}, - {} -}; - -/*****************************************************************************/ - -void run(unsigned long data) -{ - static unsigned int counter = 0; - -#ifdef ASYNC - // receive - ecrt_master_async_receive(master); - ecrt_domain_process(domain1); -#else - // send and receive - ecrt_domain_queue(domain1); - ecrt_master_run(master); - ecrt_master_sync_io(master); - ecrt_domain_process(domain1); -#endif - - // process data - //k_pos = EC_READ_U32(r_ssi); - -#ifdef ASYNC - // send - ecrt_domain_queue(domain1); - ecrt_master_run(master); - ecrt_master_async_send(master); -#endif - - if (counter) { - counter--; - } - else { - counter = FREQUENCY; - //printk(KERN_INFO "k_pos = %i\n", k_pos); - //printk(KERN_INFO "k_stat = 0x%02X\n", k_stat); - } - - // restart timer - timer.expires += HZ / FREQUENCY; - add_timer(&timer); -} - -/*****************************************************************************/ - -int __init init_mini_module(void) -{ - printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); - - if ((master = ecrt_request_master(0)) == NULL) { - printk(KERN_ERR "Requesting master 0 failed!\n"); - goto out_return; - } - - printk(KERN_INFO "Registering domain...\n"); - if (!(domain1 = ecrt_master_create_domain(master))) - { - printk(KERN_ERR "Domain creation failed!\n"); - goto out_release_master; - } - - printk(KERN_INFO "Registering domain fields...\n"); - if (ecrt_domain_register_field_list(domain1, domain1_fields)) { - printk(KERN_ERR "Field registration failed!\n"); - goto out_release_master; - } - - printk(KERN_INFO "Activating master...\n"); - if (ecrt_master_activate(master)) { - printk(KERN_ERR "Failed to activate master!\n"); - goto out_release_master; - } - -#if 0 - if (ecrt_master_fetch_sdo_lists(master)) { - printk(KERN_ERR "Failed to fetch SDO lists!\n"); - goto out_deactivate; - } - ecrt_master_print(master, 2); -#else - ecrt_master_print(master, 0); -#endif - - -#if 0 - if (!(slave = ecrt_master_get_slave(master, "5"))) { - printk(KERN_ERR "Failed to get slave 5!\n"); - goto out_deactivate; - } - - if (ecrt_slave_sdo_write_exp8(slave, 0x4061, 1, 0) || - ecrt_slave_sdo_write_exp8(slave, 0x4061, 2, 1) || - ecrt_slave_sdo_write_exp8(slave, 0x4061, 3, 1) || - ecrt_slave_sdo_write_exp8(slave, 0x4066, 0, 0) || - ecrt_slave_sdo_write_exp8(slave, 0x4067, 0, 4) || - ecrt_slave_sdo_write_exp8(slave, 0x4068, 0, 0) || - ecrt_slave_sdo_write_exp8(slave, 0x4069, 0, 25) || - ecrt_slave_sdo_write_exp8(slave, 0x406A, 0, 25) || - ecrt_slave_sdo_write_exp8(slave, 0x406B, 0, 50)) { - printk(KERN_ERR "Failed to configure SSI slave!\n"); - goto out_deactivate; - } -#endif - -#if 0 - printk(KERN_INFO "Writing alias...\n"); - if (ecrt_slave_sdo_write_exp16(slave, 0xBEEF)) { - printk(KERN_ERR "Failed to write alias!\n"); - goto out_deactivate; - } -#endif - -#ifdef ASYNC - // send once and wait... - ecrt_master_prepare_async_io(master); -#endif - - printk("Starting cyclic sample thread.\n"); - init_timer(&timer); - timer.function = run; - timer.expires = jiffies + 10; - add_timer(&timer); - - printk(KERN_INFO "=== Minimal EtherCAT environment started. ===\n"); - return 0; - -#if 0 - out_deactivate: - ecrt_master_deactivate(master); -#endif - out_release_master: - ecrt_release_master(master); - out_return: - return -1; -} - -/*****************************************************************************/ - -void __exit cleanup_mini_module(void) -{ - printk(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n"); - - if (master) { - del_timer_sync(&timer); - printk(KERN_INFO "Deactivating master...\n"); - ecrt_master_deactivate(master); - ecrt_release_master(master); - } - - printk(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n"); -} - -/*****************************************************************************/ - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT minimal test environment"); - -module_init(init_mini_module); -module_exit(cleanup_mini_module); - -/*****************************************************************************/ - diff -r 5cff10efb927 -r 0d4119024f55 rt/Makefile --- a/rt/Makefile Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -#------------------------------------------------------------------------------ -# -# Makefile -# -# $Id$ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ - -ifneq ($(KERNELRELEASE),) - -#------------------------------------------------------------------------------ -# kbuild section -#------------------------------------------------------------------------------ - -ifneq ($(wildcard $(src)/rt.conf),) -include $(src)/rt.conf -else -MODULENAME := rt -endif - -obj-m := $(MODULENAME).o - -$(MODULENAME)-objs := msr_rt.o \ - rt_lib/msr-core/msr_lists.o \ - rt_lib/msr-core/msr_main.o \ - rt_lib/msr-core/msr_charbuf.o \ - rt_lib/msr-core/msr_reg.o \ - rt_lib/msr-core/msr_interpreter.o \ - rt_lib/msr-core/msr_messages.o \ - rt_lib/msr-core/msr_proc.o \ - rt_lib/msr-core/msr_error_reg.o \ - rt_lib/msr-utils/msr_utils.o \ - rt_lib/msr-utils/msr_time.o \ - rt_lib/msr-math/msr_base64.o \ - rt_lib/msr-math/msr_hex_bin.o \ - libm.o - -EXTRA_CFLAGS := -I$(src)/rt_lib/msr-include -D_SIMULATION \ - -I/usr/include -mhard-float \ - -DSVNREV=$(shell svnversion $(src)) -DUSER=$(USER) - -#------------------------------------------------------------------------------ - -else - -#------------------------------------------------------------------------------ -# default section -#------------------------------------------------------------------------------ - -ifneq ($(wildcard rt.conf),) -include rt.conf -else -MODULENAME := msr_rt -KERNEL := $(shell uname -r) -endif - -KERNELDIR := /lib/modules/$(KERNEL)/build - -modules: - $(MAKE) -C $(KERNELDIR) M=`pwd` - -clean: - $(MAKE) -C $(KERNELDIR) M=`pwd` clean - -install: - @./install.sh $(MODULENAME) $(KERNEL) - -#------------------------------------------------------------------------------ - -endif diff -r 5cff10efb927 -r 0d4119024f55 rt/install.sh --- a/rt/install.sh Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -#!/bin/sh - -#------------------------------------------------------------------------------ -# -# Realtime module install script -# -# $Id: install.sh 5 2006-04-07 13:49:10Z fp $ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ - -# Fetch parameters - -if [ $# -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -MODULENAME=$1 -KERNEL=$2 - -MODULESDIR=/lib/modules/$KERNEL/kernel/drivers/rt - -echo "Realtime installer" -echo " target: $MODULENAME" -echo " kernel: $KERNEL" - -# Create target directory - -if [ ! -d $MODULESDIR ]; then - echo " creating $MODULESDIR..." - mkdir $MODULESDIR || exit 1 -fi - -# Install files - -echo " installing $MODULENAME..." -if ! cp $MODULENAME.ko $MODULESDIR/$MODULENAME.ko; then exit 1; fi - -# Calculate dependencies - -echo " building module dependencies..." -depmod - -# Finish - -echo "done." -exit 0 - -#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 rt/libm.o_shipped --- a/rt/libm.o_shipped Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -/usr/lib/libm.a \ No newline at end of file diff -r 5cff10efb927 -r 0d4119024f55 rt/msr_load --- a/rt/msr_load Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -#!/bin/sh -module="msr_rt" -device="msr" -mode="664" - -# Group: since distributions do it differently, look for wheel or use staff -if grep '^staff:' /etc/group > /dev/null; then - group="staff" -else - group="wheel" -fi - -# invoke insmod with all arguments we got -# and use a pathname, as newer modutils don't look in . by default -/sbin/insmod -f ./$module.ko $* || exit 1 - -major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"` - -echo $major -# Remove stale nodes and replace them, then give gid and perms -# Usually the script is shorter, it's scull that has several devices in it. - -rm -f /dev/${device} -mknod /dev/${device} c $major 0 -# ln -sf ${device}0 /dev/${device} -chgrp users /dev/${device} -chmod $mode /dev/${device} - - - - - - - - - diff -r 5cff10efb927 -r 0d4119024f55 rt/msr_param.h --- a/rt/msr_param.h Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Copyright (C) 2006 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 - * as published by the Free Software Foundation; version 2 of the License. - * - * 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 - * - *****************************************************************************/ - -#ifndef _MSR_PARAM_H_ -#define _MSR_PARAM_H_ - -#define MSR_ABTASTFREQUENZ 1000 - -#endif - -/*****************************************************************************/ diff -r 5cff10efb927 -r 0d4119024f55 rt/msr_rt.c --- a/rt/msr_rt.c Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/****************************************************************************** - * - * m s r _ r t . c - * - * Kernelmodul für 2.6 Kernel zur Meßdatenerfassung, Steuerung und Regelung. - * - * $Id$ - * - * Copyright (C) 2006 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 - * as published by the Free Software Foundation; version 2 of the License. - * - * 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 - * - *****************************************************************************/ - -// Linux -#include -#include -#include -#include -#include - -// RT_lib -#include -#include -#include -#include -#include -#include -#include "msr_param.h" - -// EtherCAT -#include "../include/ecrt.h" - -#define ASYNC - -// Defines/Makros -#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ) - -/*****************************************************************************/ -/* Globale Variablen */ - -// Adeos -static struct ipipe_domain this_domain; -static struct ipipe_sysinfo sys_info; - -// EtherCAT -ec_master_t *master = NULL; -ec_domain_t *domain1 = NULL; - -// Prozessdaten -void *r_ssi; -void *r_ssi_st; - -// KanŽäle -uint32_t k_ssi; -uint32_t k_ssi_st; - -ec_field_init_t domain1_fields[] = { - {&r_ssi, "0:3", "Beckhoff", "EL5001", "InputValue", 0}, - {&r_ssi_st, "0:3", "Beckhoff", "EL5001", "Status", 0}, - {} -}; - -/*****************************************************************************/ - -static void msr_controller_run(void) -{ -#ifdef ASYNC - // Empfangen - ecrt_master_async_receive(master); - ecrt_domain_process(domain1); -#else - // Senden und empfangen - ecrt_domain_queue(domain1); - ecrt_master_run(master); - ecrt_master_sync_io(master); - ecrt_domain_process(domain1); -#endif - - // Prozessdaten verarbeiten - k_ssi = EC_READ_U32(r_ssi); - k_ssi_st = EC_READ_U8 (r_ssi_st); - -#ifdef ASYNC - // Senden - ecrt_domain_queue(domain1); - ecrt_master_run(master); - ecrt_master_async_send(master); -#endif -} - -/*****************************************************************************/ - -int msr_globals_register(void) -{ - msr_reg_kanal("/ssi_position", "", &k_ssi, TUINT); - msr_reg_kanal("/ssi_status", "", &k_ssi_st, TUINT); - return 0; -} - -/*****************************************************************************/ - -void msr_run(unsigned irq) -{ - static int counter = 0; - - MSR_ADEOS_INTERRUPT_CODE(msr_controller_run(); msr_write_kanal_list();); - - ipipe_control_irq(irq, 0, IPIPE_ENABLE_MASK); // Interrupt bestŽätigen - if (++counter >= HZREDUCTION) { - ipipe_propagate_irq(irq); // und weiterreichen - counter = 0; - } -} - -/*****************************************************************************/ - -void domain_entry(void) -{ - printk("Domain %s started.\n", ipipe_current_domain->name); - - ipipe_get_sysinfo(&sys_info); - ipipe_virtualize_irq(ipipe_current_domain,sys_info.archdep.tmirq, - &msr_run, NULL, IPIPE_HANDLE_MASK); - - ipipe_tune_timer(1000000000UL / MSR_ABTASTFREQUENZ, 0); -} - -/*****************************************************************************/ - -int __init init_rt_module(void) -{ - struct ipipe_domain_attr attr; //ipipe -#if 1 - ec_slave_t *slave; -#endif - - // Als allererstes die RT-Lib initialisieren - if (msr_rtlib_init(1, MSR_ABTASTFREQUENZ, 10, &msr_globals_register) < 0) { - printk(KERN_ERR "Failed to initialize rtlib!\n"); - goto out_return; - } - - if ((master = ecrt_request_master(0)) == NULL) { - printk(KERN_ERR "Failed to request master 0!\n"); - goto out_msr_cleanup; - } - - //ecrt_master_print(master, 2); - - printk(KERN_INFO "Creating domains...\n"); - if (!(domain1 = ecrt_master_create_domain(master))) { - printk(KERN_ERR "Failed to create domains!\n"); - goto out_release_master; - } - - printk(KERN_INFO "Registering domain fields...\n"); - if (ecrt_domain_register_field_list(domain1, domain1_fields)) { - printk(KERN_ERR "Failed to register domain fields.\n"); - goto out_release_master; - } - - printk(KERN_INFO "Activating master...\n"); - if (ecrt_master_activate(master)) { - printk(KERN_ERR "Could not activate master!\n"); - goto out_release_master; - } - -#if 0 - if (ecrt_master_fetch_sdo_lists(master)) { - printk(KERN_ERR "Failed to fetch SDO lists!\n"); - goto out_deactivate; - } - ecrt_master_print(master, 2); -#else - ecrt_master_print(master, 0); -#endif - -#if 1 - if (!(slave = ecrt_master_get_slave(master, "0:3"))) { - printk(KERN_ERR "Failed to get slave!\n"); - goto out_deactivate; - } - - if ( - ecrt_slave_sdo_write_exp8(slave, 0x4061, 1, 1) || // disable frame error bit - ecrt_slave_sdo_write_exp8(slave, 0x4061, 2, 0) || // power failure bit - ecrt_slave_sdo_write_exp8(slave, 0x4061, 3, 1) || // inhibit time - ecrt_slave_sdo_write_exp8(slave, 0x4061, 4, 0) || // test mode - ecrt_slave_sdo_write_exp8(slave, 0x4066, 0, 1) || // dualcode - ecrt_slave_sdo_write_exp8(slave, 0x4067, 0, 5) || // 125kbaud - ecrt_slave_sdo_write_exp8(slave, 0x4068, 0, 0) || // single-turn - ecrt_slave_sdo_write_exp8(slave, 0x4069, 0, 25) || // frame size - ecrt_slave_sdo_write_exp8(slave, 0x406A, 0, 25) || // data length - ecrt_slave_sdo_write_exp16(slave, 0x406B, 0, 30000) // inhibit time in us - ) { - printk(KERN_ERR "Failed to configure SSI slave!\n"); - goto out_deactivate; - } -#endif - -#if 0 - if (!(slave = ecrt_master_get_slave(master, "1:0"))) { - printk(KERN_ERR "Failed to get slave!\n"); - goto out_deactivate; - } - if (ecrt_slave_write_alias(slave, 0x5678)) { - printk(KERN_ERR "Failed to write alias!\n"); - goto out_deactivate; - } -#endif - -#ifdef ASYNC - // Einmal senden und warten... - ecrt_master_prepare_async_io(master); -#endif - - ipipe_init_attr(&attr); - attr.name = "IPIPE-MSR-MODULE"; - attr.priority = IPIPE_ROOT_PRIO + 1; - attr.entry = &domain_entry; - ipipe_register_domain(&this_domain, &attr); - return 0; - -#if 1 - out_deactivate: - ecrt_master_deactivate(master); -#endif - out_release_master: - ecrt_release_master(master); - out_msr_cleanup: - msr_rtlib_cleanup(); - out_return: - return -1; -} - -/*****************************************************************************/ - -void __exit cleanup_rt_module(void) -{ - printk(KERN_INFO "Cleanign up rt module...\n"); - - ipipe_tune_timer(1000000000UL / HZ, 0); // Alten Timertakt wiederherstellen - ipipe_unregister_domain(&this_domain); - - printk(KERN_INFO "=== Stopping EtherCAT environment... ===\n"); - ecrt_master_deactivate(master); - ecrt_release_master(master); - printk(KERN_INFO "=== EtherCAT environment stopped. ===\n"); - - msr_rtlib_cleanup(); -} - -/*****************************************************************************/ - -#define EC_LIT(X) #X -#define EC_STR(X) EC_LIT(X) -#define COMPILE_INFO "Revision " EC_STR(SVNREV) \ - ", compiled by " EC_STR(USER) \ - " at " __DATE__ " " __TIME__ - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT real-time test environment"); -MODULE_VERSION(COMPILE_INFO); - -module_init(init_rt_module); -module_exit(cleanup_rt_module); - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:4 *** -;;; End: *** -*/ diff -r 5cff10efb927 -r 0d4119024f55 rt/msr_unload --- a/rt/msr_unload Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -#!/bin/sh -module="msr_rt" -device="msr" - -# invoke rmmod with all arguments we got -/sbin/rmmod $module $* || exit 1 - -# Remove stale nodes -rm -f /dev/${device} /dev/${device}0 diff -r 5cff10efb927 -r 0d4119024f55 rt/msrserv.pl --- a/rt/msrserv.pl Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,257 +0,0 @@ -#!/usr/bin/perl -w - -#------------------------------------------------------------------------------ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ -# -# Multithreaded Server -# according to the example from "Programming Perl" -# this code is improved according to the example from -# perldoc perlipc, so now safely being usable under Perl 5.8 -# (see note (*)) -# -# works with read/write on a device-file -# -#------------------------------------------------------------------------------ - -require 5.002; -use strict; -BEGIN { $ENV{PATH} = '/opt/msr/bin:/usr/bin:/bin' } -use Socket; -use Carp; -use FileHandle; -use Getopt::Std; - -use Sys::Syslog qw(:DEFAULT setlogsock); - -use vars qw ( - $self $pid $dolog $port $dev %opts $selfbase - $len $offset $stream $written $read $log $blksize - $instdir - $authfile %authhosts - ); - - -# Do logging to local syslogd by unix-domain socket instead of inetd -setlogsock("unix"); - -# Prototypes and some little Tools -sub spawn; -sub logmsg { - my ($level, $debug, @text) = @_; - syslog("daemon|$level", @text) if $debug > $dolog; -# print STDERR "daemon|$level", @text, "\n" if $dolog; -} -sub out { - my $waitpid = wait; - logmsg("notice", 2, "$waitpid exited"); - unlink "$selfbase.pid"; - exit 0; -} - -sub help { - print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n"; - exit; -} - -# Process Options -%opts = ( - "l" => 1, - "h" => 0, - "p" => 2345, - "d" => "/dev/msr" - ); - -getopts("lhp:d:", \%opts); - -help if $opts{"h"}; - -( $self = $0 ) =~ s+.*/++ ; -( $selfbase = $self ) =~ s/\..*//; -$log = "$selfbase.log"; -$dolog = $opts{"l"}; -$port = $opts{"p"}; -$dev = $opts{"d"}; -$blksize = 1024; # try to write as much bytes -$instdir = "/opt/msr"; -$authfile = "$instdir/etc/hosts.auth"; - -# Start logging -openlog($self, 'pid'); - -# Flush Output, dont buffer -$| = 1; - -# first fork and run in background -if ($pid = fork) { -# open LOG, ">$log" if $dolog; -# close LOG; - logmsg("notice", 2, "forked process: $pid\n"); - exit 0; -} - -# Server tells about startup success -open (PID, ">/$instdir/var/run/$selfbase.pid"); -print PID "$$\n"; -close PID; - -# Cleanup on exit (due to kill -TERM signal) -$SIG{TERM} = \&out; - -# We use streams -my $proto = getprotobyname('tcp'); - -# Open Server socket -socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; -setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) - or die "setsocketopt: $!"; -bind (Server, sockaddr_in($port, INADDR_ANY)) - or die "bind: $!"; -listen (Server, SOMAXCONN) - or die "listen: $!"; - -%authhosts = (); -# get authorized hosts -open (AUTH, $authfile) - or logmsg ("notice", 2, "Could not read allowed hosts file: $authfile"); -while () { - chomp; - my $host = lc $_; - if ($host =~ /^[\d\w]/) { - $authhosts{$_} = 1; - logmsg ("notice", 2, "Authorized host: >$host<"); - } -} -close (AUTH); - -# tell about open server socket -logmsg ("notice", 2, "Server started at port $port"); - -my $waitedpid = 0; -my $paddr; - -# wait for children to return, thus avoiding zombies -# improvement (*) -use POSIX ":sys_wait_h"; -sub REAPER { - my $child; - while (($waitedpid = waitpid(-1,WNOHANG)) > 0) { - logmsg ("notice", 2, "reaped $waitedpid", ($? ? " with exit $?" : "")); - } - $SIG{CHLD} = \&REAPER; # loathe sysV -} - -# also all sub-processes should wait for their children -$SIG{CHLD} = \&REAPER; - -# start a new server for every incoming request -# improvement (*) -- loop forever - -while ( 1 ) { - for ( $waitedpid = 0; - ($paddr = accept(Client,Server)) || $waitedpid; - $waitedpid = 0, close Client ) { - next if $waitedpid and not $paddr; - my ($port, $iaddr) = sockaddr_in($paddr); - my $name = lc gethostbyaddr($iaddr, AF_INET); - my $ipaddr = inet_ntoa($iaddr); - my $n = 0; - -# tell about the requesting client - logmsg ("info", 2, "Connection from >$ipaddr< ($name) at port $port"); - - spawn sub { - my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen); - my ($watchpegel, $shmpegel); - my ($rin, $rout, $in, $line, $data_requested, $oversample); - my (@channels); - -# to use stdio on writing to Client - Client->autoflush(); - -# Open Device - sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev"); - -# Bitmask to check for input on stdin - $rin = ""; - vec($rin, fileno(Client), 1) = 1; - -# check for authorized hosts - my $access = 'deny'; - $access = 'allow' if $authhosts{$ipaddr}; - $line = "\n"; - logmsg ("info", 2, $line); - $len = length $line; - $offset = 0; - while ($len) { - $written = syswrite (DEV, $line, $len, $offset); - $len -= $written; - $offset += $written; - } - - while ( 1 ) { - $in = select ($rout=$rin, undef, undef, 0.0); # poll client -# look for any Input from Client - if ($in) { -# exit on EOF - $len = sysread (Client, $line, $blksize) or exit; - logmsg("info", 0, "got $len bytes: \"$line\""); - $offset = 0; -# copy request to device - while ($len) { - $written = syswrite (DEV, $line, $len, $offset); - $len -= $written; - $offset += $written; - } - } -# look for some output from device - if ($len = sysread DEV, $stream, $blksize) { - print Client $stream; - } else { - select undef, undef, undef, 0.1; # calm down if nothing on device - } - } - }; - logmsg("info", 2, "spawned\n"); - } - logmsg("info", 2, "server loop\n"); -} - -sub spawn { - my $coderef = shift; - - unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') { - confess "usage: spawn CODEREF"; - } - my $pid; - if (!defined($pid = fork)) { - logmsg ("notice", 2, "fork failed: $!"); - return; - } elsif ($pid) { - logmsg ("notice", 2, "Request $pid"); - return; # Parent - } - -# do not use fdup as in the original example -# open (STDIN, "<&Client") or die "Can't dup client to stdin"; -# open (STDOUT, ">&Client") or die "Can't dup client to stdout"; -# STDOUT->autoflush(); - exit &$coderef(); -} diff -r 5cff10efb927 -r 0d4119024f55 rt/rt.conf.tmpl --- a/rt/rt.conf.tmpl Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#------------------------------------------------------------------------------ -# -# Configuration file for msr realtime modules -# -# $Id$ -# -# This file is a versioned template configuration. Copy it to "rt.conf" -# (which is ignored by Subversion) and adjust it to your needs. -# -#------------------------------------------------------------------------------ - -# Module name (without extension) -MODULENAME := ec_rt_sample - -# The kernel to compile the EtherCAT sources against -KERNEL := `uname -r` - -#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 script/ec_list.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script/ec_list.pl Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,192 @@ +#!/usr/bin/perl + +#------------------------------------------------------------------------------ +# +# e c _ l i s t . p l +# +# Userspace tool for listing EtherCAT slaves. +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +use strict; +use Getopt::Std; + +my $master_index; +my $master_dir; +my $show_sii_naming; + +#------------------------------------------------------------------------------ + +&get_options; +&query_master; +exit 0; + +#------------------------------------------------------------------------------ + +sub query_master +{ + $master_dir = "/sys/ethercat" . $master_index; + &query_slaves; +} + +#------------------------------------------------------------------------------ + +sub query_slaves +{ + my $dirhandle; + my $slave_dir; + my $entry; + my $slave_index; + my $file_name; + my $vendor_name; + my @slaves; + my $slave; + my $abs; + + unless (opendir $dirhandle, $master_dir) { + print "Failed to open directory \"$master_dir\".\n"; + exit 1; + } + + while ($entry = readdir $dirhandle) { + next unless $entry =~ /^slave(\d+)$/; + $slave_dir = $master_dir . "/" . $entry; + + $slave = {}; + $slave->{'ring_position'} = + &read_integer("$slave_dir/ring_position"); + $slave->{'coupler_address'} = + &read_string("$slave_dir/coupler_address"); + unless ($show_sii_naming) { + $slave->{'vendor_name'} = + &read_string("$slave_dir/vendor_name"); + $slave->{'product_name'} = + &read_string("$slave_dir/product_name"); + $slave->{'product_desc'} = + &read_string("$slave_dir/product_desc"); + } + else { + $slave->{'sii_name'} = + &read_string("$slave_dir/sii_name"); + } + $slave->{'type'} = + &read_string("$slave_dir/type"); + + push @slaves, $slave; + } + closedir $dirhandle; + + @slaves = sort { $a->{'ring_position'} <=> $b->{'ring_position'} } @slaves; + + print "EtherCAT bus listing for master $master_index:\n"; + for $slave (@slaves) { + if ($slave->{'type'} eq "coupler") { + print "--------------------------------------------------------\n"; + } + + $abs = sprintf "%i", $slave->{'ring_position'}; + printf(" %3s %8s ", $abs, $slave->{'coupler_address'}); + unless ($show_sii_naming) { + printf("%-12s %-10s %s\n", $slave->{'vendor_name'}, + $slave->{'product_name'}, $slave->{'product_desc'}); + } + else { + printf("%s\n", $slave->{'sii_name'}); + } + } +} + +#------------------------------------------------------------------------------ + +sub read_string +{ + (my $file_name) = @_; + my $data; + + $data = `cat $file_name 2>/dev/null`; + if ($?) { + print "ERROR: Unable to read string $file_name!\n"; + exit 1; + } + + chomp $data; + return $data; +} + +#------------------------------------------------------------------------------ + +sub read_integer +{ + (my $file_name) = @_; + + if (`cat $file_name 2>/dev/null` !~ /^(\d+)$/) { + print "ERROR: Unable to read integer $file_name!\n"; + exit 1; + } + + return int $1; +} + +#------------------------------------------------------------------------------ + +sub get_options +{ + my %opt; + my $optret; + + $optret = getopts "m:sh", \%opt; + + &print_usage if defined $opt{'h'} or $#ARGV > -1 or !$optret; + + if (defined $opt{'m'}) { + $master_index = $opt{'m'}; + } + else { + $master_index = 0; + } + + $show_sii_naming = defined $opt{'s'}; +} + +#------------------------------------------------------------------------------ + +sub print_usage +{ + print "Usage: ec_list [OPTIONS]\n"; + print " -m Query master .\n"; + print " -s Show SII naming instead of"; + print " vendor/product/description.\n"; + print " -h Show this help.\n"; + exit 0; +} + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 script/ethercat.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script/ethercat.sh Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,146 @@ +#!/bin/sh + +#------------------------------------------------------------------------------ +# +# Init script for EtherCAT +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +### BEGIN INIT INFO +# Provides: ethercat +# Required-Start: $local_fs $syslog $network +# Should-Start: $time +# Required-Stop: $local_fs $syslog $network +# Should-Stop: $time +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: IgH EtherCAT master modules +# Description: +### END INIT INFO + +#------------------------------------------------------------------------------ + +ETHERCAT_CONFIG=/etc/sysconfig/ethercat + +test -r $ETHERCAT_CONFIG || { echo "$ETHERCAT_CONFIG not existing"; + if [ "$1" = "stop" ]; then exit 0; + else exit 6; fi; } + +. $ETHERCAT_CONFIG + +#------------------------------------------------------------------------------ + +. /etc/rc.status +rc_reset + +case "$1" in + start) + echo -n "Starting EtherCAT master " + + if [ ! $DEVICE_INDEX ]; then + echo "ERROR: DEVICE_INDEX not set!" + /bin/false + rc_status -v + rc_exit + fi + + if [ ! $EOE_DEVICES ]; then + EOE_DEVICES=0 + fi + + for mod in 8139too 8139cp; do + if lsmod | grep "^$mod " > /dev/null; then + if ! rmmod $mod; then + /bin/false + rc_status -v + rc_exit + fi + fi + done + + if ! modprobe ec_master ec_eoe_devices=$EOE_DEVICES; then + /bin/false + rc_status -v + rc_exit + fi + + if ! modprobe ec_8139too ec_device_index=$DEVICE_INDEX; then + /bin/false + rc_status -v + rc_exit + fi + + rc_status -v + ;; + + stop) + echo -n "Shutting down EtherCAT master " + + for mod in ec_8139too ec_master; do + if lsmod | grep "^$mod " > /dev/null; then + if ! rmmod $mod; then + /bin/false + rc_status -v + rc_exit + fi; + fi; + done + + if ! modprobe 8139too; then + echo "Warning: Failed to restore 8139too module." + fi + + rc_status -v + ;; + + restart) + $0 stop + $0 start + + rc_status + ;; + + status) + echo -n "Checking for EtherCAT " + + lsmod | grep "^ec_master " > /dev/null + master_running=$? + lsmod | grep "^ec_8139too " > /dev/null + device_running=$? + test $master_running -eq 0 -a $device_running -eq 0 + + rc_status -v + ;; +esac +rc_exit + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 script/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script/install.sh Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,114 @@ +#!/bin/sh + +#------------------------------------------------------------------------------ +# +# EtherCAT install script +# +# $Id$ +# +# Copyright (C) 2006 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 +# as published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# 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 right to use EtherCAT Technology is granted and comes free of +# charge under condition of compatibility of product made by +# Licensee. People intending to distribute/sell products based on the +# code, have to sign an agreement to guarantee that products using +# software based on IgH EtherCAT master stay compatible with the actual +# EtherCAT specification (which are released themselves as an open +# standard) as the (only) precondition to have the right to use EtherCAT +# Technology, IP and trade marks. +# +#------------------------------------------------------------------------------ + +# Fetch parameters + +if [ $# -ne 1 ]; then + echo "This script is called by \"make\". Run \"make install\" instead." + exit 1 +fi + +KERNEL=$1 + +if [ ! -d /lib/modules/$KERNEL ]; then + echo "Kernel \"$KERNEL\" does not exist in /lib/modules!" + exit 1 +fi + +#------------------------------------------------------------------------------ + +# Copy files + +INSTALLDIR=/lib/modules/$KERNEL/kernel/drivers/net +MODULES=(master/ec_master.ko devices/ec_8139too.ko) + +echo "EtherCAT installer - Kernel: $KERNEL" +echo " Installing modules" + +for mod in ${MODULES[*]}; do + echo " $mod" + cp $mod $INSTALLDIR || exit 1 +done + +#------------------------------------------------------------------------------ + +# Update dependencies + +echo " Building module dependencies" +depmod + +#------------------------------------------------------------------------------ + +# Create configuration file + +CONFIGFILE=/etc/sysconfig/ethercat + +if [ -s $CONFIGFILE ]; then + echo " Note: Using existing configuration file." +else + echo " Creating $CONFIGFILE" + cp script/sysconfig $CONFIGFILE || exit 1 + echo " Note: Please edit DEVICE_INDEX in $CONFIGFILE!" +fi + +#------------------------------------------------------------------------------ + +# Install rc script + +echo " Installing startup script" +cp script/ethercat.sh /etc/init.d/ethercat || exit 1 +chmod +x /etc/init.d/ethercat || exit 1 +if [ ! -L /usr/sbin/rcethercat ]; then + ln -s /etc/init.d/ethercat /usr/sbin/rcethercat || exit 1 +fi + +#------------------------------------------------------------------------------ + +# Install tools + +echo " Installing tools" +cp script/ec_list.pl /usr/local/bin/ec_list || exit 1 +chmod +x /usr/local/bin/ec_list || exit 1 + +#------------------------------------------------------------------------------ + +# Finish + +echo "Done" +exit 0 + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 script/sysconfig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script/sysconfig Mon May 29 09:08:56 2006 +0000 @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------------ +# +# EtherCAT sysconfig file +# +# $Id$ +# +#------------------------------------------------------------------------------ + +# +# PCI index of the (RTL8139-)EtherCAT device +# Setting this is mandatory for the EtherCAT init script! +# +#DEVICE_INDEX=99 + +# +# Number of Ethernet-over-EtherCAT devices every master shall create +# on startup. Default is 0. +# +#EOE_DEVICES=0 + +#------------------------------------------------------------------------------ diff -r 5cff10efb927 -r 0d4119024f55 tools/ec_list.pl --- a/tools/ec_list.pl Mon Apr 24 10:47:03 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -#!/usr/bin/perl - -#------------------------------------------------------------------------------ -# -# e c _ l i s t . p l -# -# Userspace tool for listing EtherCAT slaves. -# -# $Id: slave.c 340 2006-04-11 10:17:30Z fp $ -# -# Copyright (C) 2006 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 -# as published by the Free Software Foundation; version 2 of the License. -# -# 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 -# -#------------------------------------------------------------------------------ - -use strict; -use Getopt::Std; - -my $master_index; -my $master_dir; - -#------------------------------------------------------------------------------ - -&get_options; -&query_master; -exit 0; - -#------------------------------------------------------------------------------ - -sub query_master -{ - $master_dir = "/sys/ethercat" . $master_index; - &query_slaves; -} - -#------------------------------------------------------------------------------ - -sub query_slaves -{ - my $dirhandle; - my $slave_dir; - my $entry; - my $slave_index; - my $file_name; - my $vendor_name; - my @slaves; - my $slave; - my $abs; - - unless (opendir $dirhandle, $master_dir) { - print "Failed to open directory \"$master_dir\".\n"; - exit 1; - } - - while ($entry = readdir $dirhandle) { - next unless $entry =~ /^slave(\d+)$/; - $slave_dir = $master_dir . "/" . $entry; - - $slave = {}; - $slave->{'ring_position'} = - &read_integer("$slave_dir/ring_position"); - $slave->{'coupler_address'} = - &read_string("$slave_dir/coupler_address"); - $slave->{'vendor_name'} = - &read_string("$slave_dir/vendor_name"); - $slave->{'product_name'} = - &read_string("$slave_dir/product_name"); - $slave->{'product_desc'} = - &read_string("$slave_dir/product_desc"); - $slave->{'type'} = - &read_string("$slave_dir/type"); - - push @slaves, $slave; - } - closedir $dirhandle; - - @slaves = sort { $a->{'ring_position'} <=> $b->{'ring_position'} } @slaves; - - print "EtherCAT bus listing for master $master_index:\n"; - for $slave (@slaves) { - if ($slave->{'type'} eq "coupler") { - print "--------------------------------------------------------\n"; - } - - $abs = sprintf "%i", $slave->{'ring_position'}; - printf(" %3s %8s %-12s %-10s %s\n", $abs, - $slave->{'coupler_address'}, $slave->{'vendor_name'}, - $slave->{'product_name'}, $slave->{'product_desc'}); - } -} - -#------------------------------------------------------------------------------ - -sub read_string -{ - (my $file_name) = @_; - my $data; - - $data = `cat $file_name 2>/dev/null`; - if ($?) { - print "ERROR: Unable to read string $file_name!\n"; - exit 1; - } - - chomp $data; - return $data; -} - -#------------------------------------------------------------------------------ - -sub read_integer -{ - (my $file_name) = @_; - - if (`cat $file_name 2>/dev/null` !~ /^(\d+)$/) { - print "ERROR: Unable to read integer $file_name!\n"; - exit 1; - } - - return int $1; -} - -#------------------------------------------------------------------------------ - -sub get_options -{ - my %opt; - my $optret; - - $optret = getopts "m:h", \%opt; - - &print_usage if defined $opt{'h'} or $#ARGV > -1; - - if (defined $opt{'m'}) { - $master_index = $opt{'m'}; - } - else { - $master_index = 0; - } -} - -#------------------------------------------------------------------------------ - -sub print_usage -{ - print "Usage: ec_list [OPTIONS]\n"; - print " -m Query master IDX.\n"; - print " -h Show this help.\n"; - exit 0; -} - -#------------------------------------------------------------------------------