diff --git a/NEWS b/NEWS index 6c33de30..0f70e28d 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ $Id$ Changes in version 1.3.2: +* Calculate checksum when writing EEPROM or alias address. * Removed config.kbuild and replaced Kbuild files by Kbuild.in files. ------------------------------------------------------------------------------- diff --git a/master/slave.c b/master/slave.c index 2cecdfa0..1c407011 100644 --- a/master/slave.c +++ b/master/slave.c @@ -890,6 +890,46 @@ int ec_slave_schedule_eeprom_writing(ec_eeprom_write_request_t *request) /*****************************************************************************/ +/** + * Calculates the EEPROM checksum field. + * + * The checksum is generated with the polynom x^8+x^2+x+1 (0x07) and an + * initial value of 0xff (see IEC 61158-6-12 ch. 5.4). + * + * The below code was originally generated with PYCRC + * http://www.tty1.net/pycrc + * + * ./pycrc.py --width=8 --poly=0x07 --reflect-in=0 --xor-in=0xff + * --reflect-out=0 --xor-out=0 --generate c --algorithm=bit-by-bit + * + * \return CRC8 + */ + +uint8_t ec_slave_eeprom_crc(const uint8_t *data, size_t length) +{ + unsigned int i; + uint8_t bit, byte, crc = 0x48; + + while (length--) { + byte = *data++; + for (i = 0; i < 8; i++) { + bit = crc & 0x80; + crc = (crc << 1) | ((byte >> (7 - i)) & 0x01); + if (bit) crc ^= 0x07; + } + } + + for (i = 0; i < 8; i++) { + bit = crc & 0x80; + crc <<= 1; + if (bit) crc ^= 0x07; + } + + return crc; +} + +/*****************************************************************************/ + /** * Writes complete EEPROM contents to a slave. * \return data size written in case of success, otherwise error code. @@ -904,6 +944,7 @@ ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */ const uint16_t *cat_header; uint16_t cat_type, cat_size; int ret; + uint8_t crc; if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME EC_ERR("Writing EEPROMs only allowed in idle mode!\n"); @@ -927,6 +968,12 @@ ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */ return -EINVAL; } + // calculate checksum + crc = ec_slave_eeprom_crc(data, 14); // CRC over words 0 to 6 + if (crc != data[14]) { + EC_WARN("EEPROM CRC incorrect. Must be 0x%02x.\n", crc); + } + cat_header = request.words + EC_FIRST_EEPROM_CATEGORY_OFFSET; cat_type = EC_READ_U16(cat_header); while (cat_type != 0xFFFF) { // cycle through categories @@ -964,11 +1011,12 @@ ssize_t ec_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */ { ec_eeprom_write_request_t request; char *remainder; - uint16_t alias, word; + uint16_t alias, words[8]; int ret; + uint8_t crc; if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME - EC_ERR("Writing EEPROMs only allowed in idle mode!\n"); + EC_ERR("Writing to EEPROM is only allowed in idle mode!\n"); return -EBUSY; } @@ -977,16 +1025,29 @@ ssize_t ec_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */ EC_ERR("Invalid alias value! Dropping.\n"); return -EINVAL; } + + if (!slave->eeprom_data || slave->eeprom_size < 16) { + EC_ERR("Failed to read EEPROM contents from slave %u.\n", + slave->ring_position); + return -EINVAL; + } + + // copy first 7 words of recent EEPROM contents + memcpy(words, slave->eeprom_data, 14); - // correct endianess - EC_WRITE_U16(&word, alias); + // write new alias address + EC_WRITE_U16(words + 4, alias); + + // calculate new checksum over words 0 to 6 + crc = ec_slave_eeprom_crc((const uint8_t *) words, 14); + EC_WRITE_U16(words + 7, crc); // init EEPROM write request INIT_LIST_HEAD(&request.list); request.slave = slave; - request.words = &word; - request.offset = 0x0004; - request.size = 1; + request.words = words; + request.offset = 0x0000; + request.size = 8; if ((ret = ec_slave_schedule_eeprom_writing(&request))) return ret; // error code