Introduced EEPROM write requests: EEPROM write operations from user
space are now queued and block until completion. Also, appropriate error codes are returned.
This commit is contained in:
parent
e1315d14f4
commit
2ec4d25fce
10
NEWS
10
NEWS
|
|
@ -9,16 +9,18 @@ Changes in version 1.3.0:
|
|||
* Added Intel PRO/100 ethernet driver (e100).
|
||||
* Added ethernet driver for NVIDIA nForce chipsets (forcedeth).
|
||||
* Removed "ec_eoeif_count" master module parameter.
|
||||
* Introduced "device IDs" to tell a master to wait for certain ethernet
|
||||
devices.
|
||||
* Added "main" and "backup" parameters to master module. To hand over
|
||||
* Introduced "Device IDs" to tell a master to wait for connection of certain
|
||||
ethernet devices.
|
||||
* Added "main" and "backup" parameters to master module to hand over
|
||||
device ID lists.
|
||||
* Changed format of sysconfig file and accordingly adjusted functionality
|
||||
of the init script to handle device IDs.
|
||||
of the init script to handle device ID lists.
|
||||
* Device interface changes:
|
||||
- Replaced ecdev_register() and ecdev_unregister() with ecdev_offer() and
|
||||
ecdev_withdraw(), respectively. The device modules now offer all their
|
||||
devices to the master, which decides, which ones to register.
|
||||
* All EEPROM write operations from user space are now blocking until
|
||||
completion and returning appropriate error codes.
|
||||
* Removed annoying eeprom_write_enable file. EEPROM writing always enabled.
|
||||
* Removed EtherCAT line comments from 8139too drivers.
|
||||
|
||||
|
|
|
|||
|
|
@ -274,6 +274,51 @@ void ec_fsm_master_state_broadcast(ec_fsm_master_t *fsm /**< master state machin
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Check for pending EEPROM write requests and process one.
|
||||
* \return non-zero, if an EEPROM write request is processed.
|
||||
*/
|
||||
|
||||
int ec_fsm_master_action_process_eeprom(
|
||||
ec_fsm_master_t *fsm /**< master state machine */
|
||||
)
|
||||
{
|
||||
ec_master_t *master = fsm->master;
|
||||
ec_eeprom_write_request_t *request;
|
||||
ec_slave_t *slave;
|
||||
|
||||
down(&master->eeprom_sem);
|
||||
list_for_each_entry(request, &master->eeprom_requests, list) {
|
||||
list_del_init(&request->list); // dequeue
|
||||
up(&master->eeprom_sem);
|
||||
|
||||
slave = request->slave;
|
||||
if (!slave->online || slave->error_flag) {
|
||||
EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
|
||||
slave->ring_position);
|
||||
request->state = EC_EEPROM_REQ_ERROR;
|
||||
wake_up_interruptible(&master->eeprom_queue);
|
||||
down(&master->eeprom_sem);
|
||||
continue;
|
||||
}
|
||||
|
||||
// found pending EEPROM write operation. execute it!
|
||||
EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
|
||||
fsm->eeprom_request = request;
|
||||
fsm->eeprom_index = 0;
|
||||
ec_fsm_sii_write(&fsm->fsm_sii, request->slave, request->offset,
|
||||
request->words, EC_FSM_SII_NODE);
|
||||
fsm->state = ec_fsm_master_state_write_eeprom;
|
||||
fsm->state(fsm); // execute immediately
|
||||
return 1;
|
||||
}
|
||||
|
||||
up(&master->eeprom_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
Master action: PROC_STATES.
|
||||
Processes the slave states.
|
||||
|
|
@ -368,27 +413,8 @@ void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm
|
|||
}
|
||||
|
||||
// check for pending EEPROM write operations.
|
||||
list_for_each_entry(slave, &master->slaves, list) {
|
||||
if (!slave->new_eeprom_data) continue;
|
||||
|
||||
if (!slave->online || slave->error_flag) {
|
||||
kfree(slave->new_eeprom_data);
|
||||
slave->new_eeprom_data = NULL;
|
||||
EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
|
||||
slave->ring_position);
|
||||
continue;
|
||||
}
|
||||
|
||||
// found pending EEPROM write operation. execute it!
|
||||
EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
|
||||
fsm->slave = slave;
|
||||
fsm->sii_offset = 0x0000;
|
||||
ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
|
||||
slave->new_eeprom_data, EC_FSM_SII_NODE);
|
||||
fsm->state = ec_fsm_master_state_write_eeprom;
|
||||
fsm->state(fsm); // execute immediately
|
||||
return;
|
||||
}
|
||||
if (ec_fsm_master_action_process_eeprom(fsm))
|
||||
return; // EEPROM write request found
|
||||
}
|
||||
|
||||
fsm->state = ec_fsm_master_state_end;
|
||||
|
|
@ -751,36 +777,43 @@ void ec_fsm_master_state_configure_slave(ec_fsm_master_t *fsm
|
|||
|
||||
void ec_fsm_master_state_write_eeprom(ec_fsm_master_t *fsm /**< master state machine */)
|
||||
{
|
||||
ec_slave_t *slave = fsm->slave;
|
||||
ec_master_t *master = fsm->master;
|
||||
ec_eeprom_write_request_t *request = fsm->eeprom_request;
|
||||
ec_slave_t *slave = request->slave;
|
||||
|
||||
if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
|
||||
|
||||
if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
|
||||
fsm->slave->error_flag = 1;
|
||||
slave->error_flag = 1;
|
||||
EC_ERR("Failed to write EEPROM contents to slave %i.\n",
|
||||
slave->ring_position);
|
||||
kfree(slave->new_eeprom_data);
|
||||
slave->new_eeprom_data = NULL;
|
||||
request->state = EC_EEPROM_REQ_ERROR;
|
||||
wake_up_interruptible(&master->eeprom_queue);
|
||||
fsm->state = ec_fsm_master_state_error;
|
||||
return;
|
||||
}
|
||||
|
||||
fsm->sii_offset++;
|
||||
if (fsm->sii_offset < slave->new_eeprom_size) {
|
||||
ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
|
||||
slave->new_eeprom_data + fsm->sii_offset,
|
||||
EC_FSM_SII_NODE);
|
||||
fsm->eeprom_index++;
|
||||
if (fsm->eeprom_index < request->size) {
|
||||
ec_fsm_sii_write(&fsm->fsm_sii, slave,
|
||||
request->offset + fsm->eeprom_index,
|
||||
request->words + fsm->eeprom_index,
|
||||
EC_FSM_SII_NODE);
|
||||
ec_fsm_sii_exec(&fsm->fsm_sii); // 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;
|
||||
request->state = EC_EEPROM_REQ_COMPLETED;
|
||||
wake_up_interruptible(&master->eeprom_queue);
|
||||
|
||||
// TODO: Evaluate new EEPROM contents!
|
||||
|
||||
// check for another EEPROM write request
|
||||
if (ec_fsm_master_action_process_eeprom(fsm))
|
||||
return; // processing another request
|
||||
|
||||
// restart master state machine.
|
||||
fsm->state = ec_fsm_master_state_start;
|
||||
fsm->state(fsm); // execute immediately
|
||||
|
|
|
|||
|
|
@ -51,6 +51,37 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* EEPROM request state.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EC_EEPROM_REQ_QUEUED,
|
||||
EC_EEPROM_REQ_COMPLETED,
|
||||
EC_EEPROM_REQ_ERROR
|
||||
}
|
||||
ec_eeprom_request_state_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* EEPROM write request.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct list_head list;
|
||||
ec_slave_t *slave;
|
||||
off_t offset;
|
||||
size_t size;
|
||||
const uint16_t *words;
|
||||
ec_eeprom_request_state_t state;
|
||||
}
|
||||
ec_eeprom_write_request_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct ec_fsm_master ec_fsm_master_t; /**< \see ec_fsm_master */
|
||||
|
||||
/**
|
||||
|
|
@ -69,8 +100,9 @@ struct ec_fsm_master
|
|||
ec_slave_state_t slave_states; /**< states of responding slaves */
|
||||
unsigned int validate; /**< non-zero, if validation to do */
|
||||
ec_slave_t *slave; /**< current slave */
|
||||
ec_eeprom_write_request_t *eeprom_request; /**< EEPROM write request */
|
||||
off_t eeprom_index; /**< index to EEPROM write request data */
|
||||
ec_sdo_request_t *sdo_request; /**< SDO request to process */
|
||||
uint16_t sii_offset;
|
||||
|
||||
ec_fsm_slave_t fsm_slave; /**< slave state machine */
|
||||
ec_fsm_sii_t fsm_sii; /**< SII state machine */
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
master->release_cb = NULL;
|
||||
master->cb_data = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&master->eeprom_requests);
|
||||
init_MUTEX(&master->eeprom_sem);
|
||||
init_waitqueue_head(&master->eeprom_queue);
|
||||
|
||||
master->sdo_request = NULL;
|
||||
master->sdo_seq_user = 0;
|
||||
master->sdo_seq_master = 0;
|
||||
|
|
@ -255,6 +259,9 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
|
|||
ec_eoe_t *eoe, *next_eoe;
|
||||
ec_datagram_t *datagram, *next_datagram;
|
||||
|
||||
// list of EEPROM requests is empty,
|
||||
// otherwise master could not be cleared.
|
||||
|
||||
// dequeue all datagrams
|
||||
list_for_each_entry_safe(datagram, next_datagram,
|
||||
&master->datagram_queue, queue) {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
|
|
@ -138,6 +139,12 @@ struct ec_master
|
|||
void (*release_cb)(void *); /**< lock release callback */
|
||||
void *cb_data; /**< data parameter of locking callbacks */
|
||||
|
||||
struct list_head eeprom_requests; /**< EEPROM write requests */
|
||||
struct semaphore eeprom_sem; /**< semaphore protecting the list of
|
||||
EEPROM write requests */
|
||||
wait_queue_head_t eeprom_queue; /**< wait queue for EEPROM
|
||||
write requests from user space */
|
||||
|
||||
ec_sdo_request_t *sdo_request; /**< pointer to the current SDO request */
|
||||
unsigned int sdo_seq_user; /**< sequence number for user space */
|
||||
unsigned int sdo_seq_master; /**< sequence number for master */
|
||||
|
|
|
|||
|
|
@ -128,8 +128,6 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */
|
|||
|
||||
slave->eeprom_data = NULL;
|
||||
slave->eeprom_size = 0;
|
||||
slave->new_eeprom_data = NULL;
|
||||
slave->new_eeprom_size = 0;
|
||||
|
||||
slave->sii_alias = 0;
|
||||
slave->sii_vendor_id = 0;
|
||||
|
|
@ -286,7 +284,6 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
|
|||
}
|
||||
|
||||
if (slave->eeprom_data) kfree(slave->eeprom_data);
|
||||
if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
|
||||
|
||||
kfree(slave);
|
||||
}
|
||||
|
|
@ -785,63 +782,71 @@ ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */
|
|||
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;
|
||||
ec_eeprom_write_request_t request;
|
||||
const uint16_t *cat_header;
|
||||
uint16_t cat_type, cat_size;
|
||||
ec_master_t *master = slave->master;
|
||||
|
||||
if (slave->master->mode != EC_MASTER_MODE_IDLE) {
|
||||
if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME
|
||||
EC_ERR("Writing EEPROMs only allowed in idle mode!\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (slave->new_eeprom_data) {
|
||||
EC_ERR("Slave %i already has a pending EEPROM write operation!\n",
|
||||
slave->ring_position);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
// coarse check of the data
|
||||
|
||||
if (size % 2) {
|
||||
EC_ERR("EEPROM size is odd! Dropping.\n");
|
||||
EC_ERR("EEPROM data size is odd! Dropping.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_words = (const uint16_t *) data;
|
||||
word_size = size / 2;
|
||||
// init EEPROM write request
|
||||
INIT_LIST_HEAD(&request.list);
|
||||
request.slave = slave;
|
||||
request.words = (const uint16_t *) data;
|
||||
request.offset = 0;
|
||||
request.size = size / 2;
|
||||
request.state = EC_EEPROM_REQ_QUEUED;
|
||||
|
||||
if (word_size < 0x0041) {
|
||||
if (request.size < 0x0041) {
|
||||
EC_ERR("EEPROM data too short! Dropping.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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");
|
||||
cat_header = request.words + 0x0040; // first category header
|
||||
cat_type = EC_READ_U16(cat_header);
|
||||
while (cat_type != 0xFFFF) { // cycle through categories
|
||||
if (cat_header + 1 > request.words + request.size) {
|
||||
EC_ERR("EEPROM data corrupted! Dropping.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
next_header += cat_size + 2;
|
||||
cat_type = EC_READ_U16(next_header);
|
||||
cat_size = EC_READ_U16(cat_header + 1);
|
||||
if (cat_header + cat_size + 2 > request.words + request.size) {
|
||||
EC_ERR("EEPROM data corrupted! Dropping.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
cat_header += cat_size + 2;
|
||||
cat_type = EC_READ_U16(cat_header);
|
||||
}
|
||||
|
||||
// data ok!
|
||||
// data ok: schedule EEPROM write request.
|
||||
down(&master->eeprom_sem);
|
||||
list_add_tail(&request.list, &master->eeprom_requests);
|
||||
up(&master->eeprom_sem);
|
||||
|
||||
if (!(new_data = (uint16_t *) kmalloc(word_size * 2, GFP_KERNEL))) {
|
||||
EC_ERR("Unable to allocate memory for new EEPROM data!\n");
|
||||
return -ENOMEM;
|
||||
// wait for processing through FSM
|
||||
while (wait_event_interruptible(master->eeprom_queue,
|
||||
request.state != EC_EEPROM_REQ_QUEUED)) {
|
||||
// interrupted by signal
|
||||
down(&master->eeprom_sem);
|
||||
if (!list_empty(&request.list)) {
|
||||
// still queued: safely dequeue
|
||||
list_del(&request.list);
|
||||
up(&master->eeprom_sem);
|
||||
return -EINTR;
|
||||
}
|
||||
// request processing: interrupt not possible.
|
||||
up(&master->eeprom_sem);
|
||||
}
|
||||
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 size;
|
||||
return request.state == EC_EEPROM_REQ_COMPLETED ? size : -EIO;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -221,9 +221,7 @@ struct ec_slave
|
|||
|
||||
// EEPROM
|
||||
uint8_t *eeprom_data; /**< Complete EEPROM image */
|
||||
uint16_t eeprom_size; /**< size of the EEPROM contents in byte */
|
||||
uint16_t *new_eeprom_data; /**< new EEPROM data to write */
|
||||
uint16_t new_eeprom_size; /**< size of new EEPROM data in words */
|
||||
size_t eeprom_size; /**< size of the EEPROM contents in bytes */
|
||||
|
||||
// slave information interface
|
||||
uint16_t sii_alias; /**< configured station alias */
|
||||
|
|
|
|||
Loading…
Reference in New Issue