mirror of
https://gitlab.com/etherlab.org/ethercat.git
synced 2026-02-06 11:51:45 +08:00
MERGE branches/async 222:233 -> trunk (Kommando-Warteschlangen).
This commit is contained in:
@@ -1804,21 +1804,16 @@ static void rtl8139_tx_timeout (struct net_device *dev)
|
||||
" (queue head)" : "");
|
||||
|
||||
tp->xstats.tx_timeouts++;
|
||||
|
||||
/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
||||
|
||||
printk(KERN_DEBUG "%s: tx_timeout\n", dev->name);
|
||||
|
||||
if (EtherCAT_dev_is_ec(rtl_ec_dev, dev))
|
||||
{
|
||||
EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_TIMEOUT);
|
||||
}
|
||||
printk(KERN_DEBUG "%s: tx_timeout\n", dev->name);
|
||||
|
||||
/* disable Tx ASAP, if not already */
|
||||
tmp8 = RTL_R8 (ChipCmd);
|
||||
if (tmp8 & CmdTxEnb)
|
||||
RTL_W8 (ChipCmd, CmdRxEnb);
|
||||
|
||||
/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
||||
|
||||
|
||||
if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev))
|
||||
{
|
||||
spin_lock(&tp->rx_lock);
|
||||
@@ -1840,7 +1835,7 @@ static void rtl8139_tx_timeout (struct net_device *dev)
|
||||
}
|
||||
|
||||
spin_unlock(&tp->rx_lock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rtl8139_tx_clear (tp);
|
||||
@@ -1951,14 +1946,6 @@ static void rtl8139_tx_interrupt (struct net_device *dev,
|
||||
tp->stats.tx_carrier_errors++;
|
||||
if (txstatus & TxOutOfWindow)
|
||||
tp->stats.tx_window_errors++;
|
||||
|
||||
/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
||||
|
||||
if (EtherCAT_dev_is_ec(rtl_ec_dev, dev))
|
||||
EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_ERROR);
|
||||
|
||||
/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
|
||||
|
||||
} else {
|
||||
if (txstatus & TxUnderrun) {
|
||||
/* Add 64 to the Tx FIFO threshold. */
|
||||
@@ -2033,15 +2020,6 @@ static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
|
||||
tp->xstats.rx_lost_in_ring++;
|
||||
}
|
||||
|
||||
/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
||||
|
||||
if (EtherCAT_dev_is_ec(rtl_ec_dev, dev))
|
||||
{
|
||||
EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_ERROR);
|
||||
}
|
||||
|
||||
/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
|
||||
|
||||
#ifndef CONFIG_8139_OLD_RX_RESET
|
||||
tmp8 = RTL_R8 (ChipCmd);
|
||||
RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
|
||||
|
||||
@@ -14,23 +14,10 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
struct ec_device;
|
||||
|
||||
typedef struct ec_device ec_device_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
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;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
ec_device_t *EtherCAT_dev_register(unsigned int, struct net_device *,
|
||||
irqreturn_t (*)(int, void *,
|
||||
struct pt_regs *),
|
||||
@@ -38,7 +25,6 @@ ec_device_t *EtherCAT_dev_register(unsigned int, struct net_device *,
|
||||
void EtherCAT_dev_unregister(unsigned int, ec_device_t *);
|
||||
|
||||
int EtherCAT_dev_is_ec(const ec_device_t *, const struct net_device *);
|
||||
void EtherCAT_dev_state(ec_device_t *, ec_device_state_t);
|
||||
void EtherCAT_dev_receive(ec_device_t *, const void *, size_t);
|
||||
void EtherCAT_dev_link_state(ec_device_t *, uint8_t);
|
||||
|
||||
|
||||
@@ -63,9 +63,10 @@ ec_domain_t *EtherCAT_rt_master_register_domain(ec_master_t *master,
|
||||
unsigned int timeout_us);
|
||||
|
||||
int EtherCAT_rt_master_activate(ec_master_t *master);
|
||||
|
||||
int EtherCAT_rt_master_deactivate(ec_master_t *master);
|
||||
|
||||
void EtherCAT_rt_master_xio(ec_master_t *master);
|
||||
|
||||
void EtherCAT_rt_master_debug(ec_master_t *master, int level);
|
||||
void EtherCAT_rt_master_print(const ec_master_t *master);
|
||||
|
||||
@@ -81,7 +82,11 @@ ec_slave_t *EtherCAT_rt_register_slave_field(ec_domain_t *domain,
|
||||
unsigned int field_index,
|
||||
unsigned int field_count);
|
||||
|
||||
int EtherCAT_rt_domain_xio(ec_domain_t *domain);
|
||||
int EtherCAT_rt_register_domain_fields(ec_domain_t *domain,
|
||||
ec_field_init_t *fields);
|
||||
|
||||
void EtherCAT_rt_domain_queue(ec_domain_t *domain);
|
||||
void EtherCAT_rt_domain_process(ec_domain_t *domain);
|
||||
|
||||
/*****************************************************************************/
|
||||
// Slave Methods
|
||||
|
||||
@@ -15,13 +15,10 @@ ifneq ($(KERNELRELEASE),)
|
||||
|
||||
obj-m := ec_master.o
|
||||
|
||||
ec_master-objs := module.o master.o device.o slave.o frame.o types.o \
|
||||
ec_master-objs := module.o master.o device.o slave.o command.o types.o \
|
||||
domain.o canopen.o
|
||||
|
||||
REV = `svnversion $(src)`
|
||||
DATE = `date`
|
||||
|
||||
EXTRA_CFLAGS = -DEC_REV="$(REV)" -DEC_USER="$(USER)" -DEC_DATE="$(DATE)"
|
||||
EXTRA_CFLAGS := -DSVNREV=$(shell svnversion $(src)) -DUSER=$(USER)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
@@ -48,7 +45,7 @@ doc:
|
||||
cleandoc:
|
||||
@rm -rf doc
|
||||
|
||||
.PHONY: doc
|
||||
.PHONY: doc cleandoc modules clean
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
107
master/canopen.c
107
master/canopen.c
@@ -28,10 +28,11 @@ int EtherCAT_rt_canopen_sdo_write(ec_slave_t *slave, /**< EtherCAT-Slave */
|
||||
size_t size /**< Größe des Datenfeldes */
|
||||
)
|
||||
{
|
||||
unsigned char data[0xF6];
|
||||
ec_frame_t frame;
|
||||
unsigned int tries_left, i;
|
||||
uint8_t data[0xF6];
|
||||
ec_command_t command;
|
||||
unsigned int i;
|
||||
ec_master_t *master;
|
||||
cycles_t start, end, timeout;
|
||||
|
||||
memset(data, 0x00, 0xF6);
|
||||
|
||||
@@ -56,53 +57,48 @@ int EtherCAT_rt_canopen_sdo_write(ec_slave_t *slave, /**< EtherCAT-Slave */
|
||||
value >>= 8;
|
||||
}
|
||||
|
||||
ec_frame_init_npwr(&frame, master, slave->station_address,
|
||||
0x1800, 0xF6, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x1800, 0xF6, data);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox sending failed on slave %i!\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read "written bit" of Sync-Manager
|
||||
start = get_cycles();
|
||||
timeout = cpu_khz; // 1ms
|
||||
|
||||
tries_left = 10;
|
||||
while (tries_left)
|
||||
do
|
||||
{
|
||||
ec_frame_init_nprd(&frame, master, slave->station_address, 0x808, 8);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame) < 0)) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x808, 8);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox checking failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EC_READ_U8(frame.data + 5) & 8) { // Written bit is high
|
||||
break;
|
||||
}
|
||||
end = get_cycles();
|
||||
|
||||
udelay(1000);
|
||||
tries_left--;
|
||||
if (EC_READ_U8(command.data + 5) & 8) break; // Written bit is high
|
||||
}
|
||||
while ((end - start) < timeout);
|
||||
|
||||
if (!tries_left) {
|
||||
if ((end - start) >= timeout) {
|
||||
EC_ERR("Mailbox check - Slave %i timed out.\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ec_frame_init_nprd(&frame, master, slave->station_address, 0x18F6, 0xF6);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame) < 0)) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x18F6, 0xF6);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox receiving failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EC_READ_U8 (frame.data + 5) != 0x03 || // COE
|
||||
EC_READ_U16(frame.data + 6) != 0x3000 || // SDO response
|
||||
EC_READ_U8 (frame.data + 8) >> 5 != 0x03 || // Download response
|
||||
EC_READ_U16(frame.data + 9) != sdo_index || // Index
|
||||
EC_READ_U8 (frame.data + 11) != sdo_subindex) // Subindex
|
||||
if (EC_READ_U8 (command.data + 5) != 0x03 || // COE
|
||||
EC_READ_U16(command.data + 6) != 0x3000 || // SDO response
|
||||
EC_READ_U8 (command.data + 8) >> 5 != 0x03 || // Download response
|
||||
EC_READ_U16(command.data + 9) != sdo_index || // Index
|
||||
EC_READ_U8 (command.data + 11) != sdo_subindex) // Subindex
|
||||
{
|
||||
EC_ERR("Illegal mailbox response at slave %i!\n",
|
||||
slave->ring_position);
|
||||
@@ -125,12 +121,11 @@ int EtherCAT_rt_canopen_sdo_read(ec_slave_t *slave, /**< EtherCAT-Slave */
|
||||
)
|
||||
{
|
||||
unsigned char data[0xF6];
|
||||
ec_frame_t frame;
|
||||
unsigned int tries_left;
|
||||
ec_command_t command;
|
||||
ec_master_t *master;
|
||||
cycles_t start, end, timeout;
|
||||
|
||||
memset(data, 0x00, 0xF6);
|
||||
|
||||
master = slave->master;
|
||||
|
||||
EC_WRITE_U16(data, 0x0006); // Length of the Mailbox service data
|
||||
@@ -142,60 +137,58 @@ int EtherCAT_rt_canopen_sdo_read(ec_slave_t *slave, /**< EtherCAT-Slave */
|
||||
EC_WRITE_U16(data + 9, sdo_index);
|
||||
EC_WRITE_U8 (data + 11, sdo_subindex);
|
||||
|
||||
ec_frame_init_npwr(&frame, master, slave->station_address,
|
||||
0x1800, 0xF6, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame) < 0)) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x1800, 0xF6, data);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox sending failed on slave %i!\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read "written bit" of Sync-Manager
|
||||
|
||||
tries_left = 10;
|
||||
while (tries_left)
|
||||
{
|
||||
ec_frame_init_nprd(&frame, master, slave->station_address, 0x808, 8);
|
||||
start = get_cycles();
|
||||
timeout = cpu_khz; // 1ms
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame) < 0)) {
|
||||
do
|
||||
{
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x808, 8);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox checking failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EC_READ_U8(frame.data + 5) & 8) { // Written bit is high
|
||||
end = get_cycles();
|
||||
|
||||
if (EC_READ_U8(command.data + 5) & 8) { // Written bit is high
|
||||
break;
|
||||
}
|
||||
|
||||
udelay(1000);
|
||||
tries_left--;
|
||||
}
|
||||
while (likely((end - start) < timeout));
|
||||
|
||||
if (!tries_left) {
|
||||
if (unlikely((end - start) >= timeout)) {
|
||||
EC_ERR("Mailbox check - Slave %i timed out.\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ec_frame_init_nprd(&frame, master, slave->station_address, 0x18F6, 0xF6);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame) < 0)) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x18F6, 0xF6);
|
||||
if (unlikely(ec_master_simple_io(master, &command))) {
|
||||
EC_ERR("Mailbox receiving failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EC_READ_U8 (frame.data + 5) != 0x03 || // COE
|
||||
EC_READ_U16(frame.data + 6) != 0x3000 || // SDO response
|
||||
EC_READ_U8 (frame.data + 8) >> 5 != 0x02 || // Upload response
|
||||
EC_READ_U16(frame.data + 9) != sdo_index || // Index
|
||||
EC_READ_U8 (frame.data + 11) != sdo_subindex) // Subindex
|
||||
if (EC_READ_U8 (command.data + 5) != 0x03 || // COE
|
||||
EC_READ_U16(command.data + 6) != 0x3000 || // SDO response
|
||||
EC_READ_U8 (command.data + 8) >> 5 != 0x02 || // Upload response
|
||||
EC_READ_U16(command.data + 9) != sdo_index || // Index
|
||||
EC_READ_U8 (command.data + 11) != sdo_subindex) // Subindex
|
||||
{
|
||||
EC_ERR("Illegal mailbox response at slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = EC_READ_U32(frame.data + 12);
|
||||
*value = EC_READ_U32(command.data + 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -213,7 +206,8 @@ int EtherCAT_rt_canopen_sdo_read(ec_slave_t *slave, /**< EtherCAT-Slave */
|
||||
int EtherCAT_rt_canopen_sdo_addr_write(ec_master_t *master,
|
||||
/**< EtherCAT-Master */
|
||||
const char *addr,
|
||||
/**< Addresse, siehe ec_address() */
|
||||
/**< Addresse, siehe
|
||||
ec_master_slave_address() */
|
||||
uint16_t index,
|
||||
/**< SDO-Index */
|
||||
uint8_t subindex,
|
||||
@@ -225,7 +219,7 @@ int EtherCAT_rt_canopen_sdo_addr_write(ec_master_t *master,
|
||||
)
|
||||
{
|
||||
ec_slave_t *slave;
|
||||
if (!(slave = ec_address(master, addr))) return -1;
|
||||
if (!(slave = ec_master_slave_address(master, addr))) return -1;
|
||||
return EtherCAT_rt_canopen_sdo_write(slave, index, subindex, value, size);
|
||||
}
|
||||
|
||||
@@ -242,7 +236,8 @@ int EtherCAT_rt_canopen_sdo_addr_write(ec_master_t *master,
|
||||
int EtherCAT_rt_canopen_sdo_addr_read(ec_master_t *master,
|
||||
/**< EtherCAT-Slave */
|
||||
const char *addr,
|
||||
/**< Addresse, siehe ec_address() */
|
||||
/**< Addresse, siehe
|
||||
ec_master_slave_address() */
|
||||
uint16_t index,
|
||||
/**< SDO-Index */
|
||||
uint8_t subindex,
|
||||
@@ -252,7 +247,7 @@ int EtherCAT_rt_canopen_sdo_addr_read(ec_master_t *master,
|
||||
)
|
||||
{
|
||||
ec_slave_t *slave;
|
||||
if (!(slave = ec_address(master, addr))) return -1;
|
||||
if (!(slave = ec_master_slave_address(master, addr))) return -1;
|
||||
return EtherCAT_rt_canopen_sdo_read(slave, index, subindex, value);
|
||||
}
|
||||
|
||||
|
||||
235
master/command.c
Normal file
235
master/command.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* c o m m a n d . c
|
||||
*
|
||||
* Methoden für ein EtherCAT-Kommando.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "../include/EtherCAT_si.h"
|
||||
#include "command.h"
|
||||
#include "master.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define EC_FUNC_HEADER \
|
||||
command->index = 0; \
|
||||
command->working_counter = 0; \
|
||||
command->state = EC_CMD_INIT;
|
||||
|
||||
#define EC_FUNC_WRITE_FOOTER \
|
||||
command->data_size = data_size; \
|
||||
memcpy(command->data, data, data_size);
|
||||
|
||||
#define EC_FUNC_READ_FOOTER \
|
||||
command->data_size = data_size; \
|
||||
memset(command->data, 0x00, data_size);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-NPRD-Kommando.
|
||||
|
||||
Node-adressed physical read.
|
||||
*/
|
||||
|
||||
void ec_command_init_nprd(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t node_address,
|
||||
/**< Adresse des Knotens (Slaves) */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size
|
||||
/**< Länge der zu lesenden Daten */
|
||||
)
|
||||
{
|
||||
if (unlikely(node_address == 0x0000))
|
||||
EC_WARN("Using node address 0x0000!\n");
|
||||
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_NPRD;
|
||||
command->address.physical.slave = node_address;
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_READ_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-NPWR-Kommando.
|
||||
|
||||
Node-adressed physical write.
|
||||
*/
|
||||
|
||||
void ec_command_init_npwr(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t node_address,
|
||||
/**< Adresse des Knotens (Slaves) */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size,
|
||||
/**< Länge der zu schreibenden Daten */
|
||||
const uint8_t *data
|
||||
/**< Zeiger auf Speicher mit zu schreibenden Daten */
|
||||
)
|
||||
{
|
||||
if (unlikely(node_address == 0x0000))
|
||||
EC_WARN("Using node address 0x0000!\n");
|
||||
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_NPWR;
|
||||
command->address.physical.slave = node_address;
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_WRITE_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-APRD-Kommando.
|
||||
|
||||
Autoincrement physical read.
|
||||
*/
|
||||
|
||||
void ec_command_init_aprd(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t ring_position,
|
||||
/**< Position des Slaves im Bus */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size
|
||||
/**< Länge der zu lesenden Daten */
|
||||
)
|
||||
{
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_APRD;
|
||||
command->address.physical.slave = (int16_t) ring_position * (-1);
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_READ_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-APWR-Kommando.
|
||||
|
||||
Autoincrement physical write.
|
||||
*/
|
||||
|
||||
void ec_command_init_apwr(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t ring_position,
|
||||
/**< Position des Slaves im Bus */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size,
|
||||
/**< Länge der zu schreibenden Daten */
|
||||
const uint8_t *data
|
||||
/**< Zeiger auf Speicher mit zu schreibenden Daten */
|
||||
)
|
||||
{
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_APWR;
|
||||
command->address.physical.slave = (int16_t) ring_position * (-1);
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_WRITE_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-BRD-Kommando.
|
||||
|
||||
Broadcast read.
|
||||
*/
|
||||
|
||||
void ec_command_init_brd(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size
|
||||
/**< Länge der zu lesenden Daten */
|
||||
)
|
||||
{
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_BRD;
|
||||
command->address.physical.slave = 0x0000;
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_READ_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-BWR-Kommando.
|
||||
|
||||
Broadcast write.
|
||||
*/
|
||||
|
||||
void ec_command_init_bwr(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint16_t offset,
|
||||
/**< Physikalische Speicheradresse im Slave */
|
||||
size_t data_size,
|
||||
/**< Länge der zu schreibenden Daten */
|
||||
const uint8_t *data
|
||||
/**< Zeiger auf Speicher mit zu schreibenden Daten */
|
||||
)
|
||||
{
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_BWR;
|
||||
command->address.physical.slave = 0x0000;
|
||||
command->address.physical.mem = offset;
|
||||
|
||||
EC_FUNC_WRITE_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Initialisiert ein EtherCAT-LRW-Kommando.
|
||||
|
||||
Logical read write.
|
||||
*/
|
||||
|
||||
void ec_command_init_lrw(ec_command_t *command,
|
||||
/**< EtherCAT-Rahmen */
|
||||
uint32_t offset,
|
||||
/**< Logische Startadresse */
|
||||
size_t data_size,
|
||||
/**< Länge der zu lesenden/schreibenden Daten */
|
||||
uint8_t *data
|
||||
/**< Zeiger auf die Daten */
|
||||
)
|
||||
{
|
||||
EC_FUNC_HEADER;
|
||||
|
||||
command->type = EC_CMD_LRW;
|
||||
command->address.logical = offset;
|
||||
|
||||
EC_FUNC_WRITE_FOOTER;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Emacs-Konfiguration
|
||||
;;; Local Variables: ***
|
||||
;;; c-basic-offset:4 ***
|
||||
;;; End: ***
|
||||
*/
|
||||
118
master/command.h
Normal file
118
master/command.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* c o m m a n d . h
|
||||
*
|
||||
* Struktur für ein EtherCAT-Kommando.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _EC_COMMAND_H_
|
||||
#define _EC_COMMAND_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Kommando-Typ.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EC_CMD_NONE = 0x00, /**< Dummy */
|
||||
EC_CMD_APRD = 0x01, /**< Auto-increment physical read */
|
||||
EC_CMD_APWR = 0x02, /**< Auto-increment physical write */
|
||||
EC_CMD_NPRD = 0x04, /**< Node-addressed physical read */
|
||||
EC_CMD_NPWR = 0x05, /**< Node-addressed physical write */
|
||||
EC_CMD_BRD = 0x07, /**< Broadcast read */
|
||||
EC_CMD_BWR = 0x08, /**< Broadcast write */
|
||||
EC_CMD_LRW = 0x0C /**< Logical read/write */
|
||||
}
|
||||
ec_command_type_t;
|
||||
|
||||
/**
|
||||
EtherCAT-Kommando-Zustand.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EC_CMD_INIT, /**< Neues Kommando */
|
||||
EC_CMD_QUEUED, /**< Kommando in Warteschlange */
|
||||
EC_CMD_SENT, /**< Kommando gesendet */
|
||||
EC_CMD_RECEIVED, /**< Kommando empfangen */
|
||||
EC_CMD_TIMEOUT, /**< Zeitgrenze überschritten */
|
||||
EC_CMD_ERROR /**< Fehler beim Senden oder Empfangen */
|
||||
}
|
||||
ec_command_state_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Adresse.
|
||||
|
||||
Im EtherCAT-Kommando 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 (Ringposition oder Knoten) */
|
||||
uint16_t mem; /**< Physikalische Speicheradresse im Slave */
|
||||
}
|
||||
physical; /**< Physikalische Adresse */
|
||||
|
||||
uint32_t logical; /**< Logische Adresse */
|
||||
}
|
||||
ec_address_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Kommando.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct list_head list; /**< Nötig für Liste */
|
||||
ec_command_type_t type; /**< Typ des Kommandos (APRD, NPWR, etc) */
|
||||
ec_address_t address; /**< Adresse des/der Empfänger */
|
||||
uint8_t data[EC_MAX_DATA_SIZE]; /**< Kommandodaten */
|
||||
size_t data_size; /**< Länge der zu sendenden und/oder empfangenen Daten */
|
||||
uint8_t index; /**< Kommando-Index, wird vom Master beim Senden gesetzt. */
|
||||
uint16_t working_counter; /**< Working-Counter */
|
||||
ec_command_state_t state; /**< Zustand */
|
||||
}
|
||||
ec_command_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ec_command_init_nprd(ec_command_t *, uint16_t, uint16_t, size_t);
|
||||
void ec_command_init_npwr(ec_command_t *, uint16_t, uint16_t, size_t,
|
||||
const uint8_t *);
|
||||
void ec_command_init_aprd(ec_command_t *, uint16_t, uint16_t, size_t);
|
||||
void ec_command_init_apwr(ec_command_t *, uint16_t, uint16_t, size_t,
|
||||
const uint8_t *);
|
||||
void ec_command_init_brd(ec_command_t *, uint16_t, size_t);
|
||||
void ec_command_init_bwr(ec_command_t *, uint16_t, size_t, const uint8_t *);
|
||||
void ec_command_init_lrw(ec_command_t *, uint32_t, size_t, uint8_t *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif
|
||||
|
||||
/* Emacs-Konfiguration
|
||||
;;; Local Variables: ***
|
||||
;;; c-basic-offset:4 ***
|
||||
;;; End: ***
|
||||
*/
|
||||
174
master/device.c
174
master/device.c
@@ -19,6 +19,11 @@
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ec_data_print(const uint8_t *, size_t);
|
||||
void ec_data_print_diff(const uint8_t *, const uint8_t *, size_t);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Geräte-Konstuktor.
|
||||
|
||||
@@ -26,26 +31,36 @@
|
||||
*/
|
||||
|
||||
int ec_device_init(ec_device_t *device, /**< EtherCAT-Gerät */
|
||||
ec_master_t *master /**< Zugehöriger Master */
|
||||
ec_master_t *master, /**< Zugehöriger Master */
|
||||
struct net_device *net_dev, /**< Net-Device */
|
||||
ec_isr_t isr, /**< Adresse der ISR */
|
||||
struct module *module /**< Modul-Adresse */
|
||||
)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
|
||||
device->master = master;
|
||||
device->dev = NULL;
|
||||
device->dev = net_dev;
|
||||
device->isr = isr;
|
||||
device->module = module;
|
||||
|
||||
device->open = 0;
|
||||
device->tx_time = 0;
|
||||
device->rx_time = 0;
|
||||
device->state = EC_DEVICE_STATE_READY;
|
||||
device->rx_data_size = 0;
|
||||
device->isr = NULL;
|
||||
device->module = NULL;
|
||||
device->error_reported = 0;
|
||||
device->link_state = 0; // down
|
||||
|
||||
if ((device->tx_skb = dev_alloc_skb(ETH_HLEN + EC_MAX_FRAME_SIZE)) == NULL) {
|
||||
if (!(device->tx_skb = dev_alloc_skb(ETH_HLEN + EC_MAX_FRAME_SIZE))) {
|
||||
EC_ERR("Error allocating device socket buffer!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->tx_skb->dev = net_dev;
|
||||
|
||||
// Ethernet-II-Header hinzufuegen
|
||||
skb_reserve(device->tx_skb, ETH_HLEN);
|
||||
eth = (struct ethhdr *) skb_push(device->tx_skb, ETH_HLEN);
|
||||
eth->h_proto = htons(0x88A4);
|
||||
memcpy(eth->h_source, net_dev->dev_addr, net_dev->addr_len);
|
||||
memset(eth->h_dest, 0xFF, net_dev->addr_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -61,13 +76,7 @@ int ec_device_init(ec_device_t *device, /**< EtherCAT-Ger
|
||||
void ec_device_clear(ec_device_t *device /**< EtherCAT-Gerät */)
|
||||
{
|
||||
if (device->open) ec_device_close(device);
|
||||
|
||||
device->dev = NULL;
|
||||
|
||||
if (device->tx_skb) {
|
||||
dev_kfree_skb(device->tx_skb);
|
||||
device->tx_skb = NULL;
|
||||
}
|
||||
if (device->tx_skb) dev_kfree_skb(device->tx_skb);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -100,8 +109,6 @@ int ec_device_open(ec_device_t *device /**< EtherCAT-Ger
|
||||
// Device could have received frames before
|
||||
for (i = 0; i < 4; i++) ec_device_call_isr(device);
|
||||
|
||||
// Reset old device state
|
||||
device->state = EC_DEVICE_STATE_READY;
|
||||
device->link_state = 0;
|
||||
|
||||
if (device->dev->open(device->dev) == 0) device->open = 1;
|
||||
@@ -127,10 +134,10 @@ int ec_device_close(ec_device_t *device /**< EtherCAT-Ger
|
||||
|
||||
if (!device->open) {
|
||||
EC_WARN("Device already closed!\n");
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (device->dev->stop(device->dev) == 0) device->open = 0;
|
||||
}
|
||||
|
||||
if (device->dev->stop(device->dev) == 0) device->open = 0;
|
||||
|
||||
return !device->open ? 0 : -1;
|
||||
}
|
||||
@@ -138,18 +145,14 @@ int ec_device_close(ec_device_t *device /**< EtherCAT-Ger
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Bereitet den geräteinternen Socket-Buffer auf den Versand vor.
|
||||
Liefert einen Zeiger auf den Sende-Speicher.
|
||||
|
||||
\return Zeiger auf den Speicher, in den die Frame-Daten sollen.
|
||||
*/
|
||||
|
||||
uint8_t *ec_device_prepare(ec_device_t *device /**< EtherCAT-Gerät */)
|
||||
uint8_t *ec_device_tx_data(ec_device_t *device /**< EtherCAT-Gerät */)
|
||||
{
|
||||
skb_trim(device->tx_skb, 0); // Auf Länge 0 abschneiden
|
||||
skb_reserve(device->tx_skb, ETH_HLEN); // Reserve für Ethernet-II-Header
|
||||
|
||||
// Vorerst Speicher für maximal langen Frame reservieren
|
||||
return skb_put(device->tx_skb, EC_MAX_FRAME_SIZE);
|
||||
return device->tx_skb->data + ETH_HLEN;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -163,64 +166,26 @@ uint8_t *ec_device_prepare(ec_device_t *device /**< EtherCAT-Ger
|
||||
*/
|
||||
|
||||
void ec_device_send(ec_device_t *device, /**< EtherCAT-Gerät */
|
||||
unsigned int length /**< Länge der zu sendenden Daten */
|
||||
size_t size /**< Größe der zu sendenden Daten */
|
||||
)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
|
||||
if (unlikely(!device->link_state)) // Link down
|
||||
return;
|
||||
|
||||
// Framegröße auf (jetzt bekannte) Länge abschneiden
|
||||
skb_trim(device->tx_skb, length);
|
||||
|
||||
// Ethernet-II-Header hinzufuegen
|
||||
eth = (struct ethhdr *) skb_push(device->tx_skb, ETH_HLEN);
|
||||
eth->h_proto = htons(0x88A4);
|
||||
memcpy(eth->h_source, device->dev->dev_addr, device->dev->addr_len);
|
||||
memset(eth->h_dest, 0xFF, device->dev->addr_len);
|
||||
|
||||
device->state = EC_DEVICE_STATE_SENT;
|
||||
device->rx_data_size = 0;
|
||||
device->tx_skb->len = ETH_HLEN + size;
|
||||
|
||||
if (unlikely(device->master->debug_level > 1)) {
|
||||
EC_DBG("Sending frame:\n");
|
||||
ec_data_print(device->tx_skb->data + ETH_HLEN, device->tx_skb->len);
|
||||
EC_DBG("sending frame:\n");
|
||||
ec_data_print(device->tx_skb->data + ETH_HLEN, size);
|
||||
}
|
||||
|
||||
// Senden einleiten
|
||||
rdtscl(device->tx_time); // Get CPU cycles
|
||||
device->dev->hard_start_xmit(device->tx_skb, device->dev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt die Anzahl der empfangenen Bytes zurück.
|
||||
|
||||
\return Empfangene Bytes, oder 0, wenn kein Frame empfangen wurde.
|
||||
*/
|
||||
|
||||
unsigned int ec_device_received(const ec_device_t *device)
|
||||
{
|
||||
return device->rx_data_size;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt die empfangenen Daten zurück.
|
||||
|
||||
\return Zeiger auf empfangene Daten.
|
||||
*/
|
||||
|
||||
uint8_t *ec_device_data(ec_device_t *device)
|
||||
{
|
||||
return device->rx_data;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Ruft die Interrupt-Routine der Netzwerkkarte auf.
|
||||
*/
|
||||
@@ -232,49 +197,6 @@ void ec_device_call_isr(ec_device_t *device /**< EtherCAT-Ger
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt alle Informationen über das Device-Objekt aus.
|
||||
*/
|
||||
|
||||
void ec_device_print(ec_device_t *device /**< EtherCAT-Gerät */)
|
||||
{
|
||||
EC_DBG("---EtherCAT device information begin---\n");
|
||||
|
||||
if (device) {
|
||||
EC_DBG("Assigned net_device: %X\n", (u32) device->dev);
|
||||
EC_DBG("Transmit socket buffer: %X\n", (u32) device->tx_skb);
|
||||
EC_DBG("Time of last transmission: %u\n", (u32) device->tx_time);
|
||||
EC_DBG("Time of last receive: %u\n", (u32) device->rx_time);
|
||||
EC_DBG("Actual device state: %i\n", (u8) device->state);
|
||||
EC_DBG("Receive buffer: %X\n", (u32) device->rx_data);
|
||||
EC_DBG("Receive buffer fill state: %u/%u\n",
|
||||
(u32) device->rx_data_size, EC_MAX_FRAME_SIZE);
|
||||
}
|
||||
else {
|
||||
EC_DBG("Device is NULL!\n");
|
||||
}
|
||||
|
||||
EC_DBG("---EtherCAT device information end---\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt das letzte Rahmenpaar aus.
|
||||
*/
|
||||
|
||||
void ec_device_debug(const ec_device_t *device /**< EtherCAT-Gerät */)
|
||||
{
|
||||
EC_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
||||
ec_data_print(device->tx_skb->data + ETH_HLEN, device->tx_skb->len);
|
||||
EC_DBG("--------------------------------------\n");
|
||||
ec_data_print_diff(device->tx_skb->data + ETH_HLEN, device->rx_data,
|
||||
device->rx_data_size);
|
||||
EC_DBG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt Frame-Inhalte zwecks Debugging aus.
|
||||
*/
|
||||
@@ -327,19 +249,6 @@ void ec_data_print_diff(const uint8_t *d1, /**< Daten 1 */
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
Setzt den Zustand des EtherCAT-Gerätes.
|
||||
*/
|
||||
|
||||
void EtherCAT_dev_state(ec_device_t *device, /**< EtherCAT-Gerät */
|
||||
ec_device_state_t state /**< Neuer Zustand */
|
||||
)
|
||||
{
|
||||
device->state = state;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Prüft, ob das Net-Device \a dev zum registrierten EtherCAT-Gerät gehört.
|
||||
|
||||
@@ -366,16 +275,12 @@ void EtherCAT_dev_receive(ec_device_t *device, /**< EtherCAT-Ger
|
||||
size_t size /**< Größe der empfangenen Daten */
|
||||
)
|
||||
{
|
||||
// Copy received data to ethercat-device buffer
|
||||
memcpy(device->rx_data, data, size);
|
||||
device->rx_data_size = size;
|
||||
device->state = EC_DEVICE_STATE_RECEIVED;
|
||||
|
||||
if (unlikely(device->master->debug_level > 1)) {
|
||||
EC_DBG("Received frame:\n");
|
||||
ec_data_print_diff(device->tx_skb->data + ETH_HLEN, device->rx_data,
|
||||
device->rx_data_size);
|
||||
ec_data_print_diff(device->tx_skb->data + ETH_HLEN, data, size);
|
||||
}
|
||||
|
||||
ec_master_receive(device->master, data, size);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -397,7 +302,6 @@ void EtherCAT_dev_link_state(ec_device_t *device, /**< EtherCAT-Ger
|
||||
/*****************************************************************************/
|
||||
|
||||
EXPORT_SYMBOL(EtherCAT_dev_is_ec);
|
||||
EXPORT_SYMBOL(EtherCAT_dev_state);
|
||||
EXPORT_SYMBOL(EtherCAT_dev_receive);
|
||||
EXPORT_SYMBOL(EtherCAT_dev_link_state);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "../include/EtherCAT_dev.h"
|
||||
#include "globals.h"
|
||||
|
||||
typedef irqreturn_t (*ec_isr_t)(int, void *, struct pt_regs *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
@@ -33,36 +35,24 @@ struct ec_device
|
||||
struct net_device *dev; /**< Zeiger auf das reservierte net_device */
|
||||
uint8_t 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 */
|
||||
ec_device_state_t state; /**< Zustand des Gerätes */
|
||||
uint8_t rx_data[EC_MAX_FRAME_SIZE]; /**< Speicher für empfangene Rahmen */
|
||||
size_t rx_data_size; /**< Länge des empfangenen Rahmens */
|
||||
irqreturn_t (*isr)(int, void *, struct pt_regs *); /**< Adresse der ISR */
|
||||
ec_isr_t isr; /**< Adresse der ISR */
|
||||
struct module *module; /**< Zeiger auf das Modul, das das Gerät zur
|
||||
Verfügung stellt. */
|
||||
uint8_t error_reported; /**< Zeigt an, ob ein Fehler im zyklischen Code
|
||||
bereits gemeldet wurde. */
|
||||
uint8_t link_state; /**< Verbindungszustand */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ec_device_init(ec_device_t *, ec_master_t *);
|
||||
int ec_device_init(ec_device_t *, ec_master_t *, struct net_device *,
|
||||
ec_isr_t, struct module *);
|
||||
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 *);
|
||||
uint8_t *ec_device_prepare(ec_device_t *);
|
||||
uint8_t *ec_device_tx_data(ec_device_t *);
|
||||
void ec_device_send(ec_device_t *, size_t);
|
||||
unsigned int ec_device_received(const ec_device_t *);
|
||||
uint8_t *ec_device_data(ec_device_t *);
|
||||
|
||||
void ec_device_debug(const ec_device_t *);
|
||||
void ec_data_print(const uint8_t *, size_t);
|
||||
void ec_data_print_diff(const uint8_t *, const uint8_t *, size_t);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
293
master/domain.c
293
master/domain.c
@@ -12,6 +12,8 @@
|
||||
#include "domain.h"
|
||||
#include "master.h"
|
||||
|
||||
void ec_domain_clear_field_regs(ec_domain_t *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,8 @@ void ec_domain_init(ec_domain_t *domain, /**< Dom
|
||||
|
||||
domain->data = NULL;
|
||||
domain->data_size = 0;
|
||||
domain->commands = NULL;
|
||||
domain->command_count = 0;
|
||||
domain->base_address = 0;
|
||||
domain->response_count = 0xFFFFFFFF;
|
||||
|
||||
@@ -44,17 +48,10 @@ void ec_domain_init(ec_domain_t *domain, /**< Dom
|
||||
|
||||
void ec_domain_clear(ec_domain_t *domain /**< Domäne */)
|
||||
{
|
||||
ec_field_reg_t *field_reg, *next;
|
||||
if (domain->data) kfree(domain->data);
|
||||
if (domain->commands) kfree(domain->commands);
|
||||
|
||||
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);
|
||||
}
|
||||
ec_domain_clear_field_regs(domain);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -98,6 +95,22 @@ int ec_domain_reg_field(ec_domain_t *domain, /**< Dom
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Gibt die Liste der registrierten Datenfelder frei.
|
||||
*/
|
||||
|
||||
void ec_domain_clear_field_regs(ec_domain_t *domain)
|
||||
{
|
||||
ec_field_reg_t *field_reg, *next;
|
||||
|
||||
list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) {
|
||||
list_del(&field_reg->list);
|
||||
kfree(field_reg);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Erzeugt eine Domäne.
|
||||
|
||||
@@ -111,7 +124,7 @@ int ec_domain_alloc(ec_domain_t *domain, /**< Dom
|
||||
uint32_t base_address /**< Logische Basisadresse */
|
||||
)
|
||||
{
|
||||
ec_field_reg_t *field_reg, *next;
|
||||
ec_field_reg_t *field_reg;
|
||||
ec_slave_t *slave;
|
||||
ec_fmmu_t *fmmu;
|
||||
unsigned int i, j, found, data_offset;
|
||||
@@ -139,43 +152,52 @@ int ec_domain_alloc(ec_domain_t *domain, /**< Dom
|
||||
|
||||
if (!domain->data_size) {
|
||||
EC_WARN("Domain 0x%08X contains no data!\n", (u32) domain);
|
||||
ec_domain_clear_field_regs(domain);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// Prozessdaten allozieren
|
||||
if (!(domain->data = kmalloc(domain->data_size, GFP_KERNEL))) {
|
||||
EC_ERR("Failed to allocate domain data!\n");
|
||||
|
||||
// Prozessdaten allozieren
|
||||
if (!(domain->data = kmalloc(domain->data_size, GFP_KERNEL))) {
|
||||
EC_ERR("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
|
||||
EC_ERR("FMMU not found. Please report!\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
|
||||
EC_ERR("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);
|
||||
// Kommando-Array erzeugen
|
||||
domain->command_count = domain->data_size / EC_MAX_DATA_SIZE + 1;
|
||||
if (!(domain->commands = (ec_command_t *) kmalloc(sizeof(ec_command_t)
|
||||
* domain->command_count,
|
||||
GFP_KERNEL))) {
|
||||
EC_ERR("Failed to allocate domain command array!\n");
|
||||
return -1;
|
||||
}
|
||||
INIT_LIST_HEAD(&domain->field_regs); // wichtig!
|
||||
|
||||
EC_INFO("Domain %X - Allocated %i bytes in %i command(s)\n",
|
||||
(u32) domain, domain->data_size, domain->command_count);
|
||||
|
||||
ec_domain_clear_field_regs(domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -192,8 +214,8 @@ void ec_domain_response_count(ec_domain_t *domain, /**< Dom
|
||||
{
|
||||
if (count != domain->response_count) {
|
||||
domain->response_count = count;
|
||||
EC_INFO("Domain %08X state change - %i slaves responding.\n",
|
||||
(u32) domain, count);
|
||||
EC_INFO("Domain %08X state change - %i slave%s responding.\n",
|
||||
(u32) domain, count, count == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,17 +231,28 @@ void ec_domain_response_count(ec_domain_t *domain, /**< Dom
|
||||
\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 *EtherCAT_rt_register_slave_field(ec_domain_t *domain,
|
||||
/**< Domäne */
|
||||
const char *address,
|
||||
/**< ASCII-Addresse des Slaves,
|
||||
siehe ec_master_slave_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 selben Typs */
|
||||
)
|
||||
{
|
||||
ec_slave_t *slave;
|
||||
const ec_slave_type_t *type;
|
||||
@@ -237,7 +270,7 @@ ec_slave_t *EtherCAT_rt_register_slave_field(
|
||||
master = domain->master;
|
||||
|
||||
// Adresse übersetzen
|
||||
if ((slave = ec_address(master, address)) == NULL) return NULL;
|
||||
if (!(slave = ec_master_slave_address(master, address))) return NULL;
|
||||
|
||||
if (!(type = slave->type)) {
|
||||
EC_ERR("Slave \"%s\" (position %i) has unknown type!\n", address,
|
||||
@@ -280,96 +313,96 @@ ec_slave_t *EtherCAT_rt_register_slave_field(
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Sendet und empfängt Prozessdaten der angegebenen Domäne.
|
||||
Registriert eine ganze Liste von Datenfeldern innerhalb einer Domäne.
|
||||
|
||||
Achtung: Die Liste muss mit einer NULL-Struktur ({}) abgeschlossen sein!
|
||||
|
||||
\return 0 bei Erfolg, sonst < 0
|
||||
*/
|
||||
|
||||
int EtherCAT_rt_domain_xio(ec_domain_t *domain /**< Domäne */)
|
||||
int EtherCAT_rt_register_domain_fields(ec_domain_t *domain,
|
||||
/**< Domäne */
|
||||
ec_field_init_t *fields
|
||||
/**< Array mit Datenfeldern */
|
||||
)
|
||||
{
|
||||
unsigned int offset, size, working_counter_sum;
|
||||
unsigned long start_ticks, end_ticks, timeout_ticks;
|
||||
ec_master_t *master;
|
||||
ec_frame_t *frame;
|
||||
ec_field_init_t *field;
|
||||
|
||||
master = domain->master;
|
||||
frame = &domain->frame;
|
||||
working_counter_sum = 0;
|
||||
|
||||
ec_cyclic_output(master);
|
||||
|
||||
if (unlikely(!master->device.link_state)) {
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
ec_device_call_isr(&master->device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)) {
|
||||
master->device.state = EC_DEVICE_STATE_READY;
|
||||
master->frames_lost++;
|
||||
ec_cyclic_output(master);
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
for (field = fields; field->data; field++) {
|
||||
if (!EtherCAT_rt_register_slave_field(domain, field->address,
|
||||
field->vendor, field->product,
|
||||
field->data, field->field_type,
|
||||
field->field_index,
|
||||
field->field_count)) {
|
||||
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)) {
|
||||
if (master->device.state == EC_DEVICE_STATE_RECEIVED) {
|
||||
master->frames_delayed++;
|
||||
ec_cyclic_output(master);
|
||||
}
|
||||
else {
|
||||
master->device.state = EC_DEVICE_STATE_READY;
|
||||
master->frames_lost++;
|
||||
ec_cyclic_output(master);
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(ec_frame_receive(frame) < 0)) {
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
working_counter_sum += frame->working_counter;
|
||||
|
||||
// Daten vom Rahmen in den Prozessdatenspeicher kopieren
|
||||
memcpy(domain->data + offset, frame->data, size);
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Setzt Prozessdaten-Kommandos in die Warteschlange des Masters.
|
||||
*/
|
||||
|
||||
void EtherCAT_rt_domain_queue(ec_domain_t *domain /**< Domäne */)
|
||||
{
|
||||
unsigned int offset, i;
|
||||
size_t size;
|
||||
|
||||
offset = 0;
|
||||
for (i = 0; i < domain->command_count; i++) {
|
||||
size = domain->data_size - offset;
|
||||
if (size > EC_MAX_DATA_SIZE) size = EC_MAX_DATA_SIZE;
|
||||
ec_command_init_lrw(domain->commands + i,
|
||||
domain->base_address + offset, size,
|
||||
domain->data + offset);
|
||||
ec_master_queue_command(domain->master, domain->commands + i);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Verarbeitet empfangene Prozessdaten.
|
||||
*/
|
||||
|
||||
void EtherCAT_rt_domain_process(ec_domain_t *domain /**< Domäne */)
|
||||
{
|
||||
unsigned int offset, working_counter_sum, i;
|
||||
ec_command_t *command;
|
||||
size_t size;
|
||||
|
||||
working_counter_sum = 0;
|
||||
|
||||
offset = 0;
|
||||
for (i = 0; i < domain->command_count; i++) {
|
||||
command = domain->commands + i;
|
||||
size = domain->data_size - offset;
|
||||
if (size > EC_MAX_DATA_SIZE) size = EC_MAX_DATA_SIZE;
|
||||
if (command->state == EC_CMD_RECEIVED) {
|
||||
// Daten vom Kommando- in den Prozessdatenspeicher kopieren
|
||||
memcpy(domain->data + offset, command->data, size);
|
||||
working_counter_sum += command->working_counter;
|
||||
}
|
||||
else if (unlikely(domain->master->debug_level)) {
|
||||
EC_DBG("process data command not received!\n");
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
|
||||
ec_domain_response_count(domain, working_counter_sum);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
EXPORT_SYMBOL(EtherCAT_rt_register_slave_field);
|
||||
EXPORT_SYMBOL(EtherCAT_rt_domain_xio);
|
||||
EXPORT_SYMBOL(EtherCAT_rt_register_domain_fields);
|
||||
EXPORT_SYMBOL(EtherCAT_rt_domain_queue);
|
||||
EXPORT_SYMBOL(EtherCAT_rt_domain_process);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
#include "globals.h"
|
||||
#include "slave.h"
|
||||
#include "frame.h"
|
||||
#include "command.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -46,17 +46,14 @@ struct ec_domain
|
||||
{
|
||||
struct list_head list; /**< Listenkopf */
|
||||
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 */
|
||||
|
||||
uint8_t *data; /**< Prozessdaten */
|
||||
size_t data_size; /**< Größe der Prozessdaten */
|
||||
ec_command_t *commands; /**< EtherCAT-Kommandos für die Prozessdaten */
|
||||
unsigned int command_count; /**< Anzahl allozierter Kommandos */
|
||||
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 */
|
||||
};
|
||||
|
||||
|
||||
463
master/frame.c
463
master/frame.c
@@ -1,463 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* f r a m e . c
|
||||
*
|
||||
* Methoden für einen EtherCAT-Frame.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "../include/EtherCAT_si.h"
|
||||
#include "frame.h"
|
||||
#include "master.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define EC_FUNC_HEADER \
|
||||
frame->master = master; \
|
||||
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; \
|
||||
memset(frame->data, 0x00, 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))
|
||||
EC_WARN("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))
|
||||
EC_WARN("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->device.link_state))
|
||||
return -1;
|
||||
|
||||
if (unlikely(frame->master->debug_level > 0)) {
|
||||
EC_DBG("ec_frame_send\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)) {
|
||||
EC_ERR("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)) {
|
||||
EC_DBG("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)) {
|
||||
EC_DBG("Sending command index 0x%X\n", frame->index);
|
||||
}
|
||||
|
||||
// Zeiger auf Socket-Buffer holen
|
||||
data = ec_device_prepare(&frame->master->device);
|
||||
|
||||
// EtherCAT frame header
|
||||
EC_WRITE_U16(data, (command_size & 0x7FF) | 0x1000);
|
||||
data += EC_FRAME_HEADER_SIZE;
|
||||
|
||||
// EtherCAT command header
|
||||
EC_WRITE_U8 (data, frame->type);
|
||||
EC_WRITE_U8 (data + 1, frame->index);
|
||||
EC_WRITE_U32(data + 2, frame->address.logical);
|
||||
EC_WRITE_U16(data + 6, frame->data_length & 0x7FF);
|
||||
EC_WRITE_U16(data + 8, 0x0000);
|
||||
data += EC_COMMAND_HEADER_SIZE;
|
||||
|
||||
// EtherCAT command data
|
||||
memcpy(data, frame->data, frame->data_length);
|
||||
data += frame->data_length;
|
||||
|
||||
// EtherCAT command footer
|
||||
EC_WRITE_U16(data, frame->working_counter);
|
||||
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++)
|
||||
EC_WRITE_U8(data++, 0x00);
|
||||
|
||||
// Send frame
|
||||
ec_device_send(&frame->master->device, frame_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Wertet empfangene Daten zu einem Rahmen aus.
|
||||
|
||||
\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;
|
||||
|
||||
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)) {
|
||||
EC_ERR("Received frame with incomplete EtherCAT frame header!\n");
|
||||
ec_device_debug(device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = ec_device_data(device);
|
||||
|
||||
// Länge des gesamten Frames prüfen
|
||||
frame_length = EC_READ_U16(data) & 0x07FF;
|
||||
data += EC_FRAME_HEADER_SIZE;
|
||||
|
||||
if (unlikely(frame_length > received_length)) {
|
||||
EC_ERR("Received corrupted frame (length does not match)!\n");
|
||||
ec_device_debug(device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Command header
|
||||
command_type = EC_READ_U8(data);
|
||||
command_index = EC_READ_U8(data + 1);
|
||||
data_length = EC_READ_U16(data + 6) & 0x07FF;
|
||||
data += EC_COMMAND_HEADER_SIZE;
|
||||
|
||||
if (unlikely(EC_FRAME_HEADER_SIZE + EC_COMMAND_HEADER_SIZE
|
||||
+ data_length + EC_COMMAND_FOOTER_SIZE > received_length)) {
|
||||
EC_ERR("Received frame with incomplete command data!\n");
|
||||
ec_device_debug(device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlikely(frame->type != command_type
|
||||
|| frame->index != command_index
|
||||
|| frame->data_length != data_length))
|
||||
{
|
||||
EC_WARN("WARNING - Send/Receive anomaly!\n");
|
||||
ec_device_debug(device);
|
||||
ec_device_call_isr(device); // Empfangenes "vergessen"
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Empfangene Daten in Kommandodatenspeicher kopieren
|
||||
memcpy(frame->data, data, data_length);
|
||||
data += data_length;
|
||||
|
||||
// Working-Counter setzen
|
||||
frame->working_counter = EC_READ_U16(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Sendet einen einzeln Rahmen und wartet auf dessen Empfang.
|
||||
|
||||
Wenn der Working-Counter nicht gesetzt wurde, wird der Rahmen
|
||||
nochmals gesendet.
|
||||
|
||||
\todo Das ist noch nicht schön, da hier zwei Protokollschichten
|
||||
vermischt werden.
|
||||
|
||||
\return 0 bei Erfolg, sonst < 0
|
||||
*/
|
||||
|
||||
int ec_frame_send_receive(ec_frame_t *frame
|
||||
/**< Rahmen zum Senden/Empfangen */
|
||||
)
|
||||
{
|
||||
unsigned int timeout_tries_left, response_tries_left;
|
||||
unsigned int tries;
|
||||
|
||||
tries = 0;
|
||||
response_tries_left = 10;
|
||||
do
|
||||
{
|
||||
tries++;
|
||||
if (unlikely(ec_frame_send(frame) < 0)) {
|
||||
EC_ERR("Frame sending failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeout_tries_left = 20;
|
||||
do
|
||||
{
|
||||
udelay(1);
|
||||
ec_device_call_isr(&frame->master->device);
|
||||
timeout_tries_left--;
|
||||
}
|
||||
while (unlikely(!ec_device_received(&frame->master->device)
|
||||
&& timeout_tries_left));
|
||||
|
||||
if (unlikely(!timeout_tries_left)) {
|
||||
EC_ERR("Frame timeout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlikely(ec_frame_receive(frame) < 0)) {
|
||||
EC_ERR("Frame receiving failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
response_tries_left--;
|
||||
}
|
||||
while (unlikely(!frame->working_counter && response_tries_left));
|
||||
|
||||
if (unlikely(!response_tries_left)) {
|
||||
EC_ERR("No response!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tries > 1) EC_WARN("%i tries necessary...\n", tries);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Emacs-Konfiguration
|
||||
;;; Local Variables: ***
|
||||
;;; c-basic-offset:4 ***
|
||||
;;; End: ***
|
||||
*/
|
||||
111
master/frame.h
111
master/frame.h
@@ -1,111 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* 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"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
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 (Ringposition oder Knoten) */
|
||||
uint16_t mem; /**< Physikalische Speicheradresse im Slave */
|
||||
}
|
||||
physical; /**< Physikalische Adresse */
|
||||
|
||||
uint32_t logical; /**< Logische Adresse */
|
||||
}
|
||||
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 */
|
||||
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 *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif
|
||||
|
||||
/* Emacs-Konfiguration
|
||||
;;; Local Variables: ***
|
||||
;;; c-basic-offset:4 ***
|
||||
;;; End: ***
|
||||
*/
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// EtherCAT-Protokoll
|
||||
#define EC_MAX_FRAME_SIZE 1500 /**< Maximale Größe eines EtherCAT-Frames ohne
|
||||
Ethernet-II-Header und -Prüfsumme*/
|
||||
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 */
|
||||
@@ -43,28 +43,6 @@
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
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_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif
|
||||
|
||||
/* Emacs-Konfiguration
|
||||
|
||||
509
master/master.c
509
master/master.c
File diff suppressed because it is too large
Load Diff
@@ -15,11 +15,26 @@
|
||||
|
||||
#include "device.h"
|
||||
#include "slave.h"
|
||||
#include "frame.h"
|
||||
#include "domain.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Rahmen-Statistiken.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int timeouts; /**< Kommando-Timeouts */
|
||||
unsigned int delayed; /**< Verzögerte Kommandos */
|
||||
unsigned int corrupted; /**< Verfälschte Rahmen */
|
||||
unsigned int unmatched; /**< Unpassende Kommandos */
|
||||
cycles_t t_last; /**< Timestamp-Counter bei der letzten Ausgabe */
|
||||
}
|
||||
ec_stats_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
EtherCAT-Master
|
||||
|
||||
@@ -31,16 +46,12 @@ struct ec_master
|
||||
{
|
||||
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. */
|
||||
ec_device_t *device; /**< EtherCAT-Gerät */
|
||||
struct list_head commands; /**< Kommando-Liste */
|
||||
uint8_t command_index; /**< Aktueller Kommando-Index */
|
||||
struct list_head domains; /**< Liste der Prozessdatendomänen */
|
||||
int debug_level; /**< Debug-Level im Master-Code */
|
||||
unsigned int bus_time; /**< Letzte Bus-Zeit in Mikrosekunden */
|
||||
unsigned int frames_lost; /**< Anzahl verlorener Frames */
|
||||
unsigned int frames_delayed; /**< Anzahl verzögerter Frames */
|
||||
unsigned long t_last_cyclic_output; /**< Timer-Ticks bei den letzten
|
||||
zyklischen Ausgaben */
|
||||
ec_stats_t stats; /**< Rahmen-Statistiken */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -49,19 +60,23 @@ struct ec_master
|
||||
void ec_master_init(ec_master_t *);
|
||||
void ec_master_clear(ec_master_t *);
|
||||
void ec_master_reset(ec_master_t *);
|
||||
void ec_master_clear_slaves(ec_master_t *);
|
||||
|
||||
// IO
|
||||
void ec_master_receive(ec_master_t *, const uint8_t *, size_t);
|
||||
void ec_master_queue_command(ec_master_t *, ec_command_t *);
|
||||
int ec_master_simple_io(ec_master_t *, ec_command_t *);
|
||||
|
||||
// Registration of devices
|
||||
int ec_master_open(ec_master_t *);
|
||||
void ec_master_close(ec_master_t *);
|
||||
|
||||
// Slave management
|
||||
int ec_scan_for_slaves(ec_master_t *);
|
||||
ec_slave_t *ec_address(const ec_master_t *, const char *);
|
||||
int ec_master_bus_scan(ec_master_t *);
|
||||
ec_slave_t *ec_master_slave_address(const ec_master_t *, const char *);
|
||||
|
||||
// Misc
|
||||
void ec_output_debug_data(const ec_master_t *);
|
||||
void ec_cyclic_output(ec_master_t *);
|
||||
void ec_master_debug(const ec_master_t *);
|
||||
void ec_master_output_stats(ec_master_t *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ void __exit ec_cleanup_module(void);
|
||||
#define EC_LIT(X) #X
|
||||
#define EC_STR(X) EC_LIT(X)
|
||||
|
||||
#define COMPILE_INFO "Revision " EC_STR(EC_REV) \
|
||||
", compiled by " EC_STR(EC_USER) \
|
||||
" at " EC_STR(EC_DATE)
|
||||
#define COMPILE_INFO "Revision " EC_STR(SVNREV) \
|
||||
", compiled by " EC_STR(USER) \
|
||||
" at " __DATE__ " " __TIME__
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -155,38 +155,35 @@ ec_device_t *EtherCAT_dev_register(unsigned int master_index,
|
||||
/**< Zeiger auf das Modul */
|
||||
)
|
||||
{
|
||||
ec_device_t *device;
|
||||
ec_master_t *master;
|
||||
|
||||
if (master_index >= ec_master_count) {
|
||||
EC_ERR("Master %i does not exist!\n", master_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!net_dev) {
|
||||
EC_WARN("Device is NULL!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (master_index >= ec_master_count) {
|
||||
EC_ERR("Master %i does not exist!\n", master_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
master = ec_masters + master_index;
|
||||
|
||||
if (master->device_registered) {
|
||||
if (master->device) {
|
||||
EC_ERR("Master %i already has a device!\n", master_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device = &master->device;
|
||||
if (!(master->device = (ec_device_t *) kmalloc(sizeof(ec_device_t),
|
||||
GFP_KERNEL))) {
|
||||
EC_ERR("Failed allocating device!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ec_device_init(device, master) < 0) return NULL;
|
||||
if (ec_device_init(master->device, master, net_dev, isr, module))
|
||||
return NULL;
|
||||
|
||||
device->dev = net_dev;
|
||||
device->tx_skb->dev = net_dev;
|
||||
device->isr = isr;
|
||||
device->module = module;
|
||||
|
||||
master->device_registered = 1;
|
||||
|
||||
return device;
|
||||
return master->device;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -210,13 +207,14 @@ void EtherCAT_dev_unregister(unsigned int master_index,
|
||||
|
||||
master = ec_masters + master_index;
|
||||
|
||||
if (!master->device_registered || &master->device != device) {
|
||||
if (!master->device || master->device != device) {
|
||||
EC_WARN("Unable to unregister device!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
master->device_registered = 0;
|
||||
ec_device_clear(device);
|
||||
ec_device_clear(master->device);
|
||||
kfree(master->device);
|
||||
master->device = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -253,22 +251,24 @@ ec_master_t *EtherCAT_rt_request_master(unsigned int index
|
||||
|
||||
master = &ec_masters[index];
|
||||
|
||||
if (!master->device_registered) {
|
||||
if (!master->device) {
|
||||
EC_ERR("Master %i has no device assigned yet!\n", index);
|
||||
goto req_return;
|
||||
}
|
||||
|
||||
if (!try_module_get(master->device.module)) {
|
||||
if (!try_module_get(master->device->module)) {
|
||||
EC_ERR("Failed to reserve device module!\n");
|
||||
goto req_return;
|
||||
}
|
||||
|
||||
if (ec_master_open(master) < 0) {
|
||||
if (ec_master_open(master)) {
|
||||
EC_ERR("Failed to open device!\n");
|
||||
goto req_module_put;
|
||||
}
|
||||
|
||||
if (ec_scan_for_slaves(master) != 0) {
|
||||
if (!master->device->link_state) EC_WARN("Link is DOWN.\n");
|
||||
|
||||
if (ec_master_bus_scan(master) != 0) {
|
||||
EC_ERR("Bus scan failed!\n");
|
||||
goto req_close;
|
||||
}
|
||||
@@ -282,7 +282,7 @@ ec_master_t *EtherCAT_rt_request_master(unsigned int index
|
||||
ec_master_close(master);
|
||||
|
||||
req_module_put:
|
||||
module_put(master->device.module);
|
||||
module_put(master->device->module);
|
||||
ec_master_reset(master);
|
||||
|
||||
req_return:
|
||||
@@ -318,7 +318,7 @@ void EtherCAT_rt_release_master(ec_master_t *master /**< EtherCAT-Masdter */)
|
||||
ec_master_close(master);
|
||||
ec_master_reset(master);
|
||||
|
||||
module_put(master->device.module);
|
||||
module_put(master->device->module);
|
||||
ec_masters_reserved[i] = 0;
|
||||
|
||||
EC_INFO("===== Master %i stopped. =====\n", i);
|
||||
|
||||
150
master/slave.c
150
master/slave.c
@@ -14,7 +14,8 @@
|
||||
#include "../include/EtherCAT_si.h"
|
||||
#include "globals.h"
|
||||
#include "slave.h"
|
||||
#include "frame.h"
|
||||
#include "command.h"
|
||||
#include "master.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -64,23 +65,21 @@ void ec_slave_clear(ec_slave_t *slave /**< EtherCAT-Slave */)
|
||||
|
||||
int ec_slave_fetch(ec_slave_t *slave /**< EtherCAT-Slave */)
|
||||
{
|
||||
ec_frame_t frame;
|
||||
ec_command_t command;
|
||||
|
||||
// Read base data
|
||||
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
|
||||
0x0000, 6);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x0000, 6);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_ERR("Reading base datafrom slave %i failed!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
slave->base_type = EC_READ_U8 (frame.data);
|
||||
slave->base_revision = EC_READ_U8 (frame.data + 1);
|
||||
slave->base_build = EC_READ_U16(frame.data + 2);
|
||||
slave->base_fmmu_count = EC_READ_U8 (frame.data + 4);
|
||||
slave->base_sync_count = EC_READ_U8 (frame.data + 5);
|
||||
slave->base_type = EC_READ_U8 (command.data);
|
||||
slave->base_revision = EC_READ_U8 (command.data + 1);
|
||||
slave->base_build = EC_READ_U16(command.data + 2);
|
||||
slave->base_fmmu_count = EC_READ_U8 (command.data + 4);
|
||||
slave->base_sync_count = EC_READ_U8 (command.data + 5);
|
||||
|
||||
if (slave->base_fmmu_count > EC_MAX_FMMUS)
|
||||
slave->base_fmmu_count = EC_MAX_FMMUS;
|
||||
@@ -130,9 +129,9 @@ int ec_slave_sii_read(ec_slave_t *slave,
|
||||
der Daten */
|
||||
)
|
||||
{
|
||||
ec_frame_t frame;
|
||||
ec_command_t command;
|
||||
unsigned char data[10];
|
||||
unsigned int tries_left;
|
||||
cycles_t start, end, timeout;
|
||||
|
||||
// Initiate read operation
|
||||
|
||||
@@ -141,10 +140,8 @@ int ec_slave_sii_read(ec_slave_t *slave,
|
||||
EC_WRITE_U16(data + 2, offset);
|
||||
EC_WRITE_U16(data + 4, 0x0000);
|
||||
|
||||
ec_frame_init_npwr(&frame, slave->master, slave->station_address,
|
||||
0x502, 6, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x502, 6, data);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_ERR("SII-read failed on slave %i!\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
@@ -153,29 +150,28 @@ int ec_slave_sii_read(ec_slave_t *slave,
|
||||
// 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))
|
||||
start = get_cycles();
|
||||
timeout = cpu_khz; // 1ms
|
||||
|
||||
do
|
||||
{
|
||||
udelay(10);
|
||||
|
||||
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
|
||||
0x502, 10);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x502, 10);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_ERR("Getting SII-read status failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (likely((EC_READ_U8(frame.data + 1) & 0x81) == 0)) {
|
||||
memcpy(target, frame.data + 6, 4);
|
||||
end = get_cycles();
|
||||
|
||||
if (likely((EC_READ_U8(command.data + 1) & 0x81) == 0)) {
|
||||
memcpy(target, command.data + 6, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
tries_left--;
|
||||
}
|
||||
while (likely((end - start) < timeout));
|
||||
|
||||
if (unlikely(!tries_left)) {
|
||||
if (unlikely((end - start) >= timeout)) {
|
||||
EC_ERR("SSI-read. Slave %i timed out!\n", slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
@@ -197,52 +193,49 @@ void ec_slave_state_ack(ec_slave_t *slave,
|
||||
/**< Alter Zustand */
|
||||
)
|
||||
{
|
||||
ec_frame_t frame;
|
||||
ec_command_t command;
|
||||
unsigned char data[2];
|
||||
unsigned int tries_left;
|
||||
cycles_t start, end, timeout;
|
||||
|
||||
EC_WRITE_U16(data, state | EC_ACK);
|
||||
|
||||
ec_frame_init_npwr(&frame, slave->master, slave->station_address,
|
||||
0x0120, 2, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x0120, 2, data);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_WARN("State %02X acknowledge failed on slave %i!\n",
|
||||
state, slave->ring_position);
|
||||
return;
|
||||
}
|
||||
|
||||
tries_left = 100;
|
||||
while (likely(tries_left))
|
||||
start = get_cycles();
|
||||
timeout = cpu_khz; // 1ms
|
||||
|
||||
do
|
||||
{
|
||||
udelay(10);
|
||||
|
||||
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
|
||||
0x0130, 2);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x0130, 2);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_WARN("State %02X acknowledge checking failed on slave %i!\n",
|
||||
state, slave->ring_position);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(EC_READ_U8(frame.data) != state)) {
|
||||
end = get_cycles();
|
||||
|
||||
if (unlikely(EC_READ_U8(command.data) != state)) {
|
||||
EC_WARN("Could not acknowledge state %02X on slave %i (code"
|
||||
" %02X)!\n", state, slave->ring_position,
|
||||
EC_READ_U8(frame.data));
|
||||
EC_READ_U8(command.data));
|
||||
return;
|
||||
}
|
||||
|
||||
if (likely(EC_READ_U8(frame.data) == state)) {
|
||||
if (likely(EC_READ_U8(command.data) == state)) {
|
||||
EC_INFO("Acknowleged state %02X on slave %i.\n", state,
|
||||
slave->ring_position);
|
||||
return;
|
||||
}
|
||||
|
||||
tries_left--;
|
||||
}
|
||||
while (likely((end - start) < timeout));
|
||||
|
||||
if (unlikely(!tries_left)) {
|
||||
if (unlikely((end - start) >= timeout)) {
|
||||
EC_WARN("Could not check state acknowledgement %02X of slave %i -"
|
||||
" Timeout while checking!\n", state, slave->ring_position);
|
||||
return;
|
||||
@@ -263,52 +256,51 @@ int ec_slave_state_change(ec_slave_t *slave,
|
||||
/**< Neuer Zustand */
|
||||
)
|
||||
{
|
||||
ec_frame_t frame;
|
||||
ec_command_t command;
|
||||
unsigned char data[2];
|
||||
unsigned int tries_left;
|
||||
cycles_t start, end, timeout;
|
||||
|
||||
EC_WRITE_U16(data, state);
|
||||
|
||||
ec_frame_init_npwr(&frame, slave->master, slave->station_address,
|
||||
0x0120, 2, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x0120, 2, data);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_ERR("Failed to set state %02X on slave %i!\n",
|
||||
state, slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tries_left = 100;
|
||||
while (likely(tries_left))
|
||||
start = get_cycles();
|
||||
timeout = cpu_khz; // 1ms
|
||||
|
||||
do
|
||||
{
|
||||
udelay(10);
|
||||
udelay(100); // Dem Slave etwas Zeit lassen...
|
||||
|
||||
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
|
||||
0x0130, 2);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x0130, 2);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_ERR("Failed to check state %02X on slave %i!\n",
|
||||
state, slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlikely(EC_READ_U8(frame.data) & 0x10)) { // State change error
|
||||
end = get_cycles();
|
||||
|
||||
if (unlikely(EC_READ_U8(command.data) & 0x10)) { // State change error
|
||||
EC_ERR("Could not set state %02X - Slave %i refused state change"
|
||||
" (code %02X)!\n", state, slave->ring_position,
|
||||
EC_READ_U8(frame.data));
|
||||
ec_slave_state_ack(slave, EC_READ_U8(frame.data) & 0x0F);
|
||||
EC_READ_U8(command.data));
|
||||
ec_slave_state_ack(slave, EC_READ_U8(command.data) & 0x0F);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (likely(EC_READ_U8(frame.data) == (state & 0x0F))) {
|
||||
if (likely(EC_READ_U8(command.data) == (state & 0x0F))) {
|
||||
// State change successful
|
||||
break;
|
||||
}
|
||||
|
||||
tries_left--;
|
||||
}
|
||||
while (likely((end - start) < timeout));
|
||||
|
||||
if (unlikely(!tries_left)) {
|
||||
if (unlikely((end - start) >= timeout)) {
|
||||
EC_ERR("Could not check state %02X of slave %i - Timeout!\n", state,
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
@@ -405,31 +397,27 @@ void ec_slave_print(const ec_slave_t *slave /**< EtherCAT-Slave */)
|
||||
|
||||
int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT-Slave */)
|
||||
{
|
||||
ec_frame_t frame;
|
||||
ec_command_t command;
|
||||
uint8_t data[4];
|
||||
|
||||
ec_frame_init_nprd(&frame, slave->master, slave->station_address,
|
||||
0x0300, 4);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_nprd(&command, slave->station_address, 0x0300, 4);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_WARN("Reading CRC fault counters failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// No CRC faults.
|
||||
if (!EC_READ_U16(frame.data) && !EC_READ_U16(frame.data + 2)) return 0;
|
||||
if (!EC_READ_U16(command.data) && !EC_READ_U16(command.data + 2)) return 0;
|
||||
|
||||
EC_WARN("CRC faults on slave %i. A: %i, B: %i\n", slave->ring_position,
|
||||
EC_READ_U16(frame.data), EC_READ_U16(frame.data + 2));
|
||||
EC_READ_U16(command.data), EC_READ_U16(command.data + 2));
|
||||
|
||||
// Reset CRC counters
|
||||
EC_WRITE_U16(data, 0x0000);
|
||||
EC_WRITE_U16(data + 2, 0x0000);
|
||||
ec_frame_init_npwr(&frame, slave->master, slave->station_address,
|
||||
0x0300, 4, data);
|
||||
|
||||
if (unlikely(ec_frame_send_receive(&frame))) {
|
||||
ec_command_init_npwr(&command, slave->station_address, 0x0300, 4, data);
|
||||
if (unlikely(ec_master_simple_io(slave->master, &command))) {
|
||||
EC_WARN("Resetting CRC fault counters failed on slave %i!\n",
|
||||
slave->ring_position);
|
||||
return -1;
|
||||
|
||||
@@ -15,6 +15,28 @@
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
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_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
FMMU-Konfiguration.
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define ABTASTFREQUENZ 1000
|
||||
#define ABTASTFREQUENZ 100
|
||||
|
||||
struct timer_list timer;
|
||||
|
||||
@@ -47,7 +47,9 @@ void run(unsigned long data)
|
||||
static unsigned int counter = 0;
|
||||
|
||||
// Prozessdaten lesen und schreiben
|
||||
EtherCAT_rt_domain_xio(domain1);
|
||||
EtherCAT_rt_domain_queue(domain1);
|
||||
EtherCAT_rt_master_xio(master);
|
||||
EtherCAT_rt_domain_process(domain1);
|
||||
|
||||
k_angle = EC_READ_U16(r_inc);
|
||||
k_pos = EC_READ_U32(r_ssi);
|
||||
|
||||
@@ -28,8 +28,9 @@ msr_modul-objs := msr_module.o \
|
||||
rt_lib/msr-math/msr_hex_bin.o \
|
||||
libm.o
|
||||
|
||||
EXTRA_CFLAGS := -I $(src)/rt_lib/msr-include -D_SIMULATION \
|
||||
-I/usr/include -mhard-float
|
||||
EXTRA_CFLAGS := -I$(src)/rt_lib/msr-include -D_SIMULATION \
|
||||
-I/usr/include -mhard-float \
|
||||
-DSVNREV=$(shell svnversion $(src)) -DUSER=$(USER)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -50,9 +50,11 @@ static struct ipipe_sysinfo sys_info;
|
||||
// EtherCAT
|
||||
ec_master_t *master = NULL;
|
||||
ec_domain_t *domain1 = NULL;
|
||||
ec_domain_t *domain2 = NULL;
|
||||
|
||||
// Prozessdaten
|
||||
void *r_ssi;
|
||||
void *r_ssi2;
|
||||
void *r_inc;
|
||||
|
||||
uint32_t k_angle;
|
||||
@@ -60,7 +62,11 @@ uint32_t k_pos;
|
||||
|
||||
ec_field_init_t domain1_fields[] = {
|
||||
{&r_ssi, "1", "Beckhoff", "EL5001", ec_ipvalue, 0, 1},
|
||||
{&r_inc, "10", "Beckhoff", "EL5101", ec_ipvalue, 0, 1},
|
||||
{}
|
||||
};
|
||||
|
||||
ec_field_init_t domain2_fields[] = {
|
||||
{&r_ssi2, "1", "Beckhoff", "EL5001", ec_ipvalue, 0, 1},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -68,11 +74,27 @@ ec_field_init_t domain1_fields[] = {
|
||||
|
||||
static void msr_controller_run(void)
|
||||
{
|
||||
// Prozessdaten lesen und schreiben
|
||||
EtherCAT_rt_domain_xio(domain1);
|
||||
static unsigned int counter = 0;
|
||||
|
||||
k_angle = EC_READ_U16(r_inc);
|
||||
if (counter) counter--;
|
||||
else {
|
||||
//EtherCAT_rt_master_debug(master, 2);
|
||||
counter = MSR_ABTASTFREQUENZ;
|
||||
}
|
||||
|
||||
// Prozessdaten lesen und schreiben
|
||||
EtherCAT_rt_domain_queue(domain1);
|
||||
EtherCAT_rt_domain_queue(domain2);
|
||||
|
||||
EtherCAT_rt_master_xio(master);
|
||||
|
||||
EtherCAT_rt_domain_process(domain1);
|
||||
EtherCAT_rt_domain_process(domain2);
|
||||
|
||||
//k_angle = EC_READ_U16(r_inc);
|
||||
k_pos = EC_READ_U32(r_ssi);
|
||||
|
||||
//EtherCAT_rt_master_debug(master, 0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -118,7 +140,6 @@ void domain_entry(void)
|
||||
int __init init_rt_module(void)
|
||||
{
|
||||
struct ipipe_domain_attr attr; //ipipe
|
||||
const ec_field_init_t *field;
|
||||
uint32_t version;
|
||||
|
||||
// Als allererstes die RT-lib initialisieren
|
||||
@@ -134,7 +155,7 @@ int __init init_rt_module(void)
|
||||
|
||||
//EtherCAT_rt_master_print(master);
|
||||
|
||||
printk(KERN_INFO "Registering domain...\n");
|
||||
printk(KERN_INFO "Registering domains...\n");
|
||||
|
||||
if (!(domain1 = EtherCAT_rt_master_register_domain(master, ec_sync, 100)))
|
||||
{
|
||||
@@ -142,32 +163,37 @@ int __init init_rt_module(void)
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (!(domain2 = EtherCAT_rt_master_register_domain(master, ec_sync, 100)))
|
||||
{
|
||||
printk(KERN_ERR "EtherCAT: Could not register domain!\n");
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
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 "Could not register field!\n");
|
||||
goto out_release_master;
|
||||
}
|
||||
if (EtherCAT_rt_register_domain_fields(domain1, domain1_fields)) {
|
||||
printk(KERN_ERR "Failed to register domain fields.\n");
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (EtherCAT_rt_register_domain_fields(domain2, domain2_fields)) {
|
||||
printk(KERN_ERR "Failed to register domain fields.\n");
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Activating master...\n");
|
||||
|
||||
//EtherCAT_rt_master_debug(master, 2);
|
||||
|
||||
if (EtherCAT_rt_master_activate(master)) {
|
||||
printk(KERN_ERR "Could not activate master!\n");
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
//EtherCAT_rt_master_debug(master, 0);
|
||||
|
||||
#if 1
|
||||
if (EtherCAT_rt_canopen_sdo_addr_read(master, "1", 0x100A, 1,
|
||||
if (EtherCAT_rt_canopen_sdo_addr_read(master, "6", 0x100A, 1,
|
||||
&version)) {
|
||||
printk(KERN_ERR "Could not read SSI version!\n");
|
||||
goto out_release_master;
|
||||
@@ -222,9 +248,16 @@ void __exit cleanup_rt_module(void)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define EC_LIT(X) #X
|
||||
#define EC_STR(X) EC_LIT(X)
|
||||
#define COMPILE_INFO "Revision " EC_STR(SVNREV) \
|
||||
", compiled by " EC_STR(USER) \
|
||||
" at " __DATE__ " " __TIME__
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR ("Florian Pose <fp@igh-essen.com>");
|
||||
MODULE_DESCRIPTION ("EtherCAT real-time test environment");
|
||||
MODULE_VERSION(COMPILE_INFO);
|
||||
|
||||
module_init(init_rt_module);
|
||||
module_exit(cleanup_rt_module);
|
||||
|
||||
Reference in New Issue
Block a user