Newer
Older
Florian Pose
committed
/******************************************************************************
Florian Pose
committed
* $Id$
Florian Pose
committed
* Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
Florian Pose
committed
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
Florian Pose
committed
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
Florian Pose
committed
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
Florian Pose
committed
*****************************************************************************/
/**
\file
EtherCAT master methods.
*/
/*****************************************************************************/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/version.h>
#include "globals.h"
#include "slave_config.h"
#include "device.h"
#include "master.h"
Florian Pose
committed
/*****************************************************************************/
/** Set to 1 to enable external datagram injection debugging.
*/
#define DEBUG_INJECT 0
#ifdef EC_HAVE_CYCLES
/** Frame timeout in cycles.
*/
static cycles_t timeout_cycles;
static cycles_t sdo_injection_timeout_cycles;
#else
/** Frame timeout in jiffies.
*/
static unsigned long timeout_jiffies;
static unsigned long sdo_injection_timeout_jiffies;
#endif
/*****************************************************************************/
void ec_master_clear_slave_configs(ec_master_t *);
static int ec_master_idle_thread(void *);
static int ec_master_operation_thread(void *);
void ec_master_find_dc_ref_clock(ec_master_t *);
/*****************************************************************************/
/** Static variables initializer.
*/
void ec_master_init_static(void)
{
#ifdef EC_HAVE_CYCLES
timeout_cycles = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000);
sdo_injection_timeout_cycles = (cycles_t) EC_SDO_INJECTION_TIMEOUT /* us */ * (cpu_khz / 1000);
#else
// one jiffy may always elapse between time measurement
timeout_jiffies = max(EC_IO_TIMEOUT * HZ / 1000000, 1);
sdo_injection_timeout_jiffies = max(EC_SDO_INJECTION_TIMEOUT * HZ / 1000000, 1);
#endif
}
/*****************************************************************************/
Master constructor.
\return 0 in case of success, else < 0
int ec_master_init(ec_master_t *master, /**< EtherCAT master */
unsigned int index, /**< master index */
const uint8_t *main_mac, /**< MAC address of main device */
const uint8_t *backup_mac, /**< MAC address of backup device */
dev_t device_number, /**< Character device number. */
struct class *class, /**< Device class. */
unsigned int debug_level /**< Debug level (module parameter). */
master->index = index;
master->reserved = 0;
sema_init(&master->master_sem, 1);
Florian Pose
committed
master->main_mac = main_mac;
master->backup_mac = backup_mac;
sema_init(&master->device_sem, 1);
master->active = 0;
master->injection_seq_fsm = 0;
master->injection_seq_rt = 0;
INIT_LIST_HEAD(&master->configs);
master->app_time = 0ULL;
master->app_start_time = 0ULL;
master->has_start_time = 0;
master->scan_busy = 0;
master->allow_scan = 1;
init_waitqueue_head(&master->scan_queue);
master->config_busy = 0;
master->allow_config = 1;
sema_init(&master->config_sem, 1);
init_waitqueue_head(&master->config_queue);
INIT_LIST_HEAD(&master->datagram_queue);
INIT_LIST_HEAD(&master->ext_datagram_queue);
sema_init(&master->ext_queue_sem, 1);
INIT_LIST_HEAD(&master->external_datagram_queue);
master->max_queue_size = EC_MAX_DATA_SIZE;
Martin Troxler
committed
master->debug_level = debug_level;
master->stats.timeouts = 0;
master->stats.corrupted = 0;
master->stats.unmatched = 0;
master->stats.output_jiffies = 0;
master->frames_timed_out = 0;
master->send_cb = NULL;
master->receive_cb = NULL;
Florian Pose
committed
master->cb_data = NULL;
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
Florian Pose
committed
master->app_cb_data = NULL;
INIT_LIST_HEAD(&master->sii_requests);
init_waitqueue_head(&master->sii_queue);
INIT_LIST_HEAD(&master->reg_requests);
init_waitqueue_head(&master->reg_queue);
Florian Pose
committed
// init devices
ret = ec_device_init(&master->main_device, master);
if (ret < 0)
Florian Pose
committed
ret = ec_device_init(&master->backup_device, master);
if (ret < 0)
Florian Pose
committed
goto out_clear_main;
Florian Pose
committed
// init state machine datagram
ec_datagram_init(&master->fsm_datagram);
snprintf(master->fsm_datagram.name, EC_DATAGRAM_NAME_SIZE, "master-fsm");
ret = ec_datagram_prealloc(&master->fsm_datagram, EC_MAX_DATA_SIZE);
if (ret < 0) {
Florian Pose
committed
ec_datagram_clear(&master->fsm_datagram);
Florian Pose
committed
EC_ERR("Failed to allocate FSM datagram.\n");
goto out_clear_backup;
Florian Pose
committed
}
// create state machine object
Florian Pose
committed
ec_fsm_master_init(&master->fsm, master, &master->fsm_datagram);
// init reference sync datagram
ec_datagram_init(&master->ref_sync_datagram);
snprintf(master->ref_sync_datagram.name, EC_DATAGRAM_NAME_SIZE, "refsync");
ret = ec_datagram_apwr(&master->ref_sync_datagram, 0, 0x0910, 8);
if (ret < 0) {
ec_datagram_clear(&master->ref_sync_datagram);
EC_ERR("Failed to allocate reference synchronisation datagram.\n");
goto out_clear_fsm;
}
Florian Pose
committed
// init sync datagram
ec_datagram_init(&master->sync_datagram);
snprintf(master->sync_datagram.name, EC_DATAGRAM_NAME_SIZE, "sync");
ret = ec_datagram_prealloc(&master->sync_datagram, 4);
Florian Pose
committed
if (ret < 0) {
ec_datagram_clear(&master->sync_datagram);
EC_ERR("Failed to allocate synchronisation datagram.\n");
goto out_clear_ref_sync;
Florian Pose
committed
}
// init sync monitor datagram
ec_datagram_init(&master->sync_mon_datagram);
snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE, "syncmon");
ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
if (ret < 0) {
ec_datagram_clear(&master->sync_mon_datagram);
EC_ERR("Failed to allocate sync monitoring datagram.\n");
goto out_clear_sync;
}
Florian Pose
committed
// init character device
ret = ec_cdev_init(&master->cdev, master, device_number);
if (ret)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
master->class_device = device_create(class, NULL,
MKDEV(MAJOR(device_number), master->index), NULL,
"EtherCAT%u", master->index);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
master->class_device = device_create(class, NULL,
MKDEV(MAJOR(device_number), master->index),
"EtherCAT%u", master->index);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
master->class_device = class_device_create(class, NULL,
MKDEV(MAJOR(device_number), master->index), NULL,
"EtherCAT%u", master->index);
master->class_device = class_device_create(class,
MKDEV(MAJOR(device_number), master->index), NULL,
"EtherCAT%u", master->index);
if (IS_ERR(master->class_device)) {
EC_ERR("Failed to create class device!\n");
ret = PTR_ERR(master->class_device);
goto out_clear_cdev;
}
out_clear_cdev:
ec_cdev_clear(&master->cdev);
out_clear_sync_mon:
ec_datagram_clear(&master->sync_mon_datagram);
Florian Pose
committed
out_clear_sync:
ec_datagram_clear(&master->sync_datagram);
out_clear_ref_sync:
ec_datagram_clear(&master->ref_sync_datagram);
out_clear_fsm:
ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram);
out_clear_backup:
Florian Pose
committed
ec_device_clear(&master->backup_device);
out_clear_main:
ec_device_clear(&master->main_device);
out_return:
Florian Pose
committed
/*****************************************************************************/
void ec_master_clear(
ec_master_t *master /**< EtherCAT master */
)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
device_unregister(master->class_device);
#else
class_device_unregister(master->class_device);
Florian Pose
committed
Florian Pose
committed
ec_master_clear_domains(master);
ec_master_clear_slave_configs(master);
Florian Pose
committed
ec_datagram_clear(&master->sync_mon_datagram);
Florian Pose
committed
ec_datagram_clear(&master->sync_datagram);
ec_datagram_clear(&master->ref_sync_datagram);
Florian Pose
committed
ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram);
Florian Pose
committed
ec_device_clear(&master->backup_device);
ec_device_clear(&master->main_device);
}
/*****************************************************************************/
/** Clear and free all EoE handlers.
*/
void ec_master_clear_eoe_handlers(
ec_master_t *master /**< EtherCAT master */
)
{
ec_eoe_t *eoe, *next;
list_for_each_entry_safe(eoe, next, &master->eoe_handlers, list) {
list_del(&eoe->list);
ec_eoe_clear(eoe);
kfree(eoe);
}
Florian Pose
committed
/*****************************************************************************/
void ec_master_clear_slave_configs(ec_master_t *master)
{
ec_slave_config_t *sc, *next;
list_for_each_entry_safe(sc, next, &master->configs, list) {
list_del(&sc->list);
ec_slave_config_clear(sc);
kfree(sc);
}
}
/*****************************************************************************/
/** Clear all slaves.
void ec_master_clear_slaves(ec_master_t *master)
{
// external requests are obsolete, so we wake pending waiters and remove
// them from the list
//
// SII requests
while (1) {
ec_sii_write_request_t *request;
if (list_empty(&master->sii_requests))
break;
// get first request
request = list_entry(master->sii_requests.next,
ec_sii_write_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding SII request, slave %u does not exist anymore.\n",
request->slave->ring_position);
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->sii_queue);
}
// Register requests
while (1) {
ec_reg_request_t *request;
if (list_empty(&master->reg_requests))
break;
// get first request
request = list_entry(master->reg_requests.next,
ec_reg_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding Reg request, slave %u does not exist anymore.\n",
request->slave->ring_position);
request->state = EC_INT_REQUEST_FAILURE;
wake_up(&master->reg_queue);
}
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
// SDO requests
while (1) {
ec_master_sdo_request_t *request;
if (list_empty(&slave->slave_sdo_requests))
break;
// get first request
request = list_entry(slave->slave_sdo_requests.next,
ec_master_sdo_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding SDO request, slave %u does not exist anymore.\n",
request->req.state = EC_INT_REQUEST_FAILURE;
wake_up(&slave->sdo_queue);
}
// FoE requests
while (1) {
ec_master_foe_request_t *request;
if (list_empty(&slave->foe_requests))
break;
// get first request
request = list_entry(slave->foe_requests.next,
ec_master_foe_request_t, list);
list_del_init(&request->list); // dequeue
EC_INFO("Discarding FOE request, slave %u does not exist anymore.\n",
slave->ring_position);
request->req.state = EC_INT_REQUEST_FAILURE;
wake_up(&slave->foe_queue);
}
kfree(master->slaves);
master->slaves = NULL;
}
/*****************************************************************************/
/** Clear all domains.
*/
void ec_master_clear_domains(ec_master_t *master)
Florian Pose
committed
{
ec_domain_t *domain, *next;
Florian Pose
committed
list_for_each_entry_safe(domain, next, &master->domains, list) {
Florian Pose
committed
list_del(&domain->list);
Florian Pose
committed
}
}
/*****************************************************************************/
Florian Pose
committed
void *cb_data /**< Callback data. */
Florian Pose
committed
ec_master_t *master = (ec_master_t *) cb_data;
ecrt_master_send_ext(master);
up(&master->io_sem);
}
/*****************************************************************************/
Florian Pose
committed
void *cb_data /**< Callback data. */
Florian Pose
committed
ec_master_t *master = (ec_master_t *) cb_data;
down(&master->io_sem);
ecrt_master_receive(master);
}
/*****************************************************************************/
/** Starts the master thread.
*
* \retval 0 Success.
* \retval <0 Error code.
*/
int ec_master_thread_start(
ec_master_t *master, /**< EtherCAT master */
int (*thread_func)(void *), /**< thread function to start */
const char *name /**< Thread name. */
EC_INFO("Starting %s thread.\n", name);
master->thread = kthread_run(thread_func, master, name);
if (IS_ERR(master->thread)) {
int err = (int) PTR_ERR(master->thread);
EC_ERR("Failed to start master thread (error %i)!\n", err);
return 0;
}
/*****************************************************************************/
/** Stops the master thread.
void ec_master_thread_stop(
ec_master_t *master /**< EtherCAT master */
)
unsigned long sleep_jiffies;
if (!master->thread) {
EC_WARN("ec_master_thread_stop(): Already finished!\n");
return;
}
if (master->debug_level)
EC_DBG("Stopping master thread.\n");
kthread_stop(master->thread);
master->thread = NULL;
EC_INFO("Master thread exited.\n");
if (master->fsm_datagram.state != EC_DATAGRAM_SENT)
return;
// wait for FSM datagram
sleep_jiffies = max(HZ / 100, 1); // 10 ms, at least 1 jiffy
schedule_timeout(sleep_jiffies);
}
/*****************************************************************************/
/** Transition function from ORPHANED to IDLE phase.
int ec_master_enter_idle_phase(
ec_master_t *master /**< EtherCAT master */
)
Florian Pose
committed
if (master->debug_level)
EC_DBG("ORPHANED -> IDLE.\n");
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
Florian Pose
committed
master->cb_data = master;
ret = ec_master_thread_start(master, ec_master_idle_thread,
"EtherCAT-IDLE");
if (ret)
}
/*****************************************************************************/
/** Transition function from IDLE to ORPHANED phase.
void ec_master_leave_idle_phase(ec_master_t *master /**< EtherCAT master */)
if (master->debug_level)
EC_DBG("IDLE -> ORPHANED.\n");
ec_master_eoe_stop(master);
ec_master_thread_stop(master);
Florian Pose
committed
down(&master->master_sem);
Florian Pose
committed
up(&master->master_sem);
}
/*****************************************************************************/
/** Transition function from IDLE to OPERATION phase.
int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
if (master->debug_level)
EC_DBG("IDLE -> OPERATION.\n");
down(&master->config_sem);
master->allow_config = 0; // temporarily disable slave configuration
if (master->config_busy) {
up(&master->config_sem);
// wait for slave configuration to complete
ret = wait_event_interruptible(master->config_queue,
!master->config_busy);
if (ret) {
EC_INFO("Finishing slave configuration interrupted by signal.\n");
goto out_allow;
}
if (master->debug_level)
EC_DBG("Waiting for pending slave configuration returned.\n");
} else {
up(&master->config_sem);
}
down(&master->scan_sem);
master->allow_scan = 0; // 'lock' the slave list
if (!master->scan_busy) {
up(&master->scan_sem);
} else {
up(&master->scan_sem);
// wait for slave scan to complete
ret = wait_event_interruptible(master->scan_queue, !master->scan_busy);
if (ret) {
EC_INFO("Waiting for slave scan interrupted by signal.\n");
goto out_allow;
}
if (master->debug_level)
EC_DBG("Waiting for pending slave scan returned.\n");
// set states for all slaves
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
// ... but set EoE slaves to OP
list_for_each_entry(eoe, &master->eoe_handlers, list) {
if (ec_eoe_is_open(eoe))
ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
}
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
Florian Pose
committed
master->app_cb_data = NULL;
out_allow:
master->allow_scan = 1;
master->allow_config = 1;
}
/*****************************************************************************/
/** Transition function from OPERATION to IDLE phase.
void ec_master_leave_operation_phase(
ec_master_t *master /**< EtherCAT master */
)
if (master->active)
ecrt_master_deactivate(master);
Florian Pose
committed
if (master->debug_level)
EC_DBG("OPERATION -> IDLE.\n");
Martin Troxler
committed
/*****************************************************************************/
/** Injects external datagrams that fit into the datagram queue
Martin Troxler
committed
*/
void ec_master_inject_external_datagrams(
Martin Troxler
committed
ec_master_t *master /**< EtherCAT master */
)
{
ec_datagram_t *datagram, *n;
size_t queue_size = 0;
list_for_each_entry(datagram, &master->datagram_queue, queue) {
queue_size += datagram->data_size;
}
list_for_each_entry_safe(datagram, n, &master->external_datagram_queue, queue) {
queue_size += datagram->data_size;
if (queue_size <= master->max_queue_size) {
list_del_init(&datagram->queue);
if (master->debug_level) {
EC_DBG("Injecting external datagram %08x size=%u, queue_size=%u\n",(unsigned int)datagram,datagram->data_size,queue_size);
#ifdef EC_HAVE_CYCLES
datagram->cycles_sent = 0;
#endif
datagram->jiffies_sent = 0;
ec_master_queue_datagram(master, datagram);
}
else {
if (datagram->data_size > master->max_queue_size) {
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
EC_ERR("External datagram %08x is too large, size=%u, max_queue_size=%u\n",(unsigned int)datagram,datagram->data_size,master->max_queue_size);
}
else {
#ifdef EC_HAVE_CYCLES
cycles_t cycles_now = get_cycles();
if (cycles_now - datagram->cycles_sent
> sdo_injection_timeout_cycles) {
#else
if (jiffies - datagram->jiffies_sent
> sdo_injection_timeout_jiffies) {
#endif
unsigned int time_us;
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
#ifdef EC_HAVE_CYCLES
time_us = (unsigned int) ((cycles_now - datagram->cycles_sent) * 1000LL) / cpu_khz;
#else
time_us = (unsigned int) ((jiffies - datagram->jiffies_sent) * 1000000 / HZ);
#endif
EC_ERR("Timeout %u us: injecting external datagram %08x size=%u, max_queue_size=%u\n",time_us,(unsigned int)datagram,datagram->data_size,master->max_queue_size);
if (master->debug_level) {
EC_DBG("Deferred injecting of external datagram %08x size=%u, queue_size=%u\n",(unsigned int)datagram,datagram->data_size,queue_size);
Martin Troxler
committed
}
}
/*****************************************************************************/
/** Places an external datagram in the sdo datagram queue.
Martin Troxler
committed
*/
void ec_master_queue_external_datagram(
Martin Troxler
committed
ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
{
if (master->debug_level) {
EC_DBG("Requesting external datagram %08x size=%u\n",(unsigned int)datagram,datagram->data_size);
datagram->state = EC_DATAGRAM_QUEUED;
#ifdef EC_HAVE_CYCLES
datagram->cycles_sent = get_cycles();
#endif
datagram->jiffies_sent = jiffies;
Martin Troxler
committed
down(&master->io_sem);
list_add_tail(&datagram->queue, &master->external_datagram_queue);
Martin Troxler
committed
up(&master->io_sem);
}
/*****************************************************************************/
/** Places a datagram in the datagram queue.
*/
void ec_master_queue_datagram(
ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
Martin Troxler
committed
if (datagram->state == EC_DATAGRAM_SENT)
return;
// check, if the datagram is already queued
list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
if (queued_datagram == datagram) {
datagram->skip_count++;
if (master->debug_level)
EC_DBG("skipping datagram %x.\n", (unsigned int) datagram);
datagram->state = EC_DATAGRAM_QUEUED;
return;
}
}
list_add_tail(&datagram->queue, &master->datagram_queue);
datagram->state = EC_DATAGRAM_QUEUED;
}
/*****************************************************************************/
/** Places a datagram in the non-application datagram queue.
*/
void ec_master_queue_datagram_ext(
ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
{
down(&master->ext_queue_sem);
list_add_tail(&datagram->queue, &master->ext_datagram_queue);
up(&master->ext_queue_sem);
}
/*****************************************************************************/
/** Sends the datagrams in the queue.
*
* \return 0 in case of success, else < 0
*/
void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
ec_datagram_t *datagram, *next;
uint8_t *frame_data, *cur_data;
void *follows_word;
cycles_t cycles_start, cycles_sent, cycles_end;
unsigned long jiffies_sent;
unsigned int frame_count, more_datagrams_waiting;
struct list_head sent_datagrams;
cycles_start = get_cycles();
frame_count = 0;
INIT_LIST_HEAD(&sent_datagrams);
EC_DBG("ec_master_send_datagrams\n");
// fetch pointer to transmit socket buffer
Florian Pose
committed
frame_data = ec_device_tx_data(&master->main_device);
cur_data = frame_data + EC_FRAME_HEADER_SIZE;
follows_word = NULL;
// fill current frame with datagrams
list_for_each_entry(datagram, &master->datagram_queue, queue) {
if (datagram->state != EC_DATAGRAM_QUEUED) continue;
// does the current datagram fit in the frame?
datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size
+ EC_DATAGRAM_FOOTER_SIZE;
if (cur_data - frame_data + datagram_size > ETH_DATA_LEN) {
more_datagrams_waiting = 1;
list_add_tail(&datagram->sent, &sent_datagrams);
datagram->index = master->datagram_index++;
EC_DBG("adding datagram 0x%02X\n", datagram->index);
// set "datagram following" flag in previous frame
if (follows_word)
EC_WRITE_U16(follows_word, EC_READ_U16(follows_word) | 0x8000);
// EtherCAT datagram header
EC_WRITE_U8 (cur_data, datagram->type);
EC_WRITE_U8 (cur_data + 1, datagram->index);
memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN);
EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF);
EC_WRITE_U16(cur_data + 8, 0x0000);
follows_word = cur_data + 6;
cur_data += EC_DATAGRAM_HEADER_SIZE;
// EtherCAT datagram data
memcpy(cur_data, datagram->data, datagram->data_size);
cur_data += datagram->data_size;
EC_WRITE_U16(cur_data, 0x0000); // reset working counter
cur_data += EC_DATAGRAM_FOOTER_SIZE;
if (list_empty(&sent_datagrams)) {
EC_DBG("nothing to send.\n");
break;
}
// EtherCAT frame header
EC_WRITE_U16(frame_data, ((cur_data - frame_data
- EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
// pad frame
while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN)
EC_DBG("frame size: %u\n", cur_data - frame_data);
// send frame
Florian Pose
committed
ec_device_send(&master->main_device, cur_data - frame_data);
cycles_sent = get_cycles();
jiffies_sent = jiffies;
// set datagram states and sending timestamps
list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) {
datagram->state = EC_DATAGRAM_SENT;
datagram->cycles_sent = cycles_sent;
datagram->jiffies_sent = jiffies_sent;
list_del_init(&datagram->sent); // empty list of sent datagrams
}
cycles_end = get_cycles();
EC_DBG("ec_master_send_datagrams sent %u frames in %uus.\n",
frame_count,
(unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
}
/*****************************************************************************/
/** Processes a received frame.
*
* This function is called by the network driver for every received frame.
*
* \return 0 in case of success, else < 0
*/
void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
const uint8_t *frame_data, /**< frame data */
size_t size /**< size of the received data */
)
{
size_t frame_size, data_size;
uint8_t datagram_type, datagram_index;
unsigned int cmd_follows, matched;
const uint8_t *cur_data;
if (unlikely(size < EC_FRAME_HEADER_SIZE)) {
if (master->debug_level) {
EC_DBG("Corrupted frame received (size %u < %u byte):\n",
size, EC_FRAME_HEADER_SIZE);
ec_print_data(frame_data, size);
}
master->stats.corrupted++;
ec_master_output_stats(master);
return;
}
cur_data = frame_data;
// check length of entire frame
frame_size = EC_READ_U16(cur_data) & 0x07FF;
cur_data += EC_FRAME_HEADER_SIZE;
if (unlikely(frame_size > size)) {
if (master->debug_level) {
EC_DBG("Corrupted frame received (invalid frame size %u for "
"received size %u):\n", frame_size, size);
ec_print_data(frame_data, size);
}
master->stats.corrupted++;
ec_master_output_stats(master);
return;
}
cmd_follows = 1;
while (cmd_follows) {
// process datagram header
datagram_type = EC_READ_U8 (cur_data);
datagram_index = EC_READ_U8 (cur_data + 1);
data_size = EC_READ_U16(cur_data + 6) & 0x07FF;
cmd_follows = EC_READ_U16(cur_data + 6) & 0x8000;