# HG changeset patch # User Florian Pose # Date 1172784850 0 # Node ID d6d951b766e300954d3f1c64f44746ef88bc0429 # Parent b0660152f7101450bc2abc9a36b2a191099f3ca6 Introduced EEPROM write requests: EEPROM write operations from user space are now queued and block until completion. Also, appropriate error codes are returned. diff -r b0660152f710 -r d6d951b766e3 NEWS --- a/NEWS Thu Mar 01 21:23:07 2007 +0000 +++ b/NEWS Thu Mar 01 21:34:10 2007 +0000 @@ -9,16 +9,18 @@ * 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. diff -r b0660152f710 -r d6d951b766e3 master/fsm_master.c --- a/master/fsm_master.c Thu Mar 01 21:23:07 2007 +0000 +++ b/master/fsm_master.c Thu Mar 01 21:34:10 2007 +0000 @@ -275,6 +275,51 @@ /*****************************************************************************/ /** + * 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 @@ } // 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_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; - 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); + request->state = EC_EEPROM_REQ_ERROR; + wake_up_interruptible(&master->eeprom_queue); + fsm->state = ec_fsm_master_state_error; + return; + } + + 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 diff -r b0660152f710 -r d6d951b766e3 master/fsm_master.h --- a/master/fsm_master.h Thu Mar 01 21:23:07 2007 +0000 +++ b/master/fsm_master.h Thu Mar 01 21:34:10 2007 +0000 @@ -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 @@ 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 */ diff -r b0660152f710 -r d6d951b766e3 master/master.c --- a/master/master.c Thu Mar 01 21:23:07 2007 +0000 +++ b/master/master.c Thu Mar 01 21:34:10 2007 +0000 @@ -152,6 +152,10 @@ 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 @@ 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) { diff -r b0660152f710 -r d6d951b766e3 master/master.h --- a/master/master.h Thu Mar 01 21:23:07 2007 +0000 +++ b/master/master.h Thu Mar 01 21:34:10 2007 +0000 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -138,6 +139,12 @@ 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 */ diff -r b0660152f710 -r d6d951b766e3 master/slave.c --- a/master/slave.c Thu Mar 01 21:23:07 2007 +0000 +++ b/master/slave.c Thu Mar 01 21:34:10 2007 +0000 @@ -128,8 +128,6 @@ 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 @@ } if (slave->eeprom_data) kfree(slave->eeprom_data); - if (slave->new_eeprom_data) kfree(slave->new_eeprom_data); kfree(slave); } @@ -785,63 +782,71 @@ 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->mode != EC_MASTER_MODE_IDLE) { + 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) { // 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; - - if (word_size < 0x0041) { + // 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 (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); - } - - // 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 -ENOMEM; - } - 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; + 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: schedule EEPROM write request. + down(&master->eeprom_sem); + list_add_tail(&request.list, &master->eeprom_requests); + up(&master->eeprom_sem); + + // 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); + } + + return request.state == EC_EEPROM_REQ_COMPLETED ? size : -EIO; } /*****************************************************************************/ diff -r b0660152f710 -r d6d951b766e3 master/slave.h --- a/master/slave.h Thu Mar 01 21:23:07 2007 +0000 +++ b/master/slave.h Thu Mar 01 21:34:10 2007 +0000 @@ -221,9 +221,7 @@ // 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 */