Implemented SDO reading with wait queues.
This commit is contained in:
parent
8c9fec5ce0
commit
2907aebfdf
1
NEWS
1
NEWS
|
|
@ -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
2
TODO
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
Loading…
Reference in New Issue