Device interface library for multiple platforms including Mbed.
Dependents: DeepCover Embedded Security in IoT MaximInterface MAXREFDES155#
Maxim Interface is a library framework focused on providing flexible and expressive hardware interfaces. Both communication interfaces such as I2C and 1-Wire and device interfaces such as DS18B20 are supported. Modern C++ concepts are used extensively while keeping compatibility with C++98/C++03 and requiring no external dependencies. The embedded-friendly design does not depend on exceptions or RTTI.
The full version of the project is hosted on GitLab: https://gitlab.com/iabenz/MaximInterface
Diff: MaximInterfaceDevices/DS28E15_22_25.cpp
- Revision:
- 7:9cd16581b578
- Child:
- 8:5ea891c7d1a1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MaximInterfaceDevices/DS28E15_22_25.cpp Mon Jul 22 11:44:07 2019 -0500 @@ -0,0 +1,653 @@ +/******************************************************************************* +* Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES +* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +* +* Except as contained in this notice, the name of Maxim Integrated +* Products, Inc. shall not be used except as stated in the Maxim Integrated +* Products, Inc. Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Maxim Integrated Products, Inc. retains all +* ownership rights. +*******************************************************************************/ + +#include <algorithm> +#include <MaximInterfaceCore/crc.hpp> +#include <MaximInterfaceCore/Error.hpp> +#include "DS28E15_22_25.hpp" + +namespace MaximInterfaceDevices { + +using namespace Core; +using std::copy; + +static const int shaComputationDelayMs = 3; +static const int eepromWriteDelayMs = 10; +static inline int secretEepromWriteDelayMs(bool lowPower) { + return lowPower ? 200 : 100; +} + +static const int ds28e22_25_pagesPerBlock = 2; + +static error_code +writeDataWithCrc(OneWireMaster & master, span<const uint_least8_t> data, + OneWireMaster::Level level = OneWireMaster::NormalLevel, + uint_fast16_t crcStart = 0) { + error_code result = master.writeBlock(data); + if (result) { + return result; + } + uint_least8_t response[2]; + result = master.readByte(response[0]); + if (result) { + return result; + } + result = master.readByteSetLevel(response[1], level); + if (result) { + return result; + } + if (calculateCrc16(calculateCrc16(crcStart, data), response) != 0xB001) { + result = make_error_code(DS28E15_22_25::CrcError); + } + return result; +} + +const int DS28E15_22_25::segmentsPerPage; + +DS28E15_22_25::AuthenticationData & +DS28E15_22_25::AuthenticationData::setAnonymousRomId() { + std::fill(romId().begin(), romId().end(), 0xFF); + return *this; +} + +error_code +DS28E15_22_25::writeCommandWithCrc(Command command, uint_least8_t parameter, + OneWireMaster::Level level) const { + error_code result = selectRom(*master); + if (!result) { + const uint_least8_t data[] = {static_cast<uint_least8_t>(command), + parameter}; + result = writeDataWithCrc(*master, data, level); + } + return result; +} + +static error_code readDataWithCrc(OneWireMaster & master, + span<uint_least8_t> data) { + error_code result = master.readBlock(data); + if (result) { + return result; + } + uint_least8_t response[2]; + result = master.readBlock(response); + if (result) { + return result; + } + if (calculateCrc16(calculateCrc16(data), response) != 0xB001) { + result = make_error_code(DS28E15_22_25::CrcError); + } + return result; +} + +static error_code readCsByte(OneWireMaster & master) { + uint_least8_t response; + error_code result = master.readByte(response); + if (result) { + return result; + } + if (response != 0xAA) { + result = make_error_code(DS28E15_22_25::OperationFailure); + } + return result; +} + +static error_code releaseSequence(OneWireMaster & master, Sleep & sleep, + int delayTimeMs) { + error_code result = master.writeBytePower(0xAA); + if (result) { + return result; + } + sleep(delayTimeMs); + result = master.setLevel(OneWireMaster::NormalLevel); + if (result) { + return result; + } + return readCsByte(master); +} + +DS28E15_22_25::BlockProtection & +DS28E15_22_25::BlockProtection::setBlockNum(int blockNum) { + status &= ~blockNumMask; + status |= (blockNum & blockNumMask); + return *this; +} + +bool DS28E15_22_25::BlockProtection::noProtection() const { + return !readProtection() && !writeProtection() && !eepromEmulation() && + !authProtection(); +} + +DS28E15_22_25::BlockProtection & +DS28E15_22_25::BlockProtection::setReadProtection(bool readProtection) { + if (readProtection) { + status |= readProtectionMask; + } else { + status &= ~readProtectionMask; + } + return *this; +} + +DS28E15_22_25::BlockProtection & +DS28E15_22_25::BlockProtection::setWriteProtection(bool writeProtection) { + if (writeProtection) { + status |= writeProtectionMask; + } else { + status &= ~writeProtectionMask; + } + return *this; +} + +DS28E15_22_25::BlockProtection & +DS28E15_22_25::BlockProtection::setEepromEmulation(bool eepromEmulation) { + if (eepromEmulation) { + status |= eepromEmulationMask; + } else { + status &= ~eepromEmulationMask; + } + return *this; +} + +DS28E15_22_25::BlockProtection & +DS28E15_22_25::BlockProtection::setAuthProtection(bool authProtection) { + if (authProtection) { + status |= authProtectionMask; + } else { + status &= ~authProtectionMask; + } + return *this; +} + +DS28E15_22_25::ProtectionWriteMacData::ProtectionWriteMacData() + : result_(), oldProtection_(), newProtection_() { + setOldProtection(oldProtection_); + setNewProtection(newProtection_); +} + +DS28E15_22_25::ProtectionWriteMacData & +DS28E15_22_25::ProtectionWriteMacData::setOldProtection( + BlockProtection oldProtection) { + result_[oldProtectionIdx] = oldProtection.authProtection() ? 1 : 0; + result_[oldProtectionIdx + 1] = oldProtection.eepromEmulation() ? 1 : 0; + result_[oldProtectionIdx + 2] = oldProtection.writeProtection() ? 1 : 0; + result_[oldProtectionIdx + 3] = oldProtection.readProtection() ? 1 : 0; + oldProtection_ = oldProtection; + return *this; +} + +DS28E15_22_25::ProtectionWriteMacData & +DS28E15_22_25::ProtectionWriteMacData::setNewProtection( + BlockProtection newProtection) { + result_[blockNumIdx] = newProtection.blockNum(); + result_[newProtectionIdx] = newProtection.authProtection() ? 1 : 0; + result_[newProtectionIdx + 1] = newProtection.eepromEmulation() ? 1 : 0; + result_[newProtectionIdx + 2] = newProtection.writeProtection() ? 1 : 0; + result_[newProtectionIdx + 3] = newProtection.readProtection() ? 1 : 0; + newProtection_ = newProtection; + return *this; +} + +error_code +DS28E15_22_25::writeAuthBlockProtection(BlockProtection newProtection, + Page::const_span mac) { + error_code result = + writeCommandWithCrc(AuthWriteBlockProtection, newProtection.statusByte(), + OneWireMaster::StrongLevel); + if (result) { + return result; + } + + sleep->invoke(shaComputationDelayMs); + result = master->setLevel(OneWireMaster::NormalLevel); + if (result) { + return result; + } + + result = writeDataWithCrc(*master, mac); + if (result) { + return result; + } + + result = readCsByte(*master); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, eepromWriteDelayMs); + return result; +} + +error_code DS28E15_22_25::writeBlockProtection(BlockProtection protection) { + error_code result = + writeCommandWithCrc(WriteBlockProtection, protection.statusByte()); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, eepromWriteDelayMs); + return result; +} + +error_code DS28E15_22_25::doReadBlockProtection(int blockNum, + BlockProtection & protection, + Variant variant) const { + uint_least8_t buffer = blockNum; + if (variant == DS28E22 || variant == DS28E25) { + buffer *= ds28e22_25_pagesPerBlock; + } + error_code result = writeCommandWithCrc(ReadStatus, buffer); + if (!result) { + result = master->readByte(buffer); + if (!result) { + protection.setStatusByte(buffer); + } + } + return result; +} + +error_code DS28E15_22_25::computeReadPageMac(int page_num, bool anon, + Page::span mac) const { + error_code result = + writeCommandWithCrc(ComputePageMac, (anon ? 0xE0 : 0x00) | page_num, + OneWireMaster::StrongLevel); + if (result) { + return result; + } + + sleep->invoke(shaComputationDelayMs * 2); + result = master->setLevel(OneWireMaster::NormalLevel); + if (result) { + return result; + } + + result = readCsByte(*master); + if (result) { + return result; + } + + result = readDataWithCrc(*master, mac); + return result; +} + +error_code DS28E15_22_25::doComputeSecret(int page_num, bool lock, + bool lowPower) { + error_code result = writeCommandWithCrc(ComputeAndLockSecret, + lock ? (0xE0 | page_num) : page_num); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, + shaComputationDelayMs * 2 + + secretEepromWriteDelayMs(lowPower)); + return result; +} + +error_code DS28E15_22_25::doWriteScratchpad(Page::const_span data, + Variant variant) { + const uint_least8_t parameter = + (variant == DS28E22 || variant == DS28E25) ? 0x20 : 0x00; + error_code result = writeCommandWithCrc(ReadWriteScratchpad, parameter); + if (result) { + return result; + } + + result = writeDataWithCrc(*master, data); + return result; +} + +error_code DS28E15_22_25::doReadScratchpad(Page::span data, + Variant variant) const { + const uint_least8_t parameter = + (variant == DS28E22 || variant == DS28E25) ? 0x2F : 0x0F; + error_code result = writeCommandWithCrc(ReadWriteScratchpad, parameter); + if (result) { + return result; + } + + result = readDataWithCrc(*master, data); + return result; +} + +error_code DS28E15_22_25::doLoadSecret(bool lock, bool lowPower) { + error_code result = + writeCommandWithCrc(LoadAndLockSecret, lock ? 0xE0 : 0x00); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, secretEepromWriteDelayMs(lowPower)); + return result; +} + +error_code DS28E15_22_25::readPage(int page, Page::span rdbuf) const { + error_code result = writeCommandWithCrc(ReadMemory, page); + if (result) { + return result; + } + + result = continueReadPage(rdbuf); + return result; +} + +error_code DS28E15_22_25::continueReadPage(Page::span rdbuf) const { + return readDataWithCrc(*master, rdbuf); +} + +error_code DS28E15_22_25::doWriteAuthSegment(Segment::const_span newData, + Page::const_span mac, + Variant variant, bool continuing) { + // CRC gets calculated with CS byte when continuing on DS28E22 and DS28E25. + const uint_fast16_t crcStart = + ((variant == DS28E22 || variant == DS28E25) && continuing) + ? calculateCrc16(0xAA) + : 0; + error_code result = + writeDataWithCrc(*master, newData, OneWireMaster::StrongLevel, crcStart); + if (result) { + return result; + } + + sleep->invoke(shaComputationDelayMs); + result = master->setLevel(OneWireMaster::NormalLevel); + if (result) { + return result; + } + + result = writeDataWithCrc(*master, mac); + if (result) { + return result; + } + + result = readCsByte(*master); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, eepromWriteDelayMs); + return result; +} + +error_code DS28E15_22_25::doWriteAuthSegment(int pageNum, int segmentNum, + Segment::const_span newData, + Page::const_span mac, + Variant variant) { + error_code result = + writeCommandWithCrc(AuthWriteMemory, (segmentNum << 5) | pageNum); + if (result) { + return result; + } + + result = doWriteAuthSegment(newData, mac, variant, false); + return result; +} + +error_code DS28E15_22_25::doContinueWriteAuthSegment( + Segment::const_span newData, Page::const_span mac, Variant variant) { + return doWriteAuthSegment(newData, mac, variant, true); +} + +error_code DS28E15_22_25::readSegment(int page, int segment, + Segment::span data) const { + error_code result = writeCommandWithCrc(ReadMemory, (segment << 5) | page); + if (result) { + return result; + } + + result = continueReadSegment(data); + return result; +} + +error_code DS28E15_22_25::continueReadSegment(Segment::span data) const { + return master->readBlock(data); +} + +error_code DS28E15_22_25::writeSegment(int page, int block, + Segment::const_span data) { + error_code result = writeCommandWithCrc(WriteMemory, (block << 5) | page); + if (result) { + return result; + } + + result = continueWriteSegment(data); + return result; +} + +error_code DS28E15_22_25::continueWriteSegment(Segment::const_span data) { + error_code result = writeDataWithCrc(*master, data); + if (result) { + return result; + } + + result = releaseSequence(*master, *sleep, eepromWriteDelayMs); + return result; +} + +error_code +DS28E15_22_25::doReadAllBlockProtection(span<BlockProtection> protection, + Variant variant) const { + error_code result = writeCommandWithCrc(ReadStatus, 0); + if (!result) { + if (variant == DS28E22 || variant == DS28E25) { + // Need to read extra data on DS28E22 to get CRC16. + uint_least8_t buf[DS28E25::memoryPages]; + result = readDataWithCrc(*master, buf); + if (!result) { + const int blocks = ((variant == DS28E22) ? DS28E22::memoryPages + : DS28E25::memoryPages) / + ds28e22_25_pagesPerBlock; + for (span<BlockProtection>::index_type i = 0; + i < std::min<span<BlockProtection>::index_type>(protection.size(), + blocks); + ++i) { + protection[i].setStatusByte( + (buf[i * ds28e22_25_pagesPerBlock] & 0xF0) | // Upper nibble + ((buf[i * ds28e22_25_pagesPerBlock] & 0x0F) / + ds28e22_25_pagesPerBlock)); // Lower nibble + } + } + } else { // DS28E15 + uint_least8_t buf[DS28E15::protectionBlocks]; + result = readDataWithCrc(*master, buf); + if (!result) { + for (span<BlockProtection>::index_type i = 0; + i < std::min<span<BlockProtection>::index_type>( + protection.size(), DS28E15::protectionBlocks); + ++i) { + protection[i].setStatusByte(buf[i]); + } + } + } + } + return result; +} + +const error_category & DS28E15_22_25::errorCategory() { + static class : public error_category { + public: + virtual const char * name() const { return "DS28E15_22_25"; } + + virtual std::string message(int condition) const { + switch (condition) { + case CrcError: + return "CRC Error"; + + case OperationFailure: + return "Operation Failure"; + + default: + return defaultErrorMessage(condition); + } + } + } instance; + return instance; +} + +error_code DS28E15_22_25::loadSecret(bool lock) { + // Use worst-case low power timing if the device type is not known. + return doLoadSecret(lock, true); +} + +error_code DS28E15_22_25::computeSecret(int pageNum, bool lock) { + // Use worst-case low power timing if the device type is not known. + return doComputeSecret(pageNum, lock, true); +} + +error_code DS28E15_22_25::readPersonality(Personality & personality) const { + error_code result = writeCommandWithCrc(ReadStatus, 0xE0); + if (!result) { + uint_least8_t data[4]; + result = readDataWithCrc(*master, data); + if (!result) { + personality.PB1 = data[0]; + personality.PB2 = data[1]; + personality.manId[0] = data[2]; + personality.manId[1] = data[3]; + } + } + return result; +} + +const int DS28EL15::memoryPages; +const int DS28EL15::protectionBlocks; + +error_code DS28EL15::writeScratchpad(Page::const_span data) { + return doWriteScratchpad(data, DS28E15); +} + +error_code DS28EL15::readScratchpad(Page::span data) const { + return doReadScratchpad(data, DS28E15); +} + +error_code DS28EL15::readBlockProtection(int blockNum, + BlockProtection & protection) const { + return doReadBlockProtection(blockNum, protection, DS28E15); +} + +error_code DS28EL15::writeAuthSegment(int pageNum, int segmentNum, + Segment::const_span newData, + Page::const_span mac) { + return doWriteAuthSegment(pageNum, segmentNum, newData, mac, DS28E15); +} + +error_code DS28EL15::continueWriteAuthSegment(Segment::const_span newData, + Page::const_span mac) { + return doContinueWriteAuthSegment(newData, mac, DS28E15); +} + +error_code DS28EL15::readAllBlockProtection( + span<BlockProtection, protectionBlocks> protection) const { + return doReadAllBlockProtection(protection, DS28E15); +} + +error_code DS28E15::loadSecret(bool lock) { return doLoadSecret(lock, false); } + +error_code DS28E15::computeSecret(int pageNum, bool lock) { + return doComputeSecret(pageNum, lock, false); +} + +const int DS28EL22::memoryPages; +const int DS28EL22::protectionBlocks; + +error_code DS28EL22::writeScratchpad(Page::const_span data) { + return doWriteScratchpad(data, DS28E22); +} + +error_code DS28EL22::readScratchpad(Page::span data) const { + return doReadScratchpad(data, DS28E22); +} + +error_code DS28EL22::readBlockProtection(int blockNum, + BlockProtection & protection) const { + return doReadBlockProtection(blockNum, protection, DS28E22); +} + +error_code DS28EL22::writeAuthSegment(int pageNum, int segmentNum, + Segment::const_span newData, + Page::const_span mac) { + return doWriteAuthSegment(pageNum, segmentNum, newData, mac, DS28E22); +} + +error_code DS28EL22::continueWriteAuthSegment(Segment::const_span newData, + Page::const_span mac) { + return doContinueWriteAuthSegment(newData, mac, DS28E22); +} + +error_code DS28EL22::readAllBlockProtection( + span<BlockProtection, protectionBlocks> protection) const { + return doReadAllBlockProtection(protection, DS28E22); +} + +error_code DS28E22::loadSecret(bool lock) { return doLoadSecret(lock, false); } + +error_code DS28E22::computeSecret(int pageNum, bool lock) { + return doComputeSecret(pageNum, lock, false); +} + +const int DS28EL25::memoryPages; +const int DS28EL25::protectionBlocks; + +error_code DS28EL25::writeScratchpad(Page::const_span data) { + return doWriteScratchpad(data, DS28E25); +} + +error_code DS28EL25::readScratchpad(Page::span data) const { + return doReadScratchpad(data, DS28E25); +} + +error_code DS28EL25::readBlockProtection(int blockNum, + BlockProtection & protection) const { + return doReadBlockProtection(blockNum, protection, DS28E25); +} + +error_code DS28EL25::writeAuthSegment(int pageNum, int segmentNum, + Segment::const_span newData, + Page::const_span mac) { + return doWriteAuthSegment(pageNum, segmentNum, newData, mac, DS28E25); +} + +error_code DS28EL25::continueWriteAuthSegment(Segment::const_span newData, + Page::const_span mac) { + return doContinueWriteAuthSegment(newData, mac, DS28E25); +} + +error_code DS28EL25::readAllBlockProtection( + span<BlockProtection, protectionBlocks> protection) const { + return doReadAllBlockProtection(protection, DS28E25); +} + +error_code DS28E25::loadSecret(bool lock) { return doLoadSecret(lock, false); } + +error_code DS28E25::computeSecret(int pageNum, bool lock) { + return doComputeSecret(pageNum, lock, false); +} + +} // namespace MaximInterfaceDevices