Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: DeepCover Embedded Security in IoT MaximInterface MAXREFDES155#
Devices/DS28C36_DS2476.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2017-11-06
- Revision:
- 0:f77ad7f72d04
- Child:
- 3:f818ea5172ed
File content as of revision 0:f77ad7f72d04:
/*******************************************************************************
* 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 <MaximInterface/Links/I2CMaster.hpp>
#include <MaximInterface/Utilities/Error.hpp>
#include <MaximInterface/Utilities/RomId.hpp>
#include "DS28C36_DS2476.hpp"
using std::copy;
using std::vector;
namespace MaximInterface {
using namespace Ecc256;
// DS28C36 commands.
enum Command {
WriteMemory = 0x96,
ReadMemory = 0x69,
WriteBuffer = 0x87,
ReadBuffer = 0x5A,
ReadPageProtection = 0xAA,
SetPageProtection = 0xC3,
DecrementCounter = 0xC9,
ReadRng = 0xD2,
EncryptedReadMemory = 0x4B,
ComputeAndReadPathAuthentication = 0xA5,
AuthenticatedSha2WriteMemory = 0x99,
ComputeAndLockSha2Secret = 0x3C,
GenerateEcc256KeyPair = 0xCB,
ComputeMultiblockHash = 0x33,
VerifyEcdsaSignature = 0x59,
AuthenticateEcdsaPublicKey = 0xA8,
AuthenticatedEcdsaWriteMemory = 0x89
};
static error_code convertResultByte(uint_least8_t resultByte) {
error_code errorCode;
if (resultByte != 0xAA) {
errorCode.assign((resultByte == 0x00)
? static_cast<int>(DS28C36::AuthenticationError)
: resultByte,
DS28C36::errorCategory());
}
return errorCode;
}
error_code DS28C36::writeMemory(int pageNum, const Page & page) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + page.size());
buffer.push_back(pageNum);
buffer.insert(buffer.end(), page.begin(), page.end());
error_code result = writeCommand(WriteMemory, &buffer[0], buffer.size());
if (!result) {
sleep(writeMemoryTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::readMemory(int pageNum, Page & page) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back(pageNum);
error_code result = writeCommand(ReadMemory, &buffer[0], buffer.size());
if (!result) {
sleep(readMemoryTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == (1 + page.size())) {
result = convertResultByte(buffer[0]);
copy(buffer.begin() + 1, buffer.end(), page.begin());
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::writeBuffer(const uint_least8_t * data, size_t dataSize) {
return writeCommand(WriteBuffer, data, dataSize);
}
error_code DS28C36::readBuffer(vector<uint_least8_t> & data) {
error_code result = writeCommand(ReadBuffer, NULL, 0);
if (!result) {
result = readResponse(data);
}
return result;
}
error_code DS28C36::readPageProtection(int pageNum,
PageProtection & protection) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back(pageNum);
error_code result =
writeCommand(ReadPageProtection, &buffer[0], buffer.size());
if (!result) {
sleep(readMemoryTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
protection = buffer[0];
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::setPageProtection(int pageNum,
const PageProtection & protection) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back(pageNum);
buffer.push_back(static_cast<uint_least8_t>(protection.to_ulong()));
error_code result =
writeCommand(SetPageProtection, &buffer[0], buffer.size());
if (!result) {
sleep(writeMemoryTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::decrementCounter() {
error_code result = writeCommand(DecrementCounter, NULL, 0);
if (!result) {
sleep(writeMemoryTimeMs);
vector<uint_least8_t> buffer;
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::readRng(int numBytes, vector<uint_least8_t> & data) {
if ((numBytes < 1) || (numBytes > 64)) {
return make_error_code(InvalidParameterError);
}
data.clear();
data.push_back(numBytes - 1);
error_code result = writeCommand(ReadRng, &data[0], data.size());
if (!result) {
sleep(sha256ComputationTimeMs);
result = readResponse(data);
if (!result) {
if (data.size() !=
static_cast<vector<uint_least8_t>::size_type>(numBytes)) {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::encryptedReadMemory(int pageNum, SecretNum secretNum,
EncryptedPage & page) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back((secretNum << 6) | pageNum);
error_code result =
writeCommand(EncryptedReadMemory, &buffer[0], buffer.size());
if (!result) {
sleep(readMemoryTimeMs + sha256ComputationTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == (1 + page.challenge.size() + page.data.size())) {
result = convertResultByte(buffer[0]);
vector<uint_least8_t>::const_iterator begin = buffer.begin() + 1;
vector<uint_least8_t>::const_iterator end =
begin + page.challenge.size();
copy(begin, end, page.challenge.begin());
begin = end;
end = begin + page.data.size();
copy(begin, end, page.data.begin());
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code
DS28C36::computeAndReadPageAuthentication(int pageNum, AuthType authType,
vector<uint_least8_t> & data) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
data.clear();
data.push_back((authType << 5) | pageNum);
error_code result =
writeCommand(ComputeAndReadPathAuthentication, &data[0], data.size());
if (!result) {
sleep(readMemoryTimeMs + ((authType < EcdsaWithKeyA)
? sha256ComputationTimeMs
: generateEcdsaSignatureTimeMs));
result = readResponse(data);
if (!result) {
if (data.size() > 1) {
result = convertResultByte(data[0]);
data.erase(data.begin());
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code
DS28C36::computeAndReadEcdsaPageAuthentication(int pageNum, KeyNum keyNum,
Signature & signature) {
AuthType authType;
switch (keyNum) {
case KeyNumA:
authType = EcdsaWithKeyA;
break;
case KeyNumB:
authType = EcdsaWithKeyB;
break;
case KeyNumC:
authType = EcdsaWithKeyC;
break;
default:
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> data;
error_code result = computeAndReadPageAuthentication(pageNum, authType, data);
if (!result) {
vector<uint_least8_t>::const_iterator begin = data.begin();
vector<uint_least8_t>::const_iterator end = begin + signature.s.size();
copy(begin, end, signature.s.begin());
begin = end;
end = begin + signature.r.size();
copy(begin, end, signature.r.begin());
}
return result;
}
error_code DS28C36::computeAndReadHmacPageAuthentication(int pageNum,
SecretNum secretNum,
Sha256::Hash & hmac) {
AuthType authType;
switch (secretNum) {
case SecretNumA:
authType = HmacWithSecretA;
break;
case SecretNumB:
authType = HmacWithSecretB;
break;
case SecretNumS:
authType = HmacWithSecretS;
break;
default:
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> data;
error_code result = computeAndReadPageAuthentication(pageNum, authType, data);
if (!result) {
copy(data.begin(), data.end(), hmac.begin());
}
return result;
}
error_code DS28C36::authenticatedSha2WriteMemory(int pageNum,
SecretNum secretNum,
const Page & page) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + page.size());
buffer.push_back((secretNum << 6) | pageNum);
buffer.insert(buffer.end(), page.begin(), page.end());
error_code result =
writeCommand(AuthenticatedSha2WriteMemory, &buffer[0], buffer.size());
if (!result) {
sleep(writeMemoryTimeMs + (2 * sha256ComputationTimeMs));
result = readResponse(buffer);
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
return result;
}
error_code DS28C36::computeAndLockSha2Secret(int pageNum, SecretNum msecretNum,
SecretNum dsecretNum,
bool writeProtectEnable) {
// User pages only
if (pageNum < UserData0 || pageNum > UserData15) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back((dsecretNum << 6) | (msecretNum << 4) | pageNum);
buffer.push_back(writeProtectEnable ? 0x80 : 0x00);
error_code result =
writeCommand(ComputeAndLockSha2Secret, &buffer[0], buffer.size());
if (!result) {
sleep(sha256ComputationTimeMs +
((writeProtectEnable ? 2 : 1) * writeMemoryTimeMs));
result = readResponse(buffer);
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
return result;
}
error_code DS28C36::generateEcc256KeyPair(KeyNum keyNum,
bool writeProtectEnable) {
if (keyNum == KeyNumS) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
uint_least8_t parameter = keyNum;
if (writeProtectEnable) {
parameter |= 0x80;
}
buffer.push_back(parameter);
error_code result =
writeCommand(GenerateEcc256KeyPair, &buffer[0], buffer.size());
if (!result) {
sleep(generateEccKeyPairTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::computeMultiblockHash(bool firstBlock, bool lastBlock,
const uint_least8_t * data,
size_t dataSize) {
uint_least8_t parameter = 0;
if (firstBlock) {
parameter |= 0x40;
}
if (lastBlock) {
parameter |= 0x80;
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + dataSize);
buffer.push_back(parameter);
buffer.insert(buffer.end(), data, data + dataSize);
error_code result =
writeCommand(ComputeMultiblockHash, &buffer[0], buffer.size());
if (!result) {
sleep(sha256ComputationTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::verifyEcdsaSignature(KeyNum keyNum, HashType hashType,
const Signature & signature,
PioState pioa, PioState piob) {
uint_least8_t parameter = keyNum | (hashType << 2);
if (pioa != Unchanged) {
parameter |= 0x20;
}
if (pioa == Conducting) {
parameter |= 0x10;
}
if (piob != Unchanged) {
parameter |= 0x80;
}
if (piob == Conducting) {
parameter |= 0x40;
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + signature.r.size() + signature.s.size());
buffer.push_back(parameter);
buffer.insert(buffer.end(), signature.r.begin(), signature.r.end());
buffer.insert(buffer.end(), signature.s.begin(), signature.s.end());
error_code result =
writeCommand(VerifyEcdsaSignature, &buffer[0], buffer.size());
if (!result) {
sleep(verifyEsdsaSignatureOrComputeEcdhTimeMs +
((hashType == DataInBuffer) ? sha256ComputationTimeMs : 0));
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::authenticateEcdsaPublicKey(bool authWrites, bool ecdh,
KeyNum keyNum, int csOffset,
const Signature & signature) {
if (((keyNum != KeyNumA) && (keyNum != KeyNumB)) || (csOffset < 0) ||
(csOffset > 31)) {
return make_error_code(InvalidParameterError);
}
uint_least8_t parameter = (csOffset << 3) | (keyNum << 2);
if (ecdh) {
parameter |= 0x02;
}
if (authWrites) {
parameter |= 0x01;
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + signature.r.size() + signature.s.size());
buffer.push_back(parameter);
buffer.insert(buffer.end(), signature.r.begin(), signature.r.end());
buffer.insert(buffer.end(), signature.s.begin(), signature.s.end());
error_code result =
writeCommand(AuthenticateEcdsaPublicKey, &buffer[0], buffer.size());
if (!result) {
sleep((ecdh ? 2 : 1) * verifyEsdsaSignatureOrComputeEcdhTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::authenticatedEcdsaWriteMemory(int pageNum,
const Page & page) {
if (pageNum < 0 || pageNum >= memoryPages) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.reserve(1 + page.size());
buffer.push_back(pageNum);
buffer.insert(buffer.end(), page.begin(), page.end());
error_code result =
writeCommand(AuthenticatedEcdsaWriteMemory, &buffer[0], buffer.size());
if (!result) {
sleep(verifyEsdsaSignatureOrComputeEcdhTimeMs + writeMemoryTimeMs +
sha256ComputationTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
result = convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS28C36::writeCommand(uint_least8_t command,
const uint_least8_t * parameters,
size_t parametersSize) {
error_code result = master->start(address_);
if (result) {
master->stop();
return result;
}
result = master->writeByte(command);
if (result) {
master->stop();
return result;
}
if (parameters) {
result = master->writeByte(static_cast<uint_least8_t>(parametersSize));
if (result) {
master->stop();
return result;
}
result = master->writeBlock(parameters, parametersSize);
if (result) {
master->stop();
return result;
}
}
result = master->stop();
return result;
}
error_code DS28C36::readResponse(vector<uint_least8_t> & response) {
error_code result = master->start(address_ | 1);
if (result) {
master->stop();
return result;
}
uint_least8_t length;
result = master->readByte(I2CMaster::Ack, length);
if (result) {
master->stop();
return result;
}
response.resize(length);
result = master->readBlock(I2CMaster::Nack, &response[0], response.size());
if (result) {
master->stop();
return result;
}
result = master->stop();
return result;
}
DS28C36::PageAuthenticationData
DS28C36::createPageAuthenticationData(const RomId & romId, const Page & page,
const Page & challenge, int pageNum,
const ManId & manId) {
PageAuthenticationData data;
PageAuthenticationData::iterator dataIt = data.begin();
dataIt = copy(romId.begin(), romId.end(), dataIt);
dataIt = copy(page.begin(), page.end(), dataIt);
dataIt = copy(challenge.begin(), challenge.end(), dataIt);
*dataIt = static_cast<uint_least8_t>(pageNum);
++dataIt;
copy(manId.begin(), manId.end(), dataIt);
return data;
}
const error_category & DS28C36::errorCategory() {
static class : public error_category {
public:
virtual const char * name() const { return "DS28C36"; }
virtual std::string message(int condition) const {
switch (condition) {
case ProtectionError:
return "Protection Error";
case InvalidParameterError:
return "Invalid Parameter Error";
case InvalidSequenceError:
return "Invalid Sequence Error";
case InvalidEcdsaInputOrResultError:
return "Invalid ECDSA Input or Result Error";
case AuthenticationError:
return "Authentication Error";
case InvalidResponseError:
return "Invalid Response Error";
default:
return defaultErrorMessage(condition);
}
}
} instance;
return instance;
}
error_code readRomIdAndManId(DS28C36 & ds28c36, RomId * romId, ManId * manId) {
DS28C36::Page page;
const error_code result = ds28c36.readMemory(DS28C36::RomOptions, page);
if (!result) {
DS28C36::Page::const_iterator pageIt = page.begin() + 22;
if (manId) {
copy(pageIt, pageIt + ManId::size(), manId->begin());
}
pageIt += ManId::size();
if (romId) {
copy(pageIt, pageIt + RomId::size(), romId->begin());
}
}
return result;
}
error_code readRng(DS28C36 & ds28c36, uint_least8_t * data, size_t dataSize) {
error_code result;
vector<uint_least8_t> buffer;
while (dataSize > 0) {
result = ds28c36.readRng(static_cast<int>(std::min<size_t>(dataSize, 64)),
buffer);
if (result) {
break;
}
copy(buffer.begin(), buffer.end(), data);
data += buffer.size();
dataSize -= buffer.size();
}
return result;
}
error_code computeMultiblockHash(DS28C36 & ds28c36, const uint_least8_t * data,
const size_t dataSize) {
error_code result;
size_t remainingSize = dataSize;
while ((remainingSize > 0) && !result) {
const size_t chunkSize = std::min<size_t>(remainingSize, 64);
result = ds28c36.computeMultiblockHash(
remainingSize == dataSize, remainingSize == chunkSize, data, chunkSize);
data += chunkSize;
remainingSize -= chunkSize;
}
return result;
}
error_code verifyEcdsaSignature(DS28C36 & ds28c36, DS28C36::KeyNum publicKey,
const uint_least8_t * data, size_t dataSize,
const Signature & signature,
DS28C36::PioState pioa,
DS28C36::PioState piob) {
error_code result = computeMultiblockHash(ds28c36, data, dataSize);
if (!result) {
result = ds28c36.verifyEcdsaSignature(publicKey, DS28C36::THASH, signature,
pioa, piob);
}
return result;
}
error_code verifyEcdsaSignature(DS28C36 & ds28c36, const PublicKey & publicKey,
const uint_least8_t * data, size_t dataSize,
const Signature & signature,
DS28C36::PioState pioa,
DS28C36::PioState piob) {
error_code result = ds28c36.writeMemory(DS28C36::PublicKeySX, publicKey.x);
if (!result) {
result = ds28c36.writeMemory(DS28C36::PublicKeySY, publicKey.y);
}
if (!result) {
result = verifyEcdsaSignature(ds28c36, DS28C36::KeyNumS, data, dataSize,
signature, pioa, piob);
}
return result;
}
// DS2476 additional commands.
enum DS2476_Command {
GenerateEcdsaSignature = 0x1E,
ComputeSha2UniqueSecret = 0x55,
ComputeSha2Hmac = 0x2D
};
error_code DS2476::generateEcdsaSignature(KeyNum keyNum,
Ecc256::Signature & signature) {
if (keyNum == KeyNumS) {
return make_error_code(InvalidParameterError);
}
vector<uint_least8_t> buffer;
buffer.push_back(keyNum);
error_code result =
writeCommand(GenerateEcdsaSignature, &buffer[0], buffer.size());
if (!result) {
sleep(generateEcdsaSignatureTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == (1 + signature.r.size() + signature.s.size())) {
result = convertResultByte(buffer[0]);
vector<uint_least8_t>::const_iterator begin = buffer.begin() + 1;
vector<uint_least8_t>::const_iterator end = begin + signature.s.size();
copy(begin, end, signature.s.begin());
begin = end;
end = begin + signature.r.size();
copy(begin, end, signature.r.begin());
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS2476::computeSha2UniqueSecret(SecretNum msecretNum) {
vector<uint_least8_t> buffer;
buffer.push_back(msecretNum << 4);
error_code result =
writeCommand(ComputeSha2UniqueSecret, &buffer[0], buffer.size());
if (!result) {
sleep(sha256ComputationTimeMs);
result = readResponse(buffer);
if (!result) {
if (buffer.size() == 1) {
convertResultByte(buffer[0]);
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code DS2476::computeSha2Hmac(Sha256::Hash & hmac) {
error_code result = writeCommand(ComputeSha2Hmac, NULL, 0);
if (!result) {
sleep(sha256ComputationTimeMs);
vector<uint_least8_t> buffer;
result = readResponse(buffer);
if (!result) {
if (buffer.size() == (1 + hmac.size())) {
result = convertResultByte(buffer[0]);
copy(buffer.begin() + 1, buffer.end(), hmac.begin());
} else {
result = make_error_code(InvalidResponseError);
}
}
}
return result;
}
error_code enableCoprocessor(DS2476 & ds2476) {
DS2476::Page gpioControl;
error_code result = ds2476.readMemory(DS2476::GpioControl, gpioControl);
if (!result && gpioControl[0] != 0xAA) {
gpioControl[0] = 0xAA;
result = ds2476.writeMemory(DS2476::GpioControl, gpioControl);
}
return result;
}
} // namespace MaximInterface