Implemented SDO reading with wait queues.

This commit is contained in:
Florian Pose 2007-03-09 15:11:29 +00:00
parent 8c9fec5ce0
commit 2907aebfdf
7 changed files with 113 additions and 102 deletions

1
NEWS
View File

@ -35,6 +35,7 @@ Changes in version 1.3.0:
* All EEPROM write operations from user space are now blocking until
writing has finished and return appropriate error codes.
* Implemented setting of secondary slave address (alias) via sysfs.
* Implemented SDO reading in operation mode via sysfs.
* Removed annoying eeprom_write_enable file. EEPROM writing always enabled.
* Removed EtherCAT line comments from 8139too drivers.

2
TODO
View File

@ -24,7 +24,7 @@ $Id$
- Calculate expected working counter for domains.
- Optimize alignment of process data.
- Evaluate EEPROM contents after writing.
- SDO dictionary and -access in operation mode.
- SDO dictionary fetching in operation mode.
- SDO write access in sysfs.
- Speed up IDLE state machine through fast mode with schedule().
- Configure slave ports to automatically open on link detection.

View File

@ -320,6 +320,9 @@ ssize_t ec_sdo_entry_format_data(ec_sdo_entry_t *entry, /**< SDO entry */
off += sprintf(buffer + off, "%s\n", request->data);
}
else {
off += sprintf(buffer + off,
"Unknown data type %04X, bit size %u. Data:\n",
entry->data_type, entry->bit_length);
for (i = 0; i < request->size; i++)
off += sprintf(buffer + off, "%02X (%c)\n",
request->data[i], request->data[i]);
@ -339,34 +342,35 @@ ssize_t ec_sdo_entry_read_value(ec_sdo_entry_t *entry, /**< SDO entry */
off_t off = 0;
ec_sdo_request_t request;
if (down_interruptible(&master->sdo_sem)) {
// interrupted by signal
return -ERESTARTSYS;
}
ec_sdo_request_init_read(&request, sdo, entry);
// this is necessary, because the completion object
// is completed by the ec_master_flush_sdo_requests() function.
INIT_COMPLETION(master->sdo_complete);
master->sdo_request = &request;
master->sdo_seq_user++;
master->sdo_timer.expires = jiffies + 10;
add_timer(&master->sdo_timer);
wait_for_completion(&master->sdo_complete);
master->sdo_request = NULL;
// schedule request.
down(&master->sdo_sem);
list_add_tail(&request.list, &master->sdo_requests);
up(&master->sdo_sem);
if (request.return_code == 1 && request.data) {
off += ec_sdo_entry_format_data(entry, &request, buffer);
}
else {
off = -EINVAL;
// wait for processing through FSM
if (wait_event_interruptible(master->sdo_queue,
request.state != EC_REQ_QUEUED)) {
// interrupted by signal
down(&master->sdo_sem);
if (request.state == EC_REQ_QUEUED) {
list_del(&request.list);
up(&master->sdo_sem);
return -EINTR;
}
// request already processing: interrupt not possible.
up(&master->sdo_sem);
}
// wait until master FSM has finished processing
wait_event(master->sdo_queue, request.state != EC_REQ_BUSY);
if (request.state != EC_REQ_COMPLETED)
return -EIO;
off += ec_sdo_entry_format_data(entry, &request, buffer);
ec_sdo_request_clear(&request);
return off;
}
@ -405,7 +409,7 @@ void ec_sdo_request_init_read(ec_sdo_request_t *req, /**< SDO request */
req->entry = entry;
req->data = NULL;
req->size = 0;
req->return_code = 0;
req->state = EC_REQ_QUEUED;
}
/*****************************************************************************/

View File

@ -108,12 +108,12 @@ ec_sdo_data_t;
typedef struct
{
struct list_head queue; /**< list item */
struct list_head list; /**< list item */
ec_sdo_t *sdo;
ec_sdo_entry_t *entry;
uint8_t *data; /**< pointer to SDO data */
size_t size; /**< size of SDO data */
int return_code;
ec_request_state_t state;
}
ec_sdo_request_t;

View File

@ -315,6 +315,63 @@ int ec_fsm_master_action_process_eeprom(
/*****************************************************************************/
/**
* Check for pending SDO requests and process one.
* \return non-zero, if an SDO request is processed.
*/
int ec_fsm_master_action_process_sdo(
ec_fsm_master_t *fsm /**< master state machine */
)
{
ec_master_t *master = fsm->master;
ec_sdo_request_t *request;
ec_slave_t *slave;
// search the first request to be processed
while (1) {
down(&master->sdo_sem);
if (list_empty(&master->sdo_requests)) {
up(&master->sdo_sem);
break;
}
// get first request
request =
list_entry(master->sdo_requests.next, ec_sdo_request_t, list);
list_del_init(&request->list); // dequeue
request->state = EC_REQ_BUSY;
up(&master->sdo_sem);
slave = request->sdo->slave;
if (slave->current_state == EC_SLAVE_STATE_INIT ||
slave->online_state == EC_SLAVE_OFFLINE ||
slave->error_flag) {
EC_ERR("Discarding SDO request, slave %i not ready.\n",
slave->ring_position);
request->state = EC_REQ_ERROR;
wake_up(&master->sdo_queue);
continue;
}
// found pending SDO request. execute it!
if (master->debug_level)
EC_DBG("Processing SDO request for slave %i...\n",
slave->ring_position);
// start uploading SDO
fsm->slave = slave;
fsm->sdo_request = request;
fsm->state = ec_fsm_master_state_sdo_request;
ec_fsm_coe_upload(&fsm->fsm_coe, slave, fsm->sdo_request);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
return 1;
}
return 0;
}
/*****************************************************************************/
/**
Master action: PROC_STATES.
Processes the slave states.
@ -359,30 +416,11 @@ void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm
// Check, if EoE processing has to be started
ec_master_eoe_start(master);
if (master->mode == EC_MASTER_MODE_IDLE) {
// Check for a pending SDO request
if (ec_fsm_master_action_process_sdo(fsm))
return;
// Check for a pending SDO request
if (master->sdo_seq_master != master->sdo_seq_user) {
if (master->debug_level)
EC_DBG("Processing SDO request...\n");
slave = master->sdo_request->sdo->slave;
if (slave->current_state == EC_SLAVE_STATE_INIT
|| slave->online_state == EC_SLAVE_OFFLINE) {
EC_ERR("Failed to process SDO request, slave %i not ready.\n",
slave->ring_position);
master->sdo_request->return_code = -1;
master->sdo_seq_master++;
}
else {
// start uploading SDO
fsm->slave = slave;
fsm->sdo_request = master->sdo_request;
fsm->state = ec_fsm_master_state_sdo_request;
ec_fsm_coe_upload(&fsm->fsm_coe, slave, fsm->sdo_request);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
return;
}
}
if (master->mode == EC_MASTER_MODE_IDLE) {
// check, if slaves have an SDO dictionary to read out.
list_for_each_entry(slave, &master->slaves, list) {
@ -827,16 +865,25 @@ void ec_fsm_master_state_sdo_request(ec_fsm_master_t *fsm /**< master state mach
if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
request->return_code = -1;
master->sdo_seq_master++;
EC_DBG("Failed to process SDO request for slave %i.\n",
fsm->slave->ring_position);
request->state = EC_REQ_ERROR;
wake_up(&master->sdo_queue);
fsm->state = ec_fsm_master_state_error;
return;
}
// SDO dictionary fetching finished
// SDO request finished
request->state = EC_REQ_COMPLETED;
wake_up(&master->sdo_queue);
request->return_code = 1;
master->sdo_seq_master++;
if (master->debug_level)
EC_DBG("Finished SDO request for slave %i.\n",
fsm->slave->ring_position);
// check for another SDO request
if (ec_fsm_master_action_process_sdo(fsm))
return; // processing another request
fsm->state = ec_fsm_master_state_end;
}

View File

@ -157,14 +157,9 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
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;
INIT_LIST_HEAD(&master->sdo_requests);
init_MUTEX(&master->sdo_sem);
init_timer(&master->sdo_timer);
master->sdo_timer.function = ec_master_check_sdo;
master->sdo_timer.data = (unsigned long) master;
init_completion(&master->sdo_complete);
init_waitqueue_head(&master->sdo_queue);
// init devices
if (ec_device_init(&master->main_device, master))
@ -300,21 +295,6 @@ void ec_master_destroy_domains(ec_master_t *master)
/*****************************************************************************/
/**
Flushes the SDO request queue.
*/
void ec_master_flush_sdo_requests(ec_master_t *master)
{
del_timer_sync(&master->sdo_timer);
complete(&master->sdo_complete);
master->sdo_request = NULL;
master->sdo_seq_user = 0;
master->sdo_seq_master = 0;
}
/*****************************************************************************/
/**
Internal locking callback.
*/
@ -416,7 +396,6 @@ void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */)
ec_master_eoe_stop(master);
ec_master_thread_stop(master);
ec_master_flush_sdo_requests(master);
ec_master_destroy_slaves(master);
}
@ -1237,25 +1216,6 @@ void ec_master_eoe_run(unsigned long data /**< master pointer */)
/*****************************************************************************/
/**
*/
void ec_master_check_sdo(unsigned long data /**< master pointer */)
{
ec_master_t *master = (ec_master_t *) data;
if (master->sdo_seq_master != master->sdo_seq_user) {
master->sdo_timer.expires = jiffies + 10;
add_timer(&master->sdo_timer);
return;
}
// master has processed the request
complete(&master->sdo_complete);
}
/*****************************************************************************/
/**
Measures the time, a frame is on the bus.
\return 0 in case of success, else < 0

View File

@ -150,12 +150,11 @@ struct ec_master
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 */
struct semaphore sdo_sem; /**< SDO semaphore */
struct timer_list sdo_timer; /**< timer for polling sdo processing */
struct completion sdo_complete; /**< SDO request completion object */
struct list_head sdo_requests; /**< SDO access requests */
struct semaphore sdo_sem; /**< semaphore protecting the list of
SDO access requests */
wait_queue_head_t sdo_queue; /**< wait queue for SDO access requests
from user space */
};
/*****************************************************************************/