Newer
Older
Florian Pose
committed
/******************************************************************************
Florian Pose
committed
* $Id$
* Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The IgH EtherCAT Master is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
Florian Pose
committed
*****************************************************************************/
/**
\file
EtherCAT master methods.
*/
/*****************************************************************************/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "../include/ecrt.h"
#include "globals.h"
#include "master.h"
#include "device.h"
Florian Pose
committed
/*****************************************************************************/
void ec_master_clear(struct kobject *);
Florian Pose
committed
void ec_master_destroy_domains(ec_master_t *);
void ec_master_sync_io(ec_master_t *);
static int ec_master_thread(void *);
void ec_master_eoe_run(unsigned long);
void ec_master_check_sdo(unsigned long);
ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *);
ssize_t ec_store_master_attribute(struct kobject *, struct attribute *,
const char *, size_t);
/*****************************************************************************/
EC_SYSFS_READ_WRITE_ATTR(eeprom_write_enable);
static struct attribute *ec_def_attrs[] = {
NULL,
};
static struct sysfs_ops ec_sysfs_ops = {
.show = &ec_show_master_attribute,
};
static struct kobj_type ktype_ec_master = {
.release = ec_master_clear,
.sysfs_ops = &ec_sysfs_ops,
.default_attrs = ec_def_attrs
};
/** \endcond */
/*****************************************************************************/
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 */
unsigned int eoeif_count, /**< number of EoE interfaces */
dev_t dev_num /**< number for XML cdev's */
ec_eoe_t *eoe, *next_eoe;
unsigned int i;
EC_INFO("Initializing master %i.\n", index);
master->index = index;
master->device = NULL;
Florian Pose
committed
init_MUTEX(&master->device_sem);
INIT_LIST_HEAD(&master->datagram_queue);
master->debug_level = 0;
master->stats.timeouts = 0;
master->stats.corrupted = 0;
master->stats.skipped = 0;
master->stats.unmatched = 0;
master->stats.output_jiffies = 0;
for (i = 0; i < HZ; i++) {
master->idle_cycle_times[i] = 0;
master->eoe_cycle_times[i] = 0;
}
master->idle_cycle_time_pos = 0;
master->eoe_cycle_time_pos = 0;
init_timer(&master->eoe_timer);
master->eoe_timer.function = ec_master_eoe_run;
master->eoe_timer.data = (unsigned long) master;
master->eoe_running = 0;
master->eoe_checked = 0;
INIT_LIST_HEAD(&master->eoe_handlers);
master->internal_lock = SPIN_LOCK_UNLOCKED;
master->request_cb = NULL;
master->release_cb = NULL;
master->cb_data = NULL;
master->eeprom_write_enable = 0;
master->sdo_request = NULL;
master->sdo_seq_user = 0;
master->sdo_seq_master = 0;
init_MUTEX(&master->sdo_sem);
init_timer(&master->sdo_timer);
master->sdo_timer.function = ec_master_check_sdo;
master->sdo_timer.data = (unsigned long) master;
init_completion(&master->sdo_complete);
// init XML character device
if (ec_xmldev_init(&master->xmldev, master, dev_num)) {
EC_ERR("Failed to init XML character device.\n");
// create EoE handlers
for (i = 0; i < eoeif_count; i++) {
if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate EoE-Object.\n");
goto out_clear_eoe;
}
if (ec_eoe_init(eoe)) {
kfree(eoe);
goto out_clear_eoe;
}
list_add_tail(&eoe->list, &master->eoe_handlers);
}
// create state machine object
if (ec_fsm_init(&master->fsm, master)) goto out_clear_eoe;
// init kobject and add it to the hierarchy
memset(&master->kobj, 0x00, sizeof(struct kobject));
kobject_init(&master->kobj);
master->kobj.ktype = &ktype_ec_master;
if (kobject_set_name(&master->kobj, "ethercat%i", index)) {
EC_ERR("Failed to set kobj name.\n");
kobject_put(&master->kobj);
return -1;
if (kobject_add(&master->kobj)) {
EC_ERR("Failed to add master kobj.\n");
kobject_put(&master->kobj);
return -1;
}
out_clear_eoe:
list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) {
list_del(&eoe->list);
ec_eoe_clear(eoe);
kfree(eoe);
}
ec_xmldev_clear(&master->xmldev);
out_return:
return -1;
Florian Pose
committed
/*****************************************************************************/
Master destructor.
Florian Pose
committed
Clears the kobj-hierarchy bottom up and frees the master.
*/
void ec_master_destroy(ec_master_t *master /**< EtherCAT master */)
{
ec_master_destroy_slaves(master);
ec_master_destroy_domains(master);
// destroy self
kobject_del(&master->kobj);
kobject_put(&master->kobj); // free master
}
/*****************************************************************************/
/**
Clear and free master.
This method is called by the kobject,
once there are no more references to it.
void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
ec_master_t *master = container_of(kobj, ec_master_t, kobj);
ec_eoe_t *eoe, *next_eoe;
// dequeue all datagrams
list_for_each_entry_safe(datagram, next_datagram,
&master->datagram_queue, queue) {
datagram->state = EC_DATAGRAM_ERROR;
list_del_init(&datagram->queue);
}
ec_xmldev_clear(&master->xmldev);
// clear EoE objects
list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) {
list_del(&eoe->list);
ec_eoe_clear(eoe);
kfree(eoe);
}
Florian Pose
committed
EC_INFO("Master %i freed.\n", master->index);
Florian Pose
committed
/*****************************************************************************/
Florian Pose
committed
Destroy all slaves.
Florian Pose
committed
void ec_master_destroy_slaves(ec_master_t *master)
{
list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
list_del(&slave->list);
Florian Pose
committed
ec_slave_destroy(slave);
/*****************************************************************************/
Florian Pose
committed
/**
Destroy all domains.
*/
void ec_master_destroy_domains(ec_master_t *master)
{
ec_domain_t *domain, *next_d;
list_for_each_entry_safe(domain, next_d, &master->domains, list) {
list_del(&domain->list);
ec_domain_destroy(domain);
}
}
/*****************************************************************************/
void ec_master_flush_sdo_requests(ec_master_t *master)
{
del_timer_sync(&master->sdo_timer);
complete(&master->sdo_complete);
master->sdo_request = NULL;
master->sdo_seq_user = 0;
master->sdo_seq_master = 0;
}
/*****************************************************************************/
/**
Internal locking callback.
*/
int ec_master_request_cb(void *master /**< callback data */)
{
spin_lock(&((ec_master_t *) master)->internal_lock);
return 0;
}
/*****************************************************************************/
/**
Internal unlocking callback.
*/
void ec_master_release_cb(void *master /**< callback data */)
{
spin_unlock(&((ec_master_t *) master)->internal_lock);
}
/*****************************************************************************/
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/**
* Starts the master thread.
*/
int ec_master_thread_start(ec_master_t *master /**< EtherCAT master */)
{
init_completion(&master->thread_exit);
if (!(master->thread_id =
kernel_thread(ec_master_thread, master, CLONE_KERNEL)))
return -1;
return 0;
}
/*****************************************************************************/
/**
* Stops the master thread.
*/
void ec_master_thread_stop(ec_master_t *master /**< EtherCAT master */)
{
if (master->thread_id) {
kill_proc(master->thread_id, SIGTERM, 1);
wait_for_completion(&master->thread_exit);
EC_DBG("Master thread exited.\n");
}
}
/*****************************************************************************/
* Transition function from ORPHANED to IDLE mode.
int ec_master_enter_idle_mode(ec_master_t *master /**< EtherCAT master */)
{
master->request_cb = ec_master_request_cb;
master->release_cb = ec_master_release_cb;
master->cb_data = master;
if (ec_master_thread_start(master)) {
master->mode = EC_MASTER_MODE_ORPHANED;
return -1;
}
}
/*****************************************************************************/
/**
* Transition function from IDLE to ORPHANED mode.
void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */)
ec_master_eoe_stop(master);
ec_master_thread_stop(master);
}
/*****************************************************************************/
* Transition function from IDLE to OPERATION mode.
int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */)
ec_slave_t *slave;
ec_datagram_t *datagram = &master->fsm.datagram;
ec_master_eoe_stop(master); // stop EoE timer
master->eoe_checked = 1; // prevent from starting again by FSM
ec_master_thread_stop(master);
master->mode = EC_MASTER_MODE_OPERATION;
// wait for FSM datagram
while (get_cycles() - datagram->cycles_sent
< (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) {}
ecrt_master_receive(master);
}
Florian Pose
committed
// finish running master FSM
if (ec_fsm_running(&master->fsm)) {
while (ec_fsm_exec(&master->fsm)) {
ec_master_sync_io(master);
}
}
if (master->debug_level) {
if (ec_master_measure_bus_time(master)) {
EC_ERR("Bus time measuring failed!\n");
goto out_idle;
}
}
// set initial slave states
list_for_each_entry(slave, &master->slaves, list) {
if (ec_slave_is_coupler(slave)) {
ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
}
else {
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
}
}
master->eoe_checked = 0; // allow starting EoE again
return 0;
out_idle:
master->mode = EC_MASTER_MODE_IDLE;
if (ec_master_thread_start(master)) {
EC_WARN("Failed to start master thread again!\n");
}
return -1;
}
/*****************************************************************************/
/**
* Transition function from OPERATION to IDLE mode.
*/
void ec_master_leave_operation_mode(ec_master_t *master
/**< EtherCAT master */)
{
ec_slave_t *slave;
Florian Pose
committed
ec_fsm_t *fsm = &master->fsm;
ec_datagram_t *datagram = &master->fsm.datagram;
ec_master_eoe_stop(master); // stop EoE timer
master->eoe_checked = 1; // prevent from starting again by FSM
Florian Pose
committed
// wait for FSM datagram
if (datagram->state == EC_DATAGRAM_SENT) {
Florian Pose
committed
while (get_cycles() - datagram->cycles_sent
< (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000));
Florian Pose
committed
ecrt_master_receive(master);
}
Florian Pose
committed
// finish running master FSM
if (ec_fsm_running(fsm)) {
while (ec_fsm_exec(fsm)) {
ec_master_sync_io(master);
}
}
Florian Pose
committed
// set states for all slaves
list_for_each_entry(slave, &master->slaves, list) {
ec_slave_reset(slave);
Florian Pose
committed
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
Florian Pose
committed
fsm->slave = slave;
fsm->slave_state = ec_fsm_slaveconf_state_start;
Florian Pose
committed
do {
fsm->slave_state(fsm);
ec_master_sync_io(master);
}
while (fsm->slave_state != ec_fsm_slave_state_end
&& fsm->slave_state != ec_fsm_slave_state_error);
Florian Pose
committed
}
Florian Pose
committed
ec_master_destroy_domains(master);
master->request_cb = ec_master_request_cb;
master->release_cb = ec_master_release_cb;
master->cb_data = master;
master->eoe_checked = 0; // allow EoE again
if (ec_master_thread_start(master)) {
EC_WARN("Failed to start master thread!\n");
}
}
/*****************************************************************************/
Places a datagram in the datagram queue.
void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
// check, if the datagram is already queued
list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
if (queued_datagram == datagram) {
master->stats.skipped++;
ec_master_output_stats(master);
datagram->state = EC_DATAGRAM_QUEUED;
return;
}
}
list_add_tail(&datagram->queue, &master->datagram_queue);
datagram->state = EC_DATAGRAM_QUEUED;
}
/*****************************************************************************/
/**
\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
frame_data = ec_device_tx_data(master->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);
EC_WRITE_U32(cur_data + 2, datagram->address.logical);
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: %i\n", cur_data - frame_data);
// send frame
ec_device_send(master->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 %i frames in %ius.\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)) {
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)) {
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;
cur_data += EC_DATAGRAM_HEADER_SIZE;
if (unlikely(cur_data - frame_data
+ data_size + EC_DATAGRAM_FOOTER_SIZE > size)) {
master->stats.corrupted++;
ec_master_output_stats(master);
return;
}
// search for matching datagram in the queue
matched = 0;
list_for_each_entry(datagram, &master->datagram_queue, queue) {
if (datagram->state == EC_DATAGRAM_SENT
&& datagram->type == datagram_type
&& datagram->index == datagram_index
&& datagram->data_size == data_size) {
matched = 1;
break;
}
}
if (!matched) {
master->stats.unmatched++;
ec_master_output_stats(master);
cur_data += data_size + EC_DATAGRAM_FOOTER_SIZE;
continue;
}
// copy received data into the datagram memory
memcpy(datagram->data, cur_data, data_size);
cur_data += data_size;
// set the datagram's working counter
datagram->working_counter = EC_READ_U16(cur_data);
cur_data += EC_DATAGRAM_FOOTER_SIZE;
datagram->state = EC_DATAGRAM_RECEIVED;
datagram->cycles_received = master->device->cycles_isr;
datagram->jiffies_received = master->device->jiffies_isr;
}
}
/*****************************************************************************/
/**
Output statistics in cyclic mode.
This function outputs statistical data on demand, but not more often than
necessary. The output happens at most once a second.
*/
void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */)
{
if (unlikely(jiffies - master->stats.output_jiffies >= HZ)) {
master->stats.output_jiffies = jiffies;
if (master->stats.timeouts) {
EC_WARN("%i datagram%s TIMED OUT!\n", master->stats.timeouts,
master->stats.timeouts == 1 ? "" : "s");
master->stats.timeouts = 0;
}
if (master->stats.corrupted) {
EC_WARN("%i frame%s CORRUPTED!\n", master->stats.corrupted,
master->stats.corrupted == 1 ? "" : "s");
master->stats.corrupted = 0;
if (master->stats.skipped) {
EC_WARN("%i datagram%s SKIPPED!\n", master->stats.skipped,
master->stats.skipped == 1 ? "" : "s");
master->stats.skipped = 0;
}
if (master->stats.unmatched) {
EC_WARN("%i datagram%s UNMATCHED!\n", master->stats.unmatched,
master->stats.unmatched == 1 ? "" : "s");
master->stats.unmatched = 0;
}
}
}
/*****************************************************************************/
Master kernel thread function.
static int ec_master_thread(void *data)
cycles_t cycles_start, cycles_end;
daemonize("EtherCAT2");
allow_signal(SIGTERM);
while (!signal_pending(current) && master->mode == EC_MASTER_MODE_IDLE) {
cycles_start = get_cycles();
// receive
spin_lock_bh(&master->internal_lock);
ecrt_master_receive(master);
spin_unlock_bh(&master->internal_lock);
// execute master state machine
ec_fsm_exec(&master->fsm);
// send
spin_lock_bh(&master->internal_lock);
ecrt_master_send(master);
spin_unlock_bh(&master->internal_lock);
cycles_end = get_cycles();
master->idle_cycle_times[master->idle_cycle_time_pos]
= (u32) (cycles_end - cycles_start) * 1000 / cpu_khz;
master->idle_cycle_time_pos++;
master->idle_cycle_time_pos %= HZ;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
}
EC_INFO("Master thread exiting.\n");
master->thread_id = 0;
complete_and_exit(&master->thread_exit, 0);
}
/*****************************************************************************/
Initializes a sync manager configuration page with EEPROM data.
The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
void ec_sync_config(const ec_sii_sync_t *sync, /**< sync manager */
const ec_slave_t *slave, /**< EtherCAT slave */
uint8_t *data /**> configuration memory */
Florian Pose
committed
)
size_t sync_size;
sync_size = ec_slave_calc_sync_size(slave, sync);
EC_DBG("Slave %3i, SM %i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n",
slave->ring_position, sync->index, sync->physical_start_address,
sync_size, sync->control_register, sync->enable);
EC_WRITE_U16(data, sync->physical_start_address);
EC_WRITE_U16(data + 2, sync_size);
EC_WRITE_U8 (data + 4, sync->control_register);
EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable
}
/*****************************************************************************/
/**
Initializes an FMMU configuration page.
The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes.
*/
void ec_fmmu_config(const ec_fmmu_t *fmmu, /**< FMMU */
const ec_slave_t *slave, /**< EtherCAT slave */
uint8_t *data /**> configuration memory */
Florian Pose
committed
)
{
size_t sync_size;
sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
EC_DBG("Slave %3i, FMMU %2i:"
" LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
slave->ring_position, fmmu->index, fmmu->logical_start_address,
sync_size, fmmu->sync->physical_start_address,
((fmmu->sync->control_register & 0x04) ? "out" : "in"));
Florian Pose
committed
EC_WRITE_U32(data, fmmu->logical_start_address);
EC_WRITE_U16(data + 4, sync_size); // size of fmmu
EC_WRITE_U8 (data + 6, 0x00); // logical start bit
EC_WRITE_U8 (data + 7, 0x07); // logical end bit
Florian Pose
committed
EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address);
EC_WRITE_U8 (data + 10, 0x00); // physical start bit
EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
? 0x02 : 0x01));
EC_WRITE_U16(data + 12, 0x0001); // enable
EC_WRITE_U16(data + 14, 0x0000); // reserved
Florian Pose
committed
}
/*****************************************************************************/
/**
Formats master information for SysFS read access.
\return number of bytes written
*/
ssize_t ec_master_info(ec_master_t *master, /**< EtherCAT master */
char *buffer /**< memory to store data */
)
{
off_t off = 0;
uint32_t cur, sum, min, max, pos, i;
unsigned int frames_lost;
off += sprintf(buffer + off, "\nVersion: %s", ec_master_version_str);
off += sprintf(buffer + off, "\nMode: ");
switch (master->mode) {
case EC_MASTER_MODE_ORPHANED:
off += sprintf(buffer + off, "ORPHANED");
case EC_MASTER_MODE_IDLE:
off += sprintf(buffer + off, "IDLE");
case EC_MASTER_MODE_OPERATION:
off += sprintf(buffer + off, "OPERATION");
off += sprintf(buffer + off, "\nDevice:\n");
off += sprintf(buffer + off, " Frames sent: %u\n",
master->device->tx_count);
off += sprintf(buffer + off, " Frames received: %u\n",
master->device->rx_count);
frames_lost = master->device->tx_count - master->device->rx_count;
if (frames_lost) frames_lost--;
off += sprintf(buffer + off, " Frames lost: %u\n", frames_lost);
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n");
sum = 0;
min = 0xFFFFFFFF;
max = 0;
pos = master->idle_cycle_time_pos;
for (i = 0; i < HZ; i++) {
cur = master->idle_cycle_times[(i + pos) % HZ];
sum += cur;
if (cur < min) min = cur;
if (cur > max) max = cur;
}
off += sprintf(buffer + off, " Idle cycle: %u / %u.%u / %u\n",
min, sum / HZ, (sum * 100 / HZ) % 100, max);
sum = 0;
min = 0xFFFFFFFF;
max = 0;
pos = master->eoe_cycle_time_pos;
for (i = 0; i < HZ; i++) {
cur = master->eoe_cycle_times[(i + pos) % HZ];
sum += cur;
if (cur < min) min = cur;
if (cur > max) max = cur;
}
off += sprintf(buffer + off, " EoE cycle: %u / %u.%u / %u\n",
min, sum / HZ, (sum * 100 / HZ) % 100, max);
off += sprintf(buffer + off, "\nEoE statistics (RX/TX) [bps]:\n");
list_for_each_entry(eoe, &master->eoe_handlers, list) {
off += sprintf(buffer + off, " %s: %u / %u (%u KB/s)\n",
eoe->dev->name, eoe->rx_rate, eoe->tx_rate,
((eoe->rx_rate + eoe->tx_rate) / 8 + 512) / 1024);
off += sprintf(buffer + off, "\n");
return off;
}
/*****************************************************************************/
Formats attribute data for SysFS read access.
\return number of bytes to read
ssize_t ec_show_master_attribute(struct kobject *kobj, /**< kobject */
struct attribute *attr, /**< attribute */
char *buffer /**< memory to store data */
)
{
ec_master_t *master = container_of(kobj, ec_master_t, kobj);