Implementation of 1-Wire with added Alarm Search Functionality

Dependents:   Max32630_One_Wire_Interface

Slaves/Authenticators/DS28E15_22_25/DS28E15_22_25.cpp

Committer:
j3
Date:
2016-12-13
Revision:
139:f0e0a7976846
Parent:
120:200109b73e3c

File content as of revision 139:f0e0a7976846:

/******************************************************************//**
* Copyright (C) 2016 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 "DS28E15_22_25.h"
#include "DS28E15.h"
#include "DS28E22.h"
#include "DS28E25.h"
#include "Masters/OneWireMaster.h"
#include "Utilities/crc.h"
#include "Utilities/type_traits.h"
#include "wait_api.h"

using namespace OneWire;
using namespace OneWire::crc;

/// 1-Wire device commands.
enum Command
{
    WriteMemory = 0x55,
    ReadMemory = 0xF0,
    LoadAndLockSecret = 0x33,
    ComputeAndLockSecret = 0x3C,
    ReadWriteScratchpad = 0x0F,
    ComputePageMac = 0xA5,
    ReadStatus = 0xAA,
    WriteBlockProtection = 0xC3,
    AuthWriteMemory = 0x5A,
    AuthWriteBlockProtection = 0xCC,
};

DS28E15_22_25::Segment DS28E15_22_25::segmentFromPage(unsigned int segmentNum, const Page & page)
{
    if (segmentNum > (segmentsPerPage - 1))
    {
        segmentNum = (segmentsPerPage - 1);
    }
    Segment segment;
    Page::const_iterator copyBegin = page.begin() + (segmentNum * Segment::size());
    std::copy(copyBegin, copyBegin + Segment::size(), segment.begin());
    return segment;
}

void DS28E15_22_25::segmentToPage(unsigned int segmentNum, const Segment & segment, Page & page)
{
    if (segmentNum > (segmentsPerPage - 1))
    {
        segmentNum = (segmentsPerPage - 1);
    }
    std::copy(segment.begin(), segment.end(), page.begin() + (segmentNum * segment.size()));
}

DS28E15_22_25::BlockProtection::BlockProtection(bool readProtection, bool writeProtection, bool eepromEmulation, bool authProtection, unsigned int blockNum)
{
    setReadProtection(readProtection);
    setWriteProtection(writeProtection);
    setEepromEmulation(eepromEmulation);
    setAuthProtection(authProtection);
    setBlockNum(blockNum);
}

void DS28E15_22_25::BlockProtection::setBlockNum(unsigned int blockNum)
{
    m_status &= ~blockNumMask;
    m_status |= (blockNum & blockNumMask);
}

bool DS28E15_22_25::BlockProtection::noProtection() const
{
    return !readProtection() && !writeProtection() && !eepromEmulation() && !authProtection();
}

void DS28E15_22_25::BlockProtection::setReadProtection(bool readProtection)
{
    if (readProtection)
    {
        m_status |= readProtectionMask;
    }
    else
    {
        m_status &= ~readProtectionMask;
    }
}

void DS28E15_22_25::BlockProtection::setWriteProtection(bool writeProtection)
{
    if (writeProtection)
    {
        m_status |= writeProtectionMask;
    }
    else
    {
        m_status &= ~writeProtectionMask;
    }
}

void DS28E15_22_25::BlockProtection::setEepromEmulation(bool eepromEmulation)
{
    if (eepromEmulation)
    {
        m_status |= eepromEmulationMask;
    }
    else
    {
        m_status &= ~eepromEmulationMask;
    }
}

void DS28E15_22_25::BlockProtection::setAuthProtection(bool authProtection)
{
    if (authProtection)
    {
        m_status |= authProtectionMask;
    }
    else
    {
        m_status &= ~authProtectionMask;
    }
}

DS28E15_22_25::DS28E15_22_25(RandomAccessRomIterator & selector, bool lowVoltage)
    : OneWireSlave(selector), m_manId(), m_lowVoltage(lowVoltage)
{

}

OneWireSlave::CmdResult DS28E15_22_25::writeAuthBlockProtection(const ISha256MacCoproc & MacCoproc, const BlockProtection & newProtection, const BlockProtection & oldProtection)
{
    uint8_t buf[256], cs;
    int cnt = 0;
    Mac mac;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = AuthWriteBlockProtection;
    buf[cnt++] = newProtection.statusByte();

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // read first CRC byte
    master().OWReadByte(buf[cnt++]);

    // read the last CRC and enable
    master().OWReadBytePower(buf[cnt++]);

    // now wait for the MAC computation.
    wait_ms(shaComputationDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // check CRC16
    if (calculateCrc16(buf, cnt) != 0xB001)
    {
        return CrcError;
    }

    ISha256MacCoproc::CmdResult result;
    result = computeProtectionWriteMac(MacCoproc, newProtection, oldProtection, romId(), manId(), mac);
    if (result != ISha256MacCoproc::Success)
    {
        return OperationFailure;
    }
    cnt = 0;

    // send the MAC
    master().OWWriteBlock(mac.data(), mac.size());

    // Read CRC and CS byte
    master().OWReadBlock(&buf[cnt], 3);
    cnt += 3;

    // check CRC16
    if (calculateCrc16(buf, cnt - 1, calculateCrc16(mac.data(), mac.size())) != 0xB001)
    {
        return CrcError;
    }

    // check CS
    if (buf[cnt - 1] != 0xAA)
    {
        return OperationFailure;
    }

    // send release and strong pull-up
    // DATASHEET_CORRECTION - last bit in release is a read-zero so don't check echo of write byte
    master().OWWriteBytePower(0xAA);

    // now wait for the programming.
    wait_ms(eepromWriteDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

OneWireSlave::CmdResult DS28E15_22_25::writeBlockProtection(const BlockProtection & protection)
{
    uint8_t buf[256], cs;
    int cnt = 0;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = WriteBlockProtection;

    // compute parameter byte 
    buf[cnt++] = protection.statusByte();

    master().OWWriteBlock(&buf[0], cnt);

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check CRC16
    if (calculateCrc16(buf, cnt) != 0xB001)
    {
        return CrcError;
    }

    // sent release
    master().OWWriteBytePower(0xAA);

    // now wait for the programming.
    wait_ms(eepromWriteDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doReadBlockProtection(unsigned int blockNum, BlockProtection & protection) const
{
    uint8_t buf;
    CmdResult result;
    
    result = readStatus<T>(false, false, blockNum, &buf);
    if (result == Success)
    {
        protection.setStatusByte(buf);
    }
    return result;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doReadPersonality(Personality & personality) const
{
    Personality::Buffer buffer;
    CmdResult result = readStatus<T>(true, false, 0, buffer.data());
    if (result == Success)
    {
        personality = Personality(buffer);
    }
    return result;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::readStatus(bool personality, bool allpages, unsigned int blockNum, uint8_t * rdbuf) const
{
    const size_t crcLen = 4, ds28e22_25_pagesPerBlock = 2;

    uint8_t buf[256];
    size_t cnt = 0, offset = 0;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = ReadStatus;
    if (personality)
    {
        buf[cnt++] = 0xE0;
    }
    else if (allpages)
    {
        buf[cnt++] = 0;
    }
    else
    {
        // Convert to page number for DS28E22 and DS28E25
        buf[cnt] = blockNum;
        if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
        {
            buf[cnt] *= ds28e22_25_pagesPerBlock;
        }
        cnt++;
    }

    // send the command
    master().OWWriteBlock(&buf[0], 2);

    offset = cnt + 2;

    // Set data length
    size_t rdnum;
    if (personality)
    {
        rdnum = 4;
    }
    else if (!allpages)
    {
        rdnum = 1;
    }
    else if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
    {
        rdnum = DS28E25::memoryPages; // Need to read extra data on DS28E22 to get CRC16.
    }
    else // DS28E15
    {
        rdnum = DS28E15::protectionBlocks;
    }
    rdnum += crcLen; // Add in CRC length

    // Read the bytes 
    master().OWReadBlock(&buf[cnt], rdnum);
    cnt += rdnum;

    // check the first CRC16
    if (calculateCrc16(buf, offset) != 0xB001)
    {
        return CrcError;
    }

    if (personality || allpages)
    {
        // check the second CRC16
        if (calculateCrc16(buf + offset, cnt - offset) != 0xB001)
        {
            return CrcError;
        }
    }

    // copy the data to the read buffer
    rdnum -= crcLen;
    if (allpages && (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value))
    {
        if (is_same<T, DS28E22>::value)
        {
            rdnum -= (DS28E25::memoryPages - DS28E22::memoryPages);
        }

        for (size_t i = 0; i < (rdnum / ds28e22_25_pagesPerBlock); i++)
        {
            rdbuf[i] = (buf[offset + (i * ds28e22_25_pagesPerBlock)] & 0xF0); // Upper nibble
            rdbuf[i] |= ((buf[offset + (i * ds28e22_25_pagesPerBlock)] & 0x0F) / ds28e22_25_pagesPerBlock); // Lower nibble
        }
    }
    else
    {
        std::memcpy(rdbuf, &buf[offset], rdnum);
    }

    return Success;
}

ISha256MacCoproc::CmdResult DS28E15_22_25::computeAuthMac(const ISha256MacCoproc & MacCoproc, const Page & pageData, unsigned int pageNum, const Scratchpad & challenge, const RomId & romId, const ManId & manId, Mac & mac)
{
    ISha256MacCoproc::AuthMacData authMacData;

    // insert ROM number or FF
    std::memcpy(authMacData.data(), romId.buffer.data(), romId.buffer.size());

    authMacData[10] = pageNum;

    authMacData[9] = manId[0];
    authMacData[8] = manId[1];

    authMacData[11] = 0x00;

    return MacCoproc.computeAuthMac(ISha256MacCoproc::DevicePage(pageData), challenge, authMacData, mac);
}

ISha256MacCoproc::CmdResult DS28E15_22_25::computeAuthMacAnon(const ISha256MacCoproc & MacCoproc, const Page & pageData, unsigned int pageNum, const Scratchpad & challenge, const ManId & manId, Mac & mac)
{
    RomId romId;
    romId.buffer.fill(0xFF);
    return computeAuthMac(MacCoproc, pageData, pageNum, challenge, romId, manId, mac);
}

OneWireSlave::CmdResult DS28E15_22_25::computeReadPageMac(unsigned int page_num, bool anon, Mac & mac) const
{
    uint8_t buf[256], cs;
    int cnt = 0;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = ComputePageMac;
    buf[cnt++] = ((anon) ? 0xE0 : 0x00) | page_num;

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // read first CRC byte
    master().OWReadByte(buf[cnt++]);

    // read the last CRC and enable
    master().OWReadBytePower(buf[cnt++]);

    // now wait for the MAC computation.
    wait_ms(shaComputationDelayMs * 2);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // check CRC16
    if (calculateCrc16(buf, cnt) != 0xB001)
    {
        return CrcError;
    }

    // read the CS byte
    master().OWReadByte(cs);
    if (cs != 0xAA)
    {
        return OperationFailure;
    }

    // read the MAC and CRC
    master().OWReadBlock(&buf[0], (Mac::size() + 2));

    // check CRC16
    if (calculateCrc16(buf, Mac::size() + 2) != 0xB001)
    {
        return CrcError;
    }

    // copy MAC to return buffer
    std::memcpy(mac.data(), buf, mac.size());

    return Success;
}

OneWireSlave::CmdResult DS28E15_22_25::computeSecret(unsigned int page_num, bool lock)
{
    uint8_t buf[256], cs;
    int cnt = 0;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = ComputeAndLockSecret;
    buf[cnt++] = (lock) ? (0xE0 | page_num) : page_num;  // lock flag 

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check CRC16
    if (calculateCrc16(buf, cnt) != 0xB001)
    {
        return CrcError;
    }

    // send release and strong pull-up
    master().OWWriteBytePower(0xAA);

    // now wait for the MAC computations and secret programming.
    wait_ms(shaComputationDelayMs * 2 + secretEepromWriteDelayMs());

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doWriteScratchpad(const Scratchpad & data) const
{
    uint8_t buf[256];
    int cnt = 0, offset;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = ReadWriteScratchpad;
    if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
    {
        buf[cnt++] = 0x20;
    }
    else
    {
        buf[cnt++] = 0x00;
    }

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    offset = cnt;

    // add the data
    std::memcpy(&buf[cnt], data.data(), data.size());
    cnt += data.size();

    // Send the data
    master().OWWriteBlock(data.data(), data.size());

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check first CRC16
    if (calculateCrc16(buf, offset) != 0xB001)
    {
        return CrcError;
    }

    // check the second CRC16
    if (calculateCrc16(buf + offset, cnt - offset) != 0xB001)
    {
        return CrcError;
    }

    return Success;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doReadScratchpad(Scratchpad & data) const
{
    uint8_t buf[256];
    int cnt = 0, offset;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = ReadWriteScratchpad;
    if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
    {
        buf[cnt++] = 0x2F;
    }
    else
    {
        buf[cnt++] = 0x0F;
    }

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    offset = cnt;

    // Receive the data
    master().OWReadBlock(&buf[cnt], data.size());
    cnt += data.size();

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check first CRC16
    if (calculateCrc16(buf, offset) != 0xB001)
    {
        return CrcError;
    }

    // check the second CRC16
    if (calculateCrc16(buf + offset, cnt - offset) != 0xB001)
    {
        return CrcError;
    }

    // Copy to output
    std::memcpy(data.data(), &buf[offset], data.size());

    return Success;
}

OneWireSlave::CmdResult DS28E15_22_25::loadSecret(bool lock)
{
    uint8_t buf[256], cs;
    int cnt = 0;
    
    if (selectDevice() != OneWireMaster::Success)
    {
        return CommunicationError;
    }

    buf[cnt++] = LoadAndLockSecret;
    buf[cnt++] = (lock) ? 0xE0 : 0x00;  // lock flag 

    // Send command
    master().OWWriteBlock(&buf[0], 2);

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check CRC16
    if (calculateCrc16(buf, cnt) != 0xB001)
    {
        return CrcError;
    }

    // send release and strong pull-up
    master().OWWriteBytePower(0xAA);

    // now wait for the secret programming.
    wait_ms(secretEepromWriteDelayMs());

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

OneWireSlave::CmdResult DS28E15_22_25::readPage(unsigned int page, Page & rdbuf, bool continuing) const
{
    uint8_t buf[256];
    int cnt = 0;
    int offset = 0;

    // check if not continuing a previous block write
    if (!continuing)
    {
        if (selectDevice() != OneWireMaster::Success)
        {
            return CommunicationError;
        }
        
        buf[cnt++] = ReadMemory;
        buf[cnt++] = page;   // address 

        // Send command
        master().OWWriteBlock(&buf[0], 2);

        // Read CRC
        master().OWReadBlock(&buf[cnt], 2);
        cnt += 2;

        offset = cnt;
    }

    // read data and CRC16
    master().OWReadBlock(&buf[cnt], (rdbuf.size() + 2));
    cnt += 34;

    // check the first CRC16
    if (!continuing)
    {
        if (calculateCrc16(buf, offset) != 0xB001)
        {
            return CrcError;
        }
    }

    // check the second CRC16
    if (calculateCrc16(buf + offset, cnt - offset) != 0xB001)
    {
        return CrcError;
    }

    // copy the data to the read buffer
    std::memcpy(rdbuf.data(), &buf[offset], rdbuf.size());

    return Success;
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doWriteAuthSegmentMac(unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Mac & mac, bool continuing)
{
    uint8_t buf[256], cs;
    int cnt, offset;

    cnt = 0;
    offset = 0;

    // check if not continuing a previous block write
    if (!continuing)
    {
        if (selectDevice() != OneWireMaster::Success)
        {
            return CommunicationError;
        }
        
        buf[cnt++] = AuthWriteMemory;
        buf[cnt++] = (segmentNum << 5) | pageNum;   // address 

        // Send command
        master().OWWriteBlock(&buf[0], 2);

        // Read CRC
        master().OWReadBlock(&buf[cnt], 2);
        cnt += 2;

        offset = cnt;
    }

    // add the data
    for (size_t i = 0; i < newData.size(); i++)
    {
        buf[cnt++] = newData[i];
    }

    // Send data
    master().OWWriteBlock(newData.data(), newData.size());

    // read first CRC byte
    master().OWReadByte(buf[cnt++]);

    // read the last CRC and enable power
    master().OWReadBytePower(buf[cnt++]);

    // now wait for the MAC computation.
    wait_ms(shaComputationDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // check the first CRC16
    if (!continuing)
    {
        if (calculateCrc16(buf, offset) != 0xB001)
        {
            return CrcError;
        }
    }

    // check the second CRC16
    uint16_t CRC16 = 0;

    // DS28E25/DS28E22, crc gets calculagted with CS byte 
    if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
    {
        if (continuing)
        {
            CRC16 = calculateCrc16(CRC16, 0xAA);
        }
    }

    CRC16 = calculateCrc16(buf + offset, cnt - offset, CRC16);

    if (CRC16 != 0xB001)
    {
        return CrcError;
    }

    // transmit MAC as a block
    master().OWWriteBlock(mac.data(), mac.size());

    // calculate CRC on MAC
    CRC16 = calculateCrc16(mac.data(), mac.size());

    // append read of CRC16 and CS byte
    master().OWReadBlock(&buf[0], 3);
    cnt = 3;

    // ckeck CRC16
    CRC16 = calculateCrc16(buf, cnt - 1, CRC16);

    if (CRC16 != 0xB001)
    {
        return CrcError;
    }

    // check CS
    if (buf[cnt - 1] != 0xAA)
    {
        return OperationFailure;
    }

    // send release and strong pull-up
    master().OWWriteBytePower(0xAA);

    // now wait for the programming.
    wait_ms(eepromWriteDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

ISha256MacCoproc::CmdResult DS28E15_22_25::computeSegmentWriteMac(const ISha256MacCoproc & MacCoproc, unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Segment & oldData, const RomId & romId, const ManId & manId, Mac & mac)
{
    ISha256MacCoproc::WriteMacData MT;

    // insert ROM number
    std::memcpy(&MT[0], romId.buffer.data(), romId.buffer.size());

    MT[11] = segmentNum;
    MT[10] = pageNum;
    MT[9] = manId[0];
    MT[8] = manId[1];

    // insert old data
    std::memcpy(&MT[12], oldData.data(), oldData.size());

    // insert new data
    std::memcpy(&MT[16], newData.data(), newData.size());

    return MacCoproc.computeWriteMac(MT, mac);
}

ISha256MacCoproc::CmdResult DS28E15_22_25::computeProtectionWriteMac(const ISha256MacCoproc & MacCoproc, const BlockProtection & newProtection, const BlockProtection & oldProtection, const RomId & romId, const ManId & manId, Mac & mac)
{
    ISha256MacCoproc::WriteMacData MT;

    // insert ROM number
    std::memcpy(MT.data(), romId.buffer.data(), romId.buffer.size());

    // instert block and page
    MT[11] = 0;
    MT[10] = newProtection.blockNum();

    MT[9] = manId[0];
    MT[8] = manId[1];

    // old data
    MT[12] = oldProtection.authProtection() ? 0x01 : 0x00;
    MT[13] = oldProtection.eepromEmulation() ? 0x01 : 0x00;
    MT[14] = oldProtection.writeProtection() ? 0x01 : 0x00;
    MT[15] = oldProtection.readProtection() ? 0x01 : 0x00;
    // new data
    MT[16] = newProtection.authProtection() ? 0x01 : 0x00;
    MT[17] = newProtection.eepromEmulation() ? 0x01 : 0x00;
    MT[18] = newProtection.writeProtection() ? 0x01 : 0x00;
    MT[19] = newProtection.readProtection() ? 0x01 : 0x00;

    // compute the mac
    return MacCoproc.computeWriteMac(MT, mac);
}

template <class T>
OneWireSlave::CmdResult DS28E15_22_25::doWriteAuthSegment(const ISha256MacCoproc & MacCoproc, unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Segment & oldData, bool continuing)
{
    uint8_t buf[256], cs;
    int cnt, offset;

    cnt = 0;
    offset = 0;

    // check if not continuing a previous block write
    if (!continuing)
    {
        if (selectDevice() != OneWireMaster::Success)
        {
            return CommunicationError;
        }
        
        buf[cnt++] = AuthWriteMemory;
        buf[cnt++] = (segmentNum << 5) | pageNum;   // address 

        // Send command
        master().OWWriteBlock(&buf[0], 2);

        // Read CRC
        master().OWReadBlock(&buf[cnt], 2);
        cnt += 2;

        offset = cnt;
    }

    // add the data
    for (size_t i = 0; i < newData.size(); i++)
    {
        buf[cnt++] = newData[i];
    }

    // Send data
    master().OWWriteBlock(newData.data(), newData.size());

    // read first CRC byte
    master().OWReadByte(buf[cnt++]);

    // read the last CRC and enable power
    master().OWReadBytePower(buf[cnt++]);

    // now wait for the MAC computation.
    wait_ms(shaComputationDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // check the first CRC16
    if (!continuing)
    {
        if (calculateCrc16(buf, offset) != 0xB001)
        {
            return CrcError;
        }
    }

    // check the second CRC16
    uint16_t CRC16 = 0;

    // DS28E25/DS28E22, crc gets calculated with CS byte
    if (is_same<T, DS28E22>::value || is_same<T, DS28E25>::value)
    {
        if (continuing)
            CRC16 = calculateCrc16(CRC16, 0xAA);
    }

    CRC16 = calculateCrc16(buf + offset, cnt - offset, CRC16);

    if (CRC16 != 0xB001)
    {
        return CrcError;
    }

    // compute the mac
    ISha256MacCoproc::CmdResult result;
    Mac mac;
    result = computeSegmentWriteMac(MacCoproc, pageNum, segmentNum, newData, oldData, romId(), manId(), mac);
    if (result != ISha256MacCoproc::Success)
    {
        return OperationFailure;
    }

    // transmit MAC as a block
    master().OWWriteBlock(mac.data(), mac.size());

    // calculate CRC on MAC
    CRC16 = calculateCrc16(mac.data(), mac.size());

    // append read of CRC16 and CS byte
    master().OWReadBlock(&buf[0], 3);
    cnt = 3;

    // ckeck CRC16
    CRC16 = calculateCrc16(buf, cnt - 1, CRC16);

    if (CRC16 != 0xB001)
    {
        return CrcError;
    }

    // check CS
    if (buf[cnt - 1] != 0xAA)
    {
        return OperationFailure;
    }

    // send release and strong pull-up
    master().OWWriteBytePower(0xAA);

    // now wait for the programming.
    wait_ms(eepromWriteDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

OneWireSlave::CmdResult DS28E15_22_25::readSegment(unsigned int page, unsigned int segment, Segment & data, bool continuing) const
{
    OneWireMaster::CmdResult result = OneWireMaster::OperationFailure;
    uint8_t buf[2];

    if (!continuing)
    {
        if (selectDevice() != OneWireMaster::Success)
        {
            return CommunicationError;
        }
        
        buf[0] = ReadMemory;
        buf[1] = (segment << 5) | page;

        // Transmit command
        master().OWWriteBlock(buf, 2);

        // Receive CRC
        result = master().OWReadBlock(buf, 2);
    }
    else if (segment == 0)
    {
        // Receive CRC from previous read
        result = master().OWReadBlock(buf, 2);
    }

    // Receive data
    if (result == OneWireMaster::Success)
    {
        result = master().OWReadBlock(data.data(), data.size());
    }

    return (result == OneWireMaster::Success ? OneWireSlave::Success : OneWireSlave::CommunicationError);
}

OneWireSlave::CmdResult DS28E15_22_25::writeSegment(unsigned int page, unsigned int block, const Segment & data, bool continuing)
{
    uint8_t buf[256], cs;
    int cnt = 0;
    int offset = 0;

    cnt = 0;
    offset = 0;

    // check if not continuing a previous block write
    if (!continuing)
    {
        if (selectDevice() != OneWireMaster::Success)
        {
            return CommunicationError;
        }
        
        buf[cnt++] = WriteMemory;
        buf[cnt++] = (block << 5) | page;   // address 

        // Send command 
        master().OWWriteBlock(&buf[0], 2);

        // Read CRC
        master().OWReadBlock(&buf[cnt], 2);
        cnt += 2;

        offset = cnt;
    }

    // add the data
    for (size_t i = 0; i < data.size(); i++)
    {
        buf[cnt++] = data[i];
    }

    // Send data
    master().OWWriteBlock(data.data(), data.size());

    // Read CRC
    master().OWReadBlock(&buf[cnt], 2);
    cnt += 2;

    // check the first CRC16
    if (!continuing)
    {
        if (calculateCrc16(buf, offset) != 0xB001)
        {
            return CrcError;
        }
    }

    // check the second CRC16
    if (calculateCrc16(buf + offset, cnt - offset) != 0xB001)
    {
        return CrcError;
    }

    // send release and strong pull-up
    master().OWWriteBytePower(0xAA);

    // now wait for the programming.
    wait_ms(eepromWriteDelayMs);

    // disable strong pullup
    master().OWSetLevel(OneWireMaster::NormalLevel);

    // read the CS byte
    master().OWReadByte(cs);

    if (cs == 0xAA)
    {
        return Success;
    }
    // else
    return OperationFailure;
}

ISha256MacCoproc::CmdResult DS28E15_22_25::computeNextSecret(ISha256MacCoproc & MacCoproc, const Page & bindingPage, unsigned int bindingPageNum, const Scratchpad & partialSecret, const RomId & romId, const ManId & manId)
{
    ISha256MacCoproc::SlaveSecretData slaveSecretData;

    // insert ROM number
    std::memcpy(slaveSecretData.data(), romId.buffer.data(), romId.buffer.size());

    slaveSecretData[11] = 0x00;
    slaveSecretData[10] = bindingPageNum;
    slaveSecretData[9] = manId[0];
    slaveSecretData[8] = manId[1];

    return MacCoproc.computeSlaveSecret(ISha256MacCoproc::DevicePage(bindingPage), partialSecret, slaveSecretData);
}

template <class T, size_t N>
OneWireSlave::CmdResult DS28E15_22_25::doReadAllBlockProtection(array<BlockProtection, N> & protection) const
{
    uint8_t buf[N];
    CmdResult result = readStatus<T>(false, true, 0, buf);
    if (result == Success)
    {
        for (size_t i = 0; i < N; i++)
        {
            protection[i].setStatusByte(buf[i]);
        }
    }
    return result;
}

OneWireSlave::CmdResult DS28E15::writeScratchpad(const Scratchpad & data) const
{
    return doWriteScratchpad<DS28E15>(data);
}

OneWireSlave::CmdResult DS28E15::readScratchpad(Scratchpad & data) const
{
    return doReadScratchpad<DS28E15>(data);
}

OneWireSlave::CmdResult DS28E15::readBlockProtection(unsigned int blockNum, BlockProtection & protection) const
{
    return doReadBlockProtection<DS28E15>(blockNum, protection);
}

OneWireSlave::CmdResult DS28E15::readPersonality(Personality & personality) const
{
    return doReadPersonality<DS28E15>(personality);
}

OneWireSlave::CmdResult DS28E15::writeAuthSegment(const ISha256MacCoproc & MacCoproc, unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Segment & oldData, bool continuing)
{
    return doWriteAuthSegment<DS28E15>(MacCoproc, pageNum, segmentNum, newData, oldData, continuing);
}

OneWireSlave::CmdResult DS28E15::writeAuthSegmentMac(unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Mac & mac, bool continuing)
{
    return doWriteAuthSegmentMac<DS28E15>(pageNum, segmentNum, newData, mac, continuing);
}

OneWireSlave::CmdResult DS28E15::readAllBlockProtection(array<BlockProtection, protectionBlocks> & protection) const
{
    return doReadAllBlockProtection<DS28E15>(protection);
}

OneWireSlave::CmdResult DS28E22::writeScratchpad(const Scratchpad & data) const
{
    return doWriteScratchpad<DS28E22>(data);
}

OneWireSlave::CmdResult DS28E22::readScratchpad(Scratchpad & data) const
{
    return doReadScratchpad<DS28E22>(data);
}

OneWireSlave::CmdResult DS28E22::readBlockProtection(unsigned int blockNum, BlockProtection & protection) const
{
    return doReadBlockProtection<DS28E22>(blockNum, protection);
}

OneWireSlave::CmdResult DS28E22::readPersonality(Personality & personality) const
{
    return doReadPersonality<DS28E22>(personality);
}

OneWireSlave::CmdResult DS28E22::writeAuthSegment(const ISha256MacCoproc & MacCoproc, unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Segment & oldData, bool continuing)
{
    return doWriteAuthSegment<DS28E22>(MacCoproc, pageNum, segmentNum, newData, oldData, continuing);
}

OneWireSlave::CmdResult DS28E22::writeAuthSegmentMac(unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Mac & mac, bool continuing)
{
    return doWriteAuthSegmentMac<DS28E22>(pageNum, segmentNum, newData, mac, continuing);
}

OneWireSlave::CmdResult DS28E22::readAllBlockProtection(array<BlockProtection, protectionBlocks> & protection) const
{
    return doReadAllBlockProtection<DS28E22>(protection);
}

OneWireSlave::CmdResult DS28E25::writeScratchpad(const Scratchpad & data) const
{
    return doWriteScratchpad<DS28E25>(data);
}

OneWireSlave::CmdResult DS28E25::readScratchpad(Scratchpad & data) const
{
    return doReadScratchpad<DS28E25>(data);
}

OneWireSlave::CmdResult DS28E25::readBlockProtection(unsigned int blockNum, BlockProtection & protection) const
{
    return doReadBlockProtection<DS28E25>(blockNum, protection);
}

OneWireSlave::CmdResult DS28E25::readPersonality(Personality & personality) const
{
    return doReadPersonality<DS28E25>(personality);
}

OneWireSlave::CmdResult DS28E25::writeAuthSegment(const ISha256MacCoproc & MacCoproc, unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Segment & oldData, bool continuing)
{
    return doWriteAuthSegment<DS28E25>(MacCoproc, pageNum, segmentNum, newData, oldData, continuing);
}

OneWireSlave::CmdResult DS28E25::writeAuthSegmentMac(unsigned int pageNum, unsigned int segmentNum, const Segment & newData, const Mac & mac, bool continuing)
{
    return doWriteAuthSegmentMac<DS28E25>(pageNum, segmentNum, newData, mac, continuing);
}

OneWireSlave::CmdResult DS28E25::readAllBlockProtection(array<BlockProtection, protectionBlocks> & protection) const
{
    return doReadAllBlockProtection<DS28E25>(protection);
}