Dynamische FMMU-Konfiguration, zwei Kopieroperationen eingespart, Einrückungen angepasst.

This commit is contained in:
Florian Pose 2006-02-23 09:58:50 +00:00
parent 30f4dfcc59
commit 4d65b22b0f
29 changed files with 2613 additions and 2430 deletions

View File

@ -42,6 +42,6 @@ config conf $(CONFIG_FILE):
@echo >> $(CONFIG_FILE)
@echo "$(CONFIG_FILE) erstellt."
#----------------------------------------------------------------
#------------------------------------------------------------------------------
endif

1
TODO
View File

@ -7,4 +7,5 @@ $Id$
- Ethernet over EtherCAT (EoE)
- eepro100-Kartentreiber
- Proc/SysFS-Interface mit Baumdarstellung des Busses
- Anzahl unterstützter Sync-Manager/FMMUs auslesen

View File

@ -2268,14 +2268,12 @@ no_early_rx:
}
else
{
if (EtherCAT_dev_receive(rtl_ec_dev,
&rx_ring[ring_offset + 4] + ETH_HLEN,
pkt_size - ETH_HLEN) == 0)
{
EtherCAT_dev_receive(rtl_ec_dev,
&rx_ring[ring_offset + 4] + ETH_HLEN,
pkt_size - ETH_HLEN);
dev->last_rx = jiffies;
tp->stats.rx_bytes += pkt_size;
tp->stats.rx_packets++;
}
}
/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
@ -2466,7 +2464,7 @@ irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
else
{
/* Beim EtherCAT-Device einfach alle Frames empfangen */
rtl8139_rx(dev, tp, 100); // FIXME Das ist echt dirty...
rtl8139_rx(dev, tp, 100); // FIXME
}
}

View File

@ -21,8 +21,11 @@ typedef struct ec_device ec_device_t;
typedef enum
{
EC_DEVICE_STATE_READY, EC_DEVICE_STATE_SENT, EC_DEVICE_STATE_RECEIVED,
EC_DEVICE_STATE_TIMEOUT, EC_DEVICE_STATE_ERROR
EC_DEVICE_STATE_READY = 0,
EC_DEVICE_STATE_SENT,
EC_DEVICE_STATE_RECEIVED,
EC_DEVICE_STATE_TIMEOUT,
EC_DEVICE_STATE_ERROR
}
ec_device_state_t;
@ -33,9 +36,10 @@ ec_device_t *EtherCAT_dev_register(unsigned int, struct net_device *,
struct pt_regs *),
struct module *);
void EtherCAT_dev_unregister(unsigned int, ec_device_t *);
int EtherCAT_dev_is_ec(ec_device_t *, struct net_device *);
void EtherCAT_dev_state(ec_device_t *, ec_device_state_t);
int EtherCAT_dev_receive(ec_device_t *, void *, unsigned int);
void EtherCAT_dev_receive(ec_device_t *, void *, unsigned int);
/*****************************************************************************/

View File

@ -14,106 +14,83 @@
struct ec_master;
typedef struct ec_master ec_master_t;
struct ec_slave_type;
typedef struct ec_slave_type ec_slave_type_t;
struct ec_domain;
typedef struct ec_domain ec_domain_t;
struct ec_slave;
typedef struct ec_slave ec_slave_t;
struct ec_slave_init;
typedef struct ec_slave_init ec_slave_init_t;
typedef enum
{
ec_sync,
ec_async
}
ec_domain_mode_t;
typedef enum
{
ec_status,
ec_control,
ec_ipvalue,
ec_opvalue
}
ec_field_type_t;
typedef struct
{
void **data;
const char *address;
const char *vendor;
const char *product;
ec_field_type_t field_type;
unsigned int field_index;
unsigned int field_count;
}
ec_field_init_t;
/*****************************************************************************/
// Master request functions
ec_master_t *EtherCAT_rt_request_master(unsigned int master_index);
void EtherCAT_rt_release_master(ec_master_t *master);
ec_slave_t *EtherCAT_rt_register_slave(ec_master_t *master,
const char *address,
const char *vendor_name,
const char *product_name,
int domain);
/*****************************************************************************/
// Master methods
int EtherCAT_rt_register_slave_list(ec_master_t *master,
const ec_slave_init_t *slaves,
unsigned int count);
ec_domain_t *EtherCAT_rt_master_register_domain(ec_master_t *master,
ec_domain_mode_t mode,
unsigned int timeout_us);
int EtherCAT_rt_activate_slaves(ec_master_t *master);
int EtherCAT_rt_master_activate(ec_master_t *master);
int EtherCAT_rt_deactivate_slaves(ec_master_t *master);
int EtherCAT_rt_master_deactivate(ec_master_t *master);
int EtherCAT_rt_domain_xio(ec_master_t *master, unsigned int domain,
unsigned int timeout_us);
void EtherCAT_rt_master_debug(ec_master_t *master, int level);
void EtherCAT_rt_master_print(const ec_master_t *master);
void EtherCAT_rt_debug_level(ec_master_t *master, int level);
/*****************************************************************************/
// Domain Methods
int EtherCAT_rt_canopen_sdo_write(ec_master_t *master, ec_slave_t *slave,
ec_slave_t *EtherCAT_rt_register_slave_field(ec_domain_t *domain,
const char *address,
const char *vendor_name,
const char *product_name,
void **data_ptr,
ec_field_type_t field_type,
unsigned int field_index,
unsigned int field_count);
int EtherCAT_rt_domain_xio(ec_domain_t *domain);
/*****************************************************************************/
// Slave Methods
int EtherCAT_rt_canopen_sdo_write(ec_slave_t *slave,
unsigned int sdo_index,
unsigned char sdo_subindex,
unsigned int value, unsigned int size);
/*****************************************************************************/
/**
EtherCAT-Slave
*/
struct ec_slave
{
// Base data
unsigned char base_type; /**< Slave-Typ */
unsigned char base_revision; /**< Revision */
unsigned short base_build; /**< Build-Nummer */
// Addresses
short ring_position; /**< (Negative) Position des Slaves im Bus */
unsigned short station_address; /**< Konfigurierte Slave-Adresse */
// Slave information interface
unsigned int sii_vendor_id; /**< Identifikationsnummer des Herstellers */
unsigned int sii_product_code; /**< Herstellerspezifischer Produktcode */
unsigned int sii_revision_number; /**< Revisionsnummer */
unsigned int sii_serial_number; /**< Seriennummer der Klemme */
const ec_slave_type_t *type; /**< Zeiger auf die Beschreibung
des Slave-Typs */
unsigned int logical_address; /**< Konfigurierte, logische Adresse */
void *process_data; /**< Zeiger auf den Speicherbereich
innerhalb eines Prozessdatenobjekts */
void *private_data; /**< Zeiger auf privaten Datenbereich */
int (*configure)(ec_slave_t *); /**< Zeiger auf die Funktion zur
Konfiguration */
unsigned char registered; /**< Der Slave wurde registriert */
unsigned int domain; /**< Prozessdatendomäne */
int error_reported; /**< Ein Zugriffsfehler wurde bereits gemeldet */
};
/*****************************************************************************/
/**
Beschreibung eines EtherCAT-Slave-Typs.
Diese Beschreibung dient zur Konfiguration einer bestimmten
Slave-Art. Sie enthält die Konfigurationsdaten für die
Slave-internen Sync-Manager und FMMU's.
*/
struct ec_slave_init
{
ec_slave_t **slave_ptr; /**< Zeiger auf den Slave-Zeiger, der später auf
die Slave-Struktur zeigen soll. */
const char *address; /**< ASCII-kodierte Bus-Adresse des zu
registrierenden Slaves \sa ec_address */
const char *vendor_name; /**< Name des Herstellers */
const char *product_name; /**< Name des Slaves-Typs */
unsigned int domain; /**< Domäne, in der registriert werden soll. */
};
unsigned int value,
unsigned int size);
/*****************************************************************************/

View File

@ -15,7 +15,7 @@ ifneq ($(KERNELRELEASE),)
obj-m := ec_master.o
ec_master-objs := module.o master.o device.o slave.o command.o types.o \
ec_master-objs := module.o master.o device.o slave.o frame.o types.o \
domain.o canopen.o
REV = `svnversion $(src)`

View File

@ -18,16 +18,26 @@
/*****************************************************************************/
int EtherCAT_rt_canopen_sdo_write(ec_master_t *master, ec_slave_t *slave,
unsigned int sdo_index,
unsigned char sdo_subindex,
unsigned int value, unsigned int size)
/**
Schreibt ein CANopen-SDO (service data object).
*/
int EtherCAT_rt_canopen_sdo_write(
ec_slave_t *slave, /**< EtherCAT-Slave */
unsigned int sdo_index, /**< SDO-Index */
unsigned char sdo_subindex, /**< SDO-Subindex */
unsigned int value, /**< Neuer Wert */
unsigned int size /**< Größe des Datenfeldes */
)
{
unsigned char data[0xF6];
ec_command_t cmd;
ec_frame_t frame;
unsigned int tries_left, i;
ec_master_t *master;
for (i = 0; i < 0xF6; i++) data[i] = 0x00;
memset(data, 0x00, 0xF6);
master = slave->master;
if (size == 0 || size > 4) {
printk(KERN_ERR "EtherCAT: Illegal SDO data size: %i!\n", size);
@ -55,14 +65,14 @@ int EtherCAT_rt_canopen_sdo_write(ec_master_t *master, ec_slave_t *slave,
value >>= 8;
}
ec_command_write(&cmd, slave->station_address, 0x1800, 0xF6, data);
ec_frame_init_npwr(&frame, master, slave->station_address, 0x1800, 0xF6,
data);
if (unlikely(ec_simple_send_receive(master, &cmd) < 0))
return -1;
if (unlikely(ec_frame_send_receive(&frame) < 0)) return -1;
if (unlikely(cmd.working_counter != 1)) {
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Mailbox send - Slave %i did not respond!\n",
slave->ring_position * (-1));
slave->ring_position);
return -1;
}
@ -71,18 +81,17 @@ int EtherCAT_rt_canopen_sdo_write(ec_master_t *master, ec_slave_t *slave,
tries_left = 10;
while (tries_left)
{
ec_command_read(&cmd, slave->station_address, 0x808, 8);
ec_frame_init_nprd(&frame, master, slave->station_address, 0x808, 8);
if (unlikely(ec_simple_send_receive(master, &cmd) < 0))
return -1;
if (unlikely(ec_frame_send_receive(&frame) < 0)) return -1;
if (unlikely(cmd.working_counter != 1)) {
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Mailbox check - Slave %i did not"
" respond!\n", slave->ring_position * (-1));
" respond!\n", slave->ring_position);
return -1;
}
if (cmd.data[5] & 8) { // Written bit is high
if (frame.data[5] & 8) { // Written bit is high
break;
}
@ -92,30 +101,29 @@ int EtherCAT_rt_canopen_sdo_write(ec_master_t *master, ec_slave_t *slave,
if (!tries_left) {
printk(KERN_ERR "EtherCAT: Mailbox check - Slave %i timed out.\n",
slave->ring_position * (-1));
slave->ring_position);
return -1;
}
ec_command_read(&cmd, slave->station_address, 0x18F6, 0xF6);
ec_frame_init_nprd(&frame, master, slave->station_address, 0x18F6, 0xF6);
if (unlikely(ec_simple_send_receive(master, &cmd) < 0))
return -1;
if (unlikely(ec_frame_send_receive(&frame) < 0)) return -1;
if (unlikely(cmd.working_counter != 1)) {
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Mailbox receive - Slave %i did not"
" respond!\n", slave->ring_position * (-1));
" respond!\n", slave->ring_position);
return -1;
}
if (cmd.data[5] != 0x03 // COE
|| (cmd.data[7] >> 4) != 0x03 // SDO response
|| (cmd.data[8] >> 5) != 0x03 // Initiate download response
|| (cmd.data[9] != (sdo_index & 0xFF)) // Index
|| (cmd.data[10] != ((sdo_index >> 8) & 0xFF))
|| (cmd.data[11] != sdo_subindex)) // Subindex
if (frame.data[5] != 0x03 // COE
|| (frame.data[7] >> 4) != 0x03 // SDO response
|| (frame.data[8] >> 5) != 0x03 // Initiate download response
|| (frame.data[9] != (sdo_index & 0xFF)) // Index
|| (frame.data[10] != ((sdo_index >> 8) & 0xFF))
|| (frame.data[11] != sdo_subindex)) // Subindex
{
printk(KERN_ERR "EtherCAT: Illegal mailbox response at slave %i!\n",
slave->ring_position * (-1));
slave->ring_position);
return -1;
}

View File

@ -17,3 +17,9 @@
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -1,250 +0,0 @@
/******************************************************************************
*
* c o m m a n d . c
*
* Methoden für ein EtherCAT-Kommando.
*
* $Id$
*
*****************************************************************************/
#include <linux/slab.h>
#include "command.h"
/*****************************************************************************/
/**
Kommando-Konstruktor.
Initialisiert alle Variablen innerhalb des Kommandos auf die
Default-Werte.
@param cmd Zeiger auf das zu initialisierende Kommando.
*/
void ec_command_init(ec_command_t *cmd)
{
cmd->type = EC_COMMAND_NONE;
cmd->address.logical = 0x00000000;
cmd->data_length = 0;
cmd->state = EC_COMMAND_STATE_READY;
cmd->index = 0;
cmd->working_counter = 0;
}
/*****************************************************************************/
/**
Kommando-Destruktor.
Setzt alle Attribute auf den Anfangswert zurueck.
@param cmd Zeiger auf das zu initialisierende Kommando.
*/
void ec_command_clear(ec_command_t *cmd)
{
ec_command_init(cmd);
}
/*****************************************************************************/
#define EC_FUNC_HEADER \
ec_command_init(cmd)
#define EC_FUNC_WRITE_FOOTER \
cmd->data_length = length; \
memcpy(cmd->data, data, length);
#define EC_FUNC_READ_FOOTER \
cmd->data_length = length;
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-NPRD-Kommando.
@param cmd Zeiger auf das Kommando
@param node_address Adresse des Knotens (Slaves)
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu lesenden Daten
*/
void ec_command_read(ec_command_t *cmd, unsigned short node_address,
unsigned short offset, unsigned int length)
{
if (unlikely(node_address == 0x0000))
printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n");
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_NPRD;
cmd->address.phy.dev.node = node_address;
cmd->address.phy.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-NPWR-Kommando.
Alloziert ein "node-adressed physical write"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param node_address Adresse des Knotens (Slaves)
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu schreibenden Daten
@param data Zeiger auf Speicher mit zu schreibenden Daten
*/
void ec_command_write(ec_command_t *cmd, unsigned short node_address,
unsigned short offset, unsigned int length,
const unsigned char *data)
{
if (unlikely(node_address == 0x0000))
printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n");
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_NPWR;
cmd->address.phy.dev.node = node_address;
cmd->address.phy.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-APRD-Kommando.
Alloziert ein "autoincerement physical read"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param ring_position (Negative) Position des Slaves im Bus
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu lesenden Daten
*/
void ec_command_position_read(ec_command_t *cmd, short ring_position,
unsigned short offset, unsigned int length)
{
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_APRD;
cmd->address.phy.dev.pos = ring_position;
cmd->address.phy.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-APWR-Kommando.
Alloziert ein "autoincrement physical write"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param ring_position (Negative) Position des Slaves im Bus
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu schreibenden Daten
@param data Zeiger auf Speicher mit zu schreibenden Daten
*/
void ec_command_position_write(ec_command_t *cmd, short ring_position,
unsigned short offset, unsigned int length,
const unsigned char *data)
{
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_APWR;
cmd->address.phy.dev.pos = ring_position;
cmd->address.phy.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-BRD-Kommando.
Alloziert ein "broadcast read"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu lesenden Daten
*/
void ec_command_broadcast_read(ec_command_t *cmd, unsigned short offset,
unsigned int length)
{
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_BRD;
cmd->address.phy.dev.node = 0x0000;
cmd->address.phy.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-BWR-Kommando.
Alloziert ein "broadcast write"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param offset Physikalische Speicheradresse im Slave
@param length Länge der zu schreibenden Daten
@param data Zeiger auf Speicher mit zu schreibenden Daten
*/
void ec_command_broadcast_write(ec_command_t *cmd, unsigned short offset,
unsigned int length, const unsigned char *data)
{
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_BWR;
cmd->address.phy.dev.node = 0x0000;
cmd->address.phy.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-LRW-Kommando.
Alloziert ein "logical read write"-Kommando
und fügt es in die Liste des Masters ein.
@param cmd Zeiger auf das Kommando
@param offset Logische Speicheradresse
@param length Länge der zu lesenden/schreibenden Daten
@param data Zeiger auf Speicher mit zu lesenden/schreibenden Daten
*/
void ec_command_logical_read_write(ec_command_t *cmd, unsigned int offset,
unsigned int length, unsigned char *data)
{
EC_FUNC_HEADER;
cmd->type = EC_COMMAND_LRW;
cmd->address.logical = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/

View File

@ -1,102 +0,0 @@
/******************************************************************************
*
* c o m m a n d . h
*
* Struktur für ein EtherCAT-Kommando.
*
* $Id$
*
*****************************************************************************/
#ifndef _EC_COMMAND_H_
#define _EC_COMMAND_H_
#include "globals.h"
/*****************************************************************************/
/**
Status eines EtherCAT-Kommandos.
*/
typedef enum
{
EC_COMMAND_STATE_READY, EC_COMMAND_STATE_SENT, EC_COMMAND_STATE_RECEIVED
}
ec_command_state_t;
/*****************************************************************************/
/**
EtherCAT-Adresse.
Im EtherCAT-Rahmen sind 4 Bytes für die Adresse reserviert, die je nach
Kommandotyp, eine andere Bedeutung haben können: Bei Autoinkrementbefehlen
sind die ersten zwei Bytes die (negative) Autoinkrement-Adresse, bei Knoten-
adressierten Befehlen entsprechen sie der Knotenadresse. Das dritte und
vierte Byte entspricht in diesen Fällen der physikalischen Speicheradresse
auf dem Slave. Bei einer logischen Adressierung entsprechen alle vier Bytes
der logischen Adresse.
*/
typedef union
{
struct
{
union
{
short pos; /**< (Negative) Ring-Position des Slaves */
unsigned short node; /**< Konfigurierte Knotenadresse */
}
dev;
unsigned short mem; /**< Physikalische Speicheradresse im Slave */
}
phy; /**< Physikalische Adresse */
unsigned long logical; /**< Logische Adresse */
unsigned char raw[4]; /**< Rohdaten für die Generierung des Frames */
}
ec_address_t;
/*****************************************************************************/
/**
EtherCAT-Kommando.
*/
typedef struct ec_command
{
ec_command_type_t type; /**< Typ des Kommandos (APRD, NPWR, etc) */
ec_address_t address; /**< Adresse des/der Empfänger */
unsigned int data_length; /**< Länge der zu sendenden und/oder
empfangenen Daten */
ec_command_state_t state; /**< Zustand des Kommandos
(bereit, gesendet, etc) */
unsigned char index; /**< Kommando-Index, mit der das Kommando gesendet
wurde (wird vom Master beim Senden gesetzt. */
unsigned int working_counter; /**< Working-Counter bei Empfang (wird
vom Master gesetzt) */
unsigned char data[EC_FRAME_SIZE]; /**< Kommandodaten */
}
ec_command_t;
/*****************************************************************************/
void ec_command_read(ec_command_t *, unsigned short, unsigned short,
unsigned int);
void ec_command_write(ec_command_t *, unsigned short, unsigned short,
unsigned int, const unsigned char *);
void ec_command_position_read(ec_command_t *, short, unsigned short,
unsigned int);
void ec_command_position_write(ec_command_t *, short, unsigned short,
unsigned int, const unsigned char *);
void ec_command_broadcast_read(ec_command_t *, unsigned short, unsigned int);
void ec_command_broadcast_write(ec_command_t *, unsigned short, unsigned int,
const unsigned char *);
void ec_command_logical_read_write(ec_command_t *, unsigned int, unsigned int,
unsigned char *);
/*****************************************************************************/
#endif

View File

@ -29,32 +29,22 @@
int ec_device_init(ec_device_t *ecd)
{
ecd->dev = NULL;
ecd->open = 0;
ecd->tx_time = 0;
ecd->rx_time = 0;
ecd->tx_intr_cnt = 0;
ecd->rx_intr_cnt = 0;
ecd->intr_cnt = 0;
ecd->state = EC_DEVICE_STATE_READY;
ecd->rx_data_length = 0;
ecd->isr = NULL;
ecd->module = NULL;
ecd->error_reported = 0;
ecd->dev = NULL;
ecd->open = 0;
ecd->tx_time = 0;
ecd->rx_time = 0;
ecd->state = EC_DEVICE_STATE_READY;
ecd->rx_data_length = 0;
ecd->isr = NULL;
ecd->module = NULL;
ecd->error_reported = 0;
if ((ecd->tx_skb = dev_alloc_skb(EC_FRAME_SIZE)) == NULL) {
printk(KERN_ERR "EtherCAT: Could not allocate device tx socket buffer!\n");
return -1;
}
if ((ecd->tx_skb = dev_alloc_skb(ETH_HLEN + EC_MAX_FRAME_SIZE)) == NULL) {
printk(KERN_ERR "EtherCAT: Error allocating device socket buffer!\n");
return -1;
}
if ((ecd->rx_skb = dev_alloc_skb(EC_FRAME_SIZE)) == NULL) {
dev_kfree_skb(ecd->tx_skb);
ecd->tx_skb = NULL;
printk(KERN_ERR "EtherCAT: Could not allocate device rx socket buffer!\n");
return -1;
}
return 0;
return 0;
}
/*****************************************************************************/
@ -70,19 +60,14 @@ int ec_device_init(ec_device_t *ecd)
void ec_device_clear(ec_device_t *ecd)
{
if (ecd->open) ec_device_close(ecd);
if (ecd->open) ec_device_close(ecd);
ecd->dev = NULL;
ecd->dev = NULL;
if (ecd->tx_skb) {
dev_kfree_skb(ecd->tx_skb);
ecd->tx_skb = NULL;
}
if (ecd->rx_skb) {
dev_kfree_skb(ecd->rx_skb);
ecd->rx_skb = NULL;
}
if (ecd->tx_skb) {
dev_kfree_skb(ecd->tx_skb);
ecd->tx_skb = NULL;
}
}
/*****************************************************************************/
@ -102,34 +87,32 @@ void ec_device_clear(ec_device_t *ecd)
int ec_device_open(ec_device_t *ecd)
{
unsigned int i;
unsigned int i;
if (!ecd) {
printk(KERN_ERR "EtherCAT: Trying to open a NULL device!\n");
return -1;
}
if (!ecd) {
printk(KERN_ERR "EtherCAT: Trying to open a NULL device!\n");
return -1;
}
if (!ecd->dev) {
printk(KERN_ERR "EtherCAT: No net_device to open!\n");
return -1;
}
if (!ecd->dev) {
printk(KERN_ERR "EtherCAT: No net_device to open!\n");
return -1;
}
if (ecd->open) {
printk(KERN_WARNING "EtherCAT: Device already opened!\n");
}
else {
// Device could have received frames before
for (i = 0; i < 4; i++) ec_device_call_isr(ecd);
if (ecd->open) {
printk(KERN_WARNING "EtherCAT: Device already opened!\n");
}
else {
// Device could have received frames before
for (i = 0; i < 4; i++) ec_device_call_isr(ecd);
// Reset old device state
ecd->state = EC_DEVICE_STATE_READY;
ecd->tx_intr_cnt = 0;
ecd->rx_intr_cnt = 0;
// Reset old device state
ecd->state = EC_DEVICE_STATE_READY;
if (ecd->dev->open(ecd->dev) == 0) ecd->open = 1;
}
if (ecd->dev->open(ecd->dev) == 0) ecd->open = 1;
}
return ecd->open ? 0 : -1;
return ecd->open ? 0 : -1;
}
/*****************************************************************************/
@ -140,27 +123,42 @@ int ec_device_open(ec_device_t *ecd)
@param ecd EtherCAT-Gerät
@return 0 bei Erfolg, < 0: Kein Gerät zum Schliessen oder
Schliessen fehlgeschlagen.
Schliessen fehlgeschlagen.
*/
int ec_device_close(ec_device_t *ecd)
{
if (!ecd->dev) {
printk(KERN_ERR "EtherCAT: No device to close!\n");
return -1;
}
if (!ecd->dev) {
printk(KERN_ERR "EtherCAT: No device to close!\n");
return -1;
}
if (!ecd->open) {
printk(KERN_WARNING "EtherCAT: Device already closed!\n");
}
else {
printk(KERN_INFO "EtherCAT: Stopping device (txcnt: %u, rxcnt: %u)\n",
(unsigned int) ecd->tx_intr_cnt, (unsigned int) ecd->rx_intr_cnt);
if (!ecd->open) {
printk(KERN_WARNING "EtherCAT: Device already closed!\n");
}
else {
if (ecd->dev->stop(ecd->dev) == 0) ecd->open = 0;
}
if (ecd->dev->stop(ecd->dev) == 0) ecd->open = 0;
}
return !ecd->open ? 0 : -1;
}
return !ecd->open ? 0 : -1;
/*****************************************************************************/
/**
Bereitet den geräteinternen Socket-Buffer auf den Versand vor.
\return Zeiger auf den Speicher, in den die Frame-Daten sollen.
*/
uint8_t *ec_device_prepare(ec_device_t *ecd /**< EtherCAT-Gerät */)
{
// Clear transmit socket buffer and reserve space for Ethernet-II header
skb_trim(ecd->tx_skb, 0);
skb_reserve(ecd->tx_skb, ETH_HLEN);
// Erstmal Speicher für maximal langen Frame reservieren
return skb_put(ecd->tx_skb, EC_MAX_FRAME_SIZE);
}
/*****************************************************************************/
@ -172,100 +170,57 @@ int ec_device_close(ec_device_t *ecd)
Buffer, fügt den Ethernat-II-Header hinzu und ruft die
start_xmit()-Funktion der Netzwerkkarte auf.
@param ecd EtherCAT-Gerät
@param data Zeiger auf die zu sendenden Daten
@param length Länge der zu sendenden Daten
@return 0 bei Erfolg, < 0: Vorheriger Rahmen noch
\return 0 bei Erfolg, < 0: Vorheriger Rahmen noch
nicht empfangen, oder kein Speicher mehr vorhanden
*/
int ec_device_send(ec_device_t *ecd, unsigned char *data, unsigned int length)
void ec_device_send(ec_device_t *ecd, /**< EtherCAT-Gerät */
unsigned int length /**< Länge der zu sendenden Daten */
)
{
unsigned char *frame_data;
struct ethhdr *eth;
struct ethhdr *eth;
if (unlikely(ecd->state == EC_DEVICE_STATE_SENT)) {
printk(KERN_WARNING "EtherCAT: Warning - Trying to send frame while last "
" was not received!\n");
}
// Framegroesse auf (jetzt bekannte) Laenge abschneiden
skb_trim(ecd->tx_skb, length);
// Clear transmit socket buffer and reserve
// space for Ethernet-II header
skb_trim(ecd->tx_skb, 0);
skb_reserve(ecd->tx_skb, ETH_HLEN);
// Ethernet-II-Header hinzufuegen
eth = (struct ethhdr *) skb_push(ecd->tx_skb, ETH_HLEN);
eth->h_proto = htons(0x88A4);
memcpy(eth->h_source, ecd->dev->dev_addr, ecd->dev->addr_len);
memset(eth->h_dest, 0xFF, ecd->dev->addr_len);
// Copy data to socket buffer
frame_data = skb_put(ecd->tx_skb, length);
memcpy(frame_data, data, length);
ecd->state = EC_DEVICE_STATE_SENT;
ecd->rx_data_length = 0;
// Add Ethernet-II-Header
if (unlikely((eth = (struct ethhdr *)
skb_push(ecd->tx_skb, ETH_HLEN)) == NULL)) {
printk(KERN_ERR "EtherCAT: device_send -"
" Could not allocate Ethernet-II header!\n");
return -1;
}
// Protocol type
eth->h_proto = htons(0x88A4);
// Hardware address
memcpy(eth->h_source, ecd->dev->dev_addr, ecd->dev->addr_len);
// Broadcast address
memset(eth->h_dest, 0xFF, ecd->dev->addr_len);
rdtscl(ecd->tx_time); // Get CPU cycles
// Start sending of frame
ecd->state = EC_DEVICE_STATE_SENT;
ecd->dev->hard_start_xmit(ecd->tx_skb, ecd->dev);
return 0;
// Senden einleiten
rdtscl(ecd->tx_time); // Get CPU cycles
ecd->dev->hard_start_xmit(ecd->tx_skb, ecd->dev);
}
/*****************************************************************************/
/**
Holt einen empfangenen Rahmen von der Netzwerkkarte.
Gibt die Anzahl der empfangenen Bytes zurück.
Zuerst wird geprüft, ob überhaupt ein Rahmen empfangen
wurde. Wenn ja, wird dieser in den angegebenen
Speicherbereich kopiert.
@param ecd EtherCAT-Gerät
@param data Zeiger auf den Speicherbereich, in den die
empfangenen Daten kopiert werden sollen
@return Anzahl der kopierten Bytes bei Erfolg, sonst < 0
\return Empfangene Bytes, oder 0, wenn kein Frame empfangen wurde.
*/
int ec_device_receive(ec_device_t *ecd, unsigned char *data)
unsigned int ec_device_received(const ec_device_t *ecd)
{
if (unlikely(ecd->state != EC_DEVICE_STATE_RECEIVED)) {
if (likely(ecd->error_reported)) {
printk(KERN_ERR "EtherCAT: receive - Nothing received!\n");
ecd->error_reported = 1;
}
return -1;
}
return ecd->rx_data_length;
}
if (unlikely(ecd->rx_data_length > EC_FRAME_SIZE)) {
if (likely(ecd->error_reported)) {
printk(KERN_ERR "EtherCAT: receive - "
" Reveived frame is too long (%i Bytes)!\n",
ecd->rx_data_length);
ecd->error_reported = 1;
}
return -1;
}
/*****************************************************************************/
if (unlikely(ecd->error_reported)) {
ecd->error_reported = 0;
}
/**
Gibt die empfangenen Daten zurück.
memcpy(data, ecd->rx_data, ecd->rx_data_length);
\return Adresse auf empfangene Daten.
*/
return ecd->rx_data_length;
uint8_t *ec_device_data(ec_device_t *ecd)
{
return ecd->rx_data;
}
/*****************************************************************************/
@ -280,7 +235,7 @@ int ec_device_receive(ec_device_t *ecd, unsigned char *data)
void ec_device_call_isr(ec_device_t *ecd)
{
if (likely(ecd->isr)) ecd->isr(0, ecd->dev, NULL);
if (likely(ecd->isr)) ecd->isr(0, ecd->dev, NULL);
}
/*****************************************************************************/
@ -291,41 +246,33 @@ void ec_device_call_isr(ec_device_t *ecd)
@param ecd EtherCAT-Gerät
*/
void ec_device_debug(ec_device_t *ecd)
void ec_device_print(ec_device_t *ecd)
{
printk(KERN_DEBUG "---EtherCAT device information begin---\n");
printk(KERN_DEBUG "---EtherCAT device information begin---\n");
if (ecd)
{
printk(KERN_DEBUG "Assigned net_device: %X\n",
(unsigned) ecd->dev);
printk(KERN_DEBUG "Transmit socket buffer: %X\n",
(unsigned) ecd->tx_skb);
printk(KERN_DEBUG "Receive socket buffer: %X\n",
(unsigned) ecd->rx_skb);
printk(KERN_DEBUG "Time of last transmission: %u\n",
(unsigned) ecd->tx_time);
printk(KERN_DEBUG "Time of last receive: %u\n",
(unsigned) ecd->rx_time);
printk(KERN_DEBUG "Number of transmit interrupts: %u\n",
(unsigned) ecd->tx_intr_cnt);
printk(KERN_DEBUG "Number of receive interrupts: %u\n",
(unsigned) ecd->rx_intr_cnt);
printk(KERN_DEBUG "Total Number of interrupts: %u\n",
(unsigned) ecd->intr_cnt);
printk(KERN_DEBUG "Actual device state: %i\n",
(int) ecd->state);
printk(KERN_DEBUG "Receive buffer: %X\n",
(unsigned) ecd->rx_data);
printk(KERN_DEBUG "Receive buffer fill state: %u/%u\n",
(unsigned) ecd->rx_data_length, EC_FRAME_SIZE);
}
else
{
printk(KERN_DEBUG "Device is NULL!\n");
}
if (ecd)
{
printk(KERN_DEBUG "Assigned net_device: %X\n",
(unsigned) ecd->dev);
printk(KERN_DEBUG "Transmit socket buffer: %X\n",
(unsigned) ecd->tx_skb);
printk(KERN_DEBUG "Time of last transmission: %u\n",
(unsigned) ecd->tx_time);
printk(KERN_DEBUG "Time of last receive: %u\n",
(unsigned) ecd->rx_time);
printk(KERN_DEBUG "Actual device state: %i\n",
(int) ecd->state);
printk(KERN_DEBUG "Receive buffer: %X\n",
(unsigned) ecd->rx_data);
printk(KERN_DEBUG "Receive buffer fill state: %u/%u\n",
(unsigned) ecd->rx_data_length, EC_MAX_FRAME_SIZE);
}
else
{
printk(KERN_DEBUG "Device is NULL!\n");
}
printk(KERN_DEBUG "---EtherCAT device information end---\n");
printk(KERN_DEBUG "---EtherCAT device information end---\n");
}
/******************************************************************************
@ -336,36 +283,24 @@ void ec_device_debug(ec_device_t *ecd)
void EtherCAT_dev_state(ec_device_t *ecd, ec_device_state_t state)
{
if (state == EC_DEVICE_STATE_TIMEOUT && ecd->state != EC_DEVICE_STATE_SENT) {
printk(KERN_WARNING "EtherCAT: Wrong status at timeout: %i\n", ecd->state);
}
ecd->state = state;
ecd->state = state;
}
/*****************************************************************************/
int EtherCAT_dev_is_ec(ec_device_t *ecd, struct net_device *dev)
{
return ecd && ecd->dev == dev;
return ecd && ecd->dev == dev;
}
/*****************************************************************************/
int EtherCAT_dev_receive(ec_device_t *ecd, void *data, unsigned int size)
void EtherCAT_dev_receive(ec_device_t *ecd, void *data, unsigned int size)
{
if (ecd->state != EC_DEVICE_STATE_SENT)
{
printk(KERN_WARNING "EtherCAT: Received frame while not in SENT state!\n");
return -1;
}
// Copy received data to ethercat-device buffer, skip Ethernet-II header
memcpy(ecd->rx_data, data, size);
ecd->rx_data_length = size;
ecd->state = EC_DEVICE_STATE_RECEIVED;
return 0;
// Copy received data to ethercat-device buffer
memcpy(ecd->rx_data, data, size);
ecd->rx_data_length = size;
ecd->state = EC_DEVICE_STATE_RECEIVED;
}
/*****************************************************************************/
@ -378,6 +313,6 @@ EXPORT_SYMBOL(EtherCAT_dev_receive);
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -28,26 +28,19 @@
struct ec_device
{
struct net_device *dev; /**< Zeiger auf das reservierte net_device */
unsigned int open; /**< Das net_device ist geoeffnet. */
struct sk_buff *tx_skb; /**< Zeiger auf Transmit-Socketbuffer */
struct sk_buff *rx_skb; /**< Zeiger auf Receive-Socketbuffer */
unsigned long tx_time; /**< Zeit des letzten Sendens */
unsigned long rx_time; /**< Zeit des letzten Empfangs */
unsigned long tx_intr_cnt; /**< Anzahl Tx-Interrupts */
unsigned long rx_intr_cnt; /**< Anzahl Rx-Interrupts */
unsigned long intr_cnt; /**< Anzahl Interrupts */
volatile ec_device_state_t state; /**< Gesendet, Empfangen,
Timeout, etc. */
unsigned char rx_data[EC_FRAME_SIZE]; /**< Puffer für
empfangene Rahmen */
volatile unsigned int rx_data_length; /**< Länge des zuletzt
empfangenen Rahmens */
irqreturn_t (*isr)(int, void *, struct pt_regs *); /**< Adresse der ISR */
struct module *module; /**< Zeiger auf das Modul, das das Gerät zur
Verfügung stellt. */
int error_reported; /**< Zeigt an, ob ein Fehler im zyklischen Code
bereits gemeldet wurde. */
struct net_device *dev; /**< Zeiger auf das reservierte net_device */
unsigned int open; /**< Das net_device ist geoeffnet. */
struct sk_buff *tx_skb; /**< Zeiger auf Transmit-Socketbuffer */
unsigned long tx_time; /**< Zeit des letzten Sendens */
unsigned long rx_time; /**< Zeit des letzten Empfangs */
volatile ec_device_state_t state; /**< Zustand des Gerätes */
uint8_t rx_data[EC_MAX_FRAME_SIZE]; /**< Speicher für empfangene Rahmen */
volatile unsigned int rx_data_length; /**< Länge des empfangenen Rahmens */
irqreturn_t (*isr)(int, void *, struct pt_regs *); /**< Adresse der ISR */
struct module *module; /**< Zeiger auf das Modul, das das Gerät zur
Verfügung stellt. */
int error_reported; /**< Zeigt an, ob ein Fehler im zyklischen Code
bereits gemeldet wurde. */
};
/*****************************************************************************/
@ -57,9 +50,17 @@ void ec_device_clear(ec_device_t *);
int ec_device_open(ec_device_t *);
int ec_device_close(ec_device_t *);
void ec_device_call_isr(ec_device_t *);
int ec_device_send(ec_device_t *, unsigned char *, unsigned int);
int ec_device_receive(ec_device_t *, unsigned char *);
uint8_t *ec_device_prepare(ec_device_t *);
void ec_device_send(ec_device_t *, unsigned int);
unsigned int ec_device_received(const ec_device_t *);
uint8_t *ec_device_data(ec_device_t *);
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -8,33 +8,349 @@
*
*****************************************************************************/
#include <linux/module.h>
#include "globals.h"
#include "domain.h"
#include "master.h"
/*****************************************************************************/
/**
Konstruktor einer EtherCAT-Domäne.
@param dom Zeiger auf die zu initialisierende Domäne
*/
void ec_domain_init(ec_domain_t *dom)
void ec_domain_init(ec_domain_t *domain, /**< Domäne */
ec_master_t *master, /**< Zugehöriger Master */
ec_domain_mode_t mode, /**< Synchron/Asynchron */
unsigned int timeout_us /**< Timeout in Mikrosekunden */
)
{
dom->number = -1;
dom->data_size = 0;
dom->logical_offset = 0;
dom->response_count = 0xFFFFFFFF;
domain->master = master;
domain->mode = mode;
domain->timeout_us = timeout_us;
memset(dom->data, 0x00, EC_FRAME_SIZE);
domain->data = NULL;
domain->data_size = 0;
domain->base_address = 0;
domain->response_count = 0xFFFFFFFF;
INIT_LIST_HEAD(&domain->field_regs);
}
/*****************************************************************************/
/**
Destruktor einer EtherCAT-Domäne.
*/
void ec_domain_clear(ec_domain_t *domain /**< Domäne */)
{
ec_field_reg_t *field_reg, *next;
if (domain->data) {
kfree(domain->data);
domain->data = NULL;
}
// Liste der registrierten Datenfelder löschen
list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) {
kfree(field_reg);
}
}
/*****************************************************************************/
/**
Registriert ein Feld in einer Domäne.
\returns 0 bei Erfolg, < 0 bei Fehler
*/
int ec_domain_reg_field(ec_domain_t *domain, /**< Domäne */
ec_slave_t *slave, /**< Slave */
const ec_sync_t *sync, /**< Sync-Manager */
uint32_t field_offset, /**< Datenfeld-Offset */
void **data_ptr /**< Adresse des Prozessdatenzeigers */
)
{
ec_field_reg_t *field_reg;
if (!(field_reg = (ec_field_reg_t *) kmalloc(sizeof(ec_field_reg_t),
GFP_KERNEL))) {
printk(KERN_ERR "EtherCAT: Failed to allocate field registration.\n");
return -1;
}
if (ec_slave_set_fmmu(slave, domain, sync)) {
printk(KERN_ERR "EtherCAT: FMMU configuration failed.\n");
kfree(field_reg);
return -1;
}
field_reg->slave = slave;
field_reg->sync = sync;
field_reg->field_offset = field_offset;
field_reg->data_ptr = data_ptr;
list_add_tail(&field_reg->list, &domain->field_regs);
return 0;
}
/*****************************************************************************/
/**
\returns 0 bei Erfolg, < 0 bei Fehler
*/
int ec_domain_alloc(ec_domain_t *domain, /**< Domäne */
uint32_t base_address /**< Logische Basisadresse */
)
{
ec_field_reg_t *field_reg, *next;
ec_slave_t *slave;
ec_fmmu_t *fmmu;
unsigned int i, j, found, data_offset;
if (domain->data) {
printk(KERN_ERR "EtherCAT: Domain already allocated!\n");
return -1;
}
domain->base_address = base_address;
// Größe der Prozessdaten berechnen
// und logische Adressen der FMMUs setzen
domain->data_size = 0;
for (i = 0; i < domain->master->slave_count; i++) {
slave = &domain->master->slaves[i];
for (j = 0; j < slave->fmmu_count; j++) {
fmmu = &slave->fmmus[j];
if (fmmu->domain == domain) {
fmmu->logical_start_address = base_address + domain->data_size;
domain->data_size += fmmu->sync->size;
}
}
}
if (!domain->data_size) {
printk(KERN_WARNING "EtherCAT: Domain 0x%08X contains no data!\n",
(u32) domain);
}
else {
// Prozessdaten allozieren
if (!(domain->data = kmalloc(domain->data_size, GFP_KERNEL))) {
printk(KERN_ERR "EtherCAT: Failed to allocate domain data!\n");
return -1;
}
// Prozessdaten mit Nullen vorbelegen
memset(domain->data, 0x00, domain->data_size);
// Alle Prozessdatenzeiger setzen
list_for_each_entry(field_reg, &domain->field_regs, list) {
found = 0;
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) {
data_offset = fmmu->logical_start_address - base_address
+ field_reg->field_offset;
*field_reg->data_ptr = domain->data + data_offset;
found = 1;
break;
}
}
if (!found) { // Sollte nie passieren
printk(KERN_ERR "EtherCAT: FMMU not found. Please report!\n");
return -1;
}
}
}
// Registrierungsliste wird jetzt nicht mehr gebraucht.
list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) {
kfree(field_reg);
}
INIT_LIST_HEAD(&domain->field_regs); // wichtig!
return 0;
}
/******************************************************************************
*
* Echtzeitschnittstelle
*
*****************************************************************************/
/**
Registriert einer Domäne ein Datenfeld hinzu.
\return Zeiger auf den Slave bei Erfolg, sonst NULL
*/
ec_slave_t *EtherCAT_rt_register_slave_field(
ec_domain_t *domain, /**< Domäne */
const char *address, /**< ASCII-Addresse des Slaves, siehe ec_address() */
const char *vendor_name, /**< Herstellername */
const char *product_name, /**< Produktname */
void **data_ptr, /**< Adresse des Zeigers auf die Prozessdaten */
ec_field_type_t field_type, /**< Typ des Datenfeldes */
unsigned int field_index, /**< Gibt an, ab welchem Feld mit Typ
\a field_type gezählt werden soll. */
unsigned int field_count /**< Anzahl Felder des selben Typs */
)
{
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_idx, found, i, j;
uint32_t field_offset;
if (!field_count) {
printk(KERN_ERR "EtherCAT: field_count may not be 0!\n");
return NULL;
}
master = domain->master;
// Adresse übersetzen
if ((slave = ec_address(master, address)) == NULL) return NULL;
if (!(type = slave->type)) {
printk(KERN_ERR "EtherCAT: 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)) {
printk(KERN_ERR "EtherCAT: 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);
return NULL;
}
field_idx = 0;
found = 0;
for (i = 0; type->sync_managers[i] && !found; i++) {
sync = type->sync_managers[i];
field_offset = 0;
for (j = 0; sync->fields[j]; j++) {
field = sync->fields[j];
if (field->type == field_type) {
if (field_idx == field_index) {
ec_domain_reg_field(domain, slave, sync, field_offset,
data_ptr++);
if (!(--field_count)) return slave;
}
field_idx++;
}
field_offset += field->size;
}
}
printk(KERN_ERR "EtherCAT: Slave %i (\"%s %s\") has less than %i fields of"
" type %i, starting at %i!\n", slave->ring_position,
vendor_name, product_name, field_count, field_type, field_index);
return NULL;
}
/*****************************************************************************/
/**
Sendet und empfängt Prozessdaten der angegebenen Domäne
\return 0 bei Erfolg, sonst < 0
*/
int EtherCAT_rt_domain_xio(ec_domain_t *domain /**< Domäne */)
{
unsigned int offset, size, working_counter_sum;
unsigned long start_ticks, end_ticks, timeout_ticks;
ec_master_t *master;
ec_frame_t *frame;
master = domain->master;
frame = &domain->frame;
working_counter_sum = 0;
ec_output_lost_frames(master); // Evtl. verlorene Frames ausgeben
rdtscl(start_ticks); // Sendezeit nehmen
timeout_ticks = domain->timeout_us * cpu_khz / 1000;
offset = 0;
while (offset < domain->data_size)
{
size = domain->data_size - offset;
if (size > EC_MAX_DATA_SIZE) size = EC_MAX_DATA_SIZE;
ec_frame_init_lrw(frame, master, domain->base_address + offset, size,
domain->data + offset);
if (unlikely(ec_frame_send(frame) < 0)) {
printk(KERN_ERR "EtherCAT: Could not send process data"
" command!\n");
return -1;
}
// Warten
do {
ec_device_call_isr(&master->device);
rdtscl(end_ticks); // Empfangszeit nehmen
}
while (unlikely(master->device.state == EC_DEVICE_STATE_SENT
&& end_ticks - start_ticks < timeout_ticks));
master->bus_time = (end_ticks - start_ticks) * 1000 / cpu_khz;
if (unlikely(end_ticks - start_ticks >= timeout_ticks)) {
master->device.state = EC_DEVICE_STATE_READY;
master->frames_lost++;
ec_output_lost_frames(master);
return -1;
}
if (unlikely(ec_frame_receive(frame) < 0)) {
printk(KERN_ERR "EtherCAT: Receive error!\n");
return -1;
}
if (unlikely(frame->state != ec_frame_received)) {
printk(KERN_WARNING "EtherCAT: Process data command not"
" received!\n");
return -1;
}
working_counter_sum += frame->working_counter;
// Daten vom Rahmen in den Prozessdatenspeicher kopieren
memcpy(domain->data + offset, frame->data, size);
offset += size;
}
if (working_counter_sum != domain->response_count) {
domain->response_count = working_counter_sum;
printk(KERN_INFO "EtherCAT: Domain %08X state change - %i slaves"
" responding.\n", (unsigned int) domain, working_counter_sum);
}
return 0;
}
/*****************************************************************************/
EXPORT_SYMBOL(EtherCAT_rt_register_slave_field);
EXPORT_SYMBOL(EtherCAT_rt_domain_xio);
/*****************************************************************************/
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -11,9 +11,27 @@
#ifndef _EC_DOMAIN_H_
#define _EC_DOMAIN_H_
#include <linux/list.h>
#include "globals.h"
#include "slave.h"
#include "command.h"
#include "frame.h"
/*****************************************************************************/
/**
Datenfeld-Konfiguration.
*/
typedef struct
{
struct list_head list;
ec_slave_t *slave;
const ec_sync_t *sync;
uint32_t field_offset;
void **data_ptr;
}
ec_field_reg_t;
/*****************************************************************************/
@ -24,28 +42,37 @@
Menge von Slaves.
*/
typedef struct ec_domain
struct ec_domain
{
int number; /*<< Domänen-Identifikation */
ec_command_t command; /**< Kommando zum Senden und Empfangen der
Prozessdaten */
unsigned char data[EC_FRAME_SIZE]; /**< Prozessdaten-Array */
unsigned int data_size; /**< Größe der Prozessdaten */
unsigned int logical_offset; /**< Logische Basisaddresse */
unsigned int response_count; /**< Anzahl antwortender Slaves */
}
ec_domain_t;
ec_master_t *master; /**< EtherCAT-Master, zu der die Domäne gehört. */
unsigned char *data; /**< Prozessdaten */
unsigned int data_size; /**< Größe der Prozessdaten */
ec_frame_t frame; /**< EtherCAT-Frame für die Prozessdaten */
ec_domain_mode_t mode;
unsigned int timeout_us; /**< Timeout in Mikrosekunden. */
unsigned int base_address; /**< Logische Basisaddresse der Domain */
unsigned int response_count; /**< Anzahl antwortender Slaves */
struct list_head field_regs; /**< Liste der Datenfeldregistrierungen */
};
/*****************************************************************************/
void ec_domain_init(ec_domain_t *);
void ec_domain_init(ec_domain_t *, ec_master_t *, ec_domain_mode_t,
unsigned int);
void ec_domain_clear(ec_domain_t *);
int ec_domain_alloc(ec_domain_t *, uint32_t);
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

494
master/frame.c Normal file
View File

@ -0,0 +1,494 @@
/******************************************************************************
*
* f r a m e . c
*
* Methoden für einen EtherCAT-Frame.
*
* $Id$
*
*****************************************************************************/
#include <linux/slab.h>
#include <linux/delay.h>
#include "frame.h"
#include "master.h"
/*****************************************************************************/
#define EC_FUNC_HEADER \
frame->master = master; \
frame->state = ec_frame_ready; \
frame->index = 0; \
frame->working_counter = 0;
#define EC_FUNC_WRITE_FOOTER \
frame->data_length = length; \
memcpy(frame->data, data, length);
#define EC_FUNC_READ_FOOTER \
frame->data_length = length;
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-NPRD-Kommando.
Node-adressed physical read.
*/
void ec_frame_init_nprd(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t node_address,
/**< Adresse des Knotens (Slaves) */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length
/**< Länge der zu lesenden Daten */
)
{
if (unlikely(node_address == 0x0000))
printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n");
EC_FUNC_HEADER;
frame->type = ec_frame_type_nprd;
frame->address.physical.slave = node_address;
frame->address.physical.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-NPWR-Kommando.
Node-adressed physical write.
*/
void ec_frame_init_npwr(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t node_address,
/**< Adresse des Knotens (Slaves) */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length,
/**< Länge der zu schreibenden Daten */
const uint8_t *data
/**< Zeiger auf Speicher mit zu schreibenden Daten */
)
{
if (unlikely(node_address == 0x0000))
printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n");
EC_FUNC_HEADER;
frame->type = ec_frame_type_npwr;
frame->address.physical.slave = node_address;
frame->address.physical.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-APRD-Kommando.
Autoincrement physical read.
*/
void ec_frame_init_aprd(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t ring_position,
/**< Position des Slaves im Bus */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length
/**< Länge der zu lesenden Daten */
)
{
EC_FUNC_HEADER;
frame->type = ec_frame_type_aprd;
frame->address.physical.slave = (int16_t) ring_position * (-1);
frame->address.physical.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-APWR-Kommando.
Autoincrement physical write.
*/
void ec_frame_init_apwr(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t ring_position,
/**< Position des Slaves im Bus */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length,
/**< Länge der zu schreibenden Daten */
const uint8_t *data
/**< Zeiger auf Speicher mit zu schreibenden Daten */
)
{
EC_FUNC_HEADER;
frame->type = ec_frame_type_apwr;
frame->address.physical.slave = (int16_t) ring_position * (-1);
frame->address.physical.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-BRD-Kommando.
Broadcast read.
*/
void ec_frame_init_brd(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length
/**< Länge der zu lesenden Daten */
)
{
EC_FUNC_HEADER;
frame->type = ec_frame_type_brd;
frame->address.physical.slave = 0x0000;
frame->address.physical.mem = offset;
EC_FUNC_READ_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-BWR-Kommando.
Broadcast write.
*/
void ec_frame_init_bwr(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint16_t offset,
/**< Physikalische Speicheradresse im Slave */
unsigned int length,
/**< Länge der zu schreibenden Daten */
const uint8_t *data
/**< Zeiger auf Speicher mit zu schreibenden Daten */
)
{
EC_FUNC_HEADER;
frame->type = ec_frame_type_bwr;
frame->address.physical.slave = 0x0000;
frame->address.physical.mem = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Initialisiert ein EtherCAT-LRW-Kommando.
Logical read write.
*/
void ec_frame_init_lrw(ec_frame_t *frame,
/**< EtherCAT-Rahmen */
ec_master_t *master,
/**< EtherCAT-Master */
uint32_t offset,
/**< Logische Startadresse */
unsigned int length,
/**< Länge der zu lesenden/schreibenden Daten */
uint8_t *data
/**< Zeiger auf die Daten */
)
{
EC_FUNC_HEADER;
frame->type = ec_frame_type_lrw;
frame->address.logical = offset;
EC_FUNC_WRITE_FOOTER;
}
/*****************************************************************************/
/**
Sendet einen einzelnen EtherCAT-Rahmen.
\return 0 bei Erfolg, sonst < 0
*/
int ec_frame_send(ec_frame_t *frame /**< Rahmen zum Senden */)
{
unsigned int command_size, frame_size, i;
uint8_t *data;
if (unlikely(frame->master->debug_level > 0)) {
printk(KERN_DEBUG "EtherCAT: ec_frame_send\n");
}
if (unlikely(frame->state != ec_frame_ready)) {
printk(KERN_WARNING "EtherCAT: Frame not in \"ready\" state!\n");
}
command_size = frame->data_length + EC_COMMAND_HEADER_SIZE
+ EC_COMMAND_FOOTER_SIZE;
frame_size = command_size + EC_FRAME_HEADER_SIZE;
if (unlikely(frame_size > EC_MAX_FRAME_SIZE)) {
printk(KERN_ERR "EtherCAT: Frame too long (%i)!\n", frame_size);
return -1;
}
if (frame_size < EC_MIN_FRAME_SIZE) frame_size = EC_MIN_FRAME_SIZE;
if (unlikely(frame->master->debug_level > 0)) {
printk(KERN_DEBUG "EtherCAT: Frame length: %i\n", frame_size);
}
frame->index = frame->master->command_index;
frame->master->command_index = (frame->master->command_index + 1) % 0x0100;
if (unlikely(frame->master->debug_level > 0)) {
printk(KERN_DEBUG "EtherCAT: Sending command index 0x%X\n",
frame->index);
}
frame->state = ec_frame_sent;
// Zeiger auf Socket-Buffer holen
data = ec_device_prepare(&frame->master->device);
// EtherCAT frame header
data[0] = command_size & 0xFF;
data[1] = ((command_size & 0x700) >> 8) | 0x10;
data += EC_FRAME_HEADER_SIZE;
// EtherCAT command header
data[0] = frame->type;
data[1] = frame->index;
data[2] = frame->address.raw[0];
data[3] = frame->address.raw[1];
data[4] = frame->address.raw[2];
data[5] = frame->address.raw[3];
data[6] = frame->data_length & 0xFF;
data[7] = (frame->data_length & 0x700) >> 8;
data[8] = 0x00;
data[9] = 0x00;
data += EC_COMMAND_HEADER_SIZE;
if (likely(frame->type == ec_frame_type_apwr // Write commands
|| frame->type == ec_frame_type_npwr
|| frame->type == ec_frame_type_bwr
|| frame->type == ec_frame_type_lrw)) {
memcpy(data, frame->data, frame->data_length);
}
else { // Read commands
memset(data, 0x00, frame->data_length);
}
// EtherCAT command footer
data += frame->data_length;
data[0] = frame->working_counter & 0xFF;
data[1] = (frame->working_counter & 0xFF00) >> 8;
data += EC_COMMAND_FOOTER_SIZE;
// Pad with zeros
for (i = EC_FRAME_HEADER_SIZE + EC_COMMAND_HEADER_SIZE
+ frame->data_length + EC_COMMAND_FOOTER_SIZE;
i < EC_MIN_FRAME_SIZE; i++) {
*data++ = 0x00;
}
// Send frame
ec_device_send(&frame->master->device, frame_size);
return 0;
}
/*****************************************************************************/
/**
Empfängt einen gesendeten Rahmen.
\return 0 bei Erfolg, sonst < 0
*/
int ec_frame_receive(ec_frame_t *frame /**< Gesendeter Rahmen */)
{
unsigned int received_length, frame_length, data_length;
uint8_t *data;
uint8_t command_type, command_index;
ec_device_t *device;
if (unlikely(frame->state != ec_frame_sent)) {
printk(KERN_ERR "EtherCAT: Frame was not sent!\n");
return -1;
}
device = &frame->master->device;
if (!(received_length = ec_device_received(device))) return -1;
device->state = EC_DEVICE_STATE_READY;
if (unlikely(received_length < EC_FRAME_HEADER_SIZE)) {
printk(KERN_ERR "EtherCAT: Received frame with incomplete EtherCAT"
" frame header!\n");
ec_frame_print(frame);
return -1;
}
data = ec_device_data(device);
// Länge des gesamten Frames prüfen
frame_length = (data[0] & 0xFF) | ((data[1] & 0x07) << 8);
if (unlikely(frame_length > received_length)) {
printk(KERN_ERR "EtherCAT: Received corrupted frame (length does"
" not match)!\n");
ec_frame_print(frame);
return -1;
}
// Command header
data += EC_FRAME_HEADER_SIZE;
command_type = data[0];
command_index = data[1];
data_length = (data[6] & 0xFF) | ((data[7] & 0x07) << 8);
if (unlikely(EC_FRAME_HEADER_SIZE + EC_COMMAND_HEADER_SIZE
+ data_length + EC_COMMAND_FOOTER_SIZE > received_length)) {
printk(KERN_ERR "EtherCAT: Received frame with incomplete command"
" data!\n");
ec_frame_print(frame);
return -1;
}
if (unlikely(frame->type != command_type
|| frame->index != command_index
|| frame->data_length != data_length))
{
printk(KERN_WARNING "EtherCAT: WARNING - Send/Receive anomaly!\n");
ec_frame_print(frame);
ec_device_call_isr(device); // Empfangenes "vergessen"
return -1;
}
frame->state = ec_frame_received;
// Empfangene Daten in Kommandodatenspeicher kopieren
data += EC_COMMAND_HEADER_SIZE;
memcpy(frame->data, data, data_length);
data += data_length;
// Working-Counter setzen
frame->working_counter = (data[0] & 0xFF) | ((data[1] & 0xFF) << 8);
if (unlikely(frame->master->debug_level > 1)) {
ec_frame_print(frame);
}
return 0;
}
/*****************************************************************************/
/**
Sendet einen einzeln Rahmen und wartet auf dessen Empfang.
\return 0 bei Erfolg, sonst < 0
*/
int ec_frame_send_receive(ec_frame_t *frame
/**< Rahmen zum Senden/Empfangen */
)
{
unsigned int tries_left;
if (unlikely(ec_frame_send(frame) < 0)) {
printk(KERN_ERR "EtherCAT: Frame sending failed!\n");
return -1;
}
tries_left = 20;
do
{
udelay(1);
ec_device_call_isr(&frame->master->device);
tries_left--;
}
while (unlikely(!ec_device_received(&frame->master->device)
&& tries_left));
if (unlikely(!tries_left)) {
printk(KERN_ERR "EtherCAT: Frame timeout!\n");
return -1;
}
if (unlikely(ec_frame_receive(frame) < 0)) {
printk(KERN_ERR "EtherCAT: Frame receiving failed!\n");
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Gibt Frame-Inhalte zwecks Debugging aus.
*/
void ec_frame_print(const ec_frame_t *frame /**< EtherCAT-Frame */)
{
unsigned int i;
printk(KERN_DEBUG "EtherCAT: Frame contents (%i Bytes):\n",
frame->data_length);
printk(KERN_DEBUG);
for (i = 0; i < frame->data_length; i++)
{
printk("%02X ", frame->data[i]);
if ((i + 1) % 16 == 0) printk("\n" KERN_DEBUG);
}
printk("\n");
}
/*****************************************************************************/
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

132
master/frame.h Normal file
View File

@ -0,0 +1,132 @@
/******************************************************************************
*
* f r a m e . h
*
* Struktur für einen EtherCAT-Rahmen.
*
* $Id$
*
*****************************************************************************/
#ifndef _EC_FRAME_H_
#define _EC_FRAME_H_
#include "globals.h"
#include "../include/EtherCAT_rt.h"
/*****************************************************************************/
#define EC_MAX_DATA_SIZE (EC_MAX_FRAME_SIZE - EC_FRAME_HEADER_SIZE \
- EC_COMMAND_HEADER_SIZE \
- EC_COMMAND_FOOTER_SIZE)
/*****************************************************************************/
/**
Status eines EtherCAT-Rahmens.
*/
typedef enum {
ec_frame_ready, ec_frame_sent, ec_frame_received
}
ec_frame_state_t;
/*****************************************************************************/
/**
EtherCAT-Rahmen-Typ
*/
typedef enum
{
ec_frame_type_none = 0x00, /**< Dummy */
ec_frame_type_aprd = 0x01, /**< Auto-increment physical read */
ec_frame_type_apwr = 0x02, /**< Auto-increment physical write */
ec_frame_type_nprd = 0x04, /**< Node-addressed physical read */
ec_frame_type_npwr = 0x05, /**< Node-addressed physical write */
ec_frame_type_brd = 0x07, /**< Broadcast read */
ec_frame_type_bwr = 0x08, /**< Broadcast write */
ec_frame_type_lrw = 0x0C /**< Logical read/write */
}
ec_frame_type_t;
/*****************************************************************************/
/**
EtherCAT-Adresse.
Im EtherCAT-Rahmen sind 4 Bytes für die Adresse reserviert, die je nach
Kommandotyp, eine andere Bedeutung haben können: Bei Autoinkrementbefehlen
sind die ersten zwei Bytes die (negative) Autoinkrement-Adresse, bei Knoten-
adressierten Befehlen entsprechen sie der Knotenadresse. Das dritte und
vierte Byte entspricht in diesen Fällen der physikalischen Speicheradresse
auf dem Slave. Bei einer logischen Adressierung entsprechen alle vier Bytes
der logischen Adresse.
*/
typedef union
{
struct
{
uint16_t slave; /**< Adresse des Slaves */
uint16_t mem; /**< Physikalische Speicheradresse im Slave */
}
physical; /**< Physikalische Adresse */
uint32_t logical; /**< Logische Adresse */
uint8_t raw[4]; /**< Rohdaten für die Generierung des Frames */
}
ec_address_t;
/*****************************************************************************/
/**
EtherCAT-Frame.
*/
typedef struct
{
ec_master_t *master; /**< EtherCAT-Master */
ec_frame_type_t type; /**< Typ des Frames (APRD, NPWR, etc) */
ec_address_t address; /**< Adresse des/der Empfänger */
unsigned int data_length; /**< Länge der zu sendenden und/oder empfangenen
Daten */
ec_frame_state_t state; /**< Zustand des Kommandos */
uint8_t index; /**< Kommando-Index, mit dem der Frame gesendet wurde
(wird vom Master beim Senden gesetzt). */
uint16_t working_counter; /**< Working-Counter */
uint8_t data[EC_MAX_FRAME_SIZE]; /**< Rahmendaten */
}
ec_frame_t;
/*****************************************************************************/
void ec_frame_init_nprd(ec_frame_t *, ec_master_t *, uint16_t, uint16_t,
unsigned int);
void ec_frame_init_npwr(ec_frame_t *, ec_master_t *, uint16_t, uint16_t,
unsigned int, const unsigned char *);
void ec_frame_init_aprd(ec_frame_t *, ec_master_t *, uint16_t, uint16_t,
unsigned int);
void ec_frame_init_apwr(ec_frame_t *, ec_master_t *, uint16_t, uint16_t,
unsigned int, const unsigned char *);
void ec_frame_init_brd(ec_frame_t *, ec_master_t *, uint16_t, unsigned int);
void ec_frame_init_bwr(ec_frame_t *, ec_master_t *, uint16_t, unsigned int,
const unsigned char *);
void ec_frame_init_lrw(ec_frame_t *, ec_master_t *, uint32_t, unsigned int,
unsigned char *);
int ec_frame_send(ec_frame_t *);
int ec_frame_receive(ec_frame_t *);
int ec_frame_send_receive(ec_frame_t *);
void ec_frame_print(const ec_frame_t *);
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -13,65 +13,51 @@
/*****************************************************************************/
/**
Maximale Größe eines EtherCAT-Frames
*/
#define EC_FRAME_SIZE 1500
// EtherCAT-Protokoll
#define EC_MAX_FRAME_SIZE 1500 /**< Maximale Größe eines EtherCAT-Frames ohne
Ethernet-II-Header und -Prüfsumme*/
#define EC_MIN_FRAME_SIZE 46 /** Minimale Größe, s. o. */
#define EC_FRAME_HEADER_SIZE 2 /**< Größe des EtherCAT-Frame-Headers */
#define EC_COMMAND_HEADER_SIZE 10 /**< Größe eines EtherCAT-Kommando-Headers */
#define EC_COMMAND_FOOTER_SIZE 2 /**< Größe eines EtherCAT-Kommando-Footers */
#define EC_SYNC_SIZE 8 /**< Größe einer Sync-Manager-Konfigurationsseite */
#define EC_FMMU_SIZE 16 /**< Größe einer FMMU-Konfigurationsseite */
#define EC_MAX_FMMUS 16 /**< Maximale Anzahl FMMUs pro Slave */
/**
Maximale Anzahl der Prozessdatendomänen in einem Master
*/
#define EC_MAX_DOMAINS 10
/**
NULL-Define, falls noch nicht definiert.
*/
#define EC_MASTER_MAX_DOMAINS 10 /**< Maximale Anzahl Domänen eines Masters */
#ifndef NULL
#define NULL ((void *) 0)
#define NULL ((void *) 0) /**< NULL-Define, falls noch nicht definiert. */
#endif
/*****************************************************************************/
/**
EtherCAT-Kommando-Typ
*/
typedef enum
{
EC_COMMAND_NONE = 0x00, /**< Dummy */
EC_COMMAND_APRD = 0x01, /**< Auto-increment physical read */
EC_COMMAND_APWR = 0x02, /**< Auto-increment physical write */
EC_COMMAND_NPRD = 0x04, /**< Node-addressed physical read */
EC_COMMAND_NPWR = 0x05, /**< Node-addressed physical write */
EC_COMMAND_BRD = 0x07, /**< Broadcast read */
EC_COMMAND_BWR = 0x08, /**< Broadcast write */
EC_COMMAND_LRW = 0x0C /**< Logical read/write */
}
ec_command_type_t;
/*****************************************************************************/
/**
Zustand eines EtherCAT-Slaves
*/
typedef enum
{
EC_SLAVE_STATE_UNKNOWN = 0x00, /**< Status unbekannt */
EC_SLAVE_STATE_INIT = 0x01, /**< Init-Zustand (Keine Mailbox-
Kommunikation, Kein I/O) */
EC_SLAVE_STATE_PREOP = 0x02, /**< Pre-Operational (Mailbox-
Kommunikation, Kein I/O) */
EC_SLAVE_STATE_SAVEOP = 0x04, /**< Save-Operational (Mailbox-
Kommunikation und Input Update) */
EC_SLAVE_STATE_OP = 0x08, /**< Operational, (Mailbox-
Kommunikation und Input/Output Update) */
EC_ACK = 0x10 /**< Acknoledge-Bit beim Zustandswechsel
(dies ist kein eigener Zustand) */
EC_SLAVE_STATE_UNKNOWN = 0x00, /**< Status unbekannt */
EC_SLAVE_STATE_INIT = 0x01, /**< Init-Zustand (Keine Mailbox-
Kommunikation, Kein I/O) */
EC_SLAVE_STATE_PREOP = 0x02, /**< Pre-Operational (Mailbox-
Kommunikation, Kein I/O) */
EC_SLAVE_STATE_SAVEOP = 0x04, /**< Save-Operational (Mailbox-
Kommunikation und Input Update) */
EC_SLAVE_STATE_OP = 0x08, /**< Operational, (Mailbox-
Kommunikation und Input/Output Update) */
EC_ACK = 0x10 /**< Acknoledge-Bit beim Zustandswechsel
(dies ist kein eigener Zustand) */
}
ec_slave_state_t;
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
#include "device.h"
#include "slave.h"
#include "command.h"
#include "frame.h"
#include "domain.h"
/*****************************************************************************/
@ -27,25 +27,18 @@
struct ec_master
{
ec_slave_t *bus_slaves; /**< Array von Slaves auf dem Bus */
unsigned int bus_slaves_count; /**< Anzahl Slaves auf dem Bus */
ec_device_t device; /**< EtherCAT-Gerät */
unsigned int device_registered; /**< Ein Geraet hat sich registriert. */
unsigned char command_index; /**< Aktueller Kommando-Index */
unsigned char tx_data[EC_FRAME_SIZE]; /**< Statischer Speicher
für zu sendende Daten */
unsigned int tx_data_length; /**< Länge der Daten im Sendespeicher */
unsigned char rx_data[EC_FRAME_SIZE]; /**< Statische Speicher für
eine Kopie des Rx-Buffers
im EtherCAT-Gerät */
unsigned int rx_data_length; /**< Länge der Daten im Empfangsspeicher */
ec_domain_t domains[EC_MAX_DOMAINS]; /** Prozessdatendomänen */
unsigned int domain_count;
int debug_level; /**< Debug-Level im Master-Code */
unsigned int bus_time; /**< Letzte Bus-Zeit in Mikrosekunden */
unsigned int frames_lost; /**< Anzahl verlorene Frames */
unsigned long t_lost_output; /*<< Timer-Ticks bei der letzten Ausgabe von
verlorenen Frames */
ec_slave_t *slaves; /**< Array von Slaves auf dem Bus */
unsigned int slave_count; /**< Anzahl Slaves auf dem Bus */
ec_device_t device; /**< EtherCAT-Gerät */
unsigned int device_registered; /**< Ein Geraet hat sich registriert. */
uint8_t command_index; /**< Aktueller Kommando-Index */
ec_domain_t *domains[EC_MASTER_MAX_DOMAINS]; /** Prozessdatendomänen */
unsigned int domain_count;
int debug_level; /**< Debug-Level im Master-Code */
unsigned int bus_time; /**< Letzte Bus-Zeit in Mikrosekunden */
unsigned int frames_lost; /**< Anzahl verlorene Frames */
unsigned long t_lost_output; /*<< Timer-Ticks bei der letzten Ausgabe von
verlorenen Frames */
};
/*****************************************************************************/
@ -63,8 +56,9 @@ void ec_master_close(ec_master_t *);
int ec_scan_for_slaves(ec_master_t *);
ec_slave_t *ec_address(const ec_master_t *, const char *);
// Data
int ec_simple_send_receive(ec_master_t *, ec_command_t *);
// Misc
void ec_output_debug_data(const ec_master_t *);
void ec_output_lost_frames(ec_master_t *);
/*****************************************************************************/
@ -72,6 +66,6 @@ int ec_simple_send_receive(ec_master_t *, ec_command_t *);
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -69,46 +69,45 @@ MODULE_PARM_DESC(ec_master_count, "Number of EtherCAT master to initialize.");
int __init ec_init_module(void)
{
unsigned int i;
unsigned int i;
printk(KERN_ERR "EtherCAT: Master driver, %s\n", COMPILE_INFO);
printk(KERN_ERR "EtherCAT: Master driver, %s\n", COMPILE_INFO);
if (ec_master_count < 1) {
printk(KERN_ERR "EtherCAT: Error - Illegal"
" ec_master_count: %i\n", ec_master_count);
return -1;
}
if (ec_master_count < 1) {
printk(KERN_ERR "EtherCAT: Error - Illegal"
" ec_master_count: %i\n", ec_master_count);
return -1;
}
printk(KERN_ERR "EtherCAT: Initializing %i EtherCAT master(s)...\n",
ec_master_count);
printk(KERN_ERR "EtherCAT: Initializing %i EtherCAT master(s)...\n",
ec_master_count);
if ((ec_masters =
(ec_master_t *) kmalloc(sizeof(ec_master_t)
* ec_master_count,
GFP_KERNEL)) == NULL) {
printk(KERN_ERR "EtherCAT: Could not allocate"
" memory for EtherCAT master(s)!\n");
return -1;
}
if ((ec_masters =
(ec_master_t *) kmalloc(sizeof(ec_master_t)
* ec_master_count,
GFP_KERNEL)) == NULL) {
printk(KERN_ERR "EtherCAT: Could not allocate"
" memory for EtherCAT master(s)!\n");
return -1;
}
if ((ec_masters_reserved =
(int *) kmalloc(sizeof(int) * ec_master_count,
GFP_KERNEL)) == NULL) {
printk(KERN_ERR "EtherCAT: Could not allocate"
" memory for reservation flags!\n");
kfree(ec_masters);
return -1;
}
if ((ec_masters_reserved =
(int *) kmalloc(sizeof(int) * ec_master_count,
GFP_KERNEL)) == NULL) {
printk(KERN_ERR "EtherCAT: Could not allocate"
" memory for reservation flags!\n");
kfree(ec_masters);
return -1;
}
for (i = 0; i < ec_master_count; i++)
{
ec_master_init(&ec_masters[i]);
ec_masters_reserved[i] = 0;
}
for (i = 0; i < ec_master_count; i++) {
ec_master_init(ec_masters + i);
ec_masters_reserved[i] = 0;
}
printk(KERN_ERR "EtherCAT: Master driver initialized.\n");
printk(KERN_ERR "EtherCAT: Master driver initialized.\n");
return 0;
return 0;
}
/*****************************************************************************/
@ -121,26 +120,22 @@ int __init ec_init_module(void)
void __exit ec_cleanup_module(void)
{
unsigned int i;
unsigned int i;
printk(KERN_ERR "EtherCAT: Cleaning up master driver...\n");
printk(KERN_ERR "EtherCAT: Cleaning up master driver...\n");
if (ec_masters)
{
for (i = 0; i < ec_master_count; i++)
{
if (ec_masters_reserved[i]) {
printk(KERN_WARNING "EtherCAT: Warning -"
" Master %i is still in use!\n", i);
}
ec_master_clear(&ec_masters[i]);
if (ec_masters) {
for (i = 0; i < ec_master_count; i++) {
if (ec_masters_reserved[i]) {
printk(KERN_WARNING "EtherCAT: Warning -"
" Master %i is still in use!\n", i);
}
ec_master_clear(&ec_masters[i]);
}
kfree(ec_masters);
}
kfree(ec_masters);
}
printk(KERN_ERR "EtherCAT: Master driver cleaned up.\n");
printk(KERN_ERR "EtherCAT: Master driver cleaned up.\n");
}
/******************************************************************************
@ -158,8 +153,8 @@ void __exit ec_cleanup_module(void)
@param module Zeiger auf das Modul (fuer try_module_lock())
@return 0, wenn alles o.k.,
< 0, wenn bereits ein Geraet registriert oder das Geraet nicht
geoeffnet werden konnte.
< 0, wenn bereits ein Geraet registriert oder das Geraet nicht
geoeffnet werden konnte.
*/
ec_device_t *EtherCAT_dev_register(unsigned int master_index,
@ -168,42 +163,39 @@ ec_device_t *EtherCAT_dev_register(unsigned int master_index,
struct pt_regs *),
struct module *module)
{
ec_device_t *ecd;
ec_master_t *master;
ec_device_t *ecd;
ec_master_t *master;
if (master_index >= ec_master_count) {
printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", master_index);
return NULL;
}
if (master_index >= ec_master_count) {
printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", master_index);
return NULL;
}
if (!dev) {
printk("EtherCAT: Device is NULL!\n");
return NULL;
}
if (!dev) {
printk("EtherCAT: Device is NULL!\n");
return NULL;
}
master = ec_masters + master_index;
master = ec_masters + master_index;
if (master->device_registered) {
printk(KERN_ERR "EtherCAT: Master %i already has a device!\n",
master_index);
return NULL;
}
if (master->device_registered) {
printk(KERN_ERR "EtherCAT: Master %i already has a device!\n",
master_index);
return NULL;
}
ecd = &master->device;
ecd = &master->device;
if (ec_device_init(ecd) < 0) {
return NULL;
}
if (ec_device_init(ecd) < 0) return NULL;
ecd->dev = dev;
ecd->tx_skb->dev = dev;
ecd->rx_skb->dev = dev;
ecd->isr = isr;
ecd->module = module;
ecd->dev = dev;
ecd->tx_skb->dev = dev;
ecd->isr = isr;
ecd->module = module;
master->device_registered = 1;
master->device_registered = 1;
return ecd;
return ecd;
}
/*****************************************************************************/
@ -217,22 +209,23 @@ ec_device_t *EtherCAT_dev_register(unsigned int master_index,
void EtherCAT_dev_unregister(unsigned int master_index, ec_device_t *ecd)
{
ec_master_t *master;
ec_master_t *master;
if (master_index >= ec_master_count) {
printk(KERN_WARNING "EtherCAT: Master %i does not exist!\n", master_index);
return;
}
if (master_index >= ec_master_count) {
printk(KERN_WARNING "EtherCAT: Master %i does not exist!\n",
master_index);
return;
}
master = ec_masters + master_index;
master = ec_masters + master_index;
if (!master->device_registered || &master->device != ecd) {
printk(KERN_ERR "EtherCAT: Unable to unregister device!\n");
return;
}
if (!master->device_registered || &master->device != ecd) {
printk(KERN_ERR "EtherCAT: Unable to unregister device!\n");
return;
}
master->device_registered = 0;
ec_device_clear(ecd);
master->device_registered = 0;
ec_device_clear(ecd);
}
/******************************************************************************
@ -252,54 +245,54 @@ void EtherCAT_dev_unregister(unsigned int master_index, ec_device_t *ecd)
ec_master_t *EtherCAT_rt_request_master(unsigned int index)
{
ec_master_t *master;
ec_master_t *master;
if (index < 0 || index >= ec_master_count) {
printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", index);
goto req_return;
}
if (index < 0 || index >= ec_master_count) {
printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", index);
goto req_return;
}
if (ec_masters_reserved[index]) {
printk(KERN_ERR "EtherCAT: Master %i already in use!\n", index);
goto req_return;
}
if (ec_masters_reserved[index]) {
printk(KERN_ERR "EtherCAT: Master %i already in use!\n", index);
goto req_return;
}
master = &ec_masters[index];
master = &ec_masters[index];
if (!master->device_registered) {
printk(KERN_ERR "EtherCAT: Master %i has no device assigned yet!\n",
index);
goto req_return;
}
if (!master->device_registered) {
printk(KERN_ERR "EtherCAT: Master %i has no device assigned yet!\n",
index);
goto req_return;
}
if (!try_module_get(master->device.module)) {
printk(KERN_ERR "EtherCAT: Could not reserve device module!\n");
goto req_return;
}
if (!try_module_get(master->device.module)) {
printk(KERN_ERR "EtherCAT: Failed to reserve device module!\n");
goto req_return;
}
if (ec_master_open(master) < 0) {
printk(KERN_ERR "EtherCAT: Could not open device!\n");
goto req_module_put;
}
if (ec_master_open(master) < 0) {
printk(KERN_ERR "EtherCAT: Failed to open device!\n");
goto req_module_put;
}
if (ec_scan_for_slaves(master) != 0) {
printk(KERN_ERR "EtherCAT: Could not scan for slaves!\n");
goto req_close;
}
if (ec_scan_for_slaves(master) != 0) {
printk(KERN_ERR "EtherCAT: Bus scan failed!\n");
goto req_close;
}
ec_masters_reserved[index] = 1;
printk(KERN_INFO "EtherCAT: Reserved master %i.\n", index);
ec_masters_reserved[index] = 1;
printk(KERN_INFO "EtherCAT: Reserved master %i.\n", index);
return master;
return master;
req_close:
ec_master_close(master);
ec_master_close(master);
req_module_put:
module_put(master->device.module);
module_put(master->device.module);
req_return:
return NULL;
return NULL;
}
/*****************************************************************************/
@ -312,32 +305,32 @@ ec_master_t *EtherCAT_rt_request_master(unsigned int index)
void EtherCAT_rt_release_master(ec_master_t *master)
{
unsigned int i;
unsigned int i;
for (i = 0; i < ec_master_count; i++)
{
if (&ec_masters[i] == master)
for (i = 0; i < ec_master_count; i++)
{
if (!master->device_registered) {
printk(KERN_WARNING "EtherCAT: Could not release device"
"module because of no device!\n");
return;
}
if (&ec_masters[i] == master)
{
if (!master->device_registered) {
printk(KERN_WARNING "EtherCAT: Failed to release device"
"module because of no device!\n");
return;
}
ec_master_close(master);
ec_master_reset(master);
ec_master_close(master);
ec_master_reset(master);
module_put(master->device.module);
ec_masters_reserved[i] = 0;
module_put(master->device.module);
ec_masters_reserved[i] = 0;
printk(KERN_INFO "EtherCAT: Released master %i.\n", i);
printk(KERN_INFO "EtherCAT: Released master %i.\n", i);
return;
return;
}
}
}
printk(KERN_WARNING "EtherCAT: Master %X was never requested!\n",
(unsigned int) master);
printk(KERN_WARNING "EtherCAT: Master %X was never requested!\n",
(unsigned int) master);
}
/*****************************************************************************/
@ -354,6 +347,6 @@ EXPORT_SYMBOL(EtherCAT_rt_release_master);
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -9,48 +9,431 @@
*****************************************************************************/
#include <linux/module.h>
#include <linux/delay.h>
#include "globals.h"
#include "slave.h"
#include "frame.h"
/*****************************************************************************/
/**
EtherCAT-Slave-Konstruktor.
Initialisiert einen EtherCAT-Slave.
ACHTUNG! Dieser Konstruktor wird quasi nie aufgerufen. Bitte immer das
Makro ECAT_INIT_SLAVE() in ec_slave.h anpassen!
@param slave Zeiger auf den zu initialisierenden Slave
*/
void ec_slave_init(ec_slave_t *slave)
void ec_slave_init(ec_slave_t *slave, /**< EtherCAT-Slave */
ec_master_t *master /**< EtherCAT-Master */
)
{
slave->base_type = 0;
slave->base_revision = 0;
slave->base_build = 0;
slave->ring_position = 0;
slave->station_address = 0;
slave->sii_vendor_id = 0;
slave->sii_product_code = 0;
slave->sii_revision_number = 0;
slave->sii_serial_number = 0;
slave->type = NULL;
slave->logical_address = 0;
slave->process_data = NULL;
slave->private_data = NULL;
slave->configure = NULL;
slave->registered = 0;
slave->domain = 0;
slave->error_reported = 0;
slave->master = master;
slave->base_type = 0;
slave->base_revision = 0;
slave->base_build = 0;
slave->base_fmmu_count = 0;
slave->base_sync_count = 0;
slave->ring_position = 0;
slave->station_address = 0;
slave->sii_vendor_id = 0;
slave->sii_product_code = 0;
slave->sii_revision_number = 0;
slave->sii_serial_number = 0;
slave->type = NULL;
slave->registered = 0;
slave->fmmu_count = 0;
}
/*****************************************************************************/
/**
EtherCAT-Slave-Destruktor.
*/
void ec_slave_clear(ec_slave_t *slave /**< EtherCAT-Slave */)
{
// Nichts freizugeben
}
/*****************************************************************************/
/**
Liest alle benötigten Informationen aus einem Slave.
*/
int ec_slave_fetch(ec_slave_t *slave /**< EtherCAT-Slave */)
{
ec_frame_t frame;
// Read base data
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
0x0000, 6);
if (unlikely(ec_frame_send_receive(&frame))) return -1;
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Slave %i did not respond while reading base"
" data!\n", slave->ring_position);
return -1;
}
slave->base_type = frame.data[0];
slave->base_revision = frame.data[1];
slave->base_build = frame.data[2] | (frame.data[3] << 8);
slave->base_fmmu_count = frame.data[4];
slave->base_sync_count = frame.data[5];
if (slave->base_fmmu_count > EC_MAX_FMMUS)
slave->base_fmmu_count = EC_MAX_FMMUS;
// Read identification from "Slave Information Interface" (SII)
if (unlikely(ec_slave_sii_read(slave, 0x0008, &slave->sii_vendor_id))) {
printk(KERN_ERR "EtherCAT: Could not read SII vendor id!\n");
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000A, &slave->sii_product_code))) {
printk(KERN_ERR "EtherCAT: Could not read SII product code!\n");
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000C,
&slave->sii_revision_number))) {
printk(KERN_ERR "EtherCAT: Could not read SII revision number!\n");
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000E,
&slave->sii_serial_number))) {
printk(KERN_ERR "EtherCAT: Could not read SII serial number!\n");
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Liest Daten aus dem Slave-Information-Interface
eines EtherCAT-Slaves.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_sii_read(ec_slave_t *slave,
/**< EtherCAT-Slave */
unsigned short int offset,
/**< Adresse des zu lesenden SII-Registers */
unsigned int *target
/**< Zeiger auf einen 4 Byte großen Speicher zum Ablegen
der Daten */
)
{
ec_frame_t frame;
unsigned char data[10];
unsigned int tries_left;
// Initiate read operation
data[0] = 0x00;
data[1] = 0x01;
data[2] = offset & 0xFF;
data[3] = (offset & 0xFF00) >> 8;
data[4] = 0x00;
data[5] = 0x00;
ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x502, 6,
data);
if (unlikely(ec_frame_send_receive(&frame))) return -1;
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: SII-read - Slave %i did not respond!\n",
slave->ring_position);
return -1;
}
// Der Slave legt die Informationen des Slave-Information-Interface
// in das Datenregister und löscht daraufhin ein Busy-Bit. Solange
// den Status auslesen, bis das Bit weg ist.
tries_left = 100;
while (likely(tries_left))
{
udelay(10);
ec_frame_init_nprd(&frame, slave->master, slave->station_address, 0x502,
10);
if (unlikely(ec_frame_send_receive(&frame))) return -1;
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: SII-read status -"
" Slave %i did not respond!\n", slave->ring_position);
return -1;
}
if (likely((frame.data[1] & 0x81) == 0)) {
memcpy(target, frame.data + 6, 4);
break;
}
tries_left--;
}
if (unlikely(!tries_left)) {
printk(KERN_WARNING "EtherCAT: SSI-read. Slave %i timed out!\n",
slave->ring_position);
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Bestätigt einen Fehler beim Zustandswechsel.
FIXME Funktioniert noch nicht...
*/
void ec_slave_state_ack(ec_slave_t *slave,
/**< Slave, dessen Zustand geändert werden soll */
uint8_t state
/**< Alter Zustand */
)
{
ec_frame_t frame;
unsigned char data[2];
unsigned int tries_left;
data[0] = state | EC_ACK;
data[1] = 0x00;
ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120,
2, data);
if (unlikely(ec_frame_send_receive(&frame) != 0)) {
printk(KERN_ERR "EtherCAT: Could no acknowledge state %02X - Unable to"
" send!\n", state);
return;
}
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X - Slave"
" %i did not respond!\n", state, slave->ring_position);
return;
}
tries_left = 100;
while (likely(tries_left))
{
udelay(10);
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
0x0130, 2);
if (unlikely(ec_frame_send_receive(&frame) != 0)) {
printk(KERN_ERR "EtherCAT: Could not check state acknowledgement"
" %02X - Unable to send!\n", state);
return;
}
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Could not check state acknowledgement"
" %02X - Slave %i did not respond!\n", state,
slave->ring_position);
return;
}
if (unlikely(frame.data[0] != state)) {
printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X on"
" slave %i (code %02X)!\n", state, slave->ring_position,
frame.data[0]);
return;
}
if (likely(frame.data[0] == state)) {
printk(KERN_INFO "EtherCAT: Acknowleged state %02X on slave %i.\n",
state, slave->ring_position);
return;
}
tries_left--;
}
if (unlikely(!tries_left)) {
printk(KERN_ERR "EtherCAT: Could not check state acknowledgement %02X"
" of slave %i - Timeout while checking!\n", state,
slave->ring_position);
return;
}
}
/*****************************************************************************/
/**
Ändert den Zustand eines Slaves.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_state_change(ec_slave_t *slave,
/**< Slave, dessen Zustand geändert werden soll */
uint8_t state
/**< Neuer Zustand */
)
{
ec_frame_t frame;
unsigned char data[2];
unsigned int tries_left;
data[0] = state;
data[1] = 0x00;
ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120,
2, data);
if (unlikely(ec_frame_send_receive(&frame) != 0)) {
printk(KERN_ERR "EtherCAT: Could not set state %02X - Unable to"
" send!\n", state);
return -1;
}
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i did not"
" respond!\n", state, slave->ring_position);
return -1;
}
tries_left = 100;
while (likely(tries_left))
{
udelay(10);
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
0x0130, 2);
if (unlikely(ec_frame_send_receive(&frame) != 0)) {
printk(KERN_ERR "EtherCAT: Could not check state %02X - Unable to"
" send!\n", state);
return -1;
}
if (unlikely(frame.working_counter != 1)) {
printk(KERN_ERR "EtherCAT: Could not check state %02X - Slave %i"
" did not respond!\n", state, slave->ring_position);
return -1;
}
if (unlikely(frame.data[0] & 0x10)) { // State change error
printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i"
" refused state change (code %02X)!\n", state,
slave->ring_position, frame.data[0]);
ec_slave_state_ack(slave, frame.data[0] & 0x0F);
return -1;
}
if (likely(frame.data[0] == (state & 0x0F))) {
// State change successful
break;
}
tries_left--;
}
if (unlikely(!tries_left)) {
printk(KERN_ERR "EtherCAT: Could not check state %02X of slave %i -"
" Timeout while checking!\n", state,
slave->ring_position);
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Merkt eine FMMU-Konfiguration vor.
Die FMMU wird so konfiguriert, dass sie den gesamten Datenbereich des
entsprechenden Sync-Managers abdeckt. Für jede Domäne werden separate
FMMUs konfiguriert.
Wenn die entsprechende FMMU bereits konfiguriert ist, wird dies als
Erfolg zurückgegeben.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_set_fmmu(ec_slave_t *slave, /**< EtherCAT-Slave */
const ec_domain_t *domain, /**< Domäne */
const ec_sync_t *sync /**< Sync-Manager */
)
{
unsigned int i;
// FMMU schon vorgemerkt?
for (i = 0; i < slave->fmmu_count; i++)
if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync)
return 0;
if (slave->fmmu_count >= slave->base_fmmu_count) {
printk(KERN_ERR "EtherCAT: Slave %i supports only %i FMMUs.\n",
slave->ring_position, slave->base_fmmu_count);
return -1;
}
slave->fmmus[slave->fmmu_count].domain = domain;
slave->fmmus[slave->fmmu_count].sync = sync;
slave->fmmus[slave->fmmu_count].logical_start_address = 0;
slave->fmmu_count++;
slave->registered = 1;
return 0;
}
/*****************************************************************************/
/**
Gibt alle Informationen über einen EtherCAT-Slave aus.
*/
void ec_slave_print(const ec_slave_t *slave /**< EtherCAT-Slave */)
{
printk(KERN_INFO "--- EtherCAT slave information ---\n");
if (slave->type) {
printk(KERN_INFO " Vendor \"%s\", Product \"%s\": %s\n",
slave->type->vendor_name, slave->type->product_name,
slave->type->description);
}
else {
printk(KERN_INFO " *** This slave has no type information! ***\n");
}
printk(KERN_INFO " Ring position: %i, Station address: 0x%04X\n",
slave->ring_position, slave->station_address);
printk(KERN_INFO " Base information:\n");
printk(KERN_INFO " Type %u, Revision %i, Build %i\n",
slave->base_type, slave->base_revision, slave->base_build);
printk(KERN_INFO " Supported FMMUs: %i, Sync managers: %i\n",
slave->base_fmmu_count, slave->base_sync_count);
printk(KERN_INFO " Slave information interface:\n");
printk(KERN_INFO " Vendor-ID: 0x%08X, Product code: 0x%08X\n",
slave->sii_vendor_id, slave->sii_product_code);
printk(KERN_INFO " Revision number: 0x%08X, Serial number: 0x%08X\n",
slave->sii_revision_number, slave->sii_serial_number);
}
/*****************************************************************************/
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -15,12 +15,68 @@
/*****************************************************************************/
// ec_slave_t ist in EtherCAT_rt.h ...
/**
FMMU-Konfiguration.
*/
typedef struct
{
const ec_domain_t *domain;
const ec_sync_t *sync;
uint32_t logical_start_address;
}
ec_fmmu_t;
/*****************************************************************************/
// Slave construction and deletion
void ec_slave_init(ec_slave_t *);
/**
EtherCAT-Slave
*/
struct ec_slave
{
ec_master_t *master; /**< EtherCAT-Master, zu dem der Slave gehört. */
// Addresses
uint16_t ring_position; /**< Position des Slaves im Bus */
uint16_t station_address; /**< Konfigurierte Slave-Adresse */
// Base data
uint8_t base_type; /**< Slave-Typ */
uint8_t base_revision; /**< Revision */
uint16_t base_build; /**< Build-Nummer */
uint16_t base_fmmu_count; /**< Anzahl unterstützter FMMUs */
uint16_t base_sync_count; /**< Anzahl unterstützter Sync-Manager */
// Slave information interface
uint32_t sii_vendor_id; /**< Identifikationsnummer des Herstellers */
uint32_t sii_product_code; /**< Herstellerspezifischer Produktcode */
uint32_t sii_revision_number; /**< Revisionsnummer */
uint32_t sii_serial_number; /**< Seriennummer der Klemme */
const ec_slave_type_t *type; /**< Zeiger auf die Beschreibung
des Slave-Typs */
uint8_t registered; /**< Der Slave wurde registriert */
ec_fmmu_t fmmus[EC_MAX_FMMUS]; /**< FMMU-Konfigurationen */
uint8_t fmmu_count; /**< Wieviele FMMUs schon benutzt sind. */
};
/*****************************************************************************/
// Slave construction/destruction
void ec_slave_init(ec_slave_t *, ec_master_t *);
void ec_slave_clear(ec_slave_t *);
// Slave control
int ec_slave_fetch(ec_slave_t *);
int ec_slave_sii_read(ec_slave_t *, unsigned short, unsigned int *);
int ec_slave_state_change(ec_slave_t *, uint8_t);
int ec_slave_set_fmmu(ec_slave_t *, const ec_domain_t *, const ec_sync_t *);
// Misc
void ec_slave_print(const ec_slave_t *);
/*****************************************************************************/
@ -28,6 +84,6 @@ void ec_slave_init(ec_slave_t *);
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:2 ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -15,134 +15,148 @@
/*****************************************************************************/
/*
Konfigurationen der Sync-Manager
Byte 1-2: Physical Start Address
Byte 3-4: Data Length
Byte 5: Control Byte
Byte 6: Status Byte (read only)
Byte 7-8: Enable
*/
unsigned char sm0_multi[] = {0x00, 0x18, 0xF6, 0x00, 0x26, 0x00, 0x01, 0x00};
unsigned char sm1_multi[] = {0xF6, 0x18, 0xF6, 0x00, 0x22, 0x00, 0x01, 0x00};
unsigned char sm0_1014[] = {0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00};
unsigned char sm0_20xx[] = {0x00, 0x0F, 0x01, 0x00, 0x46, 0x00, 0x01, 0x00};
unsigned char sm2_31xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00};
unsigned char sm3_31xx[] = {0x00, 0x11, 0x06, 0x00, 0x20, 0x00, 0x01, 0x00};
unsigned char sm2_41xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00};
unsigned char sm2_5001[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00};
unsigned char sm3_5001[] = {0x00, 0x11, 0x05, 0x00, 0x20, 0x00, 0x01, 0x00};
unsigned char sm2_5101[] = {0x00, 0x10, 0x03, 0x00, 0x24, 0x00, 0x01, 0x00};
unsigned char sm3_5101[] = {0x00, 0x11, 0x05, 0x00, 0x20, 0x00, 0x01, 0x00};
/*
Konfigurationen der Memory-Management-Units
Byte 1-4: Logical Start Address (is set later)
Byte 5-6: Length
Byte 7: Logical start bit
Byte 8: Logical end bit
Byte 9-10: Physical start address
Byte 11: Physical start bit
Byte 12: Direction (1: in, 2: out)
Byte 13-14: Channel enable
Byte 15-16: Reserved
*/
unsigned char fmmu0_1014[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07,
0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00};
unsigned char fmmu0_20xx[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07,
0x00, 0x0F, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00};
unsigned char fmmu0_31xx[] = {0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07,
0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00};
unsigned char fmmu0_41xx[] = {0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07,
0x00, 0x10, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00};
unsigned char fmmu0_5001[] = {0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07,
0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00};
unsigned char fmmu0_5101[] = {0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07,
0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00};
const ec_sync_t mailbox_sm0 = {0x1800, 246, 0x26, {NULL}};
const ec_sync_t mailbox_sm1 = {0x18F6, 246, 0x22, {NULL}};
/*****************************************************************************/
/* Klemmen-Objekte */
ec_slave_type_t Beckhoff_EK1100 =
{
"Beckhoff", "EK1100", "Bus Coupler",
EC_NOSYNC_SLAVE, NULL, NULL, NULL, NULL, NULL, 0
/*****************************************************************************/
const ec_slave_type_t Beckhoff_EK1100 = {
"Beckhoff", "EK1100", "Bus Coupler", EC_NOSYNC_SLAVE,
{NULL} // Keine Sync-Manager
};
ec_slave_type_t Beckhoff_EK1110 =
{
"Beckhoff", "EK1110", "Extension terminal",
EC_NOSYNC_SLAVE, NULL, NULL, NULL, NULL, NULL, 0
/*****************************************************************************/
const ec_slave_type_t Beckhoff_EK1110 = {
"Beckhoff", "EK1110", "Extension terminal", EC_NOSYNC_SLAVE,
{NULL} // Keine Sync-Manager
};
ec_slave_type_t Beckhoff_EL1014 =
{
"Beckhoff", "EL1014", "4x Digital Input",
EC_SIMPLE_SLAVE, sm0_1014, NULL, NULL, NULL, fmmu0_1014, 1
/*****************************************************************************/
const ec_field_t el1014_in = {ec_ipvalue, 1};
const ec_sync_t el1014_sm0 = { // Inputs
0x1000, 1, 0x00,
{&el1014_in, NULL}
};
ec_slave_type_t Beckhoff_EL2004 =
{
"Beckhoff", "EL2004", "4x Digital Output",
EC_SIMPLE_SLAVE, sm0_20xx, NULL, NULL, NULL, fmmu0_20xx, 1
const ec_slave_type_t Beckhoff_EL1014 = {
"Beckhoff", "EL1014", "4x Digital Input", EC_SIMPLE_SLAVE,
{&el1014_sm0, NULL}
};
ec_slave_type_t Beckhoff_EL2032 =
{
"Beckhoff", "EL2032", "2x Digital Output (2A)",
EC_SIMPLE_SLAVE, sm0_20xx, NULL, NULL, NULL, fmmu0_20xx, 1
/*****************************************************************************/
const ec_field_t el20XX_out = {ec_opvalue, 1};
const ec_sync_t el20XX_sm0 = {
0x0F00, 1, 0x46,
{&el20XX_out, NULL}
};
ec_slave_type_t Beckhoff_EL3102 =
{
"Beckhoff", "EL3102", "2x Analog Input diff.",
EC_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, fmmu0_31xx, 6
const ec_slave_type_t Beckhoff_EL2004 = {
"Beckhoff", "EL2004", "4x Digital Output", EC_SIMPLE_SLAVE,
{&el20XX_sm0, NULL}
};
ec_slave_type_t Beckhoff_EL3162 =
{
"Beckhoff", "EL3162", "2x Analog Input",
EC_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, fmmu0_31xx, 6
const ec_slave_type_t Beckhoff_EL2032 = {
"Beckhoff", "EL2032", "2x Digital Output (2A)", EC_SIMPLE_SLAVE,
{&el20XX_sm0, NULL}
};
ec_slave_type_t Beckhoff_EL4102 =
{
"Beckhoff", "EL4102", "2x Analog Output",
EC_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_41xx, NULL, fmmu0_41xx, 4
/*****************************************************************************/
const ec_field_t el31X2_st1 = {ec_status, 1};
const ec_field_t el31X2_ip1 = {ec_ipvalue, 2};
const ec_field_t el31X2_st2 = {ec_status, 1};
const ec_field_t el31X2_ip2 = {ec_ipvalue, 2};
const ec_sync_t el31X2_sm2 = {
0x1000, 4, 0x24,
{NULL}
};
ec_slave_type_t Beckhoff_EL4132 =
{
"Beckhoff", "EL4132", "2x Analog Output diff.",
EC_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_41xx, NULL, fmmu0_41xx, 4
const ec_sync_t el31X2_sm3 = {
0x1100, 6, 0x20,
{&el31X2_st1, &el31X2_ip1, &el31X2_st2, &el31X2_ip2, NULL}
};
ec_slave_type_t Beckhoff_EL5001 =
{
"Beckhoff", "EL5001", "SSI-Interface",
EC_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_5001, sm3_5001, fmmu0_5001, 5
const ec_slave_type_t Beckhoff_EL3102 = {
"Beckhoff", "EL3102", "2x Analog Input diff.", EC_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el31X2_sm2, &el31X2_sm2, NULL}
};
ec_slave_type_t Beckhoff_EL5101 =
const ec_slave_type_t Beckhoff_EL3162 = {
"Beckhoff", "EL3102", "2x Analog Input", EC_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el31X2_sm2, &el31X2_sm2, NULL}
};
/*****************************************************************************/
const ec_field_t el41X2_op = {ec_opvalue, 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_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el41X2_sm2, NULL}
};
const ec_slave_type_t Beckhoff_EL4132 = {
"Beckhoff", "EL4132", "2x Analog Output diff.", EC_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el41X2_sm2, NULL}
};
/*****************************************************************************/
const ec_field_t el5001_st = {ec_status, 1};
const ec_field_t el5001_ip = {ec_ipvalue, 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_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el5001_sm2, &el5001_sm3, NULL}
};
/*****************************************************************************/
const ec_field_t el5101_ct = {ec_control, 1};
const ec_field_t el5101_op = {ec_opvalue, 2};
const ec_field_t el5101_st = {ec_status, 1};
const ec_field_t el5101_ip = {ec_ipvalue, 2};
const ec_field_t el5101_la = {ec_ipvalue, 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_MAILBOX_SLAVE, sm0_multi, sm1_multi, sm2_5101, sm3_5101, fmmu0_5101, 5
"Beckhoff", "EL5101", "Incremental Encoder Interface", EC_MAILBOX_SLAVE,
{&mailbox_sm0, &mailbox_sm1, &el5101_sm2, &el5101_sm3, NULL}
};
/*****************************************************************************/
@ -167,10 +181,14 @@ ec_slave_ident_t slave_idents[] =
{0x00000002, 0x10063052, &Beckhoff_EL4102},
{0x00000002, 0x10243052, &Beckhoff_EL4132},
{0x00000002, 0x13893052, &Beckhoff_EL5001},
{0x00000002, 0x13ED3052, &Beckhoff_EL5101}
{0x00000002, 0x13ED3052, &Beckhoff_EL5101},
{}
};
unsigned int slave_ident_count = sizeof(slave_idents)
/ sizeof(ec_slave_ident_t);
/*****************************************************************************/
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -11,10 +11,17 @@
#ifndef _EC_TYPES_H_
#define _EC_TYPES_H_
#include <linux/types.h>
#include "../include/EtherCAT_rt.h"
/*****************************************************************************/
#define EC_MAX_FIELDS 10
#define EC_MAX_SYNC 16
/*****************************************************************************/
/**
Features eines EtherCAT-Slaves.
@ -31,6 +38,34 @@ ec_slave_features_t;
/*****************************************************************************/
/**
Prozessdatenfeld.
*/
typedef struct
{
ec_field_type_t type;
unsigned int size;
}
ec_field_t;
/*****************************************************************************/
/**
Sync-Manager.
*/
typedef struct
{
uint16_t physical_start_address;
uint16_t size;
uint8_t control_byte;
const ec_field_t *fields[EC_MAX_FIELDS];
}
ec_sync_t;
/*****************************************************************************/
/**
Beschreibung eines EtherCAT-Slave-Typs.
@ -39,28 +74,16 @@ ec_slave_features_t;
Slave-internen Sync-Manager und FMMU's.
*/
struct ec_slave_type
typedef struct ec_slave_type
{
const char *vendor_name; /**< Name des Herstellers */
const char *product_name; /**< Name des Slaves-Typs */
const char *product_desc; /**< Genauere Beschreibung des Slave-Typs */
const char *description; /**< Genauere Beschreibung des Slave-Typs */
ec_slave_features_t features; /**< Features des Slave-Typs */
const unsigned char *sm0; /**< Konfigurationsdaten des
ersten Sync-Managers */
const unsigned char *sm1; /**< Konfigurationsdaten des
zweiten Sync-Managers */
const unsigned char *sm2; /**< Konfigurationsdaten des
dritten Sync-Managers */
const unsigned char *sm3; /**< Konfigurationsdaten des
vierten Sync-Managers */
const unsigned char *fmmu0; /**< Konfigurationsdaten
der ersten FMMU */
unsigned int process_data_size; /**< Länge der Prozessdaten in Bytes */
};
const ec_sync_t *sync_managers[EC_MAX_SYNC]; /**< Sync-Manager
Konfigurationen */
}
ec_slave_type_t;
/*****************************************************************************/
@ -81,9 +104,13 @@ ec_slave_ident_t;
extern ec_slave_ident_t slave_idents[]; /**< Statisches Array der
Slave-Identifikationen */
extern unsigned int slave_ident_count; /**< Anzahl der vorhandenen
Slave-Identifikationen */
/*****************************************************************************/
#endif
/* Emacs-Konfiguration
;;; Local Variables: ***
;;; c-basic-offset:4 ***
;;; End: ***
*/

View File

@ -17,48 +17,51 @@
/*****************************************************************************/
ec_master_t *master = NULL;
ec_slave_t *s_in, *s_out, *s_ssi;
#define ABTASTFREQUENZ 1000
struct timer_list timer;
ec_slave_init_t slaves[] = {
// Zeiger, Index, Herstellername, Produktname, Domäne
{ &s_in, "1", "Beckhoff", "EL3102", 1 },
{ &s_out, "2", "Beckhoff", "EL2004", 1 },
{ &s_ssi, "3", "Beckhoff", "EL5001", 1 }
};
/*****************************************************************************/
#define SLAVE_COUNT (sizeof(slaves) / sizeof(ec_slave_init_t))
// EtherCAT
ec_master_t *master = NULL;
ec_domain_t *domain1 = NULL;
// Prozessdaten
uint8_t *dig_out1;
uint16_t *ssi_value;
uint16_t *inc_value;
uint32_t angle0;
ec_field_init_t domain1_fields[] = {
{(void **) &dig_out1, "2", "Beckhoff", "EL2004", ec_opvalue, 0, 1},
{(void **) &ssi_value, "3", "Beckhoff", "EL5001", ec_ipvalue, 0, 1},
{(void **) &inc_value, "0:4", "Beckhoff", "EL5101", ec_ipvalue, 0, 1},
{}
};
/*****************************************************************************/
void run(unsigned long data)
{
static unsigned int counter;
// Klemmen-IO
EC_WRITE_EL20XX(s_out, 3, EC_READ_EL31XX(s_in, 0) < 0);
if (!counter) {
EtherCAT_rt_debug_level(master, 2);
}
static unsigned int counter = 0;
// Prozessdaten lesen und schreiben
EtherCAT_rt_domain_xio(master, 1, 100);
EtherCAT_rt_domain_xio(domain1);
angle0 = (uint32_t) *inc_value;
if (counter) {
counter--;
}
else {
EtherCAT_rt_debug_level(master, 0);
printk("SSI status=%X value=%u\n",
EC_READ_EL5001_STATE(s_ssi), EC_READ_EL5001_VALUE(s_ssi));
counter = 1000;
counter = ABTASTFREQUENZ;
printk(KERN_INFO "angle0 = %i\n", angle0);
}
// Timer neu starten
timer.expires += HZ / 1000;
timer.expires += HZ / ABTASTFREQUENZ;
add_timer(&timer);
}
@ -66,41 +69,52 @@ void run(unsigned long data)
int __init init_mini_module(void)
{
const ec_field_init_t *field;
printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n");
if ((master = EtherCAT_rt_request_master(0)) == NULL) {
printk(KERN_ERR "EtherCAT master 0 not available!\n");
printk(KERN_ERR "Error requesting master 0!\n");
goto out_return;
}
if (EtherCAT_rt_register_slave_list(master, slaves, SLAVE_COUNT)) {
printk(KERN_ERR "Could not register slaves!\n");
EtherCAT_rt_master_print(master);
printk(KERN_INFO "Registering domain...\n");
if (!(domain1 = EtherCAT_rt_master_register_domain(master, ec_sync, 100)))
{
printk(KERN_ERR "EtherCAT: Could not register domain!\n");
goto out_release_master;
}
printk("Activating all EtherCAT slaves.\n");
printk(KERN_INFO "Registering domain fields...\n");
if (EtherCAT_rt_activate_slaves(master)) {
printk(KERN_ERR "EtherCAT: Could not activate slaves!\n");
goto out_release_master;
for (field = domain1_fields; field->data; field++)
{
if (!EtherCAT_rt_register_slave_field(domain1,
field->address,
field->vendor,
field->product,
field->data,
field->field_type,
field->field_index,
field->field_count)) {
printk(KERN_ERR "EtherCAT: Could not register field!\n");
goto out_release_master;
}
}
printk("Configuring EtherCAT slaves.\n");
printk(KERN_INFO "Activating master...\n");
if (EtherCAT_rt_canopen_sdo_write(master, s_ssi, 0x4067, 0, 2, 2)) {
printk(KERN_ERR "EtherCAT: Could not set SSI baud rate!\n");
goto out_release_master;
}
if (EtherCAT_rt_canopen_sdo_write(master, s_ssi, 0x4061, 4, 1, 1)) {
printk(KERN_ERR "EtherCAT: Could not set SSI feature bit!\n");
if (EtherCAT_rt_master_activate(master)) {
printk(KERN_ERR "EtherCAT: Could not activate master!\n");
goto out_release_master;
}
printk("Starting cyclic sample thread.\n");
init_timer(&timer);
timer.function = run;
timer.expires = jiffies + 10; // Das erste Mal sofort feuern
add_timer(&timer);
@ -126,9 +140,9 @@ void __exit cleanup_mini_module(void)
{
del_timer_sync(&timer);
printk(KERN_INFO "Deactivating slaves.\n");
printk(KERN_INFO "Deactivating master...\n");
EtherCAT_rt_deactivate_slaves(master);
EtherCAT_rt_master_deactivate(master);
EtherCAT_rt_release_master(master);
}
@ -139,7 +153,7 @@ void __exit cleanup_mini_module(void)
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Florian Pose <fp@igh-essen.com>");
MODULE_DESCRIPTION ("Minimal EtherCAT environment");
MODULE_DESCRIPTION ("EtherCAT minimal test environment");
module_init(init_mini_module);
module_exit(cleanup_mini_module);

View File

@ -14,7 +14,6 @@ ifneq ($(KERNELRELEASE),)
obj-m := msr_modul.o
msr_modul-objs := msr_module.o \
msr_jitter.o \
rt_lib/msr-core/msr_lists.o \
rt_lib/msr-core/msr_main.o \
rt_lib/msr-core/msr_charbuf.o \
@ -24,7 +23,9 @@ msr_modul-objs := msr_module.o \
rt_lib/msr-core/msr_proc.o \
rt_lib/msr-core/msr_error_reg.o \
rt_lib/msr-utils/msr_utils.o \
rt_lib/msr-utils/msr_time.o \
rt_lib/msr-math/msr_base64.o \
rt_lib/msr-math/msr_hex_bin.o \
libm.o
EXTRA_CFLAGS := -I $(src)/rt_lib/msr-include -D_SIMULATION \

View File

@ -1,129 +0,0 @@
/******************************************************************************
*
* msr_jitter.c
*
* Autor: Wilhelm Hagemeister
*
* (C) Copyright IgH 2002
* Ingenieurgemeinschaft IgH
* Heinz-B<EFBFBD>äcker Str. 34
* D-45356 Essen
* Tel.: +49 201/61 99 31
* Fax.: +49 201/61 98 36
* E-mail: hm@igh-essen.com
*
* $Id$
*
*****************************************************************************/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/msr.h> /* maschine-specific registers */
#include <linux/param.h> /* fuer HZ */
#include <msr_reg.h>
#include "msr_jitter.h"
/*--includes-----------------------------------------------------------------*/
/*--external functions-------------------------------------------------------*/
/*--external data------------------------------------------------------------*/
/*--public data--------------------------------------------------------------*/
/*--local data---------------------------------------------------------------*/
#define NUMCLASSES 16
static int jittime[NUMCLASSES]={0,1,2,5,10,20,50,100,200,500,
1000,2000,5000,10000,20000,50000}; //in usec
static int jitcount[NUMCLASSES];
static double jitpercent[NUMCLASSES];
static unsigned int tcount = 1;
static void msr_jit_read(void)
{
int i;
for(i=0;i<NUMCLASSES;i++) {
if(tcount >100) {
jitpercent[i] = jitcount[i]*100.0/tcount;
}
}
}
void msr_jitter_init(void)
{
msr_reg_int_list("/Taskinfo/Jitter/Classes","usec",
&jittime[0],MSR_R,NUMCLASSES,NULL,NULL,NULL);
msr_reg_int_list("/Taskinfo/Jitter/Count","",
&jitcount[0],MSR_R,NUMCLASSES,NULL,NULL,NULL);
msr_reg_dbl_list("/Taskinfo/Jitter/percent","%",
&jitpercent[0],MSR_R,NUMCLASSES,NULL,NULL,&msr_jit_read);
}
/******************************************************************************
*
* Function: msr_jitter_run
*
* Beschreibung:
*
*
* Parameter: Zeiger auf msr_data
*
* R<EFBFBD>ückgabe:
*
* Status: exp
*
*****************************************************************************/
void msr_jitter_run(unsigned int hz) {
int i,hit;
static int firstrun = 1;
static unsigned long k,j = 0;
unsigned int dt,jitter;
rdtscl(k);
tcount++;
//Zeitabstand zwischen zwei Interrupts in usec
dt = ((unsigned long)(100000/HZ)*((unsigned long)(k-j)))
/(current_cpu_data.loops_per_jiffy/10);
jitter = (unsigned int)abs((int)dt-(int)1000000/hz);
//jitter errechnet zum Sollabtastrate
//in die Klassen einsortieren
if(!firstrun) { //das erste mal nicht einsortieren
hit = 0;
for(i=0;i<NUMCLASSES-1;i++) {
if(jitter>=jittime[i] && jitter<jittime[i+1]) {
jitcount[i]++;
hit = 1;
break;
}
}
if(hit == 0) //grŽöŽßer als der letzte
jitcount[NUMCLASSES-1]++;
}
else
firstrun = 0;
j = k;
}

View File

@ -1,27 +0,0 @@
/******************************************************************************
*
* msr_jitter.c
*
* Autor: Wilhelm Hagemeister
*
* (C) Copyright IgH 2002
* Ingenieurgemeinschaft IgH
* Heinz-Bäcker Str. 34
* D-45356 Essen
* Tel.: +49 201/61 99 31
* Fax.: +49 201/61 98 36
* E-mail: hm@igh-essen.com
*
* $Id$
*
*****************************************************************************/
/*--Schutz vor mehrfachem includieren----------------------------------------*/
#ifndef _MSR_JITTER_H_
#define _MSR_JITTER_H_
void msr_jitter_run(unsigned int hz);
void msr_jitter_init(void);
#endif

View File

@ -21,6 +21,8 @@
// Linux
#include <linux/module.h>
#include <linux/ipipe.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
// RT_lib
#include <msr_main.h>
@ -28,105 +30,62 @@
#include <msr_messages.h>
#include <msr_float.h>
#include <msr_reg.h>
#include <msr_time.h>
#include "msr_param.h"
#include "msr_jitter.h"
// EtherCAT
#include "../include/EtherCAT_rt.h"
#include "../include/EtherCAT_si.h"
// Defines/Makros
#define TSC2US(T1, T2) ((T2 - T1) * 1000UL / cpu_khz)
#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ)
/*****************************************************************************/
/* Globale Variablen */
// RT_lib
extern struct timeval process_time;
struct timeval msr_time_increment; // Increment per Interrupt
// Adeos
static struct ipipe_domain this_domain;
static struct ipipe_sysinfo sys_info;
// EtherCAT
ec_master_t *master = NULL;
ec_slave_t *s_in1, *s_out1, *s_ssi, *s_inc;
ec_domain_t *domain1 = NULL;
uint16_t angle0;
// Prozessdaten
uint8_t *dig_out1;
uint16_t *ssi_value;
uint16_t *inc_value;
ec_slave_init_t slaves[] = {
{&s_in1, "1", "Beckhoff", "EL3102", 0},
{&s_out1, "2", "Beckhoff", "EL2004", 0},
{&s_ssi, "3", "Beckhoff", "EL5001", 0},
{&s_inc, "0:4", "Beckhoff", "EL5101", 0}
uint32_t angle0;
ec_field_init_t domain1_fields[] = {
{(void **) &dig_out1, "2", "Beckhoff", "EL2004", ec_opvalue, 0, 1},
{(void **) &ssi_value, "3", "Beckhoff", "EL5001", ec_ipvalue, 0, 1},
{(void **) &inc_value, "0:4", "Beckhoff", "EL5101", ec_ipvalue, 0, 1},
{}
};
#define SLAVE_COUNT (sizeof(slaves) / sizeof(ec_slave_init_t))
/******************************************************************************
*
* Function: msr_controller_run()
*
*****************************************************************************/
/*****************************************************************************/
static void msr_controller_run(void)
{
static unsigned int counter = 0;
msr_jitter_run(MSR_ABTASTFREQUENZ);
EC_WRITE_EL20XX(s_out1, 3, EC_READ_EL31XX(s_in1, 0) < 0);
if (!counter) {
EtherCAT_rt_debug_level(master, 2);
}
// Prozessdaten lesen und schreiben
EtherCAT_rt_domain_xio(master, 0, 40);
EtherCAT_rt_domain_xio(domain1);
if (counter) {
counter--;
}
else {
EtherCAT_rt_debug_level(master, 0);
printk("SSI status=0x%X value=%u\n",
EC_READ_EL5001_STATE(s_ssi), EC_READ_EL5001_VALUE(s_ssi));
printk("INC status=0x%X value=%u\n",
EC_READ_EL5101_STATE(s_inc), EC_READ_EL5101_VALUE(s_inc));
counter = MSR_ABTASTFREQUENZ * 5;
}
angle0 = EC_READ_EL5101_VALUE(s_inc);
angle0 = (uint32_t) *inc_value;
}
/******************************************************************************
*
* Function: msr_run(_interrupt)
*
* Beschreibung: Routine wird zyklisch im Timerinterrupt ausgef<EFBFBD>ührt
* (hier mu<EFBFBD>ß alles rein, was Echtzeit ist ...)
*
* Parameter: Zeiger auf msr_data
*
* R<EFBFBD>ückgabe:
*
* Status: exp
*
*****************************************************************************/
/*****************************************************************************/
void msr_run(unsigned irq)
{
static int counter = 0;
timeval_add(&process_time, &process_time, &msr_time_increment);
MSR_ADEOS_INTERRUPT_CODE(msr_controller_run(); msr_write_kanal_list(););
ipipe_control_irq(irq,0,IPIPE_ENABLE_MASK); //Interrupt bestŽätigen
if (counter++ > HZREDUCTION) {
ipipe_propagate_irq(irq); //und weiterreichen
ipipe_control_irq(irq, 0, IPIPE_ENABLE_MASK); // Interrupt bestŽätigen
if (++counter >= HZREDUCTION) {
ipipe_propagate_irq(irq); // und weiterreichen
counter = 0;
}
}
@ -144,75 +103,78 @@ void domain_entry(void)
ipipe_tune_timer(1000000000UL / MSR_ABTASTFREQUENZ, 0);
}
/******************************************************************************
*
* Function: msr_register_channels
*
* Beschreibung: KanŽäle registrieren
*
* Parameter:
*
* RŽückgabe:
*
* Status: exp
*
*****************************************************************************/
/*****************************************************************************/
int msr_globals_register(void)
{
//msr_reg_kanal("/value", "V", &value, TDBL);
//msr_reg_kanal("/dig1", "", &dig1, TINT);
msr_reg_kanal("/angle0", "", &angle0, TINT);
msr_reg_kanal("/angle0", "", &angle0, TUINT);
return 0;
}
/******************************************************************************
* the init/clean material
*****************************************************************************/
/*****************************************************************************/
int __init init_rt_module(void)
{
struct ipipe_domain_attr attr; //ipipe
const ec_field_init_t *field;
// Als allererstes die RT-lib initialisieren
if (msr_rtlib_init(1,MSR_ABTASTFREQUENZ,10,&msr_globals_register) < 0) {
if (msr_rtlib_init(1, MSR_ABTASTFREQUENZ, 10, &msr_globals_register) < 0) {
msr_print_warn("msr_modul: can't initialize rtlib!");
goto out_return;
}
msr_jitter_init();
printk(KERN_INFO "=== Starting EtherCAT environment... ===\n");
if ((master = EtherCAT_rt_request_master(0)) == NULL) {
printk(KERN_ERR "Error requesting master 0!\n");
goto out_msr_cleanup;
}
if (EtherCAT_rt_register_slave_list(master, slaves, SLAVE_COUNT)) {
printk(KERN_ERR "EtherCAT: Could not register slaves!\n");
EtherCAT_rt_master_print(master);
printk(KERN_INFO "Registering domain...\n");
if (!(domain1 = EtherCAT_rt_master_register_domain(master, ec_sync, 100)))
{
printk(KERN_ERR "EtherCAT: Could not register domain!\n");
goto out_release_master;
}
if (EtherCAT_rt_activate_slaves(master) < 0) {
printk(KERN_ERR "EtherCAT: Could not activate slaves!\n");
printk(KERN_INFO "Registering domain fields...\n");
for (field = domain1_fields; field->data; field++)
{
if (!EtherCAT_rt_register_slave_field(domain1,
field->address,
field->vendor,
field->product,
field->data,
field->field_type,
field->field_index,
field->field_count)) {
printk(KERN_ERR "EtherCAT: Could not register field!\n");
goto out_release_master;
}
}
printk(KERN_INFO "Activating master...\n");
if (EtherCAT_rt_master_activate(master)) {
printk(KERN_ERR "EtherCAT: Could not activate master!\n");
goto out_release_master;
}
if (EtherCAT_rt_canopen_sdo_write(master, s_ssi, 0x4067, 0, 1, 2)) {
#if 0
if (EtherCAT_rt_canopen_sdo_write(master, "0:4", 0x4067, 0, 1, 2)) {
printk(KERN_ERR "EtherCAT: Could not set SSI baud rate!\n");
goto out_release_master;
}
if (EtherCAT_rt_canopen_sdo_write(master, s_ssi, 0x4061, 4, 1, 1)) {
if (EtherCAT_rt_canopen_sdo_write(master, "0:4", 0x4061, 4, 1, 1)) {
printk(KERN_ERR "EtherCAT: Could not set SSI feature bit!\n");
goto out_release_master;
}
do_gettimeofday(&process_time);
msr_time_increment.tv_sec = 0;
msr_time_increment.tv_usec = (unsigned int) (1000000 / MSR_ABTASTFREQUENZ);
#endif
ipipe_init_attr(&attr);
attr.name = "IPIPE-MSR-MODULE";
@ -245,10 +207,10 @@ void __exit cleanup_rt_module(void)
{
printk(KERN_INFO "=== Stopping EtherCAT environment... ===\n");
printk(KERN_INFO "Deactivating slaves.\n");
printk(KERN_INFO "Deactivating master...\n");
if (EtherCAT_rt_deactivate_slaves(master) < 0) {
printk(KERN_WARNING "Warning - Could not deactivate slaves!\n");
if (EtherCAT_rt_master_deactivate(master) < 0) {
printk(KERN_WARNING "Warning - Could not deactivate master!\n");
}
EtherCAT_rt_release_master(master);
@ -262,8 +224,8 @@ void __exit cleanup_rt_module(void)
/*****************************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Wilhelm Hagemeister <hm@igh-essen.com>");
MODULE_DESCRIPTION ("EtherCAT test environment");
MODULE_AUTHOR ("Florian Pose <fp@igh-essen.com>");
MODULE_DESCRIPTION ("EtherCAT real-time test environment");
module_init(init_rt_module);
module_exit(cleanup_rt_module);