Newer
Older
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;
}
/*****************************************************************************/
1252
1253
1254
1255
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
/**
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
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->state_error = 0;
return size;
}
else if (!strcmp(buffer, "PREOP\n")) {
slave->requested_state = EC_SLAVE_STATE_PREOP;
slave->state_error = 0;
return size;
}
else if (!strcmp(buffer, "SAVEOP\n")) {
slave->requested_state = EC_SLAVE_STATE_SAVEOP;
slave->state_error = 0;
return size;
}
else if (!strcmp(buffer, "OP\n")) {
slave->requested_state = EC_SLAVE_STATE_OP;
slave->state_error = 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;
}
/******************************************************************************
* 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);
}
/*****************************************************************************/
/**< \cond */
EXPORT_SYMBOL(ecrt_slave_write_alias);
/**< \endcond */
/*****************************************************************************/