Newer
Older
Florian Pose
committed
*/
int ec_slave_prepare_fmmu(ec_slave_t *slave, /**< EtherCAT slave */
const ec_domain_t *domain, /**< domain */
const ec_sync_t *sync /**< sync manager */
Florian Pose
committed
{
unsigned int i;
// FMMU configuration already prepared?
Florian Pose
committed
for (i = 0; i < slave->fmmu_count; i++)
if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync)
return 0;
// reserve new FMMU...
Florian Pose
committed
if (slave->fmmu_count >= slave->base_fmmu_count) {
EC_ERR("Slave %i FMMU limit reached!\n", slave->ring_position);
Florian Pose
committed
return -1;
}
slave->fmmus[slave->fmmu_count].domain = domain;
slave->fmmus[slave->fmmu_count].sync = sync;
slave->fmmus[slave->fmmu_count].logical_start_address = 0;
slave->fmmu_count++;
slave->registered = 1;
return 0;
}
/*****************************************************************************/
/**
Outputs all information about a certain slave.
- 0: Only slave types and addresses
- 1: with EEPROM information
- >1: with SDO dictionaries
Florian Pose
committed
*/
void ec_slave_print(const ec_slave_t *slave, /**< EtherCAT slave */
unsigned int verbosity /**< verbosity level */
Florian Pose
committed
{
ec_eeprom_sync_t *sync;
ec_eeprom_pdo_t *pdo;
Florian Pose
committed
if (slave->type) {
EC_INFO("%i) %s %s: %s\n", slave->ring_position,
slave->type->vendor_name, slave->type->product_name,
slave->type->description);
Florian Pose
committed
}
else {
EC_INFO("%i) UNKNOWN SLAVE: vendor 0x%08X, product 0x%08X\n",
slave->ring_position, slave->sii_vendor_id,
slave->sii_product_code);
Florian Pose
committed
}
if (!verbosity) return;
EC_INFO(" Station address: 0x%04X\n", slave->station_address);
Florian Pose
committed
EC_INFO(" Data link status:\n");
for (i = 0; i < 4; i++) {
EC_INFO(" Port %i (", i);
switch (slave->sii_physical_layer[i]) {
case 0x00:
printk("EBUS");
break;
case 0x01:
printk("100BASE-TX");
break;
case 0x02:
printk("100BASE-FX");
break;
default:
printk("unknown");
}
printk(")\n");
EC_INFO(" link %s, loop %s, %s\n",
slave->dl_link[i] ? "up" : "down",
slave->dl_loop[i] ? "closed" : "open",
slave->dl_signal[i] ? "signal detected" : "no signal");
EC_INFO(" Base information:\n");
EC_INFO(" Type %u, revision %i, build %i\n",
slave->base_type, slave->base_revision, slave->base_build);
EC_INFO(" Supported FMMUs: %i, sync managers: %i\n",
slave->base_fmmu_count, slave->base_sync_count);
if (slave->sii_mailbox_protocols) {
EC_INFO(" Mailbox communication:\n");
EC_INFO(" RX mailbox: 0x%04X/%i, TX mailbox: 0x%04X/%i\n",
slave->sii_rx_mailbox_offset, slave->sii_rx_mailbox_size,
slave->sii_tx_mailbox_offset, slave->sii_tx_mailbox_size);
EC_INFO(" Supported protocols: ");
first = 1;
if (slave->sii_mailbox_protocols & EC_MBOX_AOE) {
printk("AoE");
first = 0;
}
if (slave->sii_mailbox_protocols & EC_MBOX_EOE) {
if (!first) printk(", ");
printk("EoE");
first = 0;
}
if (slave->sii_mailbox_protocols & EC_MBOX_COE) {
if (!first) printk(", ");
printk("CoE");
first = 0;
}
if (slave->sii_mailbox_protocols & EC_MBOX_FOE) {
if (!first) printk(", ");
printk("FoE");
first = 0;
if (slave->sii_mailbox_protocols & EC_MBOX_SOE) {
if (!first) printk(", ");
printk("SoE");
first = 0;
}
if (slave->sii_mailbox_protocols & EC_MBOX_VOE) {
if (!first) printk(", ");
printk("VoE");
}
printk("\n");
}
EC_INFO(" EEPROM data:\n");
EC_INFO(" EEPROM content size: %i Bytes\n", slave->eeprom_size);
EC_INFO(" Configured station alias: 0x%04X (%i)\n",
slave->sii_alias, slave->sii_alias);
EC_INFO(" Vendor-ID: 0x%08X, Product code: 0x%08X\n",
slave->sii_vendor_id, slave->sii_product_code);
EC_INFO(" Revision number: 0x%08X, Serial number: 0x%08X\n",
slave->sii_revision_number, slave->sii_serial_number);
if (slave->eeprom_group)
EC_INFO(" Group: %s\n", slave->eeprom_group);
Florian Pose
committed
if (slave->eeprom_image)
EC_INFO(" Image: %s\n", slave->eeprom_image);
if (slave->eeprom_order)
EC_INFO(" Order#: %s\n", slave->eeprom_order);
if (slave->eeprom_name)
EC_INFO(" Name: %s\n", slave->eeprom_name);
if (!list_empty(&slave->eeprom_syncs)) {
EC_INFO(" Sync-Managers:\n");
list_for_each_entry(sync, &slave->eeprom_syncs, list) {
EC_INFO(" %i: 0x%04X, length %i, control 0x%02X, %s\n",
sync->index, sync->physical_start_address,
sync->length, sync->control_register,
sync->enable ? "enable" : "disable");
Florian Pose
committed
}
Florian Pose
committed
list_for_each_entry(pdo, &slave->eeprom_pdos, list) {
EC_INFO(" %s \"%s\" (0x%04X), -> Sync-Manager %i\n",
pdo->type == EC_RX_PDO ? "RXPDO" : "TXPDO",
pdo->name ? pdo->name : "???",
pdo->index, pdo->sync_manager);
list_for_each_entry(pdo_entry, &pdo->entries, list) {
EC_INFO(" \"%s\" 0x%04X:%X, %i Bit\n",
pdo_entry->name ? pdo_entry->name : "???",
pdo_entry->index, pdo_entry->subindex,
pdo_entry->bit_length);
if (verbosity < 2) return;
if (!list_empty(&slave->sdo_dictionary)) {
EC_INFO(" SDO-Dictionary:\n");
list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
EC_INFO(" 0x%04X \"%s\"\n", sdo->index,
sdo->name ? sdo->name : "");
EC_INFO(" Object code: 0x%02X\n", sdo->object_code);
list_for_each_entry(sdo_entry, &sdo->entries, list) {
EC_INFO(" 0x%04X:%i \"%s\", type 0x%04X, %i bits\n",
sdo->index, sdo_entry->subindex,
sdo_entry->name ? sdo_entry->name : "",
sdo_entry->data_type, sdo_entry->bit_length);
Florian Pose
committed
/*****************************************************************************/
/**
Outputs the values of the CRC faoult counters and resets them.
\return 0 in case of success, else < 0
*/
int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT slave */)
{
ec_command_t *command;
command = &slave->master->simple_command;
if (ec_command_nprd(command, slave->station_address, 0x0300, 4)) return -1;
if (unlikely(ec_master_simple_io(slave->master, command))) {
EC_WARN("Reading CRC fault counters failed on slave %i!\n",
return -1;
}
if (!EC_READ_U32(command->data)) return 0; // no CRC faults
if (EC_READ_U8(command->data))
EC_WARN("%3i RX-error%s on slave %i, channel A.\n",
EC_READ_U8(command->data),
EC_READ_U8(command->data) == 1 ? "" : "s",
if (EC_READ_U8(command->data + 1))
EC_WARN("%3i invalid frame%s on slave %i, channel A.\n",
EC_READ_U8(command->data + 1),
EC_READ_U8(command->data + 1) == 1 ? "" : "s",
if (EC_READ_U8(command->data + 2))
EC_WARN("%3i RX-error%s on slave %i, channel B.\n",
EC_READ_U8(command->data + 2),
EC_READ_U8(command->data + 2) == 1 ? "" : "s",
if (EC_READ_U8(command->data + 3))
EC_WARN("%3i invalid frame%s on slave %i, channel B.\n",
EC_READ_U8(command->data + 3),
EC_READ_U8(command->data + 3) == 1 ? "" : "s",
// reset CRC counters
if (ec_command_npwr(command, slave->station_address, 0x0300, 4)) return -1;
EC_WRITE_U32(command->data, 0x00000000);
if (unlikely(ec_master_simple_io(slave->master, command))) {
EC_WARN("Resetting CRC fault counters failed on slave %i!\n",
return -1;
}
return 0;
}
/*****************************************************************************/
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
/**
Schedules an EEPROM write operation.
\return 0 in case of success, else < 0
*/
ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */
const uint8_t *data, /**< new EEPROM data */
size_t size /**< size of data in bytes */
)
{
uint16_t word_size, cat_type, cat_size;
const uint16_t *data_words, *next_header;
uint16_t *new_data;
if (!slave->master->eeprom_write_enable) {
EC_ERR("Writing EEPROMs not allowed! Enable via"
" eeprom_write_enable SysFS entry.\n");
return -1;
}
if (slave->master->mode != EC_MASTER_MODE_FREERUN) {
EC_ERR("Writing EEPROMs only allowed in freerun mode!\n");
return -1;
}
if (slave->new_eeprom_data) {
EC_ERR("Slave %i already has a pending EEPROM write operation!\n",
slave->ring_position);
return -1;
}
// coarse check of the data
if (size % 2) {
EC_ERR("EEPROM size is odd! Dropping.\n");
return -1;
}
data_words = (const uint16_t *) data;
word_size = size / 2;
if (word_size < 0x0041) {
EC_ERR("EEPROM data too short! Dropping.\n");
return -1;
}
next_header = data_words + 0x0040;
cat_type = EC_READ_U16(next_header);
while (cat_type != 0xFFFF) {
cat_type = EC_READ_U16(next_header);
cat_size = EC_READ_U16(next_header + 1);
if ((next_header + cat_size + 2) - data_words >= word_size) {
EC_ERR("EEPROM data seems to be corrupted! Dropping.\n");
return -1;
}
next_header += cat_size + 2;
cat_type = EC_READ_U16(next_header);
}
// data ok!
if (!(new_data = (uint16_t *) kmalloc(word_size * 2, GFP_KERNEL))) {
EC_ERR("Unable to allocate memory for new EEPROM data!\n");
return -1;
}
memcpy(new_data, data, size);
slave->new_eeprom_size = word_size;
slave->new_eeprom_data = new_data;
EC_INFO("EEPROM writing scheduled for slave %i, %i words.\n",
slave->ring_position, word_size);
return 0;
}
/*****************************************************************************/
Formats attribute data for SysFS read access.
\return number of bytes to read
ssize_t ec_show_slave_attribute(struct kobject *kobj, /**< slave's kobject */
struct attribute *attr, /**< attribute */
char *buffer /**< memory to store data */
)
{
ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj);
if (attr == &attr_ring_position) {
return sprintf(buffer, "%i\n", slave->ring_position);
}
Florian Pose
committed
else if (attr == &attr_coupler_address) {
return sprintf(buffer, "%i:%i\n", slave->coupler_index,
slave->coupler_subindex);
}
else if (attr == &attr_vendor_name) {
if (slave->type)
return sprintf(buffer, "%s\n", slave->type->vendor_name);
}
else if (attr == &attr_product_name) {
if (slave->type)
return sprintf(buffer, "%s\n", slave->type->product_name);
}
else if (attr == &attr_product_desc) {
if (slave->type)
return sprintf(buffer, "%s\n", slave->type->description);
}
Florian Pose
committed
else if (attr == &attr_sii_name) {
if (slave->eeprom_name)
return sprintf(buffer, "%s\n", slave->eeprom_name);
else if (attr == &attr_type) {
if (slave->type) {
if (slave->type->special == EC_TYPE_BUS_COUPLER)
return sprintf(buffer, "coupler\n");
else if (slave->type->special == EC_TYPE_INFRA)
return sprintf(buffer, "infrastructure\n");
else
return sprintf(buffer, "normal\n");
}
}
else if (attr == &attr_state) {
switch (slave->current_state) {
case EC_SLAVE_STATE_INIT:
return sprintf(buffer, "INIT\n");
case EC_SLAVE_STATE_PREOP:
return sprintf(buffer, "PREOP\n");
case EC_SLAVE_STATE_SAVEOP:
return sprintf(buffer, "SAVEOP\n");
case EC_SLAVE_STATE_OP:
return sprintf(buffer, "OP\n");
default:
return sprintf(buffer, "UNKNOWN\n");
}
}
else if (attr == &attr_eeprom) {
if (slave->eeprom_data) {
if (slave->eeprom_size > PAGE_SIZE) {
EC_ERR("EEPROM contents of slave %i exceed 1 page (%i/%i).\n",
slave->ring_position, slave->eeprom_size,
(int) PAGE_SIZE);
}
else {
memcpy(buffer, slave->eeprom_data, slave->eeprom_size);
return slave->eeprom_size;
}
}
}
/*****************************************************************************/
/**
Formats attribute data for SysFS write access.
\return number of bytes processed, or negative error code
*/
ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */
struct attribute *attr, /**< attribute */
const char *buffer, /**< memory with data */
size_t size /**< size of data to store */
)
{
ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj);
if (attr == &attr_state) {
if (!strcmp(buffer, "INIT\n")) {
slave->requested_state = EC_SLAVE_STATE_INIT;
slave->error_flag = 0;
return size;
}
else if (!strcmp(buffer, "PREOP\n")) {
slave->requested_state = EC_SLAVE_STATE_PREOP;
slave->error_flag = 0;
return size;
}
else if (!strcmp(buffer, "SAVEOP\n")) {
slave->requested_state = EC_SLAVE_STATE_SAVEOP;
slave->error_flag = 0;
return size;
}
else if (!strcmp(buffer, "OP\n")) {
slave->requested_state = EC_SLAVE_STATE_OP;
slave->error_flag = 0;
return size;
}
EC_ERR("Failed to set slave state!\n");
}
else if (attr == &attr_eeprom) {
if (!ec_slave_write_eeprom(slave, buffer, size))
return size;
}
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
/*****************************************************************************/
/**
\return size of sync manager contents
*/
size_t ec_slave_calc_sync_size(const ec_slave_t *slave, /**< EtherCAT slave */
const ec_sync_t *sync /**< sync manager */
)
{
unsigned int i, found;
const ec_field_t *field;
const ec_varsize_t *var;
size_t size;
// if size is specified, return size
if (sync->size) return sync->size;
// sync manager has variable size (size == 0).
size = 0;
for (i = 0; (field = sync->fields[i]); i++) {
found = 0;
list_for_each_entry(var, &slave->varsize_fields, list) {
if (var->field != field) continue;
size += var->size;
found = 1;
}
if (!found) {
EC_WARN("Variable data field \"%s\" of slave %i has no size"
" information!\n", field->name, slave->ring_position);
}
}
return size;
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
/**
Writes the "configured station alias" to the slave's EEPROM.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t alias /**< new alias */
)
{
return ec_slave_sii_write16(slave, 0x0004, alias);
}
/*****************************************************************************/
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
/**
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_field_size(ec_slave_t *slave, /**< EtherCAT slave */
const char *field_name, /**< data field name */
unsigned int field_index, /**< data field index */
size_t size /**< new data field size */
)
{
unsigned int i, j, field_counter;
const ec_sync_t *sync;
const ec_field_t *field;
ec_varsize_t *var;
if (!slave->type) {
EC_ERR("Slave %i has no type information!\n", slave->ring_position);
return -1;
}
field_counter = 0;
for (i = 0; (sync = slave->type->sync_managers[i]); i++) {
for (j = 0; (field = sync->fields[j]); j++) {
if (!strcmp(field->name, field_name)) {
if (field_counter++ == field_index) {
// is the size of this field variable?
if (field->size) {
EC_ERR("Field \"%s\"[%i] of slave %i has no variable"
" size!\n", field->name, field_index,
slave->ring_position);
return -1;
}
// does a size specification already exist?
list_for_each_entry(var, &slave->varsize_fields, list) {
if (var->field == field) {
EC_WARN("Resizing field \"%s\"[%i] of slave %i.\n",
field->name, field_index,
slave->ring_position);
var->size = size;
return 0;
}
}
// create a new size specification...
if (!(var = kmalloc(sizeof(ec_varsize_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for varsize_t!\n");
return -1;
}
var->field = field;
var->size = size;
list_add_tail(&var->list, &slave->varsize_fields);
return 0;
}
}
}
}
EC_ERR("Slave %i (\"%s %s\") has no field \"%s\"[%i]!\n",
slave->ring_position, slave->type->vendor_name,
slave->type->product_name, field_name, field_index);
return -1;
}
/*****************************************************************************/
/**< \cond */
EXPORT_SYMBOL(ecrt_slave_field_size);
/**< \endcond */
/*****************************************************************************/