Skip to content
Snippets Groups Projects
Commit ff953519 authored by Florian Pose's avatar Florian Pose
Browse files

EEPROM writing via SysFS.

parent 65733fe4
No related branches found
No related tags found
No related merge requests found
...@@ -58,6 +58,7 @@ void ec_fsm_master_validate_product(ec_fsm_t *); ...@@ -58,6 +58,7 @@ void ec_fsm_master_validate_product(ec_fsm_t *);
void ec_fsm_master_reconfigure(ec_fsm_t *); void ec_fsm_master_reconfigure(ec_fsm_t *);
void ec_fsm_master_address(ec_fsm_t *); void ec_fsm_master_address(ec_fsm_t *);
void ec_fsm_master_conf(ec_fsm_t *); void ec_fsm_master_conf(ec_fsm_t *);
void ec_fsm_master_eeprom(ec_fsm_t *);
void ec_fsm_slave_start_reading(ec_fsm_t *); void ec_fsm_slave_start_reading(ec_fsm_t *);
void ec_fsm_slave_read_status(ec_fsm_t *); void ec_fsm_slave_read_status(ec_fsm_t *);
...@@ -77,8 +78,11 @@ void ec_fsm_slave_op(ec_fsm_t *); ...@@ -77,8 +78,11 @@ void ec_fsm_slave_op(ec_fsm_t *);
void ec_fsm_slave_op2(ec_fsm_t *); void ec_fsm_slave_op2(ec_fsm_t *);
void ec_fsm_sii_start_reading(ec_fsm_t *); void ec_fsm_sii_start_reading(ec_fsm_t *);
void ec_fsm_sii_check(ec_fsm_t *); void ec_fsm_sii_read_check(ec_fsm_t *);
void ec_fsm_sii_fetch(ec_fsm_t *); void ec_fsm_sii_read_fetch(ec_fsm_t *);
void ec_fsm_sii_start_writing(ec_fsm_t *);
void ec_fsm_sii_write_check(ec_fsm_t *);
void ec_fsm_sii_write_check2(ec_fsm_t *);
void ec_fsm_sii_end(ec_fsm_t *); void ec_fsm_sii_end(ec_fsm_t *);
void ec_fsm_sii_error(ec_fsm_t *); void ec_fsm_sii_error(ec_fsm_t *);
...@@ -406,7 +410,25 @@ void ec_fsm_master_proc_states(ec_fsm_t *fsm /**< finite state machine */) ...@@ -406,7 +410,25 @@ void ec_fsm_master_proc_states(ec_fsm_t *fsm /**< finite state machine */)
return; return;
} }
// nothing to configure. restart master state machine. if (master->mode == EC_MASTER_MODE_FREERUN) {
// nothing to configure. check for pending EEPROM write operations.
list_for_each_entry(slave, &master->slaves, list) {
if (!slave->new_eeprom_data) continue;
// found pending EEPROM write operation. execute it!
EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
fsm->sii_offset = 0x0000;
memcpy(fsm->sii_value, slave->new_eeprom_data, 2);
fsm->sii_mode = 1;
fsm->sii_state = ec_fsm_sii_start_writing;
fsm->slave = slave;
fsm->master_state = ec_fsm_master_eeprom;
fsm->master_state(fsm); // execute immediately
return;
}
}
// nothing to do. restart master state machine.
fsm->master_state = ec_fsm_master_start; fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately fsm->master_state(fsm); // execute immediately
} }
...@@ -434,7 +456,7 @@ void ec_fsm_master_validate_vendor(ec_fsm_t *fsm /**< finite state machine */) ...@@ -434,7 +456,7 @@ void ec_fsm_master_validate_vendor(ec_fsm_t *fsm /**< finite state machine */)
if (fsm->sii_state != ec_fsm_sii_end) return; if (fsm->sii_state != ec_fsm_sii_end) return;
if (EC_READ_U32(fsm->sii_result) != slave->sii_vendor_id) { if (EC_READ_U32(fsm->sii_value) != slave->sii_vendor_id) {
EC_ERR("Slave %i: invalid vendor ID!\n", slave->ring_position); EC_ERR("Slave %i: invalid vendor ID!\n", slave->ring_position);
fsm->master_state = ec_fsm_master_start; fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately fsm->master_state(fsm); // execute immediately
...@@ -472,10 +494,10 @@ void ec_fsm_master_validate_product(ec_fsm_t *fsm /**< finite state machine */) ...@@ -472,10 +494,10 @@ void ec_fsm_master_validate_product(ec_fsm_t *fsm /**< finite state machine */)
if (fsm->sii_state != ec_fsm_sii_end) return; if (fsm->sii_state != ec_fsm_sii_end) return;
if (EC_READ_U32(fsm->sii_result) != slave->sii_product_code) { if (EC_READ_U32(fsm->sii_value) != slave->sii_product_code) {
EC_ERR("Slave %i: invalid product code!\n", slave->ring_position); EC_ERR("Slave %i: invalid product code!\n", slave->ring_position);
EC_ERR("expected 0x%08X, got 0x%08X.\n", slave->sii_product_code, EC_ERR("expected 0x%08X, got 0x%08X.\n", slave->sii_product_code,
EC_READ_U32(fsm->sii_result)); EC_READ_U32(fsm->sii_value));
fsm->master_state = ec_fsm_master_start; fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately fsm->master_state(fsm); // execute immediately
return; return;
...@@ -666,6 +688,49 @@ void ec_fsm_master_conf(ec_fsm_t *fsm /**< finite state machine */) ...@@ -666,6 +688,49 @@ void ec_fsm_master_conf(ec_fsm_t *fsm /**< finite state machine */)
fsm->master_state(fsm); // execute immediately fsm->master_state(fsm); // execute immediately
} }
/*****************************************************************************/
/**
Master state: EEPROM.
*/
void ec_fsm_master_eeprom(ec_fsm_t *fsm /**< finite state machine */)
{
ec_slave_t *slave = fsm->slave;
fsm->sii_state(fsm); // execute SII state machine
if (fsm->sii_state == ec_fsm_sii_error) {
EC_ERR("Failed to write EEPROM contents to slave %i.\n",
slave->ring_position);
kfree(slave->new_eeprom_data);
slave->new_eeprom_data = NULL;
fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately
return;
}
if (fsm->sii_state != ec_fsm_sii_end) return;
fsm->sii_offset++;
if (fsm->sii_offset < slave->new_eeprom_size) {
memcpy(fsm->sii_value, slave->new_eeprom_data + fsm->sii_offset, 2);
fsm->sii_state = ec_fsm_sii_start_writing;
fsm->sii_state(fsm); // execute immediately
return;
}
// finished writing EEPROM
EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position);
kfree(slave->new_eeprom_data);
slave->new_eeprom_data = NULL;
// restart master state machine.
fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately
return;
}
/****************************************************************************** /******************************************************************************
* slave state machine * slave state machine
*****************************************************************************/ *****************************************************************************/
...@@ -834,8 +899,8 @@ void ec_fsm_slave_fetch_eeprom(ec_fsm_t *fsm /**< finite state machine */) ...@@ -834,8 +899,8 @@ void ec_fsm_slave_fetch_eeprom(ec_fsm_t *fsm /**< finite state machine */)
if (fsm->sii_state != ec_fsm_sii_end) return; if (fsm->sii_state != ec_fsm_sii_end) return;
cat_type = EC_READ_U16(fsm->sii_result); cat_type = EC_READ_U16(fsm->sii_value);
cat_size = EC_READ_U16(fsm->sii_result + 2); cat_size = EC_READ_U16(fsm->sii_value + 2);
if (cat_type != 0xFFFF) { // not the last category if (cat_type != 0xFFFF) { // not the last category
fsm->sii_offset += cat_size + 2; fsm->sii_offset += cat_size + 2;
...@@ -895,10 +960,10 @@ void ec_fsm_slave_fetch_eeprom2(ec_fsm_t *fsm /**< finite state machine */) ...@@ -895,10 +960,10 @@ void ec_fsm_slave_fetch_eeprom2(ec_fsm_t *fsm /**< finite state machine */)
// 2 words fetched // 2 words fetched
if (fsm->sii_offset + 2 <= slave->eeprom_size / 2) { // 2 words fit if (fsm->sii_offset + 2 <= slave->eeprom_size / 2) { // 2 words fit
memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_result, 4); memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 4);
} }
else { // copy the last word else { // copy the last word
memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_result, 2); memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 2);
} }
if (fsm->sii_offset + 2 < slave->eeprom_size / 2) { if (fsm->sii_offset + 2 < slave->eeprom_size / 2) {
...@@ -1305,17 +1370,17 @@ void ec_fsm_sii_start_reading(ec_fsm_t *fsm /**< finite state machine */) ...@@ -1305,17 +1370,17 @@ void ec_fsm_sii_start_reading(ec_fsm_t *fsm /**< finite state machine */)
EC_WRITE_U8 (command->data + 1, 0x01); // request read operation EC_WRITE_U8 (command->data + 1, 0x01); // request read operation
EC_WRITE_U16(command->data + 2, fsm->sii_offset); EC_WRITE_U16(command->data + 2, fsm->sii_offset);
ec_master_queue_command(fsm->master, command); ec_master_queue_command(fsm->master, command);
fsm->sii_state = ec_fsm_sii_check; fsm->sii_state = ec_fsm_sii_read_check;
} }
/*****************************************************************************/ /*****************************************************************************/
/** /**
SII state: CHECK. SII state: READ_CHECK.
Checks, if the SII-read-command has been sent and issues a fetch command. Checks, if the SII-read-command has been sent and issues a fetch command.
*/ */
void ec_fsm_sii_check(ec_fsm_t *fsm /**< finite state machine */) void ec_fsm_sii_read_check(ec_fsm_t *fsm /**< finite state machine */)
{ {
ec_command_t *command = &fsm->command; ec_command_t *command = &fsm->command;
...@@ -1336,17 +1401,17 @@ void ec_fsm_sii_check(ec_fsm_t *fsm /**< finite state machine */) ...@@ -1336,17 +1401,17 @@ void ec_fsm_sii_check(ec_fsm_t *fsm /**< finite state machine */)
} }
ec_master_queue_command(fsm->master, command); ec_master_queue_command(fsm->master, command);
fsm->sii_state = ec_fsm_sii_fetch; fsm->sii_state = ec_fsm_sii_read_fetch;
} }
/*****************************************************************************/ /*****************************************************************************/
/** /**
SII state: FETCH. SII state: READ_FETCH.
Fetches the result of an SII-read command. Fetches the result of an SII-read command.
*/ */
void ec_fsm_sii_fetch(ec_fsm_t *fsm /**< finite state machine */) void ec_fsm_sii_read_fetch(ec_fsm_t *fsm /**< finite state machine */)
{ {
ec_command_t *command = &fsm->command; ec_command_t *command = &fsm->command;
...@@ -1391,12 +1456,92 @@ void ec_fsm_sii_fetch(ec_fsm_t *fsm /**< finite state machine */) ...@@ -1391,12 +1456,92 @@ void ec_fsm_sii_fetch(ec_fsm_t *fsm /**< finite state machine */)
#endif #endif
// SII value received. // SII value received.
memcpy(fsm->sii_result, command->data + 6, 4); memcpy(fsm->sii_value, command->data + 6, 4);
fsm->sii_state = ec_fsm_sii_end; fsm->sii_state = ec_fsm_sii_end;
} }
/*****************************************************************************/ /*****************************************************************************/
/**
SII state: START_WRITING.
Starts reading the slave information interface.
*/
void ec_fsm_sii_start_writing(ec_fsm_t *fsm /**< finite state machine */)
{
ec_command_t *command = &fsm->command;
// initiate write operation
ec_command_npwr(command, fsm->slave->station_address, 0x502, 8);
EC_WRITE_U8 (command->data, 0x01); // enable write access
EC_WRITE_U8 (command->data + 1, 0x02); // request write operation
EC_WRITE_U32(command->data + 2, fsm->sii_offset);
memcpy(command->data + 6, fsm->sii_value, 2);
ec_master_queue_command(fsm->master, command);
fsm->sii_state = ec_fsm_sii_write_check;
}
/*****************************************************************************/
/**
SII state: WRITE_CHECK.
*/
void ec_fsm_sii_write_check(ec_fsm_t *fsm /**< finite state machine */)
{
ec_command_t *command = &fsm->command;
if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) {
EC_ERR("SII: Reception of write command failed.\n");
fsm->sii_state = ec_fsm_sii_error;
return;
}
fsm->sii_start = get_cycles();
// issue check/fetch command
ec_command_nprd(command, fsm->slave->station_address, 0x502, 2);
ec_master_queue_command(fsm->master, command);
fsm->sii_state = ec_fsm_sii_write_check2;
}
/*****************************************************************************/
/**
SII state: WRITE_CHECK2.
*/
void ec_fsm_sii_write_check2(ec_fsm_t *fsm /**< finite state machine */)
{
ec_command_t *command = &fsm->command;
if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) {
EC_ERR("SII: Reception of write check command failed.\n");
fsm->sii_state = ec_fsm_sii_error;
return;
}
if (EC_READ_U8(command->data + 1) & 0x82) {
// still busy... timeout?
if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) {
EC_ERR("SII: Write timeout.\n");
fsm->sii_state = ec_fsm_sii_error;
}
// issue check/fetch command again
ec_master_queue_command(fsm->master, command);
}
else if (EC_READ_U8(command->data + 1) & 0x40) {
EC_ERR("SII: Write operation failed!\n");
fsm->sii_state = ec_fsm_sii_error;
}
else { // success
fsm->sii_state = ec_fsm_sii_end;
}
}
/*****************************************************************************/
/** /**
SII state: END. SII state: END.
End state of the slave SII state machine. End state of the slave SII state machine.
......
...@@ -71,7 +71,7 @@ struct ec_fsm ...@@ -71,7 +71,7 @@ struct ec_fsm
void (*sii_state)(ec_fsm_t *); /**< SII state function */ void (*sii_state)(ec_fsm_t *); /**< SII state function */
uint16_t sii_offset; /**< input: offset in SII */ uint16_t sii_offset; /**< input: offset in SII */
unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */ unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */
uint8_t sii_result[4]; /**< output: raw SII value (32bit) */ uint8_t sii_value[4]; /**< raw SII value (32bit) */
cycles_t sii_start; /**< sii start */ cycles_t sii_start; /**< sii start */
void (*change_state)(ec_fsm_t *); /**< slave state change state function */ void (*change_state)(ec_fsm_t *); /**< slave state change state function */
......
...@@ -69,7 +69,7 @@ EC_SYSFS_READ_ATTR(product_desc); ...@@ -69,7 +69,7 @@ EC_SYSFS_READ_ATTR(product_desc);
EC_SYSFS_READ_ATTR(sii_name); EC_SYSFS_READ_ATTR(sii_name);
EC_SYSFS_READ_ATTR(type); EC_SYSFS_READ_ATTR(type);
EC_SYSFS_READ_WRITE_ATTR(state); EC_SYSFS_READ_WRITE_ATTR(state);
EC_SYSFS_READ_ATTR(eeprom); EC_SYSFS_READ_WRITE_ATTR(eeprom);
static struct attribute *def_attrs[] = { static struct attribute *def_attrs[] = {
&attr_ring_position, &attr_ring_position,
...@@ -157,6 +157,8 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */ ...@@ -157,6 +157,8 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */
slave->current_state = EC_SLAVE_STATE_UNKNOWN; slave->current_state = EC_SLAVE_STATE_UNKNOWN;
slave->state_error = 0; slave->state_error = 0;
slave->online = 1; slave->online = 1;
slave->new_eeprom_data = NULL;
slave->new_eeprom_size = 0;
ec_command_init(&slave->mbox_command); ec_command_init(&slave->mbox_command);
...@@ -239,6 +241,7 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */) ...@@ -239,6 +241,7 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
} }
if (slave->eeprom_data) kfree(slave->eeprom_data); if (slave->eeprom_data) kfree(slave->eeprom_data);
if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
ec_command_clear(&slave->mbox_command); ec_command_clear(&slave->mbox_command);
} }
...@@ -1246,6 +1249,83 @@ int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT slave */) ...@@ -1246,6 +1249,83 @@ int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT slave */)
/*****************************************************************************/ /*****************************************************************************/
/**
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. Formats attribute data for SysFS read access.
\return number of bytes to read \return number of bytes to read
...@@ -1359,6 +1439,10 @@ ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */ ...@@ -1359,6 +1439,10 @@ ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */
EC_ERR("Failed to set slave state!\n"); EC_ERR("Failed to set slave state!\n");
} }
else if (attr == &attr_eeprom) {
if (!ec_slave_write_eeprom(slave, buffer, size))
return size;
}
return -EINVAL; return -EINVAL;
} }
......
...@@ -271,6 +271,9 @@ struct ec_slave ...@@ -271,6 +271,9 @@ struct ec_slave
char *eeprom_order; /**< slave order number acc. to EEPROM */ char *eeprom_order; /**< slave order number acc. to EEPROM */
char *eeprom_name; /**< slave name acc. to EEPROM */ char *eeprom_name; /**< slave name acc. to EEPROM */
uint16_t *new_eeprom_data; /**< new EEPROM data to write */
size_t new_eeprom_size; /**< size of new EEPROM data in words */
struct list_head sdo_dictionary; /**< SDO directory list */ struct list_head sdo_dictionary; /**< SDO directory list */
ec_command_t mbox_command; /**< mailbox command */ ec_command_t mbox_command; /**< mailbox command */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment