You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

457 lines
14 KiB

/*
Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2009
Copyright Stichting C.A. Muller Radioastronomiestation, 2009
This program 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 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Simple tool to enumerate all Ethercat devices on the bus.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <ec/ec.h>
#include <ec/esc.h>
#include <ec/esc_esi.h>
#include <ec/esc_registers.h>
#include <ec/esc_id.h>
#include <log/log.h>
bool option_verbose = true;
bool option_short = false;
bool option_portcheck = true;
bool linkcheck = true;
void describe_esc(struct ec *ec, struct ec_dgram_addr *ec_addr)
{
int r, r2;
uint8_t val8;
uint16_t val16;
uint32_t val32;
uint8_t eeprom[1024/8];
uint32_t vendorid, productcode;
char *vendorid_str, *productcode_str;
int i;
size_t eeprom_size;
uint16_t eeprom_version;
struct esc_device *dev;
struct esc_esi_category *cat;
bool link_port0, link_port1, link_port2, link_port3;
dev = esc_device_create2(ec, ec_addr, "ec_enum");
ec_addr->addr.position.off = ESC_ADDR_MAP_TYPE;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
char *type;
switch (val8) {
case ESC_TYPE_ESC:
type = "ESC";
break;
case ESC_TYPE_IPCORE:
type = "IP core";
break;
case ESC_TYPE_ET1100:
type = "ET1100";
break;
case ESC_TYPE_ET1200:
type = "ET1200";
break;
case ESC_TYPE_TRINAMIC:
type = "Trinamic ESC";
break;
default:
type = "unknown";
}
if (option_verbose) printf("\tType: 0x%02x = %s\n", val8, type);
}
if (option_verbose) {
ec_addr->addr.position.off = ESC_ADDR_MAP_REVISION;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
ec_addr->addr.position.off = ESC_ADDR_MAP_BUILD;
r2 = ec_datagram_read(ec, ec_addr, &val16, 2);
if (r == 1 && r2 == 2) {
printf("\tRevision & Build: 0x%02x 0x%04x\n", val8, le16toh(val16));
}
ec_addr->addr.position.off = ESC_ADDR_MAP_FMMUS_SUPPORTED;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
printf("\tFMMUs supported: %d\n", val8);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_SYNCMANAGERS_SUPPORTED;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
printf("\tSyncmanagers supported: %d\n", val8);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_RAM_SIZE;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
printf("\tRAM size: %d Kb\n", val8);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_FEATURES;
r = ec_datagram_read(ec, ec_addr, &val16, 2);
if (r == 2) {
val16 = le16toh(val16);
printf("\tFeatures: 0x%02x\n", val16);
if (val16 & ESC_FEATURE_FMMU_BYTE_ORIENTED)
printf("\t\tFMMU is byte oriented\n");
if (val16 & ESC_FEATURE_DISTRIBUTED_CLOCKS)
printf("\t\tDistributed clocks\n");
if (val16 & ESC_FEATURE_64BIT_DISTRIBUTED_CLOCKS)
printf("\t\t64bit wide distributed clocks\n");
if (val16 & ESC_FEATURE_LOW_JITTER_EBUS)
printf("\t\tLow jitter EBUS\n");
if (val16 & ESC_FEATURE_ENHANCED_LINK_DETECTION_EBUS)
printf("\t\tEnhanced link detection EBUS\n");
if (val16 & ESC_FEATURE_ENHANCED_LINK_DETECTION_MII)
printf("\t\tEnhanced link detection MII\n");
if (val16 & ESC_FEATURE_SEPERATE_FCS_HANDLING)
printf("\t\tSeperate FCS handling\n");
if (val16 & ESC_FEATURE_ENHANCED_DC_SYNC_ACTIVATION)
printf("\t\tEnhanced DC sync activation\n");
}
ec_addr->addr.position.off = ESC_ADDR_MAP_SYNC_LATCH_PDI_CONFIG;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
printf("\tSync/Latch PDI config: 0x%02x\n", val8);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_ESC_CONFIGURATION;
r = ec_datagram_read(ec, ec_addr, &val8, 1);
if (r == 1) {
printf("\tESC config: 0x%02x\n", val8);
}
}
if (option_portcheck || option_verbose) {
ec_addr->addr.position.off = ESC_ADDR_MAP_DL_STATUS;
r = ec_datagram_read(ec, ec_addr, &val16, 2);
if (r != 2) {
printf("Could not read DL status\n");
}
uint16_t dl_status = le16toh(val16);
link_port0 = dl_status & ESC_DL_STATUS_LINK_PORT0;
link_port1 = dl_status & ESC_DL_STATUS_LINK_PORT1;
link_port2 = dl_status & ESC_DL_STATUS_LINK_PORT2;
link_port3 = dl_status & ESC_DL_STATUS_LINK_PORT3;
if (option_verbose) printf("\tDL Status: 0x%04x\n", dl_status);
if (option_verbose) printf("\t\tEEPROM: %s\n", dl_status & ESC_DL_STATUS_EEPROM_LOADED ? "Loaded" : "Not loaded");
if (option_verbose) printf("\t\tWatchdog: %s\n", dl_status & ESC_DL_STATUS_WATCHDOG_RELOADED ? "Reloaded" : "Expired");
if (option_verbose) printf("\t\tEnhanced link detection: %s\n", dl_status & ESC_DL_STATUS_ENHANCED_LINK_DETECT ? "Activated" : "Deactivated");
printf("\t\tport0: %s\n", link_port0 ? "Link detected" : "No link");
printf("\t\tport1: %s\n", link_port1 ? "Link detected" : "No link");
printf("\t\tport2: %s\n", link_port2 ? "Link detected" : "No link");
printf("\t\tport3: %s\n", link_port3 ? "Link detected" : "No link");
if (option_portcheck) {
uint32_t t0;
int32_t diff01;
int32_t diff02;
int32_t diff03;
ec_addr->addr.position.off = ESC_ADDR_MAP_DC_RX_TIME_PORT0;
ec_addr->type = EC_DGRAM_ADDR_BROADCAST;
val32 = 0;
r = ec_datagram_write(ec, ec_addr, &val32, 4);
if (r != 4) {
printf("Could not perform broadcast write to address 0x%04x: %d\n", ESC_ADDR_MAP_DC_RX_TIME_PORT0, r);
}
ec_addr->type = EC_DGRAM_ADDR_AUTO_INC;
ec_addr->addr.position.off = ESC_ADDR_MAP_DC_RX_TIME_PORT0;
r = ec_datagram_read(ec, ec_addr, &val32, 4);
if (r == 4) {
t0 = le32toh(val32);
if (option_verbose) printf("\tRx time port0: %ld\n", le32toh(val32));
}
ec_addr->addr.position.off = ESC_ADDR_MAP_DC_RX_TIME_PORT1;
r = ec_datagram_read(ec, ec_addr, &val32, 4);
if (r == 4) {
diff01 = (int32_t)(le32toh(val32) - t0);
if (option_verbose) printf("\tTime between port0 and port1: %"PRId32" ns\n", diff01);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_DC_RX_TIME_PORT2;
r = ec_datagram_read(ec, ec_addr, &val32, 4);
if (r == 4) {
diff02 = (int32_t)(le32toh(val32) - t0);
if (option_verbose) printf("\tTime between port0 and port2: %"PRId32" ns\n", diff02);
}
ec_addr->addr.position.off = ESC_ADDR_MAP_DC_RX_TIME_PORT3;
r = ec_datagram_read(ec, ec_addr, &val32, 4);
if (r == 4) {
diff03 = (int32_t)(le32toh(val32) - t0);
if (option_verbose) printf("\tTime between port0 and port3: %"PRId32" ns\n", diff03);
}
if (!link_port0) {
printf("\tport0 not connected\n");
linkcheck = false;
}
if (link_port1 && diff01 < 0) {
printf("\tport0 is not connected towards the master, port1 is earlier in the chain\n");
linkcheck = false;
}
if (link_port2 && diff02 < 0) {
printf("\tport0 is not connected towards the master, port2 is earlier in the chain\n");
linkcheck = false;
}
if (link_port3 && diff03 < 0) {
printf("\tport0 is not connected towards the master, port3 is earlier in the chain\n");
linkcheck = false;
}
}
}
if (option_verbose) {
enum esc_al_state state = esc_al_state_get(dev);
enum esc_al_status status = esc_al_status_code_get(dev);
printf("\tAL state: %s\n", esc_al_state2str(state));
printf("\tAL status: %s\n", esc_al_status2str(status));
}
vendorid = esc_esi_vendorid_get(dev);
productcode = esc_esi_productcode_get(dev);
vendorid_str = "unknown vendorid";
productcode_str = "unkown productcode";
for (i = 0; i < esc_vendor_list_len; i++)
if (esc_vendor_list[i].vendorid == vendorid)
vendorid_str = esc_vendor_list[i].name;
for (i = 0; i < esc_device_list_len; i++)
if (esc_device_list[i].vendorid == vendorid &&
esc_device_list[i].productcode == productcode)
productcode_str = esc_device_list[i].name;
if (option_short) {
printf("0x%08x:0x%08x\t%s\t%s\n", vendorid, productcode, vendorid_str, productcode_str);
}
if (option_verbose) {
printf("\tESI VendorId: 0x%04x\n", vendorid);
printf("\t\tVendor: %s\n", vendorid_str);
printf("\tESI ProductCode: 0x%08x\n", productcode);
printf("\t\tProduct: %s\n", productcode_str);
printf("\tESI RevisionNo: 0x%08x\n", esc_esi_revisionno_get(dev));
printf("\tESI SerialNo: 0x%04x\n", esc_esi_serialno_get(dev));
eeprom_size = esc_esi_read16(ec, ec_addr, ESC_ESI_ADDR_SIZE);
printf("\tESI Size: 0x%08zx\n", eeprom_size);
eeprom_version = esc_esi_read16(ec, ec_addr, ESC_ESI_ADDR_VERSION);
printf("\tESI Version: 0x%08x\n", eeprom_version);
val16 = esc_esi_read16(ec, ec_addr, ESC_ESI_BOOTSTRAP_RX_MAILBOX_OFFSET);
printf("\tESI Bootstrap RX mailbox offset: 0x%04x\n", val16);
val16 = esc_esi_read16(ec, ec_addr, ESC_ESI_BOOTSTRAP_RX_MAILBOX_SIZE);
printf("\tESI Bootstrap RX mailbox size: 0x%04x\n", val16);
val16 = esc_esi_read16(ec, ec_addr, ESC_ESI_BOOTSTRAP_TX_MAILBOX_OFFSET);
printf("\tESI Bootstrap TX mailbox offset: 0x%04x\n", val16);
val16 = esc_esi_read16(ec, ec_addr, ESC_ESI_BOOTSTRAP_TX_MAILBOX_SIZE);
printf("\tESI Bootstrap TX mailbox size: 0x%04x\n", val16);
printf("\tESI Standard RX mailbox offset: 0x%04zx\n",
esc_esi_standard_rx_mailbox_offset_get(ec, ec_addr));
printf("\tESI Standard RX mailbox size: 0x%04zx\n",
esc_esi_standard_rx_mailbox_size_get(ec, ec_addr));
printf("\tESI Standard TX mailbox offset: 0x%04zx\n",
esc_esi_standard_tx_mailbox_offset_get(ec, ec_addr));
printf("\tESI Standard TX mailbox size: 0x%04zx\n",
esc_esi_standard_rx_mailbox_size_get(ec, ec_addr));
val16 = esc_esi_read16(ec, ec_addr, ESC_ESI_MAILBOX_PROTOCOL);
printf("\tESI Mailbox Protocol: 0x%04x\n", val16);
r = esc_esi_eeprom_read(ec, ec_addr, &eeprom, 0, sizeof(eeprom));
if (r == sizeof(eeprom)) {
uint8_t crc_dev = eeprom[0x0e];
uint8_t crc_calc = esc_esi_crc8(eeprom, 0x0e);
printf("\tESI EEPROM checksum: %s (device: 0x%02x, calculated: 0x%02x\n",
crc_dev == crc_calc ? "correct" : "incorrect", crc_dev, crc_calc);
printf("\tESI EEPROM content:\n");
for (i = 0; i < sizeof(eeprom)/sizeof(*eeprom); i += 16) {
char *c;
int j;
printf("\t\t0x%04x (0x%04x): "
"%02x%02x %02x%02x %02x%02x %02x%02x "
"%02x%02x %02x%02x %02x%02x %02x%02x ",
i, i/2,
eeprom[i+0], eeprom[i+1], eeprom[i+2], eeprom[i+3],
eeprom[i+4], eeprom[i+5], eeprom[i+6], eeprom[i+7],
eeprom[i+8], eeprom[i+9], eeprom[i+10], eeprom[i+11],
eeprom[i+12], eeprom[i+13], eeprom[i+14], eeprom[i+15]);
c = (char*)&eeprom[i];
for (j = 0; j < 16; j++) {
if (isprint(c[j])) {
printf("%c", c[j]);
} else {
printf(".");
}
}
printf("\n");
}
} else {
printf("\tESI EEPROM read returned: %d expected: %zd.\n",
r, sizeof(eeprom));
}
esc_esi_device_fill(dev);
for (cat = dev->categories; cat; cat = cat->next) {
printf("\tcategory type: 0x%04x\n", cat->type);
printf("\tcategory length: 0x%04zx\n", cat->length);
for (i = 0; i < cat->length; i += 16) {
char *c;
int j;
printf("\t\t0x%04x (0x%04x): ", i, i/2);
if (cat->length - i >= 2)
printf("%02x%02x ", cat->data[i+0], cat->data[i+1]);
else
printf(" ");
if (cat->length - i >= 4)
printf("%02x%02x ", cat->data[i+2], cat->data[i+3]);
else
printf(" ");
if (cat->length - i >= 6)
printf("%02x%02x ", cat->data[i+4], cat->data[i+5]);
else
printf(" ");
if (cat->length - i >= 8)
printf("%02x%02x ", cat->data[i+6], cat->data[i+7]);
else
printf(" ");
if (cat->length - i >= 10)
printf("%02x%02x ", cat->data[i+8], cat->data[i+9]);
else
printf(" ");
if (cat->length - i >= 12)
printf("%02x%02x ", cat->data[i+10], cat->data[i+11]);
else
printf(" ");
if (cat->length - i >= 14)
printf("%02x%02x ", cat->data[i+12], cat->data[i+13]);
else
printf(" ");
if (cat->length - i >= 16)
printf("%02x%02x ", cat->data[i+14], cat->data[i+15]);
else
printf(" ");
c = (char*)&cat->data[i];
for (j = 0; j < 16; j++) {
if (cat->length - i < j) {
printf(" ");
} else if (isprint(c[j])) {
printf("%c", c[j]);
} else {
printf(".");
}
}
printf("\n");
}
}
for (i = 0; i < ESC_SYNCMANAGER_MAX; i++) {
printf("\tsm%d: %zd bytes @ 0x%zx, enabled: %d\n",
i, dev->sm[i].len, dev->sm[i].start, dev->sm[i].enabled);
}
printf("\n");
}
esc_device_destroy(dev);
}
int main(int argc, char **argv)
{
int slaves;
int i;
struct ec *ec;
if (argc < 2) {
printf("Usage: %s [ethernet device]\n", argv[0]);
return -1;
}
for (i = 2; i < argc; i++) {
if (!strcmp(argv[i], "-s")) {
option_verbose = false;
option_short = true;
option_portcheck = false;
}
if (!strcmp(argv[i], "-v")) {
option_verbose = true;
option_short = false;
option_portcheck = true;
}
if (!strcmp(argv[i], "-l")) {
option_portcheck = true;
option_verbose = false;
option_short = false;
}
}
if (!(ec = ec_init(argv[1], 0, 0))) {
printf("EtherCat init failed\n");
return -1;
}
slaves = ec_slave_count(ec);
if (option_verbose || option_portcheck) printf("Number of slaves found: %d\n", slaves);
for (i = 0; i < slaves; i++) {
struct ec_dgram_addr ec_addr;
if (option_verbose || option_portcheck) printf("EtherCat slave controller %d:\n", i);
log_flush_stdout();
ec_addr_set_auto_inc_nr(&ec_addr, i);
describe_esc(ec, &ec_addr);
}
if (option_portcheck) {
printf("Port check %s\n", linkcheck ? "passed" : "failed");
}
return 0;
}