VERSION 1.1: New realtime interface, only state machines.

This commit is contained in:
Florian Pose 2006-08-03 12:51:17 +00:00
parent fe60abcfc7
commit 258b4d998d
27 changed files with 1216 additions and 3524 deletions

6
TODO
View File

@ -8,12 +8,6 @@ $Id$
Important things to do:
* New SDO-Configuration interface
* Remove slave type information from master / New RT Interface
* Always use state machines / remove simple IO code
* SysFS interface
- Add secondary slave address
- Add SDO dictionary

View File

@ -42,8 +42,8 @@
#include <linux/interrupt.h>
#include "../../include/ecrt.h" // EtherCAT realtime interface
#include "../../include/ecdb.h" // EtherCAT slave database
#define ASYNC
#define FREQUENCY 100
/*****************************************************************************/
@ -56,16 +56,14 @@ ec_domain_t *domain1 = NULL;
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
// data fields
//void *r_ssi_input, *r_ssi_status, *r_4102[3];
void *r_kbus_in, *r_kbus_out;
void *r_ana_out;
// channels
uint32_t k_pos;
uint8_t k_stat;
ec_field_init_t domain1_fields[] = {
//{&r_kbus_out, "0", "Beckhoff", "BK1120", "Outputs", 0},
//{&r_kbus_in, "0", "Beckhoff", "BK1120", "Inputs", 0},
ec_pdo_reg_t domain1_pdos[] = {
{"1", Beckhoff_EL4132_Output1, &r_ana_out},
{}
};
@ -74,31 +72,19 @@ ec_field_init_t domain1_fields[] = {
void run(unsigned long data)
{
static unsigned int counter = 0;
unsigned int i;
spin_lock(&master_lock);
#ifdef ASYNC
// receive
ecrt_master_async_receive(master);
ecrt_master_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
ecrt_master_send(master);
spin_unlock(&master_lock);
@ -107,9 +93,10 @@ void run(unsigned long data)
}
else {
counter = FREQUENCY;
printk(KERN_INFO "input = ");
for (i = 0; i < 22; i++) printk("%02X ", *((uint8_t *) r_kbus_in + i));
printk("\n");
//printk(KERN_INFO "input = ");
//for (i = 0; i < 22; i++)
// printk("%02X ", *((uint8_t *) r_kbus_in + i));
//printk("\n");
}
// restart timer
@ -136,8 +123,6 @@ void release_lock(void *data)
int __init init_mini_module(void)
{
ec_slave_t *slave;
printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n");
if ((master = ecrt_request_master(0)) == NULL) {
@ -154,69 +139,19 @@ int __init init_mini_module(void)
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");
printk(KERN_INFO "Registering PDOs...\n");
if (ecrt_domain_register_pdo_list(domain1, domain1_pdos)) {
printk(KERN_ERR "PDO registration failed!\n");
goto out_release_master;
}
#if 1
printk(KERN_INFO "Setting variable data field sizes...\n");
if (!(slave = ecrt_master_get_slave(master, "0"))) {
printk(KERN_ERR "Failed to get slave!\n");
goto out_deactivate;
}
ecrt_slave_field_size(slave, "Outputs", 0, 0x16);
ecrt_slave_field_size(slave, "Inputs", 0, 0x16);
#endif
printk(KERN_INFO "Activating master...\n");
if (ecrt_master_activate(master)) {
printk(KERN_ERR "Failed to activate master!\n");
goto out_release_master;
}
#if 1
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
#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
ecrt_master_prepare(master);
printk("Starting cyclic sample thread.\n");
init_timer(&timer);
@ -227,10 +162,6 @@ int __init init_mini_module(void)
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:

View File

@ -48,8 +48,7 @@
// EtherCAT
#include "../../include/ecrt.h"
#define ASYNC
#include "../../include/ecdb.h"
#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ)
#define TIMERTICKS (1000000000 / MSR_ABTASTFREQUENZ)
@ -66,16 +65,13 @@ ec_master_t *master = NULL;
ec_domain_t *domain1 = NULL;
// raw process data
void *r_ssi;
void *r_ssi_st;
void *r_ana_out;
// Channels
uint32_t k_ssi;
uint32_t k_ssi_st;
double k_ana_out;
ec_field_init_t domain1_fields[] = {
{&r_ssi, "0:3", "Beckhoff", "EL5001", "InputValue", 0},
{&r_ssi_st, "0:3", "Beckhoff", "EL5001", "Status", 0},
ec_pdo_reg_t domain1_pdos[] = {
{"3", Beckhoff_EL4132_Output1, &r_ana_out},
{}
};
@ -85,28 +81,16 @@ void msr_controller_run(void)
{
rt_sem_wait(&master_sem);
#ifdef ASYNC
// Empfangen
ecrt_master_async_receive(master);
// receive
ecrt_master_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);
// Process data
EC_WRITE_S16(r_ana_out, k_ana_out / 10.0 * 0x7FFF);
#ifdef ASYNC
// Senden
ecrt_domain_queue(domain1);
// Send
ecrt_master_run(master);
ecrt_master_async_send(master);
#endif
ecrt_master_send(master);
rt_sem_signal(&master_sem);
@ -128,8 +112,7 @@ void msr_run(long data)
int msr_reg(void)
{
msr_reg_kanal("/ssi_position", "", &k_ssi, TUINT);
msr_reg_kanal("/ssi_status", "", &k_ssi_st, TUINT);
msr_reg_kanal("/ana_out", "", &k_ana_out, TDBL);
return 0;
}
@ -157,9 +140,6 @@ void release_lock(void *data)
int __init init_mod(void)
{
RTIME ticks;
#if 0
ec_slave_t *slave;
#endif
printk(KERN_INFO "=== Starting EtherCAT RTAI MSR sample module... ===\n");
@ -171,7 +151,7 @@ int __init init_mod(void)
goto out_return;
}
if ((master = ecrt_request_master(0)) == NULL) {
if (!(master = ecrt_request_master(0))) {
printk(KERN_ERR "Failed to request master 0!\n");
goto out_msr_cleanup;
}
@ -184,9 +164,9 @@ int __init init_mod(void)
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");
printk(KERN_INFO "Registering PDOs...\n");
if (ecrt_domain_register_pdo_list(domain1, domain1_pdos)) {
printk(KERN_ERR "Failed to register PDOs.\n");
goto out_release_master;
}
@ -196,59 +176,7 @@ int __init init_mod(void)
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;
}
ecrt_master_prepare(master);
printk("Starting cyclic sample thread...\n");
ticks = start_rt_timer(nano2count(TIMERTICKS));
@ -268,7 +196,6 @@ int __init init_mod(void)
rt_task_delete(&task);
out_stop_timer:
stop_rt_timer();
out_deactivate:
ecrt_master_deactivate(master);
out_release_master:
ecrt_release_master(master);

View File

@ -42,6 +42,7 @@
// EtherCAT
#include "../../include/ecrt.h"
#include "../../include/ecdb.h"
/*****************************************************************************/
@ -51,12 +52,8 @@ 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)
/*****************************************************************************/
@ -64,22 +61,20 @@ MODULE_DESCRIPTION("EtherCAT RTAI sample module");
// RTAI
RT_TASK task;
SEM master_sem;
cycles_t t_last_start = 0;
cycles_t t_critical;
cycles_t t_last_start = 0, t_critical;
// EtherCAT
ec_master_t *master = NULL;
ec_domain_t *domain1 = NULL;
// data fields
void *r_ssi_input;
void *r_ana_out;
// 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},
ec_pdo_reg_t domain1_pdos[] = {
{"2", Beckhoff_EL4132_Output1, &r_ana_out},
{}
};
@ -92,27 +87,14 @@ void run(long data)
t_last_start = get_cycles();
rt_sem_wait(&master_sem);
#ifdef ASYNC
// receive
ecrt_master_async_receive(master);
ecrt_master_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);
//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
ecrt_master_send(master);
rt_sem_signal(&master_sem);
rt_task_wait_period();
@ -158,15 +140,14 @@ int __init init_mod(void)
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
printk(KERN_INFO "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master)))
{
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");
printk(KERN_INFO "Registering PDOs...\n");
if (ecrt_domain_register_pdo_list(domain1, domain1_pdos)) {
printk(KERN_ERR "PDO registration failed!\n");
goto out_release_master;
}
@ -176,53 +157,7 @@ int __init init_mod(void)
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;
}
ecrt_master_prepare(master);
printk("Starting cyclic sample thread...\n");
requested_ticks = nano2count(TIMERTICKS);
@ -248,7 +183,6 @@ int __init init_mod(void)
rt_task_delete(&task);
out_stop_timer:
stop_rt_timer();
out_deactivate:
ecrt_master_deactivate(master);
out_release_master:
ecrt_release_master(master);

51
include/ecdb.h Normal file
View File

@ -0,0 +1,51 @@
/******************************************************************************
*
* $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 Slave Database.
*/
/*****************************************************************************/
#ifndef __ECDB_H__
#define __ECDB_H__
/*****************************************************************************/
#define Beckhoff_EL4132_Output1 0x00000002, 0x10243052, 0x6411, 1
#define Beckhoff_EL4132_Output2 0x00000002, 0x10243052, 0x6411, 2
/*****************************************************************************/
#endif

View File

@ -69,23 +69,22 @@ struct ec_slave;
typedef struct ec_slave ec_slave_t; /**< \see ec_slave */
/**
Initialization type for field registrations.
This type is used as a parameter for the ec_domain_register_field_list()
Initialization type for PDO registrations.
This type is used as a parameter for the ec_domain_register_pdo_list()
function.
*/
typedef struct
{
void **data_ptr; /**< address of the process data pointer */
const char *slave_address; /**< slave address string (see
ecrt_master_get_slave()) */
const char *vendor_name; /**< vendor name */
const char *product_name; /**< product name */
const char *field_name; /**< data field name */
unsigned int field_index; /**< index in data fields with same name */
unsigned int field_count; /**< number of data fields with same name */
uint32_t vendor_id; /**< vendor ID */
uint32_t product_code; /**< product code */
uint16_t pdo_index; /**< PDO index */
uint8_t pdo_subindex; /**< PDO subindex */
void **data_ptr; /**< address of the process data pointer */
}
ec_field_init_t;
ec_pdo_reg_t;
/******************************************************************************
* Master request functions
@ -106,66 +105,47 @@ ec_domain_t *ecrt_master_create_domain(ec_master_t *master);
int ecrt_master_activate(ec_master_t *master);
void ecrt_master_deactivate(ec_master_t *master);
int ecrt_master_fetch_sdo_lists(ec_master_t *master);
void ecrt_master_prepare(ec_master_t *master);
void ecrt_master_sync_io(ec_master_t *master);
void ecrt_master_async_send(ec_master_t *master);
void ecrt_master_async_receive(ec_master_t *master);
void ecrt_master_prepare_async_io(ec_master_t *master);
void ecrt_master_send(ec_master_t *master);
void ecrt_master_receive(ec_master_t *master);
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);
ec_slave_t *ecrt_master_get_slave(const ec_master_t *, const char *);
int ecrt_master_state(const ec_master_t *master);
/******************************************************************************
* Domain Methods
*****************************************************************************/
ec_slave_t *ecrt_domain_register_field(ec_domain_t *domain,
const char *address,
const char *vendor_name,
const char *product_name,
void **data_ptr, const char *field_name,
unsigned int field_index,
unsigned int field_count);
int ecrt_domain_register_field_list(ec_domain_t *domain,
const ec_field_init_t *fields);
ec_slave_t *ecrt_domain_register_pdo(ec_domain_t *domain,
const char *address,
uint32_t vendor_id,
uint32_t product_code,
uint16_t pdo_index,
uint8_t pdo_subindex,
void **data_ptr);
int ecrt_domain_register_pdo_list(ec_domain_t *domain,
const ec_pdo_reg_t *pdos);
void ecrt_domain_queue(ec_domain_t *domain);
void ecrt_domain_process(ec_domain_t *domain);
int ecrt_domain_state(ec_domain_t *domain);
int ecrt_domain_state(const ec_domain_t *domain);
/******************************************************************************
* Slave Methods
*****************************************************************************/
/* there SDO functions are deprecated! */
int ecrt_slave_conf_sdo8(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint8_t value);
int ecrt_slave_conf_sdo16(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint16_t value);
int ecrt_slave_conf_sdo32(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint32_t value);
int ecrt_slave_sdo_read_exp8(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint8_t *value);
int ecrt_slave_sdo_read_exp16(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint16_t *value);
int ecrt_slave_sdo_read_exp32(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint32_t *value);
int ecrt_slave_sdo_write_exp8(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint8_t value);
int ecrt_slave_sdo_write_exp16(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint16_t value);
int ecrt_slave_sdo_write_exp32(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint32_t value);
int ecrt_slave_sdo_read(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint8_t *data, size_t *size);
int ecrt_slave_write_alias(ec_slave_t *slave, uint16_t alias); // deprecated!
int ecrt_slave_field_size(ec_slave_t *slave, const char *field_name,
unsigned int field_index, size_t size);
int ecrt_slave_pdo_size(ec_slave_t *slave, uint16_t pdo_index,
uint8_t pdo_subindex, size_t size);
/******************************************************************************
* Bitwise read/write macros

View File

@ -42,8 +42,8 @@ ifneq ($(KERNELRELEASE),)
obj-m := ec_master.o
ec_master-objs := module.o master.o device.o slave.o datagram.o types.o \
domain.o mailbox.o canopen.o ethernet.o debug.o fsm.o
ec_master-objs := module.o master.o device.o slave.o datagram.o \
domain.o mailbox.o ethernet.o debug.o fsm.o
REV := $(shell svnversion $(src) 2>/dev/null || echo "unknown")

View File

@ -1,726 +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; 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
Canopen-over-EtherCAT functions.
*/
/*****************************************************************************/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "master.h"
#include "mailbox.h"
/*****************************************************************************/
void ec_canopen_abort_msg(uint32_t);
int ec_slave_fetch_sdo_descriptions(ec_slave_t *, ec_datagram_t *);
int ec_slave_fetch_sdo_entries(ec_slave_t *, ec_datagram_t *,
ec_sdo_t *, uint8_t);
/*****************************************************************************/
/**
Reads 32 bit of a CANopen SDO in expedited mode.
\return 0 in case of success, else < 0
*/
int ec_slave_sdo_read_exp(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint8_t *target /**< 4-byte memory */
)
{
ec_datagram_t datagram;
size_t rec_size;
uint8_t *data;
ec_datagram_init(&datagram);
if (!(data = ec_slave_mbox_prepare_send(slave, &datagram, 0x03, 6)))
goto err;
EC_WRITE_U16(data, 0x2 << 12); // SDO request
EC_WRITE_U8 (data + 2, (0x1 << 1 // expedited transfer
| 0x2 << 5)); // initiate upload request
EC_WRITE_U16(data + 3, sdo_index);
EC_WRITE_U8 (data + 5, sdo_subindex);
if (!(data = ec_slave_mbox_simple_io(slave, &datagram, &rec_size)))
goto err;
if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
EC_ERR("SDO upload 0x%04X:%X aborted on slave %i.\n",
sdo_index, sdo_subindex, slave->ring_position);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
goto err;
}
if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
EC_READ_U8 (data + 2) >> 5 != 0x2 || // upload response
EC_READ_U16(data + 3) != sdo_index || // index
EC_READ_U8 (data + 5) != sdo_subindex) { // subindex
EC_ERR("SDO upload 0x%04X:%X failed:\n", sdo_index, sdo_subindex);
EC_ERR("Invalid SDO upload response at slave %i!\n",
slave->ring_position);
ec_print_data(data, rec_size);
goto err;
}
memcpy(target, data + 6, 4);
ec_datagram_clear(&datagram);
return 0;
err:
ec_datagram_clear(&datagram);
return -1;
}
/*****************************************************************************/
/**
Writes a CANopen SDO using expedited mode.
\return 0 in case of success, else < 0
*/
int ec_slave_sdo_write_exp(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
const uint8_t *sdo_data, /**< new value */
size_t size /**< Data size in bytes (1 - 4) */
)
{
uint8_t *data;
size_t rec_size;
ec_datagram_t datagram;
ec_datagram_init(&datagram);
if (size == 0 || size > 4) {
EC_ERR("Invalid data size!\n");
goto err;
}
if (!(data = ec_slave_mbox_prepare_send(slave, &datagram, 0x03, 0x0A)))
goto err;
EC_WRITE_U16(data, 0x2 << 12); // SDO request
EC_WRITE_U8 (data + 2, (0x1 // size specified
| 0x1 << 1 // expedited transfer
| (4 - size) << 2 // data set size
| 0x1 << 5)); // initiate download request
EC_WRITE_U16(data + 3, sdo_index);
EC_WRITE_U8 (data + 5, sdo_subindex);
memcpy(data + 6, sdo_data, size);
if (size < 4) memset(data + 6 + size, 0x00, 4 - size);
if (!(data = ec_slave_mbox_simple_io(slave, &datagram, &rec_size)))
goto err;
if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
EC_ERR("SDO download 0x%04X:%X (%i bytes) aborted on slave %i.\n",
sdo_index, sdo_subindex, size, slave->ring_position);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
return -1;
}
if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
EC_READ_U8 (data + 2) >> 5 != 0x3 || // download response
EC_READ_U16(data + 3) != sdo_index || // index
EC_READ_U8 (data + 5) != sdo_subindex) { // subindex
EC_ERR("SDO download 0x%04X:%X (%i bytes) failed:\n",
sdo_index, sdo_subindex, size);
EC_ERR("Invalid SDO download response at slave %i!\n",
slave->ring_position);
ec_print_data(data, rec_size);
return -1;
}
ec_datagram_clear(&datagram);
return 0;
err:
ec_datagram_clear(&datagram);
return -1;
}
/*****************************************************************************/
/**
Reads a CANopen SDO in normal mode.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
\todo Make size non-pointer.
*/
int ecrt_slave_sdo_read(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint8_t *target, /**< memory for value */
size_t *size /**< target memory size */
)
{
uint8_t *data;
size_t rec_size, data_size;
uint32_t complete_size;
ec_datagram_t datagram;
ec_datagram_init(&datagram);
if (!(data = ec_slave_mbox_prepare_send(slave, &datagram, 0x03, 6)))
goto err;
EC_WRITE_U16(data, 0x2 << 12); // SDO request
EC_WRITE_U8 (data + 2, 0x2 << 5); // initiate upload request
EC_WRITE_U16(data + 3, sdo_index);
EC_WRITE_U8 (data + 5, sdo_subindex);
if (!(data = ec_slave_mbox_simple_io(slave, &datagram, &rec_size)))
goto err;
if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
EC_ERR("SDO upload 0x%04X:%X aborted on slave %i.\n",
sdo_index, sdo_subindex, slave->ring_position);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
goto err;
}
if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
EC_READ_U8 (data + 2) >> 5 != 0x2 || // initiate upload response
EC_READ_U16(data + 3) != sdo_index || // index
EC_READ_U8 (data + 5) != sdo_subindex) { // subindex
EC_ERR("SDO upload 0x%04X:%X failed:\n", sdo_index, sdo_subindex);
EC_ERR("Invalid SDO upload response at slave %i!\n",
slave->ring_position);
ec_print_data(data, rec_size);
goto err;
}
if (rec_size < 10) {
EC_ERR("Received currupted SDO upload response!\n");
ec_print_data(data, rec_size);
goto err;
}
if ((complete_size = EC_READ_U32(data + 6)) > *size) {
EC_ERR("SDO data does not fit into buffer (%i / %i)!\n",
complete_size, *size);
goto err;
}
data_size = rec_size - 10;
if (data_size != complete_size) {
EC_ERR("SDO data incomplete - Fragmenting not implemented.\n");
goto err;
}
memcpy(target, data + 10, data_size);
ec_datagram_clear(&datagram);
return 0;
err:
ec_datagram_clear(&datagram);
return -1;
}
/*****************************************************************************/
/**
Fetches the SDO dictionary of a slave.
\return 0 in case of success, else < 0
*/
int ec_slave_fetch_sdo_list(ec_slave_t *slave /**< EtherCAT slave */)
{
uint8_t *data;
size_t rec_size;
unsigned int i, sdo_count;
ec_sdo_t *sdo;
uint16_t sdo_index;
ec_datagram_t datagram;
ec_datagram_init(&datagram);
if (!(data = ec_slave_mbox_prepare_send(slave, &datagram, 0x03, 8)))
goto err;
EC_WRITE_U16(data, 0x8 << 12); // SDO information
EC_WRITE_U8 (data + 2, 0x01); // Get OD List Request
EC_WRITE_U8 (data + 3, 0x00);
EC_WRITE_U16(data + 4, 0x0000);
EC_WRITE_U16(data + 6, 0x0001); // deliver all SDOs!
if (unlikely(ec_master_simple_io(slave->master, &datagram))) {
EC_ERR("Mailbox checking failed on slave %i!\n", slave->ring_position);
goto err;
}
do {
if (!(data = ec_slave_mbox_simple_receive(slave, &datagram,
0x03, &rec_size)))
goto err;
if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
(EC_READ_U8(data + 2) & 0x7F) == 0x07) { // error response
EC_ERR("SDO information error response at slave %i!\n",
slave->ring_position);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
goto err;
}
if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
(EC_READ_U8 (data + 2) & 0x7F) != 0x02) { // Get OD List response
EC_ERR("Invalid SDO list response at slave %i!\n",
slave->ring_position);
ec_print_data(data, rec_size);
goto err;
}
if (rec_size < 8) {
EC_ERR("Invalid data size!\n");
ec_print_data(data, rec_size);
goto err;
}
sdo_count = (rec_size - 8) / 2;
for (i = 0; i < sdo_count; i++) {
sdo_index = EC_READ_U16(data + 8 + i * 2);
if (!sdo_index) continue; // sometimes index is 0... ???
if (!(sdo = (ec_sdo_t *) kmalloc(sizeof(ec_sdo_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO!\n");
goto err;
}
// Initialize SDO object
sdo->index = sdo_index;
//sdo->unkown = 0x0000;
sdo->object_code = 0x00;
sdo->name = NULL;
INIT_LIST_HEAD(&sdo->entries);
list_add_tail(&sdo->list, &slave->sdo_dictionary);
}
}
while (EC_READ_U8(data + 2) & 0x80);
// Fetch all SDO descriptions
if (ec_slave_fetch_sdo_descriptions(slave, &datagram)) goto err;
ec_datagram_clear(&datagram);
return 0;
err:
ec_datagram_clear(&datagram);
return -1;
}
/*****************************************************************************/
/**
Fetches the SDO descriptions for the known SDOs.
\return 0 in case of success, else < 0
*/
int ec_slave_fetch_sdo_descriptions(ec_slave_t *slave, /**< EtherCAT slave */
ec_datagram_t *datagram /**< datagram */
)
{
uint8_t *data;
size_t rec_size, name_size;
ec_sdo_t *sdo;
list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8)))
return -1;
EC_WRITE_U16(data, 0x8 << 12); // SDO information
EC_WRITE_U8 (data + 2, 0x03); // Get object description request
EC_WRITE_U8 (data + 3, 0x00);
EC_WRITE_U16(data + 4, 0x0000);
EC_WRITE_U16(data + 6, sdo->index); // SDO index
if (!(data = ec_slave_mbox_simple_io(slave, datagram, &rec_size)))
return -1;
if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
(EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
EC_ERR("SDO information error response at slave %i while"
" fetching SDO 0x%04X!\n", slave->ring_position,
sdo->index);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
return -1;
}
if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
(EC_READ_U8 (data + 2) & 0x7F) != 0x04 || // Object desc. response
EC_READ_U16(data + 6) != sdo->index) { // SDO index
EC_ERR("Invalid object description response at slave %i while"
" fetching SDO 0x%04X!\n", slave->ring_position,
sdo->index);
ec_print_data(data, rec_size);
return -1;
}
if (rec_size < 12) {
EC_ERR("Invalid data size!\n");
ec_print_data(data, rec_size);
return -1;
}
#if 0
EC_DBG("object desc response:\n");
ec_print_data(data, rec_size);
#endif
//sdo->unknown = EC_READ_U16(data + 8);
sdo->object_code = EC_READ_U8(data + 11);
name_size = rec_size - 12;
if (name_size) {
if (!(sdo->name = kmalloc(name_size + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate SDO name!\n");
return -1;
}
memcpy(sdo->name, data + 12, name_size);
sdo->name[name_size] = 0;
}
if (EC_READ_U8(data + 2) & 0x80) {
EC_ERR("Fragment follows (not implemented)!\n");
return -1;
}
// Fetch all entries (subindices)
if (ec_slave_fetch_sdo_entries(slave, datagram, sdo,
EC_READ_U8(data + 10)))
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Fetches all entries (subindices) to an SDO.
\return 0 in case of success, else < 0
*/
int ec_slave_fetch_sdo_entries(ec_slave_t *slave, /**< EtherCAT slave */
ec_datagram_t *datagram, /**< datagram */
ec_sdo_t *sdo, /**< SDO */
uint8_t subindices /**< number of subindices */
)
{
uint8_t *data;
size_t rec_size, data_size;
uint8_t i;
ec_sdo_entry_t *entry;
for (i = 1; i <= subindices; i++) {
if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10)))
return -1;
EC_WRITE_U16(data, 0x8 << 12); // SDO information
EC_WRITE_U8 (data + 2, 0x05); // Get entry description request
EC_WRITE_U8 (data + 3, 0x00);
EC_WRITE_U16(data + 4, 0x0000);
EC_WRITE_U16(data + 6, sdo->index); // SDO index
EC_WRITE_U8 (data + 8, i); // SDO subindex
EC_WRITE_U8 (data + 9, 0x00); // value info (no values)
if (!(data = ec_slave_mbox_simple_io(slave, datagram, &rec_size)))
return -1;
if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
(EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
EC_ERR("SDO information error response at slave %i while"
" fetching SDO entry 0x%04X:%i!\n", slave->ring_position,
sdo->index, i);
ec_canopen_abort_msg(EC_READ_U32(data + 6));
return -1;
}
if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
(EC_READ_U8(data + 2) & 0x7F) != 0x06 || // Entry desc. response
EC_READ_U16(data + 6) != sdo->index || // SDO index
EC_READ_U8(data + 8) != i) { // SDO subindex
EC_ERR("Invalid entry description response at slave %i while"
" fetching SDO entry 0x%04X:%i!\n", slave->ring_position,
sdo->index, i);
ec_print_data(data, rec_size);
return -1;
}
if (rec_size < 16) {
EC_ERR("Invalid data size!\n");
ec_print_data(data, rec_size);
return -1;
}
if (!EC_READ_U16(data + 12)) // bit length = 0
continue;
data_size = rec_size - 16;
if (!(entry = (ec_sdo_entry_t *)
kmalloc(sizeof(ec_sdo_entry_t) + data_size + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate entry!\n");
return -1;
}
entry->subindex = i;
entry->data_type = EC_READ_U16(data + 10);
entry->bit_length = EC_READ_U16(data + 12);
// memory for name string appended to entry
entry->name = (uint8_t *) entry + sizeof(ec_sdo_entry_t);
memcpy(entry->name, data + 16, data_size);
entry->name[data_size] = 0;
list_add_tail(&entry->list, &sdo->entries);
}
return 0;
}
/*****************************************************************************/
/**
SDO abort messages.
The "abort SDO transfer request" supplies an abort code,
which can be translated to clear text. This table does
the mapping of the codes and messages.
*/
const ec_code_msg_t sdo_abort_messages[] = {
{0x05030000, "Toggle bit not changed"},
{0x05040000, "SDO protocol timeout"},
{0x05040001, "Client/Server command specifier not valid or unknown"},
{0x05040005, "Out of memory"},
{0x06010000, "Unsupported access to an object"},
{0x06010001, "Attempt to read a write-only object"},
{0x06010002, "Attempt to write a read-only object"},
{0x06020000, "This object does not exist in the object directory"},
{0x06040041, "The object cannot be mapped into the PDO"},
{0x06040042, "The number and length of the objects to be mapped would"
" exceed the PDO length"},
{0x06040043, "General parameter incompatibility reason"},
{0x06040047, "Gerneral internal incompatibility in device"},
{0x06060000, "Access failure due to a hardware error"},
{0x06070010, "Data type does not match, length of service parameter does"
" not match"},
{0x06070012, "Data type does not match, length of service parameter too"
" high"},
{0x06070013, "Data type does not match, length of service parameter too"
" low"},
{0x06090011, "Subindex does not exist"},
{0x06090030, "Value range of parameter exceeded"},
{0x06090031, "Value of parameter written too high"},
{0x06090032, "Value of parameter written too low"},
{0x06090036, "Maximum value is less than minimum value"},
{0x08000000, "General error"},
{0x08000020, "Data cannot be transferred or stored to the application"},
{0x08000021, "Data cannot be transferred or stored to the application"
" because of local control"},
{0x08000022, "Data cannot be transferred or stored to the application"
" because of the present device state"},
{0x08000023, "Object dictionary dynamic generation fails or no object"
" dictionary is present"},
{}
};
/*****************************************************************************/
/**
Outputs an SDO abort message.
*/
void ec_canopen_abort_msg(uint32_t abort_code)
{
const ec_code_msg_t *abort_msg;
for (abort_msg = sdo_abort_messages; abort_msg->code; abort_msg++) {
if (abort_msg->code == abort_code) {
EC_ERR("SDO abort message 0x%08X: \"%s\".\n",
abort_msg->code, abort_msg->message);
return;
}
}
EC_ERR("Unknown SDO abort code 0x%08X.\n", abort_code);
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
/**
Reads an 8-bit SDO in expedited mode.
See ec_slave_sdo_read_exp()
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_read_exp8(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint8_t *target /**< memory for read value */
)
{
uint8_t data[4];
if (ec_slave_sdo_read_exp(slave, sdo_index, sdo_subindex, data)) return -1;
*target = EC_READ_U8(data);
return 0;
}
/*****************************************************************************/
/**
Reads a 16-bit SDO in expedited mode.
See ec_slave_sdo_read_exp()
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_read_exp16(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint16_t *target /**< memory for read value */
)
{
uint8_t data[4];
if (ec_slave_sdo_read_exp(slave, sdo_index, sdo_subindex, data)) return -1;
*target = EC_READ_U16(data);
return 0;
}
/*****************************************************************************/
/**
Reads a 32-bit SDO in expedited mode.
See ec_slave_sdo_read_exp()
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_read_exp32(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint32_t *target /**< memory for read value */
)
{
uint8_t data[4];
if (ec_slave_sdo_read_exp(slave, sdo_index, sdo_subindex, data)) return -1;
*target = EC_READ_U32(data);
return 0;
}
/*****************************************************************************/
/**
Writes an 8-bit SDO in expedited mode.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_write_exp8(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint8_t value /**< new value */
)
{
return ec_slave_sdo_write_exp(slave, sdo_index, sdo_subindex, &value, 1);
}
/*****************************************************************************/
/**
Writes a 16-bit SDO in expedited mode.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_write_exp16(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint16_t value /**< new value */
)
{
uint8_t data[2];
EC_WRITE_U16(data, value);
return ec_slave_sdo_write_exp(slave, sdo_index, sdo_subindex, data, 2);
}
/*****************************************************************************/
/**
Writes a 32-bit SDO in expedited mode.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_sdo_write_exp32(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint32_t value /**< new value */
)
{
uint8_t data[4];
EC_WRITE_U32(data, value);
return ec_slave_sdo_write_exp(slave, sdo_index, sdo_subindex, data, 4);
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_slave_sdo_read_exp8);
EXPORT_SYMBOL(ecrt_slave_sdo_read_exp16);
EXPORT_SYMBOL(ecrt_slave_sdo_read_exp32);
EXPORT_SYMBOL(ecrt_slave_sdo_write_exp8);
EXPORT_SYMBOL(ecrt_slave_sdo_write_exp16);
EXPORT_SYMBOL(ecrt_slave_sdo_write_exp32);
EXPORT_SYMBOL(ecrt_slave_sdo_read);
/** \endcond */
/*****************************************************************************/

View File

@ -52,7 +52,7 @@
return -1; \
datagram->index = 0; \
datagram->working_counter = 0; \
datagram->state = EC_CMD_INIT;
datagram->state = EC_DATAGRAM_INIT;
#define EC_FUNC_FOOTER \
datagram->data_size = data_size; \
@ -69,14 +69,14 @@
void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram */)
{
datagram->type = EC_CMD_NONE;
datagram->type = EC_DATAGRAM_NONE;
datagram->address.logical = 0x00000000;
datagram->data = NULL;
datagram->mem_size = 0;
datagram->data_size = 0;
datagram->index = 0x00;
datagram->working_counter = 0x00;
datagram->state = EC_CMD_INIT;
datagram->state = EC_DATAGRAM_INIT;
datagram->t_sent = 0;
}
@ -142,7 +142,7 @@ int ec_datagram_nprd(ec_datagram_t *datagram,
EC_WARN("Using node address 0x0000!\n");
EC_FUNC_HEADER;
datagram->type = EC_CMD_NPRD;
datagram->type = EC_DATAGRAM_NPRD;
datagram->address.physical.slave = node_address;
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -170,7 +170,7 @@ int ec_datagram_npwr(ec_datagram_t *datagram,
EC_WARN("Using node address 0x0000!\n");
EC_FUNC_HEADER;
datagram->type = EC_CMD_NPWR;
datagram->type = EC_DATAGRAM_NPWR;
datagram->address.physical.slave = node_address;
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -195,7 +195,7 @@ int ec_datagram_aprd(ec_datagram_t *datagram,
)
{
EC_FUNC_HEADER;
datagram->type = EC_CMD_APRD;
datagram->type = EC_DATAGRAM_APRD;
datagram->address.physical.slave = (int16_t) ring_position * (-1);
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -220,7 +220,7 @@ int ec_datagram_apwr(ec_datagram_t *datagram,
)
{
EC_FUNC_HEADER;
datagram->type = EC_CMD_APWR;
datagram->type = EC_DATAGRAM_APWR;
datagram->address.physical.slave = (int16_t) ring_position * (-1);
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -243,7 +243,7 @@ int ec_datagram_brd(ec_datagram_t *datagram,
)
{
EC_FUNC_HEADER;
datagram->type = EC_CMD_BRD;
datagram->type = EC_DATAGRAM_BRD;
datagram->address.physical.slave = 0x0000;
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -266,7 +266,7 @@ int ec_datagram_bwr(ec_datagram_t *datagram,
)
{
EC_FUNC_HEADER;
datagram->type = EC_CMD_BWR;
datagram->type = EC_DATAGRAM_BWR;
datagram->address.physical.slave = 0x0000;
datagram->address.physical.mem = offset;
EC_FUNC_FOOTER;
@ -289,7 +289,7 @@ int ec_datagram_lrw(ec_datagram_t *datagram,
)
{
EC_FUNC_HEADER;
datagram->type = EC_CMD_LRW;
datagram->type = EC_DATAGRAM_LRW;
datagram->address.logical = offset;
EC_FUNC_FOOTER;
}

View File

@ -55,14 +55,14 @@
typedef enum
{
EC_CMD_NONE = 0x00, /**< Dummy */
EC_CMD_APRD = 0x01, /**< Auto-increment physical read */
EC_CMD_APWR = 0x02, /**< Auto-increment physical write */
EC_CMD_NPRD = 0x04, /**< Node-addressed physical read */
EC_CMD_NPWR = 0x05, /**< Node-addressed physical write */
EC_CMD_BRD = 0x07, /**< Broadcast read */
EC_CMD_BWR = 0x08, /**< Broadcast write */
EC_CMD_LRW = 0x0C /**< Logical read/write */
EC_DATAGRAM_NONE = 0x00, /**< Dummy */
EC_DATAGRAM_APRD = 0x01, /**< Auto-increment physical read */
EC_DATAGRAM_APWR = 0x02, /**< Auto-increment physical write */
EC_DATAGRAM_NPRD = 0x04, /**< Node-addressed physical read */
EC_DATAGRAM_NPWR = 0x05, /**< Node-addressed physical write */
EC_DATAGRAM_BRD = 0x07, /**< Broadcast read */
EC_DATAGRAM_BWR = 0x08, /**< Broadcast write */
EC_DATAGRAM_LRW = 0x0C /**< Logical read/write */
}
ec_datagram_type_t;
@ -72,12 +72,12 @@ ec_datagram_type_t;
typedef enum
{
EC_CMD_INIT, /**< new datagram */
EC_CMD_QUEUED, /**< datagram queued by master */
EC_CMD_SENT, /**< datagram has been sent */
EC_CMD_RECEIVED, /**< datagram has been received */
EC_CMD_TIMEOUT, /**< datagram timed out */
EC_CMD_ERROR /**< error while sending/receiving */
EC_DATAGRAM_INIT, /**< new datagram */
EC_DATAGRAM_QUEUED, /**< datagram queued by master */
EC_DATAGRAM_SENT, /**< datagram has been sent */
EC_DATAGRAM_RECEIVED, /**< datagram has been received */
EC_DATAGRAM_TIMED_OUT, /**< datagram timed out */
EC_DATAGRAM_ERROR /**< error while sending/receiving */
}
ec_datagram_state_t;

View File

@ -46,7 +46,23 @@
/*****************************************************************************/
void ec_domain_clear_field_regs(ec_domain_t *);
/**
Data registration type.
*/
typedef struct
{
struct list_head list; /**< list item */
ec_slave_t *slave; /**< slave */
const ec_sii_sync_t *sync; /**< sync manager */
off_t sync_offset; /**< pdo offset */
void **data_ptr; /**< pointer to process data pointer(s) */
}
ec_data_reg_t;
/*****************************************************************************/
void ec_domain_clear_data_regs(ec_domain_t *);
ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
/*****************************************************************************/
@ -91,7 +107,7 @@ int ec_domain_init(ec_domain_t *domain, /**< EtherCAT domain */
domain->base_address = 0;
domain->response_count = 0xFFFFFFFF;
INIT_LIST_HEAD(&domain->field_regs);
INIT_LIST_HEAD(&domain->data_regs);
INIT_LIST_HEAD(&domain->datagrams);
// init kobject and add it to the hierarchy
@ -127,7 +143,7 @@ void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
kfree(datagram);
}
ec_domain_clear_field_regs(domain);
ec_domain_clear_data_regs(domain);
kfree(domain);
}
@ -139,34 +155,70 @@ void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
\return 0 in case of success, else < 0
*/
int ec_domain_reg_field(ec_domain_t *domain, /**< EtherCAT domain */
ec_slave_t *slave, /**< slave */
const ec_sync_t *sync, /**< sync manager */
uint32_t field_offset, /**< data field offset */
void **data_ptr /**< pointer to the process data
pointer */
)
int ec_domain_reg_pdo_entry(ec_domain_t *domain, /**< EtherCAT domain */
ec_slave_t *slave, /**< slave */
const ec_sii_pdo_t *pdo,
const ec_sii_pdo_entry_t *entry,
/**< PDO registration entry */
void **data_ptr /**< pointer to the process data
pointer */
)
{
ec_field_reg_t *field_reg;
ec_data_reg_t *data_reg;
const ec_sii_sync_t *sync;
const ec_sii_pdo_t *other_pdo;
const ec_sii_pdo_entry_t *other_entry;
unsigned int bit_offset, byte_offset, sync_found;
if (!(field_reg =
(ec_field_reg_t *) kmalloc(sizeof(ec_field_reg_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate field registration.\n");
// Find sync manager for PDO
sync_found = 0;
list_for_each_entry(sync, &slave->sii_syncs, list) {
if (sync->index == pdo->sync_index) {
sync_found = 1;
break;
}
}
if (!sync_found) {
EC_ERR("No sync manager for PDO 0x%04X:%i.",
pdo->index, entry->subindex);
return -1;
}
// Calculate offset for process data pointer
bit_offset = 0;
byte_offset = 0;
list_for_each_entry(other_pdo, &slave->sii_pdos, list) {
if (other_pdo->sync_index != sync->index) continue;
list_for_each_entry(other_entry, &pdo->entries, list) {
if (other_entry == entry) {
byte_offset = bit_offset / 8;
break;
}
bit_offset += other_entry->bit_length;
}
}
// Allocate memory for data registration object
if (!(data_reg =
(ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate data registration.\n");
return -1;
}
if (ec_slave_prepare_fmmu(slave, domain, sync)) {
EC_ERR("FMMU configuration failed.\n");
kfree(field_reg);
kfree(data_reg);
return -1;
}
field_reg->slave = slave;
field_reg->sync = sync;
field_reg->field_offset = field_offset;
field_reg->data_ptr = data_ptr;
data_reg->slave = slave;
data_reg->sync = sync;
data_reg->sync_offset = byte_offset;
data_reg->data_ptr = data_ptr;
list_add_tail(&field_reg->list, &domain->field_regs);
list_add_tail(&data_reg->list, &domain->data_regs);
return 0;
}
@ -176,13 +228,13 @@ int ec_domain_reg_field(ec_domain_t *domain, /**< EtherCAT domain */
Clears the list of the registered data fields.
*/
void ec_domain_clear_field_regs(ec_domain_t *domain /**< EtherCAT domain */)
void ec_domain_clear_data_regs(ec_domain_t *domain /**< EtherCAT domain */)
{
ec_field_reg_t *field_reg, *next;
ec_data_reg_t *data_reg, *next;
list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) {
list_del(&field_reg->list);
kfree(field_reg);
list_for_each_entry_safe(data_reg, next, &domain->data_regs, list) {
list_del(&data_reg->list);
kfree(data_reg);
}
}
@ -222,7 +274,7 @@ int ec_domain_add_datagram(ec_domain_t *domain, /**< EtherCAT domain */
Creates a domain.
Reserves domain memory, calculates the logical addresses of the
corresponding FMMUs and sets the process data pointer of the registered
data fields.
process data.
\return 0 in case of success, else < 0
*/
@ -230,22 +282,22 @@ int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
uint32_t base_address /**< logical base address */
)
{
ec_field_reg_t *field_reg;
ec_data_reg_t *data_reg;
ec_slave_t *slave;
ec_fmmu_t *fmmu;
unsigned int i, j, cmd_count;
uint32_t field_off, field_off_cmd;
uint32_t cmd_offset;
size_t cmd_data_size, sync_size;
unsigned int i, j, datagram_count;
uint32_t pdo_off, pdo_off_datagram;
uint32_t datagram_offset;
size_t datagram_data_size, sync_size;
ec_datagram_t *datagram;
domain->base_address = base_address;
// calculate size of process data and allocate memory
domain->data_size = 0;
cmd_offset = base_address;
cmd_data_size = 0;
cmd_count = 0;
datagram_offset = base_address;
datagram_data_size = 0;
datagram_count = 0;
list_for_each_entry(slave, &domain->master->slaves, list) {
for (j = 0; j < slave->fmmu_count; j++) {
fmmu = &slave->fmmus[j];
@ -253,47 +305,48 @@ int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
fmmu->logical_start_address = base_address + domain->data_size;
sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
domain->data_size += sync_size;
if (cmd_data_size + sync_size > EC_MAX_DATA_SIZE) {
if (ec_domain_add_datagram(domain, cmd_offset,
cmd_data_size)) return -1;
cmd_offset += cmd_data_size;
cmd_data_size = 0;
cmd_count++;
if (datagram_data_size + sync_size > EC_MAX_DATA_SIZE) {
if (ec_domain_add_datagram(domain, datagram_offset,
datagram_data_size)) return -1;
datagram_offset += datagram_data_size;
datagram_data_size = 0;
datagram_count++;
}
cmd_data_size += sync_size;
datagram_data_size += sync_size;
}
}
}
// allocate last datagram
if (cmd_data_size) {
if (ec_domain_add_datagram(domain, cmd_offset, cmd_data_size))
if (datagram_data_size) {
if (ec_domain_add_datagram(domain, datagram_offset,
datagram_data_size))
return -1;
cmd_count++;
datagram_count++;
}
if (!cmd_count) {
if (!datagram_count) {
EC_WARN("Domain %i contains no data!\n", domain->index);
ec_domain_clear_field_regs(domain);
ec_domain_clear_data_regs(domain);
return 0;
}
// set all process data pointers
list_for_each_entry(field_reg, &domain->field_regs, list) {
for (i = 0; i < field_reg->slave->fmmu_count; i++) {
fmmu = &field_reg->slave->fmmus[i];
if (fmmu->domain == domain && fmmu->sync == field_reg->sync) {
field_off = fmmu->logical_start_address +
field_reg->field_offset;
list_for_each_entry(data_reg, &domain->data_regs, list) {
for (i = 0; i < data_reg->slave->fmmu_count; i++) {
fmmu = &data_reg->slave->fmmus[i];
if (fmmu->domain == domain && fmmu->sync == data_reg->sync) {
pdo_off = fmmu->logical_start_address + data_reg->sync_offset;
// search datagram
list_for_each_entry(datagram, &domain->datagrams, list) {
field_off_cmd = field_off - datagram->address.logical;
if (field_off >= datagram->address.logical &&
field_off_cmd < datagram->mem_size) {
*field_reg->data_ptr = datagram->data + field_off_cmd;
pdo_off_datagram = pdo_off - datagram->address.logical;
if (pdo_off >= datagram->address.logical &&
pdo_off_datagram < datagram->mem_size) {
*data_reg->data_ptr = datagram->data +
pdo_off_datagram;
}
}
if (!field_reg->data_ptr) {
if (!data_reg->data_ptr) {
EC_ERR("Failed to assign data pointer!\n");
return -1;
}
@ -303,10 +356,10 @@ int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
}
EC_INFO("Domain %i - Allocated %i bytes in %i datagram%s\n",
domain->index, domain->data_size, cmd_count,
cmd_count == 1 ? "" : "s");
domain->index, domain->data_size, datagram_count,
datagram_count == 1 ? "" : "s");
ec_domain_clear_field_regs(domain);
ec_domain_clear_data_regs(domain);
return 0;
}
@ -314,20 +367,15 @@ int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
/*****************************************************************************/
/**
Sets the number of responding slaves and outputs it on demand.
This number isn't really the number of responding slaves, but the sum of
the working counters of all domain datagrams. Some slaves increase the
working counter by 2, some by 1.
Places all process data datagrams in the masters datagram queue.
*/
void ec_domain_response_count(ec_domain_t *domain, /**< EtherCAT domain */
unsigned int count /**< new WC sum */
)
void ec_domain_queue(ec_domain_t *domain /**< EtherCAT domain */)
{
if (count != domain->response_count) {
domain->response_count = count;
EC_INFO("Domain %i working counter change: %i\n", domain->index,
count);
ec_datagram_t *datagram;
list_for_each_entry(datagram, &domain->datagrams, list) {
ec_master_queue_datagram(domain->master, datagram);
}
}
@ -357,62 +405,45 @@ ssize_t ec_show_domain_attribute(struct kobject *kobj, /**< kobject */
*****************************************************************************/
/**
Registers a data field in a domain.
- If \a data_ptr is NULL, the slave is only checked against its type.
- If \a field_count is 0, it is assumed that one data field is to be
registered.
- If \a field_count is greater then 1, it is assumed that \a data_ptr
is an array of the respective size.
Registers a PDO in a domain.
- If \a data_ptr is NULL, the slave is only validated.
\return pointer to the slave on success, else NULL
\ingroup RealtimeInterface
*/
ec_slave_t *ecrt_domain_register_field(ec_domain_t *domain,
/**< EtherCAT domain */
const char *address,
/**< ASCII address of the slave,
see ecrt_master_get_slave() */
const char *vendor_name,
/**< vendor name */
const char *product_name,
/**< product name */
void **data_ptr,
/**< address of the process data
pointer */
const char *field_name,
/**< data field name */
unsigned int field_index,
/**< offset of data fields with
\a field_type */
unsigned int field_count
/**< number of data fields (with
the same type) to register */
)
ec_slave_t *ecrt_domain_register_pdo(ec_domain_t *domain,
/**< EtherCAT domain */
const char *address,
/**< ASCII address of the slave,
see ecrt_master_get_slave() */
uint32_t vendor_id,
/**< vendor ID */
uint32_t product_code,
/**< product code */
uint16_t pdo_index,
/**< PDO index */
uint8_t pdo_subindex,
/**< PDO subindex */
void **data_ptr
/**< address of the process data
pointer */
)
{
ec_slave_t *slave;
const ec_slave_type_t *type;
ec_master_t *master;
const ec_sync_t *sync;
const ec_field_t *field;
unsigned int field_counter, i, j, orig_field_index, orig_field_count;
uint32_t field_offset;
const ec_sii_pdo_t *pdo;
const ec_sii_pdo_entry_t *entry;
master = domain->master;
// translate address
if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
if (!(type = slave->type)) {
EC_ERR("Slave \"%s\" (position %i) has unknown type!\n", address,
slave->ring_position);
return NULL;
}
if (strcmp(vendor_name, type->vendor_name) ||
strcmp(product_name, type->product_name)) {
EC_ERR("Invalid slave type at position %i - Requested: \"%s %s\","
" found: \"%s %s\".\n", slave->ring_position, vendor_name,
product_name, type->vendor_name, type->product_name);
if (vendor_id != slave->sii_vendor_id ||
product_code != slave->sii_product_code) {
EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
" found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
product_code, slave->sii_vendor_id, slave->sii_product_code);
return NULL;
}
@ -421,32 +452,22 @@ ec_slave_t *ecrt_domain_register_field(ec_domain_t *domain,
slave->registered = 1;
}
if (!field_count) field_count = 1;
orig_field_index = field_index;
orig_field_count = field_count;
list_for_each_entry(pdo, &slave->sii_pdos, list) {
list_for_each_entry(entry, &pdo->entries, list) {
if (entry->index != pdo_index
|| entry->subindex != pdo_subindex) continue;
field_counter = 0;
for (i = 0; type->sync_managers[i]; i++) {
sync = type->sync_managers[i];
field_offset = 0;
for (j = 0; sync->fields[j]; j++) {
field = sync->fields[j];
if (!strcmp(field->name, field_name)) {
if (field_counter++ == field_index) {
if (data_ptr)
ec_domain_reg_field(domain, slave, sync, field_offset,
data_ptr++);
if (!(--field_count)) return slave;
field_index++;
}
if (data_ptr) {
ec_domain_reg_pdo_entry(domain, slave, pdo, entry, data_ptr);
}
field_offset += field->size;
return slave;
}
}
EC_ERR("Slave %i (\"%s %s\") registration mismatch: Field \"%s\","
" index %i, count %i.\n", slave->ring_position, vendor_name,
product_name, field_name, orig_field_index, orig_field_count);
EC_ERR("Slave %i does not provide PDO 0x%04X:%i.\n",
slave->ring_position, pdo_index, pdo_subindex);
slave->registered = 0;
return NULL;
}
@ -459,20 +480,21 @@ ec_slave_t *ecrt_domain_register_field(ec_domain_t *domain,
\ingroup RealtimeInterface
*/
int ecrt_domain_register_field_list(ec_domain_t *domain,
/**< EtherCAT domain */
const ec_field_init_t *fields
/**< array of data field registrations */
)
int ecrt_domain_register_pdo_list(ec_domain_t *domain,
/**< EtherCAT domain */
const ec_pdo_reg_t *pdos
/**< array of PDO registrations */
)
{
const ec_field_init_t *field;
const ec_pdo_reg_t *pdo;
for (field = fields; field->slave_address; field++)
if (!ecrt_domain_register_field(domain, field->slave_address,
field->vendor_name,
field->product_name, field->data_ptr,
field->field_name, field->field_index,
field->field_count))
for (pdo = pdos; pdo->slave_address; pdo++)
if (!ecrt_domain_register_pdo(domain, pdo->slave_address,
pdo->vendor_id,
pdo->product_code,
pdo->pdo_index,
pdo->pdo_subindex,
pdo->data_ptr))
return -1;
return 0;
@ -481,23 +503,7 @@ int ecrt_domain_register_field_list(ec_domain_t *domain,
/*****************************************************************************/
/**
Places all process data datagrams in the masters datagram queue.
\ingroup RealtimeInterface
*/
void ecrt_domain_queue(ec_domain_t *domain /**< EtherCAT domain */)
{
ec_datagram_t *datagram;
list_for_each_entry(datagram, &domain->datagrams, list) {
ec_master_queue_datagram(domain->master, datagram);
}
}
/*****************************************************************************/
/**
Processes received process data.
Processes received process data and requeues the domain datagram(s).
\ingroup RealtimeInterface
*/
@ -507,14 +513,19 @@ void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
ec_datagram_t *datagram;
working_counter_sum = 0;
list_for_each_entry(datagram, &domain->datagrams, list) {
if (datagram->state == EC_CMD_RECEIVED) {
if (datagram->state == EC_DATAGRAM_RECEIVED) {
working_counter_sum += datagram->working_counter;
}
}
ec_domain_response_count(domain, working_counter_sum);
if (working_counter_sum != domain->response_count) {
domain->response_count = working_counter_sum;
EC_INFO("Domain %i working counter change: %i\n", domain->index,
domain->response_count);
}
ec_domain_queue(domain);
}
/*****************************************************************************/
@ -525,12 +536,12 @@ void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
\ingroup RealtimeInterface
*/
int ecrt_domain_state(ec_domain_t *domain /**< EtherCAT domain */)
int ecrt_domain_state(const ec_domain_t *domain /**< EtherCAT domain */)
{
ec_datagram_t *datagram;
list_for_each_entry(datagram, &domain->datagrams, list) {
if (datagram->state != EC_CMD_RECEIVED) return -1;
if (datagram->state != EC_DATAGRAM_RECEIVED) return -1;
}
return 0;
@ -540,9 +551,8 @@ int ecrt_domain_state(ec_domain_t *domain /**< EtherCAT domain */)
/** \cond */
EXPORT_SYMBOL(ecrt_domain_register_field);
EXPORT_SYMBOL(ecrt_domain_register_field_list);
EXPORT_SYMBOL(ecrt_domain_queue);
EXPORT_SYMBOL(ecrt_domain_register_pdo);
EXPORT_SYMBOL(ecrt_domain_register_pdo_list);
EXPORT_SYMBOL(ecrt_domain_process);
EXPORT_SYMBOL(ecrt_domain_state);

View File

@ -45,24 +45,8 @@
#include <linux/kobject.h>
#include "globals.h"
#include "slave.h"
#include "datagram.h"
/*****************************************************************************/
/**
Data field registration type.
*/
typedef struct
{
struct list_head list; /**< list item */
ec_slave_t *slave; /**< slave */
const ec_sync_t *sync; /**< sync manager */
uint32_t field_offset; /**< data field offset */
void **data_ptr; /**< pointer to process data pointer(s) */
}
ec_field_reg_t;
#include "master.h"
/*****************************************************************************/
@ -82,7 +66,7 @@ struct ec_domain
struct list_head datagrams; /**< process data datagrams */
uint32_t base_address; /**< logical offset address of the process data */
unsigned int response_count; /**< number of responding slaves */
struct list_head field_regs; /**< data field registrations */
struct list_head data_regs; /**< PDO data registrations */
};
/*****************************************************************************/
@ -90,6 +74,7 @@ struct ec_domain
int ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
void ec_domain_clear(struct kobject *);
int ec_domain_alloc(ec_domain_t *, uint32_t);
void ec_domain_queue(ec_domain_t *);
/*****************************************************************************/

View File

@ -155,10 +155,8 @@ int ec_eoe_init(ec_eoe_t *eoe /**< EoE handler */)
void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */)
{
if (eoe->dev) { // TODO: dev never NULL?
unregister_netdev(eoe->dev);
free_netdev(eoe->dev);
}
unregister_netdev(eoe->dev);
free_netdev(eoe->dev);
// empty transmit queue
ec_eoe_flush(eoe);
@ -292,22 +290,6 @@ 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
*****************************************************************************/
@ -338,7 +320,7 @@ void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */)
{
if (eoe->datagram.state != EC_CMD_RECEIVED) {
if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
eoe->stats.rx_errors++;
eoe->state = ec_eoe_state_tx_start;
return;
@ -369,7 +351,7 @@ void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */)
uint8_t frame_number, fragment_offset, fragment_number;
off_t offset;
if (eoe->datagram.state != EC_CMD_RECEIVED) {
if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
eoe->stats.rx_errors++;
eoe->state = ec_eoe_state_tx_start;
return;
@ -570,7 +552,7 @@ void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */)
{
if (eoe->datagram.state != EC_CMD_RECEIVED) {
if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
eoe->stats.tx_errors++;
eoe->state = ec_eoe_state_rx_start;
return;

View File

@ -98,6 +98,5 @@ 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 *);
/*****************************************************************************/

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@
#ifndef __EC_STATES__
#define __EC_STATES__
#include "globals.h"
#include "../include/ecrt.h"
#include "datagram.h"
#include "slave.h"
@ -84,6 +85,14 @@ void ec_fsm_clear(ec_fsm_t *);
void ec_fsm_reset(ec_fsm_t *);
void ec_fsm_execute(ec_fsm_t *);
void ec_fsm_startup(ec_fsm_t *);
int ec_fsm_startup_running(ec_fsm_t *);
int ec_fsm_startup_success(ec_fsm_t *);
void ec_fsm_configuration(ec_fsm_t *);
int ec_fsm_configuration_running(ec_fsm_t *);
int ec_fsm_configuration_success(ec_fsm_t *);
/*****************************************************************************/
#endif

View File

@ -51,7 +51,7 @@
#define EC_MASTER_VERSION_MAIN 1
/** master sub version (after the dot) */
#define EC_MASTER_VERSION_SUB 0
#define EC_MASTER_VERSION_SUB 1
/** master extra version (just a string) */
#define EC_MASTER_VERSION_EXTRA "trunk"
@ -65,6 +65,9 @@
/** clock frequency for the EoE state machines */
#define EC_EOE_FREQUENCY 1000
/** datagram timeout in microseconds */
#define EC_IO_TIMEOUT 500
/******************************************************************************
* EtherCAT protocol
*****************************************************************************/
@ -170,9 +173,9 @@
/*****************************************************************************/
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);
void ec_print_data(const uint8_t *, size_t);
void ec_print_data_diff(const uint8_t *, const uint8_t *, size_t);
size_t ec_state_string(uint8_t, char *);
/*****************************************************************************/
@ -192,4 +195,3 @@ ec_code_msg_t;
/*****************************************************************************/
#endif

View File

@ -165,83 +165,3 @@ uint8_t *ec_slave_mbox_fetch(const ec_slave_t *slave, /**< slave */
}
/*****************************************************************************/
/**
Sends a mailbox datagram and waits for its reception.
\return pointer to the received data
*/
uint8_t *ec_slave_mbox_simple_io(const ec_slave_t *slave, /**< slave */
ec_datagram_t *datagram, /**< datagram */
size_t *size /**< size of the received data */
)
{
uint8_t type;
type = EC_READ_U8(datagram->data + 5);
if (unlikely(ec_master_simple_io(slave->master, datagram))) {
EC_ERR("Mailbox checking failed on slave %i!\n",
slave->ring_position);
return NULL;
}
return ec_slave_mbox_simple_receive(slave, datagram, type, size);
}
/*****************************************************************************/
/**
Waits for the reception of a mailbox datagram.
\return pointer to the received data
*/
uint8_t *ec_slave_mbox_simple_receive(const ec_slave_t *slave, /**< slave */
ec_datagram_t *datagram, /**< datagram */
uint8_t type, /**< expected protocol */
size_t *size /**< received data size */
)
{
cycles_t start, end, timeout;
start = get_cycles();
timeout = (cycles_t) 100 * cpu_khz; // 100ms
while (1)
{
if (ec_slave_mbox_prepare_check(slave, datagram)) return NULL;
if (unlikely(ec_master_simple_io(slave->master, datagram))) {
EC_ERR("Mailbox checking failed on slave %i!\n",
slave->ring_position);
return NULL;
}
end = get_cycles();
if (ec_slave_mbox_check(datagram))
break; // proceed with receiving data
if ((end - start) >= timeout) {
EC_ERR("Mailbox check - Slave %i timed out.\n",
slave->ring_position);
return NULL;
}
udelay(100);
}
if (ec_slave_mbox_prepare_fetch(slave, datagram)) return NULL;
if (unlikely(ec_master_simple_io(slave->master, datagram))) {
EC_ERR("Mailbox receiving failed on slave %i!\n",
slave->ring_position);
return NULL;
}
if (unlikely(slave->master->debug_level) > 1)
EC_DBG("Mailbox receive took %ius.\n",
((unsigned int) (end - start) * 1000 / cpu_khz));
return ec_slave_mbox_fetch(slave, datagram, type, size);
}
/*****************************************************************************/

View File

@ -48,13 +48,13 @@
#include "globals.h"
#include "master.h"
#include "slave.h"
#include "types.h"
#include "device.h"
#include "datagram.h"
#include "ethernet.h"
/*****************************************************************************/
void ec_master_sync_io(ec_master_t *);
void ec_master_idle_run(void *);
void ec_master_eoe_run(unsigned long);
ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *);
@ -115,7 +115,6 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
INIT_LIST_HEAD(&master->datagram_queue);
INIT_LIST_HEAD(&master->domains);
INIT_LIST_HEAD(&master->eoe_handlers);
ec_datagram_init(&master->simple_datagram);
INIT_WORK(&master->idle_work, ec_master_idle_run, (void *) master);
init_timer(&master->eoe_timer);
master->eoe_timer.function = ec_master_eoe_run;
@ -186,7 +185,6 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
ec_master_reset(master);
ec_fsm_clear(&master->fsm);
ec_datagram_clear(&master->simple_datagram);
destroy_workqueue(master->workqueue);
// clear EoE objects
@ -225,7 +223,7 @@ void ec_master_reset(ec_master_t *master /**< EtherCAT master */)
list_for_each_entry_safe(datagram, next_c,
&master->datagram_queue, queue) {
list_del_init(&datagram->queue);
datagram->state = EC_CMD_ERROR;
datagram->state = EC_DATAGRAM_ERROR;
}
// clear domains
@ -237,7 +235,6 @@ void ec_master_reset(ec_master_t *master /**< EtherCAT master */)
master->datagram_index = 0;
master->debug_level = 0;
master->timeout = 500; // 500us
master->stats.timeouts = 0;
master->stats.delayed = 0;
@ -289,7 +286,7 @@ void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
// check, if the datagram is already queued
list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
if (queued_datagram == datagram) {
datagram->state = EC_CMD_QUEUED;
datagram->state = EC_DATAGRAM_QUEUED;
if (unlikely(master->debug_level))
EC_WARN("datagram already queued.\n");
return;
@ -297,7 +294,7 @@ void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
}
list_add_tail(&datagram->queue, &master->datagram_queue);
datagram->state = EC_CMD_QUEUED;
datagram->state = EC_DATAGRAM_QUEUED;
}
/*****************************************************************************/
@ -331,7 +328,7 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
// fill current frame with datagrams
list_for_each_entry(datagram, &master->datagram_queue, queue) {
if (datagram->state != EC_CMD_QUEUED) continue;
if (datagram->state != EC_DATAGRAM_QUEUED) continue;
// does the current datagram fit in the frame?
datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size
@ -341,7 +338,7 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
break;
}
datagram->state = EC_CMD_SENT;
datagram->state = EC_DATAGRAM_SENT;
datagram->t_sent = t_start;
datagram->index = master->datagram_index++;
@ -456,7 +453,7 @@ void ec_master_receive(ec_master_t *master, /**< EtherCAT master */
// search for matching datagram in the queue
matched = 0;
list_for_each_entry(datagram, &master->datagram_queue, queue) {
if (datagram->state == EC_CMD_SENT
if (datagram->state == EC_DATAGRAM_SENT
&& datagram->type == datagram_type
&& datagram->index == datagram_index
&& datagram->data_size == data_size) {
@ -482,57 +479,13 @@ void ec_master_receive(ec_master_t *master, /**< EtherCAT master */
cur_data += EC_DATAGRAM_FOOTER_SIZE;
// dequeue the received datagram
datagram->state = EC_CMD_RECEIVED;
datagram->state = EC_DATAGRAM_RECEIVED;
list_del_init(&datagram->queue);
}
}
/*****************************************************************************/
/**
Sends a single datagram and waits for its reception.
If the slave doesn't respond, the datagram is sent again.
\return 0 in case of success, else < 0
*/
int ec_master_simple_io(ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
{
unsigned int response_tries_left;
response_tries_left = 10000;
while (1)
{
ec_master_queue_datagram(master, datagram);
ecrt_master_sync_io(master);
if (datagram->state == EC_CMD_RECEIVED) {
if (likely(datagram->working_counter))
return 0;
}
else if (datagram->state == EC_CMD_TIMEOUT) {
EC_ERR("Simple-IO TIMEOUT!\n");
return -1;
}
else if (datagram->state == EC_CMD_ERROR) {
EC_ERR("Simple-IO datagram error!\n");
return -1;
}
// no direct response, wait a little bit...
udelay(100);
if (unlikely(--response_tries_left)) {
EC_ERR("No response in simple-IO!\n");
return -1;
}
}
}
/*****************************************************************************/
/**
Scans the EtherCAT bus for slaves.
Creates a list of slave structures for further processing.
@ -541,102 +494,22 @@ int ec_master_simple_io(ec_master_t *master, /**< EtherCAT master */
int ec_master_bus_scan(ec_master_t *master /**< EtherCAT master */)
{
ec_slave_t *slave;
ec_slave_ident_t *ident;
ec_datagram_t *datagram;
unsigned int i;
uint16_t coupler_index, coupler_subindex;
uint16_t reverse_coupler_index, current_coupler_index;
ec_fsm_t *fsm = &master->fsm;
if (!list_empty(&master->slaves)) {
EC_ERR("Slave scan already done!\n");
ec_fsm_startup(fsm); // init startup state machine
do {
ec_fsm_execute(fsm);
ec_master_sync_io(master);
}
while (ec_fsm_startup_running(fsm));
if (!ec_fsm_startup_success(fsm)) {
ec_master_clear_slaves(master);
return -1;
}
datagram = &master->simple_datagram;
// determine number of slaves on bus
if (ec_datagram_brd(datagram, 0x0000, 4)) return -1;
if (unlikely(ec_master_simple_io(master, datagram))) return -1;
master->slave_count = datagram->working_counter;
EC_INFO("Found %i slaves on bus.\n", master->slave_count);
if (!master->slave_count) return 0;
// init slaves
for (i = 0; i < master->slave_count; i++) {
if (!(slave =
(ec_slave_t *) kmalloc(sizeof(ec_slave_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate slave %i!\n", i);
goto out_free;
}
if (ec_slave_init(slave, master, i, i + 1)) goto out_free;
if (kobject_add(&slave->kobj)) {
EC_ERR("Failed to add kobject.\n");
kobject_put(&slave->kobj); // free
goto out_free;
}
list_add_tail(&slave->list, &master->slaves);
}
coupler_index = 0;
reverse_coupler_index = 0xFFFF;
current_coupler_index = 0x3FFF;
coupler_subindex = 0;
// for every slave on the bus
list_for_each_entry(slave, &master->slaves, list) {
// write station address
if (ec_datagram_apwr(datagram, slave->ring_position, 0x0010,
sizeof(uint16_t))) goto out_free;
EC_WRITE_U16(datagram->data, slave->station_address);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Writing station address failed on slave %i!\n",
slave->ring_position);
goto out_free;
}
// fetch all slave information
if (ec_slave_fetch(slave)) goto out_free;
// search for identification in "database"
ident = slave_idents;
while (ident->type) {
if (unlikely(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("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 (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;
}
slave->coupler_index = current_coupler_index;
slave->coupler_subindex = coupler_subindex;
coupler_subindex++;
}
return 0;
out_free:
ec_master_clear_slaves(master);
return -1;
}
/*****************************************************************************/
@ -682,7 +555,7 @@ void ec_master_idle_start(ec_master_t *master /**< EtherCAT master */)
{
if (master->mode == EC_MASTER_MODE_IDLE) return;
if (master->mode == EC_MASTER_MODE_RUNNING) {
if (master->mode == EC_MASTER_MODE_OPERATION) {
EC_ERR("ec_master_idle_start: Master already running!\n");
return;
}
@ -731,12 +604,12 @@ void ec_master_idle_run(void *data /**< master pointer */)
// aquire master lock
spin_lock_bh(&master->internal_lock);
ecrt_master_async_receive(master);
ecrt_master_receive(master);
// execute master state machine
ec_fsm_execute(&master->fsm);
ecrt_master_async_send(master);
ecrt_master_send(master);
// release master lock
spin_unlock_bh(&master->internal_lock);
@ -748,11 +621,11 @@ void ec_master_idle_run(void *data /**< master pointer */)
/*****************************************************************************/
/**
Initializes a sync manager configuration page.
Initializes a sync manager configuration page with EEPROM data.
The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
*/
void ec_sync_config(const ec_sync_t *sync, /**< sync manager */
void ec_sync_config(const ec_sii_sync_t *sync, /**< sync manager */
const ec_slave_t *slave, /**< EtherCAT slave */
uint8_t *data /**> configuration memory */
)
@ -761,29 +634,6 @@ void ec_sync_config(const ec_sync_t *sync, /**< sync manager */
sync_size = ec_slave_calc_sync_size(slave, sync);
EC_WRITE_U16(data, sync->physical_start_address);
EC_WRITE_U16(data + 2, sync_size);
EC_WRITE_U8 (data + 4, sync->control_byte);
EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
EC_WRITE_U16(data + 6, 0x0001); // enable
}
/*****************************************************************************/
/**
Initializes a sync manager configuration page with EEPROM data.
The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
*/
void ec_eeprom_sync_config(const ec_eeprom_sync_t *sync, /**< sync manager */
const ec_slave_t *slave, /**< EtherCAT slave */
uint8_t *data /**> configuration memory */
)
{
size_t sync_size;
sync_size = ec_slave_calc_eeprom_sync_size(slave, sync);
if (slave->master->debug_level) {
EC_INFO("Slave %i, sync manager %i:\n", slave->ring_position,
sync->index);
@ -821,7 +671,8 @@ void ec_fmmu_config(const ec_fmmu_t *fmmu, /**< FMMU */
EC_WRITE_U8 (data + 7, 0x07); // logical end bit
EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address);
EC_WRITE_U8 (data + 10, 0x00); // physical start bit
EC_WRITE_U8 (data + 11, (fmmu->sync->control_byte & 0x04) ? 0x02 : 0x01);
EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
? 0x02 : 0x01));
EC_WRITE_U16(data + 12, 0x0001); // enable
EC_WRITE_U16(data + 14, 0x0000); // reserved
}
@ -849,8 +700,8 @@ ssize_t ec_show_master_attribute(struct kobject *kobj, /**< kobject */
return sprintf(buffer, "ORPHANED\n");
case EC_MASTER_MODE_IDLE:
return sprintf(buffer, "IDLE\n");
case EC_MASTER_MODE_RUNNING:
return sprintf(buffer, "RUNNING\n");
case EC_MASTER_MODE_OPERATION:
return sprintf(buffer, "OPERATION\n");
}
}
else if (attr == &attr_debug_level) {
@ -922,7 +773,7 @@ ssize_t ec_store_master_attribute(struct kobject *kobj, /**< slave's kobject */
/*****************************************************************************/
/**
Starts Ethernet-over-EtherCAT processing for all EoE-capable slaves.
Starts/Stops Ethernet-over-EtherCAT processing on demand.
*/
void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
@ -933,12 +784,17 @@ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
if (master->eoe_running) return;
// if the locking callbacks are not set in operation mode,
// the EoE timer my not be started.
if (master->mode == EC_MASTER_MODE_OPERATION
&& (!master->request_cb || !master->release_cb)) 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
coupled = 0;
list_for_each_entry(slave, &master->slaves, list) {
if (!(slave->sii_mailbox_protocols & EC_MBOX_EOE)) continue;
@ -950,12 +806,8 @@ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
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;
}
if (eoe->opened) slave->requested_state = EC_SLAVE_STATE_OP;
else slave->requested_state = EC_SLAVE_STATE_INIT;
slave->error_flag = 0;
break;
}
@ -1026,7 +878,7 @@ void ec_master_eoe_run(unsigned long data /**< master pointer */)
if (!active) goto queue_timer;
// aquire master lock...
if (master->mode == EC_MASTER_MODE_RUNNING) {
if (master->mode == EC_MASTER_MODE_OPERATION) {
// request_cb must return 0, if the lock has been aquired!
if (master->request_cb(master->cb_data))
goto queue_timer;
@ -1038,14 +890,14 @@ void ec_master_eoe_run(unsigned long data /**< master pointer */)
goto queue_timer;
// actual EoE stuff
ecrt_master_async_receive(master);
ecrt_master_receive(master);
list_for_each_entry(eoe, &master->eoe_handlers, list) {
ec_eoe_run(eoe);
}
ecrt_master_async_send(master);
ecrt_master_send(master);
// release lock...
if (master->mode == EC_MASTER_MODE_RUNNING) {
if (master->mode == EC_MASTER_MODE_OPERATION) {
master->release_cb(master->cb_data);
}
else if (master->mode == EC_MASTER_MODE_IDLE) {
@ -1057,6 +909,38 @@ void ec_master_eoe_run(unsigned long data /**< master pointer */)
add_timer(&master->eoe_timer);
}
/*****************************************************************************/
/**
Calculates Advanced Position Adresses.
*/
void ec_master_calc_addressing(ec_master_t *master /**< EtherCAT master */)
{
uint16_t coupler_index, coupler_subindex;
uint16_t reverse_coupler_index, current_coupler_index;
ec_slave_t *slave;
coupler_index = 0;
reverse_coupler_index = 0xFFFF;
current_coupler_index = 0x0000;
coupler_subindex = 0;
list_for_each_entry(slave, &master->slaves, list) {
if (ec_slave_is_coupler(slave)) {
if (slave->sii_alias)
current_coupler_index = reverse_coupler_index--;
else
current_coupler_index = coupler_index++;
coupler_subindex = 0;
}
slave->coupler_index = current_coupler_index;
slave->coupler_subindex = coupler_subindex;
coupler_subindex++;
}
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
@ -1115,17 +999,10 @@ ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< master */)
int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
{
unsigned int j;
ec_slave_t *slave;
ec_datagram_t *datagram;
const ec_sync_t *sync;
const ec_slave_type_t *type;
const ec_fmmu_t *fmmu;
uint32_t domain_offset;
ec_domain_t *domain;
ec_eeprom_sync_t *eeprom_sync, mbox_sync;
datagram = &master->simple_datagram;
ec_fsm_t *fsm = &master->fsm;
ec_slave_t *slave;
// allocate all domains
domain_offset = 0;
@ -1137,150 +1014,30 @@ int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
domain_offset += domain->data_size;
}
// configure and activate slaves
// determine initial states.
list_for_each_entry(slave, &master->slaves, list) {
// change state to INIT
if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_INIT)))
return -1;
// check for slave registration
type = slave->type;
if (!type)
EC_WARN("Slave %i has unknown type!\n", slave->ring_position);
// check and reset CRC fault counters
ec_slave_check_crc(slave);
// reset FMMUs
if (slave->base_fmmu_count) {
if (ec_datagram_npwr(datagram, slave->station_address, 0x0600,
EC_FMMU_SIZE * slave->base_fmmu_count))
return -1;
memset(datagram->data, 0x00,
EC_FMMU_SIZE * slave->base_fmmu_count);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Resetting FMMUs failed on slave %i!\n",
slave->ring_position);
return -1;
}
if (ec_slave_is_coupler(slave) || slave->registered) {
slave->requested_state = EC_SLAVE_STATE_OP;
}
// reset sync managers
if (slave->base_sync_count) {
if (ec_datagram_npwr(datagram, slave->station_address, 0x0800,
EC_SYNC_SIZE * slave->base_sync_count))
return -1;
memset(datagram->data, 0x00,
EC_SYNC_SIZE * slave->base_sync_count);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Resetting sync managers failed on slave %i!\n",
slave->ring_position);
return -1;
}
else {
slave->requested_state = EC_SLAVE_STATE_PREOP;
}
// configure sync managers
// does the slave provide sync manager information?
if (!list_empty(&slave->eeprom_syncs)) {
list_for_each_entry(eeprom_sync, &slave->eeprom_syncs, list) {
if (ec_datagram_npwr(datagram, slave->station_address,
0x800 + eeprom_sync->index *
EC_SYNC_SIZE,
EC_SYNC_SIZE)) return -1;
ec_eeprom_sync_config(eeprom_sync, slave, datagram->data);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Setting sync manager %i failed on slave %i!\n",
eeprom_sync->index, slave->ring_position);
return -1;
}
}
}
else if (type) { // known slave type, take type's SM information
for (j = 0; type->sync_managers[j] && j < EC_MAX_SYNC; j++) {
sync = type->sync_managers[j];
if (ec_datagram_npwr(datagram, slave->station_address,
0x0800 + j * EC_SYNC_SIZE, EC_SYNC_SIZE))
return -1;
ec_sync_config(sync, slave, datagram->data);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Setting sync manager %i failed on slave %i!\n",
j, slave->ring_position);
return -1;
}
}
}
// no sync manager information; guess mailbox settings
else if (slave->sii_mailbox_protocols) {
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;
if (ec_datagram_npwr(datagram, slave->station_address,
0x800,EC_SYNC_SIZE)) return -1;
ec_eeprom_sync_config(&mbox_sync, slave, datagram->data);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Setting sync manager 0 failed on slave %i!\n",
slave->ring_position);
return -1;
}
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;
if (ec_datagram_npwr(datagram, slave->station_address,
0x808, EC_SYNC_SIZE)) return -1;
ec_eeprom_sync_config(&mbox_sync, slave, datagram->data);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Setting sync manager 1 failed on slave %i!\n",
slave->ring_position);
return -1;
}
}
// change state to PREOP
if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_PREOP)))
return -1;
// stop activation here for slaves without type
if (!type) continue;
// slaves that are not registered are only brought into PREOP
// state -> nice blinking and mailbox communication possible
if (!slave->registered && !slave->type->special) {
EC_WARN("Slave %i was not registered!\n", slave->ring_position);
continue;
}
// configure FMMUs
for (j = 0; j < slave->fmmu_count; j++) {
fmmu = &slave->fmmus[j];
if (ec_datagram_npwr(datagram, slave->station_address,
0x0600 + j * EC_FMMU_SIZE, EC_FMMU_SIZE))
return -1;
ec_fmmu_config(fmmu, slave, datagram->data);
if (unlikely(ec_master_simple_io(master, datagram))) {
EC_ERR("Setting FMMU %i failed on slave %i!\n",
j, slave->ring_position);
return -1;
}
}
// change state to SAVEOP
if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_SAVEOP)))
return -1;
// change state to OP
if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_OP)))
return -1;
}
ec_fsm_configuration(fsm); // init configuration state machine
do {
ec_fsm_execute(fsm);
ec_master_sync_io(master);
}
while (ec_fsm_configuration_running(fsm));
if (!ec_fsm_configuration_success(fsm)) {
return -1;
}
ec_fsm_reset(&master->fsm); // prepare for operation state machine
return 0;
}
@ -1293,59 +1050,39 @@ int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_deactivate(ec_master_t *master /**< EtherCAT master */)
{
ec_fsm_t *fsm = &master->fsm;
ec_slave_t *slave;
list_for_each_entry(slave, &master->slaves, list) {
ec_slave_check_crc(slave);
ec_slave_state_change(slave, EC_SLAVE_STATE_INIT);
}
}
/*****************************************************************************/
/**
Fetches the SDO dictionaries of all slaves.
Slaves that do not support the CoE protocol are left out.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_master_fetch_sdo_lists(ec_master_t *master /**< EtherCAT master */)
{
ec_slave_t *slave;
list_for_each_entry(slave, &master->slaves, list) {
if (slave->sii_mailbox_protocols & EC_MBOX_COE) {
if (unlikely(ec_slave_fetch_sdo_list(slave))) {
EC_ERR("Failed to fetch SDO list on slave %i!\n",
slave->ring_position);
return -1;
}
}
slave->requested_state = EC_SLAVE_STATE_INIT;
}
return 0;
ec_fsm_configuration(fsm); // init configuration state machine
do {
ec_fsm_execute(fsm);
ec_master_sync_io(master);
}
while (ec_fsm_configuration_running(fsm));
}
/*****************************************************************************/
/**
Sends queued datagrams and waits for their reception.
\ingroup RealtimeInterface
*/
void ecrt_master_sync_io(ec_master_t *master /**< EtherCAT master */)
void ec_master_sync_io(ec_master_t *master /**< EtherCAT master */)
{
ec_datagram_t *datagram, *n;
unsigned int datagrams_sent;
cycles_t t_start, t_end, t_timeout;
// send datagrams
ecrt_master_async_send(master);
ecrt_master_send(master);
t_start = get_cycles(); // measure io time
t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000);
t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
while (1) { // active waiting
ec_device_call_isr(master->device);
@ -1355,9 +1092,9 @@ void ecrt_master_sync_io(ec_master_t *master /**< EtherCAT master */)
datagrams_sent = 0;
list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) {
if (datagram->state == EC_CMD_RECEIVED)
if (datagram->state == EC_DATAGRAM_RECEIVED)
list_del_init(&datagram->queue);
else if (datagram->state == EC_CMD_SENT)
else if (datagram->state == EC_DATAGRAM_SENT)
datagrams_sent++;
}
@ -1367,13 +1104,13 @@ void ecrt_master_sync_io(ec_master_t *master /**< EtherCAT master */)
// timeout; dequeue all datagrams
list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) {
switch (datagram->state) {
case EC_CMD_SENT:
case EC_CMD_QUEUED:
datagram->state = EC_CMD_TIMEOUT;
case EC_DATAGRAM_SENT:
case EC_DATAGRAM_QUEUED:
datagram->state = EC_DATAGRAM_TIMED_OUT;
master->stats.timeouts++;
ec_master_output_stats(master);
break;
case EC_CMD_RECEIVED:
case EC_DATAGRAM_RECEIVED:
master->stats.delayed++;
ec_master_output_stats(master);
break;
@ -1391,14 +1128,14 @@ void ecrt_master_sync_io(ec_master_t *master /**< EtherCAT master */)
\ingroup RealtimeInterface
*/
void ecrt_master_async_send(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_send(ec_master_t *master /**< EtherCAT master */)
{
ec_datagram_t *datagram, *n;
if (unlikely(!master->device->link_state)) {
// link is down, no datagram can be sent
list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) {
datagram->state = EC_CMD_ERROR;
datagram->state = EC_DATAGRAM_ERROR;
list_del_init(&datagram->queue);
}
@ -1418,7 +1155,7 @@ void ecrt_master_async_send(ec_master_t *master /**< EtherCAT master */)
\ingroup RealtimeInterface
*/
void ecrt_master_async_receive(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */)
{
ec_datagram_t *datagram, *next;
cycles_t t_received, t_timeout;
@ -1426,21 +1163,21 @@ void ecrt_master_async_receive(ec_master_t *master /**< EtherCAT master */)
ec_device_call_isr(master->device);
t_received = get_cycles();
t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000);
t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
// dequeue all received datagrams
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue)
if (datagram->state == EC_CMD_RECEIVED)
if (datagram->state == EC_DATAGRAM_RECEIVED)
list_del_init(&datagram->queue);
// dequeue all datagrams that timed out
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
switch (datagram->state) {
case EC_CMD_SENT:
case EC_CMD_QUEUED:
case EC_DATAGRAM_SENT:
case EC_DATAGRAM_QUEUED:
if (t_received - datagram->t_sent > t_timeout) {
list_del_init(&datagram->queue);
datagram->state = EC_CMD_TIMEOUT;
datagram->state = EC_DATAGRAM_TIMED_OUT;
master->stats.timeouts++;
ec_master_output_stats(master);
}
@ -1456,23 +1193,23 @@ void ecrt_master_async_receive(ec_master_t *master /**< EtherCAT master */)
/**
Prepares synchronous IO.
Queues all domain datagrams and sends them. Then waits a certain time, so
that ecrt_master_sasync_receive() can be called securely.
that ecrt_master_receive() can be called securely.
\ingroup RealtimeInterface
*/
void ecrt_master_prepare_async_io(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_prepare(ec_master_t *master /**< EtherCAT master */)
{
ec_domain_t *domain;
cycles_t t_start, t_end, t_timeout;
// queue datagrams of all domains
list_for_each_entry(domain, &master->domains, list)
ecrt_domain_queue(domain);
ec_domain_queue(domain);
ecrt_master_async_send(master);
ecrt_master_send(master);
t_start = get_cycles(); // take sending time
t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000);
t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
// active waiting
while (1) {
@ -1576,8 +1313,7 @@ ec_slave_t *ecrt_master_get_slave(const ec_master_t *master, /**< Master */
}
if (alias_requested) {
if (!alias_slave->type ||
alias_slave->type->special != EC_TYPE_BUS_COUPLER) {
if (!ec_slave_is_coupler(alias_slave)) {
EC_ERR("Slave address \"%s\": Alias slave must be bus coupler"
" in colon mode.\n", address);
return NULL;
@ -1628,92 +1364,16 @@ void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */
/*****************************************************************************/
/**
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
- 2: additional frame data output
\ingroup RealtimeInterface
*/
void ecrt_master_debug(ec_master_t *master, /**< EtherCAT master */
int level /**< debug level */
)
{
if (level != master->debug_level) {
master->debug_level = level;
EC_INFO("Master debug level set to %i.\n", level);
}
}
/*****************************************************************************/
/**
Outputs all master information.
Verbosity:
- 0: Only slave types and positions
- 1: with EEPROM contents
- >1: with SDO dictionaries
\ingroup RealtimeInterface
*/
void ecrt_master_print(const ec_master_t *master, /**< EtherCAT master */
unsigned int verbosity /**< verbosity level */
)
{
ec_slave_t *slave;
ec_eoe_t *eoe;
EC_INFO("*** Begin master information ***\n");
if (master->slave_count) {
EC_INFO("Slave list:\n");
list_for_each_entry(slave, &master->slaves, list)
ec_slave_print(slave, verbosity);
}
if (!list_empty(&master->eoe_handlers)) {
EC_INFO("Ethernet-over-EtherCAT (EoE) objects:\n");
list_for_each_entry(eoe, &master->eoe_handlers, list) {
ec_eoe_print(eoe);
}
}
EC_INFO("*** End master information ***\n");
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_master_create_domain);
EXPORT_SYMBOL(ecrt_master_activate);
EXPORT_SYMBOL(ecrt_master_deactivate);
EXPORT_SYMBOL(ecrt_master_fetch_sdo_lists);
EXPORT_SYMBOL(ecrt_master_prepare_async_io);
EXPORT_SYMBOL(ecrt_master_sync_io);
EXPORT_SYMBOL(ecrt_master_async_send);
EXPORT_SYMBOL(ecrt_master_async_receive);
EXPORT_SYMBOL(ecrt_master_prepare);
EXPORT_SYMBOL(ecrt_master_send);
EXPORT_SYMBOL(ecrt_master_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);
/** \endcond */

View File

@ -59,7 +59,7 @@ typedef enum
{
EC_MASTER_MODE_ORPHANED,
EC_MASTER_MODE_IDLE,
EC_MASTER_MODE_RUNNING
EC_MASTER_MODE_OPERATION
}
ec_master_mode_t;
@ -104,10 +104,6 @@ struct ec_master
struct list_head domains; /**< list of domains */
ec_datagram_t simple_datagram; /**< datagram structure for
initialization */
unsigned int timeout; /**< timeout in synchronous IO */
int debug_level; /**< master debug level */
ec_stats_t stats; /**< cyclic statistics */
@ -145,18 +141,18 @@ 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_datagram(ec_master_t *, ec_datagram_t *);
int ec_master_simple_io(ec_master_t *, ec_datagram_t *);
// slave management
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 *, const ec_slave_t *, uint8_t *);
void ec_eeprom_sync_config(const ec_eeprom_sync_t *, const ec_slave_t *,
uint8_t *);
void ec_fmmu_config(const ec_fmmu_t *, const ec_slave_t *, uint8_t *);
void ec_master_output_stats(ec_master_t *);
void ec_master_clear_slaves(ec_master_t *);
// other methods
void ec_sync_config(const ec_sii_sync_t *, const ec_slave_t *, uint8_t *);
void ec_fmmu_config(const ec_fmmu_t *, const ec_slave_t *, uint8_t *);
void ec_master_calc_addressing(ec_master_t *);
/*****************************************************************************/

View File

@ -237,33 +237,38 @@ void ec_print_data_diff(const uint8_t *d1, /**< first data */
Prints slave states in clear text.
*/
void ec_print_states(const uint8_t states /**< slave states */)
size_t ec_state_string(uint8_t states, /**< slave states */
char *buffer /**< target buffer (min. 25 bytes) */
)
{
off_t off = 0;
unsigned int first = 1;
if (!states) {
printk("(unknown)");
return;
off += sprintf(buffer + off, "(unknown)");
return off;
}
if (states & EC_SLAVE_STATE_INIT) {
printk("INIT");
off += sprintf(buffer + off, "INIT");
first = 0;
}
if (states & EC_SLAVE_STATE_PREOP) {
if (!first) printk(", ");
printk("PREOP");
if (!first) off += sprintf(buffer + off, ", ");
off += sprintf(buffer + off, "PREOP");
first = 0;
}
if (states & EC_SLAVE_STATE_SAVEOP) {
if (!first) printk(", ");
printk("SAVEOP");
if (!first) off += sprintf(buffer + off, ", ");
off += sprintf(buffer + off, "SAVEOP");
first = 0;
}
if (states & EC_SLAVE_STATE_OP) {
if (!first) printk(", ");
printk("OP");
if (!first) off += sprintf(buffer + off, ", ");
off += sprintf(buffer + off, "OP");
}
return off;
}
/******************************************************************************
@ -426,7 +431,7 @@ ec_master_t *ecrt_request_master(unsigned int master_index
ec_master_idle_stop(master);
ec_master_reset(master);
master->mode = EC_MASTER_MODE_RUNNING;
master->mode = EC_MASTER_MODE_OPERATION;
if (!master->device->link_state) EC_WARN("Link is DOWN.\n");

File diff suppressed because it is too large Load Diff

View File

@ -44,9 +44,10 @@
#include <linux/list.h>
#include <linux/kobject.h>
#include "../include/ecrt.h"
#include "globals.h"
#include "datagram.h"
#include "types.h"
/*****************************************************************************/
@ -89,20 +90,6 @@ enum
/*****************************************************************************/
/**
FMMU configuration.
*/
typedef struct
{
const ec_domain_t *domain; /**< domain */
const ec_sync_t *sync; /**< sync manager */
uint32_t logical_start_address; /**< logical start address */
}
ec_fmmu_t;
/*****************************************************************************/
/**
String object (EEPROM).
*/
@ -113,7 +100,7 @@ typedef struct
size_t size; /**< size in bytes */
char *data; /**< string data */
}
ec_eeprom_string_t;
ec_sii_string_t;
/*****************************************************************************/
@ -130,7 +117,7 @@ typedef struct
uint8_t control_register; /**< control register value */
uint8_t enable; /**< enable bit */
}
ec_eeprom_sync_t;
ec_sii_sync_t;
/*****************************************************************************/
@ -143,7 +130,7 @@ typedef enum
EC_RX_PDO, /**< Reveive PDO */
EC_TX_PDO /**< Transmit PDO */
}
ec_pdo_type_t;
ec_sii_pdo_type_t;
/*****************************************************************************/
@ -154,13 +141,13 @@ ec_pdo_type_t;
typedef struct
{
struct list_head list; /**< list item */
ec_pdo_type_t type; /**< PDO type */
ec_sii_pdo_type_t type; /**< PDO type */
uint16_t index; /**< PDO index */
uint8_t sync_manager; /**< assigned sync manager */
uint8_t sync_index; /**< assigned sync manager */
char *name; /**< PDO name */
struct list_head entries; /**< entry list */
}
ec_eeprom_pdo_t;
ec_sii_pdo_t;
/*****************************************************************************/
@ -176,7 +163,7 @@ typedef struct
char *name; /**< entry name */
uint8_t bit_length; /**< entry length in bit */
}
ec_eeprom_pdo_entry_t;
ec_sii_pdo_entry_t;
/*****************************************************************************/
@ -188,7 +175,6 @@ typedef struct
{
struct list_head list; /**< list item */
uint16_t index; /**< SDO index */
//uint16_t type;
uint8_t object_code; /**< object code */
char *name; /**< SDO name */
struct list_head entries; /**< entry list */
@ -213,6 +199,20 @@ ec_sdo_entry_t;
/*****************************************************************************/
/**
FMMU configuration.
*/
typedef struct
{
const ec_domain_t *domain; /**< domain */
const ec_sii_sync_t *sync; /**< sync manager */
uint32_t logical_start_address; /**< logical start address */
}
ec_fmmu_t;
/*****************************************************************************/
/**
Variable-sized field information.
*/
@ -220,7 +220,7 @@ ec_sdo_entry_t;
typedef struct
{
struct list_head list; /**< list item */
const ec_field_t *field; /**< data field */
const ec_sii_pdo_t *pdo; /**< PDO */
size_t size; /**< field size */
}
ec_varsize_t;
@ -237,6 +237,12 @@ struct ec_slave
struct kobject kobj; /**< kobject */
ec_master_t *master; /**< master owning the slave */
ec_slave_state_t requested_state; /**< requested slave state */
ec_slave_state_t current_state; /**< current slave state */
unsigned int error_flag; /**< stop processing after an error */
unsigned int online; /**< non-zero, if the slave responds. */
uint8_t registered; /**< true, if slave has been registered */
// addresses
uint16_t ring_position; /**< ring position */
uint16_t station_address; /**< configured station address */
@ -255,6 +261,12 @@ struct ec_slave
uint8_t dl_loop[4]; /**< loop closed */
uint8_t dl_signal[4]; /**< detected signal on RX port */
// EEPROM
uint8_t *eeprom_data; /**< Complete EEPROM image */
uint16_t eeprom_size; /**< size of the EEPROM contents in byte */
uint16_t *new_eeprom_data; /**< new EEPROM data to write */
uint16_t new_eeprom_size; /**< size of new EEPROM data in words */
// slave information interface
uint16_t sii_alias; /**< configured station alias */
uint32_t sii_vendor_id; /**< vendor id */
@ -267,34 +279,19 @@ struct ec_slave
uint16_t sii_tx_mailbox_size; /**< mailbox size (slave to master) */
uint16_t sii_mailbox_protocols; /**< supported mailbox protocols */
uint8_t sii_physical_layer[4]; /**< port media */
const ec_slave_type_t *type; /**< pointer to slave type object */
uint8_t registered; /**< true, if slave has been registered */
struct list_head sii_strings; /**< EEPROM STRING categories */
struct list_head sii_syncs; /**< EEPROM SYNC MANAGER categories */
struct list_head sii_pdos; /**< EEPROM [RT]XPDO categories */
char *sii_group; /**< slave group acc. to EEPROM */
char *sii_image; /**< slave image name acc. to EEPROM */
char *sii_order; /**< slave order number acc. to EEPROM */
char *sii_name; /**< slave name acc. to EEPROM */
ec_fmmu_t fmmus[EC_MAX_FMMUS]; /**< FMMU configurations */
uint8_t fmmu_count; /**< number of FMMUs used */
uint8_t *eeprom_data; /**< Complete EEPROM image */
uint16_t eeprom_size; /**< size of the EEPROM contents in byte */
struct list_head eeprom_strings; /**< EEPROM STRING categories */
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 */
uint16_t *new_eeprom_data; /**< new EEPROM data to write */
size_t new_eeprom_size; /**< size of new EEPROM data in words */
struct list_head sdo_dictionary; /**< SDO directory list */
ec_slave_state_t requested_state; /**< requested slave state */
ec_slave_state_t current_state; /**< current slave state */
unsigned int error_flag; /**< stop processing after an error */
unsigned int online; /**< non-zero, if the slave responds. */
struct list_head varsize_fields; /**< size information for variable-sized
data fields. */
};
@ -305,32 +302,27 @@ struct ec_slave
int ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
void ec_slave_clear(struct kobject *);
// slave control
int ec_slave_fetch(ec_slave_t *);
int ec_slave_sii_read16(ec_slave_t *, uint16_t, uint16_t *);
int ec_slave_sii_read32(ec_slave_t *, uint16_t, uint32_t *);
int ec_slave_sii_write16(ec_slave_t *, uint16_t, uint16_t);
int ec_slave_state_change(ec_slave_t *, uint8_t);
int ec_slave_prepare_fmmu(ec_slave_t *, const ec_domain_t *,
const ec_sync_t *);
const ec_sii_sync_t *);
// CoE
int ec_slave_fetch_sdo_list(ec_slave_t *);
//int ec_slave_fetch_sdo_list(ec_slave_t *);
// state machine
// SII categories
int ec_slave_fetch_strings(ec_slave_t *, const uint8_t *);
int ec_slave_fetch_general(ec_slave_t *, const uint8_t *);
void 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_fetch_pdo(ec_slave_t *, const uint8_t *, size_t,
ec_sii_pdo_type_t);
int ec_slave_locate_string(ec_slave_t *, unsigned int, char **);
// misc.
size_t ec_slave_calc_sync_size(const ec_slave_t *, const ec_sync_t *);
uint16_t ec_slave_calc_eeprom_sync_size(const ec_slave_t *,
const ec_eeprom_sync_t *);
uint16_t ec_slave_calc_sync_size(const ec_slave_t *,
const ec_sii_sync_t *);
void ec_slave_print(const ec_slave_t *, unsigned int);
int ec_slave_check_crc(ec_slave_t *);
int ec_slave_is_coupler(const ec_slave_t *);
//int ec_slave_check_crc(ec_slave_t *);
/*****************************************************************************/

View File

@ -1,280 +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; 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 slave descriptions.
\cond
*/
/*****************************************************************************/
#include <linux/module.h>
#include "globals.h"
#include "types.h"
/*****************************************************************************/
const ec_sync_t mailbox_sm0 = {0x1800, 246, 0x26, {NULL}};
const ec_sync_t mailbox_sm1 = {0x18F6, 246, 0x22, {NULL}};
/******************************************************************************
* slave objects
*****************************************************************************/
const ec_slave_type_t Beckhoff_EK1100 = {
"Beckhoff", "EK1100", "Bus Coupler", EC_TYPE_BUS_COUPLER,
{NULL} // no sync managers
};
/*****************************************************************************/
const ec_slave_type_t Beckhoff_EK1110 = {
"Beckhoff", "EK1110", "Extension terminal", EC_TYPE_INFRA,
{NULL} // no sync managers
};
/*****************************************************************************/
const ec_field_t bk1120_out = {"Outputs", 0}; // variable size
const ec_field_t bk1120_in = {"Inputs", 0}; // variable size
const ec_sync_t bk1120_sm0 = {0x1C00, 264, 0x26, {NULL}};
const ec_sync_t bk1120_sm1 = {0x1E00, 264, 0x22, {NULL}};
const ec_sync_t bk1120_sm2 = { // outputs
0x1000, 0, 0x24, // variable size
{&bk1120_out, NULL}
};
const ec_sync_t bk1120_sm3 = { // inputs
0x1600, 0, 0x00, // variable size
{&bk1120_in, NULL}
};
const ec_slave_type_t Beckhoff_BK1120 = {
"Beckhoff", "BK1120", "KBUS Coupler", EC_TYPE_NORMAL,
{&bk1120_sm0, &bk1120_sm1, &bk1120_sm2, &bk1120_sm3, NULL}
};
/*****************************************************************************/
const ec_field_t el1004_in = {"InputValue", 1};
const ec_sync_t el1004_sm0 = { // inputs
0x1000, 1, 0x00,
{&el1004_in, NULL}
};
const ec_slave_type_t Beckhoff_EL1004 = {
"Beckhoff", "EL1004", "4x Digital Input, 3ms", EC_TYPE_NORMAL,
{&el1004_sm0, NULL}
};
/*****************************************************************************/
const ec_field_t el1014_in = {"InputValue", 1};
const ec_sync_t el1014_sm0 = { // inputs
0x1000, 1, 0x00,
{&el1014_in, NULL}
};
const ec_slave_type_t Beckhoff_EL1014 = {
"Beckhoff", "EL1014", "4x Digital Input, 10us", EC_TYPE_NORMAL,
{&el1014_sm0, NULL}
};
/*****************************************************************************/
const ec_field_t el20XX_out = {"OutputValue", 1};
const ec_sync_t el20XX_sm0 = {
0x0F00, 1, 0x46,
{&el20XX_out, NULL}
};
const ec_slave_type_t Beckhoff_EL2004 = {
"Beckhoff", "EL2004", "4x Digital Output", EC_TYPE_NORMAL,
{&el20XX_sm0, NULL}
};
const ec_slave_type_t Beckhoff_EL2032 = {
"Beckhoff", "EL2032", "2x Digital Output (2A)", EC_TYPE_NORMAL,
{&el20XX_sm0, NULL}
};
/*****************************************************************************/
const ec_field_t el31X2_st1 = {"Status", 1};
const ec_field_t el31X2_ip1 = {"InputValue", 2};
const ec_field_t el31X2_st2 = {"Status", 1};
const ec_field_t el31X2_ip2 = {"InputValue", 2};
const ec_sync_t el31X2_sm2 = {0x1000, 4, 0x24, {NULL}};
const ec_sync_t el31X2_sm3 = {
0x1100, 6, 0x20,
{&el31X2_st1, &el31X2_ip1, &el31X2_st2, &el31X2_ip2, NULL}
};
const ec_slave_type_t Beckhoff_EL3102 = {
"Beckhoff", "EL3102", "2x Analog Input diff.", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el31X2_sm2, &el31X2_sm3, NULL}
};
const ec_slave_type_t Beckhoff_EL3162 = {
"Beckhoff", "EL3162", "2x Analog Input", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el31X2_sm2, &el31X2_sm3, NULL}
};
/*****************************************************************************/
const ec_field_t el41X2_op = {"OutputValue", 2};
const ec_sync_t el41X2_sm2 = {
0x1000, 4, 0x24,
{&el41X2_op, &el41X2_op, NULL}
};
const ec_slave_type_t Beckhoff_EL4102 = {
"Beckhoff", "EL4102", "2x Analog Output", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el41X2_sm2, NULL}
};
const ec_slave_type_t Beckhoff_EL4132 = {
"Beckhoff", "EL4132", "2x Analog Output diff.", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el41X2_sm2, NULL}
};
/*****************************************************************************/
const ec_field_t el5001_st = {"Status", 1};
const ec_field_t el5001_ip = {"InputValue", 4};
const ec_sync_t el5001_sm2 = {
0x1000, 4, 0x24,
{NULL}
};
const ec_sync_t el5001_sm3 = {
0x1100, 5, 0x20,
{&el5001_st, &el5001_ip, NULL}
};
const ec_slave_type_t Beckhoff_EL5001 = {
"Beckhoff", "EL5001", "SSI-Interface", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el5001_sm2, &el5001_sm3, NULL}
};
/*****************************************************************************/
const ec_field_t el5101_ct = {"Control", 1};
const ec_field_t el5101_op = {"OutputValue", 2};
const ec_field_t el5101_st = {"Status", 1};
const ec_field_t el5101_ip = {"InputValue", 2};
const ec_field_t el5101_la = {"LatchValue", 2};
const ec_sync_t el5101_sm2 = {
0x1000, 3, 0x24,
{&el5101_ct, &el5101_op, NULL}
};
const ec_sync_t el5101_sm3 = {
0x1100, 5, 0x20,
{&el5101_st, &el5101_ip, &el5101_la, NULL}
};
const ec_slave_type_t Beckhoff_EL5101 = {
"Beckhoff", "EL5101", "Incremental Encoder Interface", EC_TYPE_NORMAL,
{&mailbox_sm0, &mailbox_sm1, &el5101_sm2, &el5101_sm3, NULL}
};
/*****************************************************************************/
const ec_sync_t el6601_sm0 = {0x1800, 522, 0x26, {NULL}};
const ec_sync_t el6601_sm1 = {0x1C00, 522, 0x22, {NULL}};
const ec_slave_type_t Beckhoff_EL6601 = {
"Beckhoff", "EL6601", "1-Port Ethernet Switch Terminal", EC_TYPE_EOE,
{&el6601_sm0, &el6601_sm1, NULL, NULL, NULL}
};
/*****************************************************************************/
const ec_field_t trlinenc2_st = {"Status", 1};
const ec_field_t trlinenc2_ip = {"InputValue", 4};
const ec_sync_t trlinenc2_sm0 = {0x1800, 192, 0x26, {NULL}};
const ec_sync_t trlinenc2_sm1 = {0x1C00, 192, 0x22, {NULL}};
const ec_sync_t trlinenc2_sm2 = {0x1000, 4, 0x24, {NULL}};
const ec_sync_t trlinenc2_sm3 = {
0x1100, 5, 0x20,
{&trlinenc2_st, &trlinenc2_ip, NULL}
};
const ec_slave_type_t TR_Electronic_LinEnc2 = {
"TR-Electronic", "LinEnc2", "SSI-Encoder", EC_TYPE_NORMAL,
{&trlinenc2_sm0, &trlinenc2_sm1, &trlinenc2_sm2, &trlinenc2_sm3, NULL}
};
/** \endcond */
/*****************************************************************************/
/**
Mapping between vendor IDs and product codes <=> slave objects.
*/
ec_slave_ident_t slave_idents[] = {
{0x00000002, 0x03EC3052, &Beckhoff_EL1004},
{0x00000002, 0x03F63052, &Beckhoff_EL1014},
{0x00000002, 0x044C2C52, &Beckhoff_EK1100},
{0x00000002, 0x04562C52, &Beckhoff_EK1110},
{0x00000002, 0x04602C22, &Beckhoff_BK1120},
{0x00000002, 0x07D43052, &Beckhoff_EL2004},
{0x00000002, 0x07F03052, &Beckhoff_EL2032},
{0x00000002, 0x0C1E3052, &Beckhoff_EL3102},
{0x00000002, 0x0C5A3052, &Beckhoff_EL3162},
{0x00000002, 0x10063052, &Beckhoff_EL4102},
{0x00000002, 0x10243052, &Beckhoff_EL4132},
{0x00000002, 0x13893052, &Beckhoff_EL5001},
{0x00000002, 0x13ED3052, &Beckhoff_EL5101},
{0x00000002, 0x19C93052, &Beckhoff_EL6601},
{0x000000D4, 0x00000017, &TR_Electronic_LinEnc2},
{}
};
/*****************************************************************************/

View File

@ -1,130 +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; 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 slave types.
*/
/*****************************************************************************/
#ifndef _EC_TYPES_H_
#define _EC_TYPES_H_
#include <linux/types.h>
#include "../include/ecrt.h"
/*****************************************************************************/
#define EC_MAX_FIELDS 10 /**< maximal number of data fields per sync manager */
#define EC_MAX_SYNC 16 /**< maximal number of sync managers per type */
/*****************************************************************************/
/**
Special slaves.
*/
typedef enum
{
EC_TYPE_NORMAL, /**< no special slave */
EC_TYPE_BUS_COUPLER, /**< slave is a bus coupler */
EC_TYPE_INFRA, /**< infrastructure slaves, that contain no process data */
EC_TYPE_EOE /**< slave is an EoE switch */
}
ec_special_type_t;
/*****************************************************************************/
/**
Process data field.
*/
typedef struct
{
const char *name; /**< field name */
size_t size; /**< field size in bytes */
}
ec_field_t;
/*****************************************************************************/
/**
Sync manager.
*/
typedef struct
{
uint16_t physical_start_address; /**< physical start address */
uint16_t size; /**< size in bytes */
uint8_t control_byte; /**< control register value */
const ec_field_t *fields[EC_MAX_FIELDS]; /**< field array */
}
ec_sync_t;
/*****************************************************************************/
/**
Slave description type.
*/
typedef struct ec_slave_type
{
const char *vendor_name; /**< vendor name*/
const char *product_name; /**< product name */
const char *description; /**< free description */
ec_special_type_t special; /**< special slave type? */
const ec_sync_t *sync_managers[EC_MAX_SYNC]; /**< sync managers */
}
ec_slave_type_t;
/*****************************************************************************/
/**
Slave type identification.
*/
typedef struct
{
uint32_t vendor_id; /**< vendor id */
uint32_t product_code; /**< product code */
const ec_slave_type_t *type; /**< associated slave description object */
}
ec_slave_ident_t;
extern ec_slave_ident_t slave_idents[]; /**< array with slave descriptions */
/*****************************************************************************/
#endif

View File

@ -163,7 +163,7 @@ case "$1" in
fi
# unload conflicting modules at first
for mod in 8139too 8139cp; do
for mod in 8139too; do
if lsmod | grep "^$mod " > /dev/null; then
if ! rmmod $mod; then
/bin/false
@ -175,6 +175,7 @@ case "$1" in
# load master module
if ! modprobe ec_master ec_eoeif_count=$EOE_INTERFACES; then
modprobe 8139too
/bin/false
rc_status -v
rc_exit
@ -182,6 +183,8 @@ case "$1" in
# load device module
if ! modprobe ec_8139too ec_device_index=$DEVICE_INDEX; then
rmmod ec_master
modprobe 8139too
/bin/false
rc_status -v
rc_exit

View File

@ -42,7 +42,6 @@ use Getopt::Std;
my $master_index;
my $master_dir;
my $show_sii_naming;
#------------------------------------------------------------------------------
@ -63,45 +62,43 @@ sub query_master
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;
my $line;
unless (opendir $dirhandle, $master_dir) {
print "Failed to open directory \"$master_dir\".\n";
exit 1;
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->{'advanced_position'} =
&read_string("$slave_dir/advanced_position");
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->{'name'} =
&read_string("$slave_dir/name");
}
$slave->{'type'} =
&read_string("$slave_dir/type");
$slave = {};
push @slaves, $slave;
open INFO, "$master_dir/$entry/info" or die
"ERROR: Failed to open $master_dir/$entry/info";
while ($line = <INFO>) {
if ($line =~ /^Name: (.*)$/) {
$slave->{'name'} = $1;
}
elsif ($line =~ /^Ring position: (\d+)$/) {
$slave->{'ring_position'} = $1;
}
elsif ($line =~ /^Advanced position: (\d+:\d+)$/) {
$slave->{'advanced_position'} = $1;
}
elsif ($line =~ /^State: (.+)$/) {
$slave->{'state'} = $1;
}
}
close INFO;
push @slaves, $slave;
}
closedir $dirhandle;
@ -109,82 +106,40 @@ sub query_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->{'advanced_position'});
unless ($show_sii_naming) {
printf("%-12s %-10s %s\n", $slave->{'vendor_name'},
$slave->{'product_name'}, $slave->{'product_desc'});
}
else {
printf("%s\n", $slave->{'name'});
}
$abs = sprintf "%i", $slave->{'ring_position'};
printf(" %3s %8s %-6s %s\n",
$abs, $slave->{'advanced_position'},
$slave->{'state'}, $slave->{'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;
$optret = getopts "m:h", \%opt;
&print_usage if defined $opt{'h'} or $#ARGV > -1 or !$optret;
if (defined $opt{'m'}) {
$master_index = $opt{'m'};
$master_index = $opt{'m'};
}
else {
$master_index = 0;
$master_index = 0;
}
$show_sii_naming = defined $opt{'s'};
}
#------------------------------------------------------------------------------
sub print_usage
{
print "Usage: $0 [OPTIONS]\n";
my $cmd = `basename $0`;
chomp $cmd;
print "Usage: $cmd [OPTIONS]\n";
print " -m <IDX> Query master <IDX>.\n";
print " -s Show EEPROM name instead of";
print " vendor/product/description.\n";
print " -h Show this help.\n";
exit 0;
}