Merge branch 'enhanced_device' of github.com:john30/ebusd into enhanced_device

This commit is contained in:
john30
2020-12-06 17:04:29 +01:00
29 changed files with 4207 additions and 308 deletions
+122 -66
View File
@@ -67,7 +67,8 @@ result_t PollRequest::prepare(symbol_t ownMasterAddress) {
istringstream input;
result_t result = m_message->prepareMaster(m_index, ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, &m_master);
if (result == RESULT_OK) {
logInfo(lf_bus, "poll cmd: %s", m_master.getStr().c_str());
string str = m_master.getStr();
logInfo(lf_bus, "poll cmd: %s", str.c_str());
}
return result;
}
@@ -99,7 +100,8 @@ result_t ScanRequest::prepare(symbol_t ownMasterAddress) {
istringstream input;
m_result = m_message->prepareMaster(m_index, ownMasterAddress, dstAddress, UI_FIELD_SEPARATOR, &input, &m_master);
if (m_result >= RESULT_OK) {
logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getStr().c_str());
string str = m_master.getStr();
logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, str.c_str());
}
return m_result;
}
@@ -181,7 +183,8 @@ bool ScanRequest::notify(result_t result, const SlaveSymbolString& slave) {
bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) {
if (result == RESULT_OK) {
logDebug(lf_bus, "read res: %s", slave.getStr().c_str());
string str = m_master.getStr();
logDebug(lf_bus, "read res: %s", str.c_str());
}
m_result = result;
*m_slave = slave;
@@ -280,7 +283,7 @@ bool GrabbedMessage::dump(bool unknown, MessageMap* messages, bool first, bool d
if (remain == 0) {
return true;
}
for (const auto it : *types) {
for (const auto& it : *types) {
const DataType* baseType = it.second;
if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types
continue;
@@ -426,23 +429,17 @@ result_t BusHandler::handleSymbol() {
unsigned int timeout = SYN_TIMEOUT;
symbol_t sendSymbol = ESC;
bool sending = false;
BusRequest* startRequest = nullptr;
// check if another symbol has to be sent and determine timeout for receive
switch (m_state) {
case bs_noSignal:
timeout = m_generateSynInterval > 0 ? m_generateSynInterval+m_transferLatency : SIGNAL_TIMEOUT;
timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT;
break;
case bs_skip:
timeout = SYN_TIMEOUT;
break;
case bs_ready:
if (m_currentRequest != nullptr) {
setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up
} else if (m_remainLockCount == 0) {
startRequest = m_nextRequests.peek();
if (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) {
BusRequest* startRequest = m_nextRequests.peek();
if (startRequest == nullptr && m_pollInterval > 0) { // check for poll/scan
time_t now;
time(&now);
@@ -450,7 +447,7 @@ result_t BusHandler::handleSymbol() {
Message* message = m_messages->getNextPoll();
if (message != nullptr) {
m_lastPoll = now;
PollRequest* request = new PollRequest(message);
auto request = new PollRequest(message);
result_t ret = request->prepare(m_ownMasterAddress);
if (ret != RESULT_OK) {
logError(lf_bus, "prepare poll message: %s", getResultCode(ret));
@@ -463,19 +460,33 @@ result_t BusHandler::handleSymbol() {
}
}
if (startRequest != nullptr) { // initiate arbitration
sendSymbol = startRequest->m_master[0];
sending = true;
logDebug(lf_bus, "start request %2.2x", startRequest->m_master[0]);
result_t ret = m_device->startArbitration(startRequest->m_master[0]);
if (ret == RESULT_OK) {
logDebug(lf_bus, "arbitration start with %2.2x", startRequest->m_master[0]);
} else {
logError(lf_bus, "arbitration start: %s", getResultCode(ret));
m_nextRequests.remove(startRequest);
m_currentRequest = startRequest;
setState(bs_ready, ret); // force the failed request to be notified
}
}
}
break;
case bs_ready:
if (m_currentRequest != nullptr) {
setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up
}
break;
case bs_recvCmd:
case bs_recvCmdCrc:
timeout = m_slaveRecvTimeout;
break;
case bs_recvCmdAck:
timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency:0);
timeout = m_slaveRecvTimeout;
break;
case bs_recvRes:
@@ -488,7 +499,7 @@ result_t BusHandler::handleSymbol() {
break;
case bs_recvResAck:
timeout = m_slaveRecvTimeout+m_transferLatency;
timeout = m_slaveRecvTimeout;
break;
case bs_sendCmd:
@@ -541,11 +552,11 @@ result_t BusHandler::handleSymbol() {
// send symbol if necessary
result_t result;
struct timespec sentTime, recvTime;
struct timespec sentTime = {}, recvTime = {};
if (sending) {
if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) {
if (m_escape) {
sendSymbol = sendSymbol == ESC ? 0x00 : 0x01;
sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01);
} else {
m_escape = sendSymbol;
sendSymbol = ESC;
@@ -555,63 +566,109 @@ result_t BusHandler::handleSymbol() {
clockGettime(&sentTime);
if (result == RESULT_OK) {
if (m_state == bs_ready) {
timeout = m_transferLatency+m_busAcquireTimeout;
timeout = m_busAcquireTimeout;
} else {
timeout = m_transferLatency+SEND_TIMEOUT;
timeout = SEND_TIMEOUT;
}
} else {
sending = false;
timeout = SYN_TIMEOUT;
if (startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
setState(bs_skip, result);
}
} else {
clockGettime(&sentTime); // for measuring arbitration delay in enhanced protocol
}
// receive next symbol (optionally check reception of sent symbol)
symbol_t recvSymbol;
bool isAutoSyn = !sending && m_generateSynInterval == SYN_TIMEOUT && (m_state == bs_noSignal || m_state == bs_skip);
result = m_device->recv(timeout+(isAutoSyn ? 0 : m_transferLatency), &recvSymbol);
ArbitrationState arbitrationState = as_none;
result = m_device->recv(timeout, &recvSymbol, &arbitrationState);
if (sending) {
clockGettime(&recvTime);
}
bool sentAutoSyn = false;
if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
// check if acting as AUTO-SYN generator is required
result = m_device->send(SYN);
if (result == RESULT_OK) {
clockGettime(&sentTime);
recvSymbol = ESC;
result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol);
clockGettime(&recvTime);
if (result == RESULT_ERR_TIMEOUT) {
return setState(bs_noSignal, result);
}
if (result != RESULT_OK) {
logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result));
} else if (recvSymbol != SYN) {
logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol);
} else {
measureLatency(&sentTime, &recvTime);
if (m_generateSynInterval != SYN_TIMEOUT) {
// received own AUTO-SYN symbol back again: act as AUTO-SYN generator now
m_generateSynInterval = SYN_TIMEOUT;
logNotice(lf_bus, "acting as AUTO-SYN generator");
}
m_remainLockCount = 0;
m_lastSynReceiveTime = recvTime;
return setState(bs_ready, result);
}
if (result != RESULT_OK) {
return setState(bs_skip, result);
}
return setState(bs_skip, result);
clockGettime(&sentTime);
recvSymbol = ESC;
result = m_device->recv(SEND_TIMEOUT, &recvSymbol, &arbitrationState);
clockGettime(&recvTime);
if (result != RESULT_OK) {
logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result));
return setState(bs_noSignal, result);
}
if (recvSymbol != SYN) {
logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol);
return setState(bs_noSignal, result);
}
measureLatency(&sentTime, &recvTime);
if (m_generateSynInterval != SYN_TIMEOUT) {
// received own AUTO-SYN symbol back again: act as AUTO-SYN generator now
m_generateSynInterval = SYN_TIMEOUT;
logNotice(lf_bus, "acting as AUTO-SYN generator");
}
m_remainLockCount = 0;
m_lastSynReceiveTime = recvTime;
sentAutoSyn = true;
setState(bs_ready, RESULT_OK);
}
switch (arbitrationState) {
case as_lost:
logDebug(lf_bus, "arbitration lost");
if (m_currentRequest == nullptr) {
BusRequest *startRequest = m_nextRequests.peek();
if (startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
}
setState(m_state, RESULT_ERR_BUS_LOST);
break;
case as_won: // implies RESULT_OK
if (m_currentRequest != nullptr) {
logNotice(lf_bus, "arbitration won while handling another request");
setState(bs_ready, RESULT_OK); // force the current request to be notified
} else {
BusRequest *startRequest = m_nextRequests.peek();
if (m_state != bs_ready || startRequest == nullptr || !m_nextRequests.remove(startRequest)) {
logNotice(lf_bus, "arbitration won in invalid state %s", getStateCode(m_state));
setState(bs_ready, RESULT_ERR_TIMEOUT);
} else {
logDebug(lf_bus, "arbitration won");
m_currentRequest = startRequest;
sendSymbol = m_currentRequest->m_master[0];
sending = true;
}
}
break;
case as_running:
break;
case as_error:
logError(lf_bus, "arbitration start error");
// cancel request
if (!m_currentRequest) {
BusRequest *startRequest = m_nextRequests.peek();
if (startRequest && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest;
}
}
if (m_currentRequest) {
setState(m_state, RESULT_ERR_BUS_LOST);
}
break;
default: // only as_none
break;
}
if (sentAutoSyn && !sending) {
return RESULT_OK;
}
time_t now;
time(&now);
if (result != RESULT_OK) {
if (sending && startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1)
// at least one full second has passed since last received symbol
|| m_state == bs_noSignal) {
@@ -677,19 +734,14 @@ result_t BusHandler::handleSymbol() {
return RESULT_OK;
case bs_ready:
if (startRequest != nullptr && sending) {
if (!m_nextRequests.remove(startRequest)) {
// request already removed (e.g. due to timeout)
return setState(bs_skip, RESULT_ERR_TIMEOUT);
}
m_currentRequest = startRequest;
if (m_currentRequest != nullptr && sending) {
// check arbitration
if (recvSymbol == sendSymbol) { // arbitration successful
// measure arbitration delay
long long latencyLong = (sentTime.tv_sec*1000000000 + sentTime.tv_nsec
- m_lastSynReceiveTime.tv_sec*1000000000 - m_lastSynReceiveTime.tv_nsec)/1000;
if (latencyLong >= 0 && latencyLong <= 10000) { // skip clock skew or out of reasonable range
int latency = static_cast<int>(latencyLong);
auto latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "arbitration delay %d micros", latency);
if (m_arbitrationDelayMin < 0 || (latency < m_arbitrationDelayMin || latency > m_arbitrationDelayMax)) {
if (m_arbitrationDelayMin == -1 || latency < m_arbitrationDelayMin) {
@@ -962,6 +1014,9 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
}
m_currentRequest = nullptr;
}
if (state == bs_skip) {
m_device->startArbitration(SYN); // reset arbitration state
}
}
if (state == bs_noSignal) { // notify all requests
@@ -996,6 +1051,7 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
} else if (m_state == bs_noSignal) {
logNotice(lf_bus, "signal acquired");
}
// logDebug(lf_bus, "state: from %s to %s with %s", getStateCode(m_state), getStateCode(state), getResultCode(result));
m_state = state;
if (state == bs_ready || state == bs_skip) {
@@ -1017,7 +1073,7 @@ void BusHandler::measureLatency(struct timespec* sentTime, struct timespec* recv
if (latencyLong < 0 || latencyLong > 1000) {
return; // clock skew or out of reasonable range
}
int latency = static_cast<int>(latencyLong);
auto latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "send/receive symbol latency %d ms", latency);
if (m_symbolLatencyMin >= 0 && (latency >= m_symbolLatencyMin && latency <= m_symbolLatencyMax)) {
return;
@@ -1299,7 +1355,7 @@ bool BusHandler::formatScanResult(symbol_t slave, bool leadingNewline, ostringst
*output << endl;
}
*output << hex << setw(2) << setfill('0') << static_cast<unsigned>(slave);
for (const auto result : it->second) {
for (const auto &result : it->second) {
*output << result;
}
return true;
@@ -1428,7 +1484,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
const auto it = m_scanResults.find(address);
if (it != m_scanResults.end()) {
*output << ",\"s\":\"";
for (const auto result : it->second) {
for (const auto& result : it->second) {
*output << result;
}
*output << "\"";
@@ -1444,7 +1500,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
if (!loadedFiles.empty()) {
*output << ",\"f\":[";
bool first = true;
for (const auto loadedFile : loadedFiles) {
for (const auto& loadedFile : loadedFiles) {
if (first) {
first = false;
} else {
+19 -20
View File
@@ -45,20 +45,23 @@ namespace ebusd {
using std::string;
/** the default time [us] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15000
/** the default time [ms] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15
/** the maximum allowed time [us] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 50800
/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 51
/** the time [us] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250000
/** the time [ms] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250
/** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION 4700
#define SYMBOL_DURATION_MICROS 4700
/** the maximum allowed time [us] for retrieving back a sent symbol (2x symbol duration). */
#define SEND_TIMEOUT (2*SYMBOL_DURATION)
/** the maximum duration [ms] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION 5
/** the maximum allowed time [ms] for retrieving back a sent symbol (2x symbol duration). */
#define SEND_TIMEOUT ((int)((2*SYMBOL_DURATION_MICROS+999)/1000))
/** the possible bus states. */
enum BusState {
@@ -368,9 +371,8 @@ class BusHandler : public WaitThread {
* @param answer whether to answer queries for the own master/slave address.
* @param busLostRetries the number of times a send is repeated due to lost arbitration.
* @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration).
* @param transferLatency the bus transfer latency in microseconds.
* @param busAcquireTimeout the maximum time in microseconds for bus acquisition.
* @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge.
* @param busAcquireTimeout the maximum time in milliseconds for bus acquisition.
* @param slaveRecvTimeout the maximum time in milliseconds an addressed slave is expected to acknowledge.
* @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection.
* @param generateSyn whether to enable AUTO-SYN symbol generation.
* @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled.
@@ -378,14 +380,14 @@ class BusHandler : public WaitThread {
BusHandler(Device* device, MessageMap* messages,
symbol_t ownAddress, bool answer,
unsigned int busLostRetries, unsigned int failedSendRetries,
unsigned int transferLatency, unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout,
unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout,
unsigned int lockCount, bool generateSyn,
unsigned int pollInterval)
: WaitThread(), m_device(device), m_reconnect(false), m_messages(messages),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)),
m_answer(answer), m_addressConflict(false),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0),
m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount ? 1 : 0),
m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0),
@@ -686,13 +688,10 @@ class BusHandler : public WaitThread {
/** the number of times a failed send is repeated (other than lost arbitration). */
const unsigned int m_failedSendRetries;
/** the bus transfer latency in microseconds. */
const unsigned int m_transferLatency;
/** the maximum time in microseconds for bus acquisition. */
/** the maximum time in milliseconds for bus acquisition. */
const unsigned int m_busAcquireTimeout;
/** the maximum time in microseconds an addressed slave is expected to acknowledge. */
/** the maximum time in milliseconds an addressed slave is expected to acknowledge. */
const unsigned int m_slaveRecvTimeout;
/** the number of masters already seen. */
@@ -707,7 +706,7 @@ class BusHandler : public WaitThread {
/** the remaining number of AUTO-SYN symbols before sending is allowed again. */
unsigned int m_remainLockCount;
/** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
/** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
unsigned int m_generateSynInterval;
/** the interval in seconds in which poll messages are cycled, or 0 if disabled. */
+19 -15
View File
@@ -79,7 +79,7 @@ static struct options opt = {
false, // noDeviceCheck
false, // readOnly
false, // initialSend
-1, // latency
0, // extraLatency
CONFIG_PATH, // configPath
false, // scanConfig
@@ -92,7 +92,7 @@ static struct options opt = {
0x31, // address
false, // answer
9400, // acquireTimeout
10, // acquireTimeout
3, // acquireRetries
2, // sendRetries
SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout
@@ -184,7 +184,7 @@ static const struct argp_option argpoptions[] = {
{"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 },
{"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 },
{"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 },
{"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 },
{"latency", O_DEVLAT, "MSEC", 0, "Extra transfer latency in ms [0]", 0 },
{nullptr, 0, nullptr, 0, "Message configuration options:", 2 },
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTP URL) [" CONFIG_PATH
@@ -204,10 +204,10 @@ static const struct argp_option argpoptions[] = {
{nullptr, 0, nullptr, 0, "eBUS options:", 3 },
{"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 },
{"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 },
{"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 },
{"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 0 },
{"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 },
{"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 },
{"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 },
{"receivetimeout", O_RCVTIM, "MSEC", 0, "Expect a slave to answer within MSEC us [25]", 0 },
{"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 },
{"generatesyn", O_GENSYN, nullptr, 0, "Enable AUTO-SYN symbol generation", 0 },
@@ -267,6 +267,7 @@ static map<string, DataFieldTemplates*> s_templatesByPath;
error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input;
result_t result = RESULT_OK;
unsigned int value;
switch (key) {
// Device options:
@@ -295,12 +296,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
opt->initialSend = true;
break;
case O_DEVLAT: // --latency=10000
opt->latency = parseInt(arg, 10, 0, 200000, &result);
if (result != RESULT_OK) {
case O_DEVLAT: // --latency=10
value = parseInt(arg, 10, 0, 200000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>200)) { // backwards compatible (micros)
argp_error(state, "invalid latency");
return EINVAL;
}
opt->extraLatency = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
// Message configuration options:
@@ -376,12 +378,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
opt->answer = true;
break;
case O_ACQTIM: // --acquiretimeout=9400
opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK) {
case O_ACQTIM: // --acquiretimeout=10
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros)
argp_error(state, "invalid acquiretimeout");
return EINVAL;
}
opt->acquireTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
case O_ACQRET: // --acquireretries=3
opt->acquireRetries = parseInt(arg, 10, 0, 10, &result);
@@ -397,12 +400,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
return EINVAL;
}
break;
case O_RCVTIM: // --receivetimeout=25000
opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK) {
case O_RCVTIM: // --receivetimeout=25
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros)
argp_error(state, "invalid receivetimeout");
return EINVAL;
}
opt->receiveTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
case O_MASCNT: // --numbermasters=0
opt->masterCount = parseInt(arg, 10, 0, 25, &result);
@@ -1306,7 +1310,7 @@ int main(int argc, char* argv[]) {
}
// open the device
Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend);
Device *device = Device::create(opt.device, opt.extraLatency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend);
if (device == nullptr) {
logError(lf_main, "unable to create device %s", opt.device);
return EINVAL;
+3 -3
View File
@@ -39,7 +39,7 @@ struct options {
bool noDeviceCheck; //!< skip serial eBUS device test
bool readOnly; //!< read-only access to the device
bool initialSend; //!< send an initial escape symbol after connecting device
int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP]
const char* configPath; //!< path to CSV configuration files [http://ebusd.eu/config/]
bool scanConfig; //!< pick configuration files matching initial scan
@@ -54,10 +54,10 @@ struct options {
symbol_t address; //!< own bus address [31]
bool answer; //!< answer to requests from other masters
unsigned int acquireTimeout; //!< bus acquisition timeout in us [9400]
unsigned int acquireTimeout; //!< bus acquisition timeout in ms [10]
unsigned int acquireRetries; //!< number of retries for bus acquisition [3]
unsigned int sendRetries; //!< number of retries for failed sends [2]
unsigned int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
unsigned int receiveTimeout; //!< timeout for receiving answer from slave in ms [25]
unsigned int masterCount; //!< expected number of masters for arbitration [0]
bool generateSyn; //!< enable AUTO-SYN symbol generation
+17 -9
View File
@@ -141,16 +141,10 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag
}
}
// create BusHandler
unsigned int latency;
if (opt.latency < 0) {
latency = device->getLatency();
} else {
latency = (unsigned int)opt.latency;
}
m_busHandler = new BusHandler(m_device, m_messages,
m_address, opt.answer,
opt.acquireRetries, opt.sendRetries,
latency, opt.acquireTimeout, opt.receiveTimeout,
opt.acquireTimeout, opt.receiveTimeout,
opt.masterCount, opt.generateSyn,
opt.pollInterval);
m_busHandler->start("bushandler");
@@ -473,12 +467,18 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) {
}
if (m_logRawBuffer.tellp() == 0 || received != m_logRawLastReceived) {
m_logRawLastReceived = received;
if (m_logRawBuffer.tellp() == 0 && m_logRawLastSymbol != SYN) {
m_logRawBuffer << "...";
}
m_logRawBuffer << (received ? "<" : ">");
}
m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast<unsigned>(symbol);
m_logRawLastSymbol = symbol;
}
if (symbol == SYN && m_logRawBuffer.tellp() > 0) { // flush
m_logRawLastSymbol = symbol;
if (m_logRawBuffer.tellp() > (symbol == SYN ? 0 : 64)) { // flush: direction+5 hdr+24 max data+crc+direction+ack+1
if (symbol != SYN) {
m_logRawBuffer << "...";
}
const string bufStr = m_logRawBuffer.str();
const char* str = bufStr.c_str();
if (m_logRawFile) {
@@ -490,6 +490,14 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) {
}
}
void MainLoop::notifyStatus(bool error, const char* message) {
if (error) {
logError(lf_bus, "device status: %s", message);
} else {
logNotice(lf_bus, "device status: %s", message);
}
}
result_t MainLoop::decodeMessage(const string &data, bool isHttp, bool* connected, ClientSettings* settings,
string* user, bool* reload, ostringstream* ostream) {
string token, previous;
+3
View File
@@ -132,6 +132,9 @@ class MainLoop : public Thread, DeviceListener {
// @copydoc
void notifyDeviceData(symbol_t symbol, bool received) override;
// @copydoc
void notifyStatus(bool error, const char* message) override;
protected:
// @copydoc
+437 -102
View File
@@ -40,6 +40,8 @@
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <ios>
#include <iomanip>
#include "lib/ebus/data.h"
namespace ebusd {
@@ -50,16 +52,64 @@ namespace ebusd {
#define POLLRDHUP 0
#endif
Device::~Device() {
close();
// ebusd enhanced protocol IDs:
#define ENH_REQ_INIT ((uint8_t)0x0)
#define ENH_RES_RESETTED ((uint8_t)0x0)
#define ENH_REQ_SEND ((uint8_t)0x1)
#define ENH_RES_RECEIVED ((uint8_t)0x1)
#define ENH_REQ_START ((uint8_t)0x2)
#define ENH_RES_STARTED ((uint8_t)0x2)
#define ENH_RES_FAILED ((uint8_t)0xa)
#define ENH_RES_ERROR_EBUS ((uint8_t)0xb)
#define ENH_RES_ERROR_HOST ((uint8_t)0xc)
// ebusd enhanced error codes for the ERROR_* responses
#define ENH_ERR_FRAMING ((uint8_t)0x00)
#define ENH_ERR_OVERRUN ((uint8_t)0x01)
#define ENH_BYTE_FLAG ((uint8_t)0x80)
#define ENH_BYTE_MASK ((uint8_t)0xc0)
#define ENH_BYTE1 ((uint8_t)0xc0)
#define ENH_BYTE2 ((uint8_t)0x80)
#define makeEnhancedSequence(cmd, data) {(uint8_t)(ENH_BYTE1 | ((cmd)<<2) | (((data)&0xc0)>>6)), (uint8_t)(ENH_BYTE2 | ((data)&0x3f))}
Device::Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend,
bool enhancedProto)
: m_name(name), m_checkDevice(checkDevice),
m_latency(HOST_LATENCY_MS+latency), m_readOnly(readOnly), m_initialSend(initialSend),
m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN),
m_arbitrationCheck(false), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0) {
m_buffer = reinterpret_cast<symbol_t*>(malloc(m_bufSize));
if (!m_buffer) {
m_bufSize = 0;
}
}
Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) {
Device::~Device() {
close();
if (m_buffer) {
free(m_buffer);
}
}
Device* Device::create(const char* name, unsigned int extraLatency, bool checkDevice, bool readOnly, bool initialSend) {
bool enhanced = strncmp(name, "enh:", 4) == 0;
if (enhanced) {
name += 4;
}
if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) {
char* in = strdup(name);
bool udp = false;
char* addrpos = in;
char* portpos = strchr(addrpos, ':');
if (!enhanced && portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) {
enhanced = true; // support enhtcp:<ip>:<port> and enhudp:<ip>:<port>
addrpos += 3;
if (portpos == addrpos) {
addrpos++;
portpos = strchr(addrpos, ':');
}
} // else: support enh:<ip>:<port> defaulting to TCP
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
addrpos += 4;
portpos = strchr(addrpos, ':');
@@ -69,7 +119,7 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i
return nullptr; // invalid protocol or missing port
}
result_t result = RESULT_OK;
unsigned int port = parseInt(portpos+1, 10, 1, 65535, &result);
uint16_t port = (uint16_t)parseInt(portpos+1, 10, 1, 65535, &result);
if (result != RESULT_OK) {
free(in);
return nullptr; // invalid port
@@ -77,9 +127,31 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i
*portpos = 0;
char* hostOrIp = strdup(addrpos);
free(in);
return new NetworkDevice(name, hostOrIp, port, readOnly, initialSend, udp);
return new NetworkDevice(name, hostOrIp, port, extraLatency, readOnly, initialSend, udp, enhanced);
}
return new SerialDevice(name, checkDevice, readOnly, initialSend);
// support enh:/dev/<device>
return new SerialDevice(name, checkDevice, extraLatency, readOnly, initialSend, enhanced);
}
result_t Device::open() {
close();
return m_bufSize == 0 ? RESULT_ERR_DEVICE : RESULT_OK;
}
result_t Device::afterOpen() {
m_bufLen = 0;
if (m_enhancedProto) {
symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0); // TODO define additional feature flags
if (::write(m_fd, buf, 2) != 2) {
return RESULT_ERR_SEND;
}
if (m_listener != nullptr) {
m_listener->notifyStatus(false, "resetting");
}
} else if (m_initialSend && !write(ESC)) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
}
void Device::close() {
@@ -87,6 +159,7 @@ void Device::close() {
::close(m_fd);
m_fd = -1;
}
m_bufLen = 0; // flush read buffer
}
bool Device::isValid() {
@@ -103,7 +176,7 @@ result_t Device::send(symbol_t value) {
if (!isValid()) {
return RESULT_ERR_DEVICE;
}
if (m_readOnly || write(value) != 1) {
if (m_readOnly || !write(value)) {
return RESULT_ERR_SEND;
}
if (m_listener != nullptr) {
@@ -112,74 +185,383 @@ result_t Device::send(symbol_t value) {
return RESULT_OK;
}
result_t Device::recv(unsigned int timeout, symbol_t* value) {
/**
* the maximum duration in milliseconds to wait for an enhanced sequence to complete after the first part was already
* retrieved: 2* (Start+8Bit+Stop+Extra @ 9600Bd)
*/
#define ENHANCED_COMPLETE_WAIT_DURATION 3
bool Device::cancelRunningArbitration(ArbitrationState* arbitrationState) {
if (m_enhancedProto && m_arbitrationMaster != SYN) {
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
write(SYN, true);
return true;
}
if (m_enhancedProto || m_arbitrationMaster == SYN) {
return false;
}
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
return true;
}
result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) {
if (m_arbitrationMaster!=SYN) {
*arbitrationState = as_running;
}
if (!isValid()) {
cancelRunningArbitration(arbitrationState);
return RESULT_ERR_DEVICE;
}
if (!available() && timeout > 0) {
int ret;
struct timespec tdiff;
bool repeat = false;
bool repeated = false;
timeout += m_latency;
do {
repeat = false;
bool isAvailable = available();
if (!isAvailable && timeout > 0) {
int ret;
struct timespec tdiff;
// set select timeout
tdiff.tv_sec = timeout/1000000;
tdiff.tv_nsec = (timeout%1000000)*1000;
// set select timeout
tdiff.tv_sec = timeout/1000;
tdiff.tv_nsec = (timeout%1000)*1000000;
#ifdef HAVE_PPOLL
nfds_t nfds = 1;
struct pollfd fds[nfds];
nfds_t nfds = 1;
struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds));
memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
ret = ppoll(fds, nfds, &tdiff, nullptr);
if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) {
ret = -1;
}
fds[0].fd = m_fd;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
ret = ppoll(fds, nfds, &tdiff, nullptr);
if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) {
ret = -1;
}
#else
#ifdef HAVE_PSELECT
fd_set readfds, exceptfds;
fd_set readfds, exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds);
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr);
if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) {
ret = -1;
}
ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr);
if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) {
ret = -1;
}
#else
ret = 1; // ignore timeout if neither ppoll nor pselect are available
ret = 1; // ignore timeout if neither ppoll nor pselect are available
#endif
#endif
if (ret == -1) {
close();
return RESULT_ERR_DEVICE;
if (ret == -1) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "poll error %d\n", errno);
#endif
close();
cancelRunningArbitration(arbitrationState);
return RESULT_ERR_DEVICE;
}
if (ret == 0) {
return RESULT_ERR_TIMEOUT;
}
}
if (ret == 0) {
// directly read byte from device
bool incomplete = false;
if (!read(value, isAvailable, arbitrationState, &incomplete)) {
if (!isAvailable && incomplete && !repeated) {
// for a two-byte transfer another poll is needed
repeat = true;
repeated = true;
timeout = m_latency+ENHANCED_COMPLETE_WAIT_DURATION;
continue;
}
return RESULT_ERR_TIMEOUT;
}
} while (repeat);
if (m_enhancedProto || *value != SYN || m_arbitrationMaster == SYN) {
if (m_listener != nullptr) {
m_listener->notifyDeviceData(*value, true);
}
if (!m_enhancedProto && m_arbitrationMaster != SYN) {
if (m_arbitrationCheck) {
*arbitrationState = *value == m_arbitrationMaster ? as_won : as_lost;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
} else {
*arbitrationState = m_arbitrationMaster == SYN ? as_none : as_start;
}
}
return RESULT_OK;
}
// directly read byte from device
ssize_t nbytes = read(value);
if (nbytes == 0) {
return RESULT_ERR_EOF;
}
if (nbytes < 0) {
close();
return RESULT_ERR_DEVICE;
}
// non-enhanced: arbitration executed by ebusd itself
bool wrote = write(m_arbitrationMaster); // send as fast as possible
if (m_listener != nullptr) {
m_listener->notifyDeviceData(*value, true);
}
if (!wrote) {
cancelRunningArbitration(arbitrationState);
return RESULT_OK;
}
if (m_listener != nullptr) {
m_listener->notifyDeviceData(m_arbitrationMaster, false);
}
m_arbitrationCheck = true;
*arbitrationState = as_running;
return RESULT_OK;
}
result_t Device::startArbitration(symbol_t masterAddress) {
if (m_arbitrationCheck) {
if (masterAddress != SYN) {
return RESULT_ERR_ARB_RUNNING; // should not occur
}
m_arbitrationCheck = false;
m_arbitrationMaster = SYN;
if (m_enhancedProto) {
// cancel running arbitration
if (!write(SYN, true)) {
return RESULT_ERR_SEND;
}
}
return RESULT_OK;
}
if (m_readOnly) {
return RESULT_ERR_SEND;
}
m_arbitrationMaster = masterAddress;
if (m_enhancedProto && masterAddress != SYN) {
if (!write(masterAddress, true)) {
m_arbitrationMaster = SYN;
return RESULT_ERR_SEND;
}
m_arbitrationCheck = true;
}
return RESULT_OK;
}
bool Device::write(symbol_t value, bool startArbitration) {
if (m_enhancedProto) {
symbol_t buf[2] = makeEnhancedSequence(startArbitration ? ENH_REQ_START : ENH_REQ_SEND, value);
return ::write(m_fd, buf, 2) == 2;
}
return ::write(m_fd, &value, 1) == 1;
}
bool Device::available() {
if (m_bufLen <= 0) {
return false;
}
if (!m_enhancedProto) {
return true;
}
// peek into the received enhanced proto bytes to determine symbol availability
for (size_t pos = 0; pos < m_bufLen; pos++) {
symbol_t ch = m_buffer[(pos+m_bufPos)%m_bufSize];
if (!(ch&ENH_BYTE_FLAG)) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail direct\n");
#endif
return true;
}
if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) {
if (pos+1 >= m_bufLen) {
return false;
}
// peek into next byte to check if enhanced sequence is ok
ch = m_buffer[(pos+m_bufPos+1)%m_bufSize];
if (!(ch&ENH_BYTE_FLAG) || (ch&ENH_BYTE_MASK) != ENH_BYTE2) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced following bad\n");
#endif
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected available enhanced following byte 1");
}
// drop first byte of invalid sequence
m_bufPos = (m_bufPos + 1) % m_bufSize;
m_bufLen--;
pos--;
continue;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced\n");
#endif
return true;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced bad\n");
#endif
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected available enhanced byte 2");
}
// skip byte from erroneous protocol
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
pos--;
}
return false;
}
bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState, bool* incomplete) {
if (!isAvailable) {
if (m_bufLen > 0 && m_bufPos != 0) {
if (m_bufLen > m_bufSize / 2) {
// more than half of input buffer consumed is taken as signal that ebusd is too slow
m_bufLen = 0;
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "buffer overflow");
}
} else {
size_t tail;
if (m_bufPos+m_bufLen > m_bufSize) {
// move wrapped tail away
tail = (m_bufPos+m_bufLen) % m_bufSize;
size_t head = m_bufLen-tail;
memmove(m_buffer+head, m_buffer, tail);
} else {
tail = 0;
}
// move head to first position
memmove(m_buffer, m_buffer + m_bufPos, m_bufLen - tail);
}
}
m_bufPos = 0;
// fill up the buffer
ssize_t size = ::read(m_fd, m_buffer + m_bufLen, m_bufSize - m_bufLen);
if (size <= 0) {
return false;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw <");
for (int pos=0; pos<size; pos++) {
fprintf(stdout, " %2.2x", m_buffer[m_bufLen+pos]);
}
fprintf(stdout, "\n");
#endif
m_bufLen += size;
}
if (!available()) {
if (incomplete) {
*incomplete = m_enhancedProto && m_bufLen > 0;
}
return false;
}
if (!m_enhancedProto) {
*value = m_buffer[m_bufPos];
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return true;
}
while (m_bufLen > 0) {
symbol_t ch = m_buffer[m_bufPos];
if (!(ch&ENH_BYTE_FLAG)) {
*value = ch;
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return true;
}
uint8_t kind = ch&ENH_BYTE_MASK;
if (kind == ENH_BYTE1 && m_bufLen<2) {
return false; // transfer not complete yet
}
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
if (kind == ENH_BYTE2) {
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected enhanced byte 2");
}
return false;
}
// kind is ENH_BYTE1
symbol_t ch2 = m_buffer[m_bufPos];
m_bufPos = (m_bufPos + 1) % m_bufSize;
m_bufLen--;
if ((ch2 & ENH_BYTE_MASK) != ENH_BYTE2) {
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "missing enhanced byte 2");
}
return false;
}
symbol_t data = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f));
symbol_t cmd = (ch>>2)&0xf;
switch (cmd) {
case ENH_RES_STARTED:
*arbitrationState = as_won;
if (m_listener != NULL) {
m_listener->notifyDeviceData(data, false);
}
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
*value = data;
return true;
case ENH_RES_FAILED:
*arbitrationState = as_lost;
if (m_listener != NULL) {
m_listener->notifyDeviceData(m_arbitrationMaster, false);
}
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
*value = data;
return true;
case ENH_RES_RECEIVED:
*value = data;
return true;
case ENH_RES_RESETTED:
if (*arbitrationState != as_none) {
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
}
// TODO define additional feature flags
if (m_listener != nullptr) {
m_listener->notifyStatus(false, "reset");
}
break;
case ENH_RES_ERROR_EBUS:
case ENH_RES_ERROR_HOST:
if (m_listener != nullptr) {
ostringstream stream;
stream << (cmd==ENH_RES_ERROR_EBUS ? "eBUS comm error: " : "host comm error: ");
switch (data) {
case ENH_ERR_FRAMING:
stream << "framing";
break;
case ENH_ERR_OVERRUN:
stream << "overrun";
break;
default:
stream << "unknown 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned>(data);
break;
}
string str = stream.str();
m_listener->notifyStatus(true, str.c_str());
}
cancelRunningArbitration(arbitrationState);
break;
default:
if (m_listener != nullptr) {
ostringstream stream;
stream << "unexpected enhanced command 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned>(cmd);
string str = stream.str();
m_listener->notifyStatus(true, str.c_str());
}
return false;
}
}
return false;
}
result_t SerialDevice::open() {
if (m_fd != -1) {
close();
result_t result = Device::open();
if (result != RESULT_OK) {
return result;
}
struct termios newSettings;
@@ -223,7 +605,7 @@ result_t SerialDevice::open() {
// create new settings
memset(&newSettings, 0, sizeof(newSettings));
cfsetspeed(&newSettings, B2400);
cfsetspeed(&newSettings, m_enhancedProto ? B9600 : B2400);
newSettings.c_cflag |= (CS8 | CLOCAL | CREAD);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors
@@ -237,7 +619,7 @@ result_t SerialDevice::open() {
tcflush(m_fd, TCIFLUSH);
// activate new settings of serial device
if (tcsetattr(m_fd, TCSAFLUSH, &newSettings)) {
if (tcsetattr(m_fd, TCSANOW, &newSettings)) {
close();
return RESULT_ERR_DEVICE;
}
@@ -245,10 +627,7 @@ result_t SerialDevice::open() {
// set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
return afterOpen();
}
void SerialDevice::close() {
@@ -282,8 +661,9 @@ void SerialDevice::checkDevice() {
#endif
result_t NetworkDevice::open() {
if (m_fd != -1) {
close();
result_t result = Device::open();
if (result != RESULT_OK) {
return result;
}
struct sockaddr_in address;
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
@@ -341,23 +721,7 @@ result_t NetworkDevice::open() {
close();
return RESULT_ERR_GENERIC_IO;
}
if (m_bufSize == 0) {
m_bufSize = MAX_LEN+1;
m_buffer = reinterpret_cast<symbol_t*>(malloc(m_bufSize));
if (!m_buffer) {
m_bufSize = 0;
}
}
m_bufLen = 0;
if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
}
void NetworkDevice::close() {
m_bufLen = 0; // flush read buffer
Device::close();
return afterOpen();
}
void NetworkDevice::checkDevice() {
@@ -367,33 +731,4 @@ void NetworkDevice::checkDevice() {
}
}
bool NetworkDevice::available() {
return m_buffer && m_bufLen > 0;
}
ssize_t NetworkDevice::write(symbol_t value) {
m_bufLen = 0; // flush read buffer
return Device::write(value);
}
ssize_t NetworkDevice::read(symbol_t* value) {
if (available()) {
*value = m_buffer[m_bufPos];
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return 1;
}
if (m_bufSize > 0) {
ssize_t size = ::read(m_fd, m_buffer, m_bufSize);
if (size <= 0) {
return size;
}
*value = m_buffer[0];
m_bufPos = 1;
m_bufLen = size-1;
return size;
}
return Device::read(value);
}
} // namespace ebusd
+121 -56
View File
@@ -40,6 +40,26 @@ namespace ebusd {
* to a file and/or forwarding it to a logging function.
*/
/** the transfer latency of the network device [ms]. */
#define NETWORK_LATENCY_MS 10
/** the latency of the host [ms]. */
#ifdef __CYGWIN__
#define HOST_LATENCY_MS 20
#else
#define HOST_LATENCY_MS 0
#endif
/** the arbitration state handled by @a Device. */
enum ArbitrationState {
as_none, //!< no arbitration in process
as_start, //!< arbitration start requested
as_error, //!< error while sending master address
as_running, //!< arbitration currently running (master address sent, waiting for reception)
as_lost, //!< arbitration lost
as_won, //!< arbitration won
};
/**
* Interface for listening to data received on/sent to a device.
*/
@@ -56,6 +76,13 @@ class DeviceListener {
* @param received @a true on reception, @a false on sending.
*/
virtual void notifyDeviceData(symbol_t symbol, bool received) = 0; // abstract
/**
* Called to notify a status message from the device.
* @param error true for an error message, false for an info message.
* @param message the message string.
*/
virtual void notifyStatus(bool error, const char* message) = 0; // abstract
};
@@ -63,18 +90,20 @@ class DeviceListener {
* The base class for accessing an eBUS.
*/
class Device {
public:
protected:
/**
* Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param checkDevice whether to regularly check the device availability.
* @param latency the bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/
Device(const char* name, bool checkDevice, bool readOnly, bool initialSend)
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
m_listener(nullptr) {}
Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend,
bool enhancedProto=false);
public:
/**
* Destructor.
*/
@@ -83,26 +112,33 @@ class Device {
/**
* Factory method for creating a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @return the new @a Device, or nullptr on error.
* Note: the caller needs to free the created instance.
*/
static Device* create(const char* name, bool checkDevice = true, bool readOnly = false,
bool initialSend = false);
static Device* create(const char* name, unsigned int extraLatency = 0, bool checkDevice = true,
bool readOnly = false, bool initialSend = false);
/**
* Get the transfer latency of this device.
* @return the transfer latency in microseconds.
* @return the transfer latency in milliseconds.
*/
virtual unsigned int getLatency() const { return 0; }
virtual unsigned int getLatency() const { return m_latency; }
/**
* Open the file descriptor.
* @return the @a result_t code.
*/
virtual result_t open() = 0; // abstract
virtual result_t open();
/**
* Has to be called by subclasses upon successful opening the device as last action in open().
* @return the @a result_t code.
*/
result_t afterOpen();
/**
* Close the file descriptor if opened.
@@ -118,11 +154,27 @@ class Device {
/**
* Read a single byte from the device.
* @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
* @param timeout maximum time to wait for the byte in milliseconds, or 0 for infinite.
* @param value the reference in which the received byte value is stored.
* @param arbitrationState the reference in which the current @a ArbitrationState is stored on success. When set to
* @a as_won, the received byte is the master address that was successfully arbitrated with.
* @return the result_t code.
*/
result_t recv(unsigned int timeout, symbol_t* value);
result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState);
/**
* Start the arbitration with the specified master address. A subsequent request while an arbitration is currently in
* checking state will always result in @a RESULT_ERR_DUPLICATE.
* @param masterAddress the master address, or @a SYN to cancel a previous arbitration request.
* @return the result_t code.
*/
result_t startArbitration(symbol_t masterAddress);
/**
* Return whether the device is currently in arbitration.
* @return true when the device is currently in arbitration.
*/
bool isArbitrating() const { return m_arbitrationMaster != SYN; };
/**
* Return the device name.
@@ -156,37 +208,54 @@ class Device {
virtual void checkDevice() = 0; // abstract
/**
* Check whether a byte is available immediately (without waiting).
* @return true when a a byte is available immediately.
* Cancel a running arbitration.
* @param arbitrationState the reference in which @a as_error is stored when cancelled.
* @return true if it was cancelled, false if not.
*/
virtual bool available() { return false; }
bool cancelRunningArbitration(ArbitrationState* arbitrationState);
/**
* Write a single byte.
* @param value the byte value to write.
* @return the number of bytes written, or -1 on error.
* @param startArbitration true to start arbitration.
* @return true on success, false on error.
*/
virtual ssize_t write(symbol_t value) { return ::write(m_fd, &value, 1); }
virtual bool write(symbol_t value, bool startArbitration=false);
/**
* Check whether a symbol is available for reading immediately (without waiting).
* @return true when a symbol is available for reading immediately.
*/
virtual bool available();
/**
* Read a single byte.
* @param value the reference in which the read byte value is stored.
* @return the number of bytes read, or -1 on error.
* @param isAvailable the result of the immediately preceding call to @a available().
* @param arbitrationState the variable in which to store the current/received arbitration state (mandatory for enhanced proto).
* @param incomplete the variable in which to store when a partial transfer needs another poll.
* @return true on success, false on error.
*/
virtual ssize_t read(symbol_t* value) { return ::read(m_fd, value, 1); }
virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState=nullptr, bool* incomplete=nullptr);
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
const char* m_name;
/** whether to regularly check the device availability (only for serial devices). */
/** whether to regularly check the device availability. */
const bool m_checkDevice;
/** the bus transfer latency in milliseconds. */
const unsigned int m_latency;
/** whether to allow read access to the device only. */
const bool m_readOnly;
/** whether to send an initial @a ESC symbol in @a open(). */
const bool m_initialSend;
/** whether the device supports the ebusd enhanced protocol. */
const bool m_enhancedProto;
/** the opened file descriptor, or -1. */
int m_fd;
@@ -194,8 +263,27 @@ class Device {
private:
/** the @a DeviceListener, or nullptr. */
DeviceListener* m_listener;
/** the arbitration master address to send when in arbitration, or @a SYN. */
symbol_t m_arbitrationMaster;
/** true when in arbitration and the next received symbol needs to be checked against the sent master address. */
bool m_arbitrationCheck;
/** the read buffer. */
symbol_t* m_buffer;
/** the read buffer size (multiple of 4). */
size_t m_bufSize;
/** the read buffer fill length. */
size_t m_bufLen;
/** the read buffer read position. */
size_t m_bufPos;
};
/**
* The @a Device for directly connected serial interfaces (tty).
*/
@@ -204,12 +292,15 @@ class SerialDevice : public Device {
/**
* Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param checkDevice whether to regularly check the device availability.
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/
SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend)
: Device(name, checkDevice, readOnly, initialSend) {}
SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency, bool readOnly, bool initialSend,
bool enhancedProto=false)
: Device(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto) {}
// @copydoc
result_t open() override;
@@ -239,48 +330,34 @@ class NetworkDevice : public Device {
* @param address the socket address of the device.
* @param hostOrIp the host name or IP address of the device.
* @param port the TCP or UDP port of the device.
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param udp true for UDP, false to TCP.
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/
NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, bool readOnly, bool initialSend, bool udp)
: Device(name, true, readOnly, initialSend), m_hostOrIp(hostOrIp), m_port(port), m_udp(udp),
m_buffer(nullptr), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, unsigned int extraLatency, bool readOnly,
bool initialSend, bool udp, bool enhancedProto=false)
: Device(name, true, NETWORK_LATENCY_MS+extraLatency, readOnly, initialSend, enhancedProto),
m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {}
/**
* Destructor.
*/
virtual ~NetworkDevice() {
~NetworkDevice() override {
if (m_hostOrIp) {
free((void*)m_hostOrIp);
}
if (m_buffer) {
free(m_buffer);
}
}
// @copydoc
unsigned int getLatency() const override { return 10000; }
// @copydoc
result_t open() override;
// @copydoc
void close() override;
protected:
// @copydoc
void checkDevice() override;
// @copydoc
bool available() override;
// @copydoc
ssize_t write(symbol_t value) override;
// @copydoc
ssize_t read(symbol_t* value) override;
private:
/** the host name or IP address of the device. */
@@ -291,18 +368,6 @@ class NetworkDevice : public Device {
/** true for UDP, false to TCP. */
const bool m_udp;
/** the buffer memory, or nullptr. */
symbol_t* m_buffer;
/** the buffer size. */
size_t m_bufSize;
/** the buffer fill length. */
size_t m_bufLen;
/** the buffer read position. */
size_t m_bufPos;
};
} // namespace ebusd
+1
View File
@@ -44,6 +44,7 @@ const char* getResultCode(result_t resultCode) {
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost";
case RESULT_ERR_ARB_RUNNING: return "ERR: arbitration running";
case RESULT_ERR_CRC: return "ERR: CRC error";
case RESULT_ERR_ACK: return "ERR: ACK error";
case RESULT_ERR_NAK: return "ERR: NAK received";
+8 -7
View File
@@ -56,15 +56,16 @@ enum result_t {
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
RESULT_ERR_CRC = -19, //!< CRC error
RESULT_ERR_ACK = -20, //!< ACK error
RESULT_ERR_NAK = -21, //!< NAK received
RESULT_ERR_ARB_RUNNING = -19, //!< arbitration running
RESULT_ERR_CRC = -20, //!< CRC error
RESULT_ERR_ACK = -21, //!< ACK error
RESULT_ERR_NAK = -22, //!< NAK received
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
RESULT_ERR_SYMBOL = -24, //!< wrong symbol received instead of sent symbol
RESULT_ERR_NO_SIGNAL = -23, //!< no signal found on the bus
RESULT_ERR_SYN = -24, //!< SYN received instead of answer
RESULT_ERR_SYMBOL = -25, //!< wrong symbol received instead of sent symbol
RESULT_ERR_NOTAUTHORIZED = -25 //!< not authorized for this action
RESULT_ERR_NOTAUTHORIZED = -26 //!< not authorized for this action
};
+5 -5
View File
@@ -72,19 +72,19 @@ using std::vector;
typedef unsigned char symbol_t;
/** escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA. */
#define ESC 0xA9
#define ESC ((symbol_t)0xA9)
/** synchronization symbol. */
#define SYN 0xAA
#define SYN ((symbol_t)0xAA)
/** positive acknowledge symbol. */
#define ACK 0x00
#define ACK ((symbol_t)0x00)
/** negative acknowledge symbol. */
#define NAK 0xFF
#define NAK ((symbol_t)0xFF)
/** the broadcast destination address. */
#define BROADCAST 0xFE
#define BROADCAST ((symbol_t)0xFE)
/**
* Parse an unsigned int value.
Regular → Executable
-4
View File
@@ -11,10 +11,6 @@ add_executable(test_filereader test_filereader.cpp)
target_link_libraries(test_filereader ebus pthread)
add_test(filereader test_filereader)
add_executable(test_device test_device.cpp)
target_link_libraries(test_device ebus pthread ${test_LIBS})
add_test(device test_device)
add_executable(test_symbol test_symbol.cpp)
target_link_libraries(test_symbol ebus pthread)
add_test(symbol test_symbol)
Regular → Executable
-5
View File
@@ -3,7 +3,6 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
-Wno-unused-parameter
noinst_PROGRAMS = test_filereader \
test_device \
test_symbol \
test_data \
test_message
@@ -11,9 +10,6 @@ noinst_PROGRAMS = test_filereader \
test_filereader_SOURCES = test_filereader.cpp
test_filereader_LDADD = ../libebus.a -lpthread
test_device_SOURCES = test_device.cpp
test_device_LDADD = ../libebus.a -lpthread
test_symbol_SOURCES = test_symbol.cpp
test_symbol_LDADD = ../libebus.a -lpthread
@@ -24,7 +20,6 @@ test_message_SOURCES = test_message.cpp
test_message_LDADD = ../libebus.a -lpthread
if CONTRIB
test_device_LDADD += ../contrib/libebuscontrib.a
test_data_LDADD += ../contrib/libebuscontrib.a
test_message_LDADD += ../contrib/libebuscontrib.a
endif
+3
View File
@@ -175,6 +175,9 @@ void closeLogFile() {
}
bool needsLog(const LogFacility facility, const LogLevel level) {
if (s_logFile == nullptr && !s_useSyslog) {
return false;
}
return s_facilityLogLevel[facility] >= level;
}
+16
View File
@@ -50,6 +50,22 @@ bool RotateFile::setEnabled(bool enabled) {
if (enabled) {
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
m_fileSize = 0;
#ifdef FORWARD_RAW_TTY
if (!m_textMode && isatty(fileno(m_stream)) == 1) {
int fd = fileno(m_stream);
struct termios newSettings;
memset(&newSettings, 0, sizeof(newSettings));
cfsetspeed(&newSettings, B2400);
newSettings.c_cflag |= (CS8 | CLOCAL);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors
newSettings.c_oflag &= ~OPOST;
// activate new settings of serial device
tcsetattr(fd, TCSANOW, &newSettings);
}
#endif
}
return true;
}
+34 -5
View File
@@ -84,25 +84,54 @@ WaitThread::~WaitThread() {
void WaitThread::stop() {
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
Thread::stop();
pthread_mutex_unlock(&m_mutex);
}
bool WaitThread::join() {
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
stop();
return Thread::join();
}
bool WaitThread::Wait(int seconds) {
pthread_mutex_lock(&m_mutex);
struct timespec t;
clockGettime(&t);
t.tv_sec += seconds;
pthread_mutex_lock(&m_mutex);
pthread_cond_timedwait(&m_cond, &m_mutex, &t);
pthread_mutex_unlock(&m_mutex);
return isRunning();
}
NotifiableThread::NotifiableThread()
: WaitThread(), m_notified(false) {
}
void NotifiableThread::notify() {
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
m_notified = true;
pthread_mutex_unlock(&m_mutex);
}
bool NotifiableThread::waitNotified(int millis) {
pthread_mutex_lock(&m_mutex);
if (!m_notified) {
struct timespec t;
clockGettime(&t);
t.tv_sec += millis / 1000000000;
t.tv_nsec += (millis % 1000000000) * 1000000;
if (t.tv_nsec > 1000000000) {
t.tv_sec++;
t.tv_nsec -= 1000000000;
}
pthread_cond_timedwait(&m_cond, &m_mutex, &t);
}
bool notified = m_notified;
m_notified = false;
pthread_mutex_unlock(&m_mutex);
return notified;
}
} // namespace ebusd
+33 -4
View File
@@ -1,6 +1,6 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2018 John Baier <ebusd@ebusd.eu>, Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) 2014-2020 John Baier <ebusd@ebusd.eu>, Roland Jax 2012-2014 <ebusd@liwest.at>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@ namespace ebusd {
/** \file lib/utils/thread.h */
/**
* wrapper class for pthread.
* Wrapper class for pthread.
*/
class Thread {
public:
@@ -82,7 +82,7 @@ class Thread {
/**
* Thread entry method to be overridden by derived class.
*/
virtual void run() = 0;
virtual void run() = 0; // abstract
private:
@@ -134,7 +134,7 @@ class WaitThread : public Thread {
bool Wait(int seconds);
private:
protected:
/** the mutex for waiting. */
pthread_mutex_t m_mutex;
@@ -143,6 +143,35 @@ class WaitThread : public Thread {
};
/**
* A @a WaitThread that can be waited on.
*/
class NotifiableThread : public WaitThread {
public:
/**
* Constructor.
*/
NotifiableThread();
/**
* Notify another thread currently in @a wait().
*/
void notify();
/**
* Wait for getting notified up to the specified amount of time.
* @param millis the maximum number of milliseconds to wait.
* @return true if @a notify() was called while waiting.
*/
bool waitNotified(int millis);
private:
/** whether @a notify() was called while waiting. */
bool m_notified;
};
/**
* A simple mutex.
*/
+5 -2
View File
@@ -1,5 +1,6 @@
set(ebusctl_SOURCES ebusctl.cpp)
set(ebusfeed_SOURCES ebusfeed.cpp)
set(ebuspicloader_SOURCES ebuspicloader.cpp intelhex/intelhexclass.cpp)
if(HAVE_CONTRIB)
set(ebusfeed_LIBS ${ebusfeed_LIBS} ebuscontrib)
@@ -7,11 +8,13 @@ endif(HAVE_CONTRIB)
include_directories(../lib/ebus)
include_directories(../lib/utils)
include_directories(intelhex)
add_executable(ebusctl ${ebusctl_SOURCES})
add_executable(ebusfeed ${ebusfeed_SOURCES})
add_executable(ebuspicloader ${ebuspicloader_SOURCES})
target_link_libraries(ebusctl utils ebus ${LIB_ARGP} ${ebusctl_LIBS})
target_link_libraries(ebusfeed ebus ${LIB_ARGP} ${ebusfeed_LIBS})
target_link_libraries(ebuspicloader ${LIB_ARGP})
install(TARGETS ebusctl EXPORT ebusd DESTINATION usr/bin)
install(TARGETS ebusctl ebuspicloader EXPORT ebusd DESTINATION usr/bin)
+4 -1
View File
@@ -2,7 +2,8 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
-isystem$(top_srcdir)
bin_PROGRAMS = ebusctl \
ebusfeed
ebusfeed \
ebuspicloader
ebusctl_SOURCES = ebusctl.cpp
ebusctl_LDADD = ../lib/utils/libutils.a
@@ -11,6 +12,8 @@ ebusfeed_SOURCES = ebusfeed.cpp
ebusfeed_LDADD = ../lib/utils/libutils.a \
../lib/ebus/libebus.a
ebuspicloader_SOURCES = ebuspicloader.cpp
if CONTRIB
ebusfeed_LDADD += ../lib/ebus/contrib/libebuscontrib.a
endif
+864
View File
@@ -0,0 +1,864 @@
#include <iostream>
#include <fstream>
#include <fcntl.h>
#include <poll.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
#include <cstdlib>
#include <iomanip>
#include <string>
#include <cstring>
#include <argp.h>
#include "intelhex/intelhexclass.h"
/** the version string of the program. */
const char *argp_program_version = "eBUS adapter PIC firmware loader";
/** the documentation of the program. */
static const char argpdoc[] =
"A tool for loading firmware to the eBUS adapter PIC."
"\vPORT is the serial port to use (e.g./dev/ttyUSB0)";
static const char argpargsdoc[] = "PORT";
/** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = {
{"verbose", 'v', nullptr, 0, "enable verbose output", 0 },
{"dhcp", 'd', nullptr, 0, "set IP address to DHCP", 0 },
{"ip", 'i', "IP", 0, "set IP address (e.g. 192.168.0.10)", 0 },
{"mask", 'm', "MASK", 0, "set IP mask (e.g. 24)", 0 },
{"macip", 'M', nullptr, 0, "set the MAC address suffix from the IP address", 0 },
{"flash", 'f', "FILE", 0, "flash the FILE to the device", 0 },
{"reset", 'r', nullptr, 0, "reset the device at the end on success", 0 },
{nullptr, 0, nullptr, 0, nullptr, 0 },
};
static bool verbose = false;
static bool setDhcp = false;
static bool setIp = false;
static uint8_t setIpAddress[] = {0, 0, 0, 0};
static bool setMacFromIp = false;
static bool setMask = false;
static uint8_t setMaskLen = 0x1f;
static char* flashFile = nullptr;
static bool reset = false;
bool parseByte(const char *arg, uint8_t minValue, uint8_t maxValue, uint8_t *result) {
char* strEnd = nullptr;
unsigned long value = 0;
strEnd = nullptr;
value = strtoul(arg, &strEnd, 10);
if (strEnd == nullptr || strEnd == arg || *strEnd != 0) {
return false;
}
if (value<minValue || value>maxValue) {
return false;
}
*result = (uint8_t)value;
return true;
}
bool parseShort(const char *arg, uint16_t minValue, uint16_t maxValue, uint16_t *result) {
char* strEnd = nullptr;
unsigned long value = 0;
strEnd = nullptr;
value = strtoul(arg, &strEnd, 10);
if (strEnd == nullptr || strEnd == arg || *strEnd != 0) {
return false;
}
if (value<minValue || value>maxValue) {
return false;
}
*result = (uint16_t)value;
return true;
}
error_t parse_opt(int key, char *arg, struct argp_state *state) {
char *ip = nullptr, *part = nullptr;
int pos = 0, sum = 0;
struct stat st;
switch (key) {
case 'v': // --verbose
verbose = true;
break;
case 'd': // --dhcp
if (setIp || setMask) {
argp_error(state, "either DHCP or IP address is needed");
return EINVAL;
}
setDhcp = true;
break;
case 'i': // --ip=192.168.0.10
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid IP address");
return EINVAL;
}
if (setDhcp) {
argp_error(state, "either DHCP or IP address is needed");
return EINVAL;
}
ip = strdup(arg);
part = strtok(ip, ".");
for (pos=0; part && pos<4; pos++) {
if (!parseByte(part, 0, 255, setIpAddress+pos)) {
break;
}
sum += setIpAddress[pos];
part = strtok(nullptr, ".");
}
free(ip);
if (pos!=4 || part || sum==0) {
argp_error(state, "invalid IP address");
return EINVAL;
}
setIp = true;
break;
case 'm':
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid IP mask");
return EINVAL;
}
if (setDhcp) {
argp_error(state, "either DHCP or IP address is needed");
return EINVAL;
}
if (!parseByte(arg, 0, 0x1e, &setMaskLen)) {
argp_error(state, "invalid IP mask");
return EINVAL;
}
setMask = true;
break;
case 'M':
setMacFromIp = true;
break;
case 'f':
if (arg == nullptr || arg[0] == 0 || stat(arg, &st) != 0 || !S_ISREG(st.st_mode)) {
argp_error(state, "invalid flash file");
return EINVAL;
}
flashFile = arg;
break;
case 'r':
reset = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
// START: copy from generated bootloader
#define WRITE_FLASH_BLOCKSIZE 32
#define ERASE_FLASH_BLOCKSIZE 32
#define END_FLASH 0x4000
// Frame Format
//
// [<COMMAND><DATALEN><ADDRL><ADDRH><ADDRU><...DATA...>]
// These values are negative because the FSR is set to PACKET_DATA to minimize FSR reloads.
typedef union
{
struct __attribute__((__packed__))
{
uint8_t command;
uint16_t data_length;
uint8_t EE_key_1;
uint8_t EE_key_2;
uint8_t address_L;
uint8_t address_H;
uint8_t address_U;
uint8_t address_unused;
uint8_t data[2*WRITE_FLASH_BLOCKSIZE];
};
uint8_t buffer[2*WRITE_FLASH_BLOCKSIZE+9];
}frame_t;
#define STX 0x55
#define READ_VERSION 0
#define READ_FLASH 1
#define WRITE_FLASH 2
#define ERASE_FLASH 3
#define READ_EE_DATA 4
#define WRITE_EE_DATA 5
#define READ_CONFIG 6
#define WRITE_CONFIG 7
#define CALC_CHECKSUM 8
#define RESET_DEVICE 9
#define CALC_CRC 10
#define MINOR_VERSION 0x08 // Version
#define MAJOR_VERSION 0x00
//#define STX 0x55 // Actually code 0x55 is 'U' But this is what the autobaud feature of the PIC16F1 EUSART is looking for
#define ERROR_ADDRESS_OUT_OF_RANGE 0xFE
#define ERROR_INVALID_COMMAND 0xFF
#define COMMAND_SUCCESS 0x01
// END: copy from generated bootloader
#define FRAME_HEADER_LEN 9
#define FRAME_MAX_LEN (FRAME_HEADER_LEN+2*WRITE_FLASH_BLOCKSIZE)
#define BAUDRATE B115200
#define WAIT_BYTE_TRANSFERRED_MILLIS 200
#define WAIT_BITRATE_DETECTION_MILLIS 80
#define WAIT_RESPONSE_TIMEOUT_MILLIS 100
long long getTime() {
timespec_t ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec*1000+ts.tv_nsec/1000000;
}
ssize_t waitWrite(int fd, uint8_t *data, size_t len, int timeoutMillis) {
int ret;
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLOUT | POLLERR | POLLHUP;
ret = poll(&pfd, 1, timeoutMillis);
if (ret >= 0 && pfd.revents & (POLLERR | POLLHUP)) {
return -1;
}
if (ret <= 0) {
return ret;
}
ret = write(fd, data, len);
if (ret<0) {
return ret;
}
#ifdef DEBUG_RAW
std::cout<<"> "<<std::dec<<static_cast<unsigned>(ret)<<"/"<<static_cast<unsigned>(len)<<":"<<std::hex;
for (int pos = 0; pos<ret; pos++) {
std::cout<<" "<<std::setw(2)<<std::setfill('0')<<static_cast<unsigned>(data[pos]);
}
std::cout<<std::endl;
#endif
return ret;
}
ssize_t waitRead(int fd, uint8_t *data, size_t len, int timeoutMillis) {
int ret;
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN | POLLERR | POLLHUP;
ret = poll(&pfd, 1, timeoutMillis);
if (ret >= 0 && pfd.revents & (POLLERR | POLLHUP)) {
return -1;
}
if (ret <= 0) {
return ret;
}
ret = read(fd, data, len);
if (ret<0) {
return ret;
}
#ifdef DEBUG_RAW
std::cout<<"< "<<std::dec<<static_cast<unsigned>(ret)<<"/"<<static_cast<unsigned>(len)<<":"<<std::hex;
for (int pos = 0; pos<ret; pos++) {
std::cout<<" "<<std::setw(2)<<std::setfill('0')<<static_cast<unsigned>(data[pos]);
}
std::cout<<std::endl;
#endif
return ret;
}
ssize_t sendReceiveFrame(int fd, frame_t& frame, size_t sendDataLen, ssize_t fixReceiveDataLen,
int responseTimeoutExtraMillis=0, bool hideErrors=false) {
// send 0x55 for auto baud detection in PIC
unsigned char ch = STX;
ssize_t cnt = waitWrite(fd, &ch, 1, WAIT_BYTE_TRANSFERRED_MILLIS);
if (cnt < 0) {
if (!hideErrors) {
std::cerr << "write sync failed" << std::endl;
}
return cnt;
}
if (cnt == 0) {
if (!hideErrors) {
std::cerr << "write sync timed out" << std::endl;
}
return cnt;
}
// wait for bitrate detection to finish in PIC
usleep(WAIT_BITRATE_DETECTION_MILLIS*1000);
uint8_t writeCommand = frame.command;
size_t len = FRAME_HEADER_LEN+sendDataLen;
size_t noData = 0;
for (size_t pos=0; pos<len; ) {
cnt = waitWrite(fd, frame.buffer+pos, len-pos, WAIT_BYTE_TRANSFERRED_MILLIS);
if (cnt<0) {
if (!hideErrors) {
std::cerr << "write data failed" << std::endl;
}
return cnt;
}
if (cnt==0) {
if (!hideErrors) {
std::cerr << "write data timed out" << std::endl;
}
return -1;
}
pos += cnt;
}
cnt = waitRead(fd, &ch, 1, WAIT_RESPONSE_TIMEOUT_MILLIS + responseTimeoutExtraMillis);
if (cnt<0) {
if (!hideErrors) {
std::cerr << "read sync failed" << std::endl;
}
return cnt;
}
if (cnt==0) {
if (!hideErrors) {
std::cerr << "read sync timed out" << std::endl;
}
return -1;
}
if (ch!=STX) {
if (!hideErrors) {
std::cerr << "did not receive sync: 0x" << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<unsigned>(ch) << std::endl;
}
return -1;
}
// read the answer from the device
len = FRAME_HEADER_LEN; // start with the header itself
noData = 0;
for (size_t pos=0; pos<len; ) {
cnt = waitRead(fd, frame.buffer+pos, len-pos, WAIT_BYTE_TRANSFERRED_MILLIS);
if (cnt<0) {
if (!hideErrors) {
std::cerr << "read data failed" << std::endl;
}
return cnt;
}
if (cnt==0) {
if (!hideErrors) {
std::cerr << "read data timed out" << std::endl;
}
return -1;
}
pos += cnt;
if (pos==FRAME_HEADER_LEN) {
if (fixReceiveDataLen<0) {
len += frame.data_length;
} else {
len += fixReceiveDataLen;
}
fixReceiveDataLen = 0;
}
}
uint8_t dummy[4];
waitRead(fd, dummy, 4, WAIT_BYTE_TRANSFERRED_MILLIS); // read away potential nonsense tail
if (frame.command!=writeCommand) {
if (!hideErrors) {
std::cerr << "unexpected answer" << std::endl;
}
return -1;
}
return 0;
}
int readVersion(int fd, bool verbose=true) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = READ_VERSION;
ssize_t ret = sendReceiveFrame(fd, frame, 0, 16);
if (ret!=0) {
return ret;
}
if (frame.data[0] != MINOR_VERSION || frame.data[1] != MAJOR_VERSION) {
std::cerr<<"unexpected version"<<std::endl;
return -1;
}
if (verbose) {
std::cout << "Max packet size: " << static_cast<unsigned>(frame.data[2] | (frame.data[3] << 8)) << std::endl;
}
std::cout<<"Device ID: "<<std::setfill('0')<<std::setw(4)<<std::hex<<static_cast<unsigned>(frame.data[6] | (frame.data[7]<<8));
if (frame.data[6]==0xb0 && frame.data[7]==0x30) {
std::cout<<" (PIC16F15356)";
}
std::cout<<std::endl;
if (verbose) {
std::cout<<"Blocksize erase: "<<std::dec<<static_cast<unsigned>(frame.data[10])<<std::endl;
std::cout<<"Blocksize write: "<<std::dec<<static_cast<unsigned>(frame.data[11])<<std::endl;
std::cout<<"User ID 1: "<<std::setfill('0')<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.data[12])<<std::endl;
std::cout<<"User ID 2: "<<std::setfill('0')<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.data[13])<<std::endl;
std::cout<<"User ID 3: "<<std::setfill('0')<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.data[14])<<std::endl;
std::cout<<"User ID 4: "<<std::setfill('0')<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.data[15])<<std::endl;
}
return 0;
}
int printFrameData(frame_t frame, bool skipHigh) {
uint16_t address = (frame.address_H<<8)|frame.address_L;
int pos;
std::cout<<std::hex;
for (pos = 0; pos<frame.data_length;) {
if ((pos%16)==0) {
std::cout<<std::setw(4)<<static_cast<unsigned>(address)<<":";
}
std::cout<<" "<<std::setw(2)<<static_cast<unsigned>(frame.data[pos++]);
if (skipHigh) {
pos++;
} else if (pos<frame.data_length) {
std::cout<<" "<<std::setw(2)<<static_cast<unsigned>(frame.data[pos++]);
}
address++;
if ((pos%16)==0) {
std::cout<<std::endl;
}
}
if ((pos%16)!=0) {
std::cout<<std::endl;
}
return 0;
}
int printFrame(frame_t frame) {
std::cout<<"command: 0x"<<std::setfill('0')<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.command)<<std::endl;
std::cout<<"data_length: "<<std::dec<<static_cast<unsigned>(frame.data_length)<<std::endl;
std::cout<<"address: 0x"<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.address_H)<<std::setw(2)<<std::hex<<static_cast<unsigned>(frame.address_L);
for (int pos = 0; pos<frame.data_length; ) {
if ((pos%16)==0) {
std::cout<<std::endl<<std::setw(4)<<static_cast<unsigned>(pos)<<":"<<std::endl;
}
std::cout<<" "<<std::setw(2)<<static_cast<unsigned>(frame.data[pos++]);
pos++;
}
std::cout<<std::endl;
return 0;
}
int readConfig(int fd, uint16_t address, uint16_t len, bool skipHigh=false, bool print=true, uint8_t* storeData=nullptr) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = READ_CONFIG;
frame.data_length = len;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
ssize_t ret = sendReceiveFrame(fd, frame, 0, len);
if (ret!=0) {
return ret;
}
if (print) {
printFrameData(frame, skipHigh);
}
if (storeData) {
memcpy(storeData, frame.data, len);
}
return 0;
}
int writeConfig(int fd, uint16_t address, uint16_t len, uint8_t* data) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = WRITE_CONFIG;
frame.data_length = len;
frame.EE_key_1 = 0x55;
frame.EE_key_2 = 0xaa;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
memcpy(frame.data, data, len);
ssize_t ret = sendReceiveFrame(fd, frame, len, 1, 50);
if (ret!=0) {
return ret;
}
if (frame.data[0]!=COMMAND_SUCCESS) {
return -1;
}
return 0;
}
int readFlash(int fd, uint16_t address, bool skipHigh=false, bool print=true, uint8_t* storeData=nullptr) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = READ_FLASH;
frame.data_length = 0x10;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
ssize_t ret = sendReceiveFrame(fd, frame, 0, -1);
if (ret!=0) {
return ret;
}
if (print) {
printFrameData(frame, skipHigh);
}
if (storeData) {
memcpy(storeData, frame.data, 0x10);
}
return 0;
}
int writeFlash(int fd, uint16_t address, uint16_t len, uint8_t* data, bool hideErrors=false) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = WRITE_FLASH;
frame.data_length = len;
frame.EE_key_1 = 0x55;
frame.EE_key_2 = 0xaa;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
memcpy(frame.data, data, len);
ssize_t ret = sendReceiveFrame(fd, frame, len, 1, len*30, hideErrors);
if (ret!=0) {
return ret;
}
if (frame.data[0]!=COMMAND_SUCCESS) {
return -1;
}
return 0;
}
int eraseFlash(int fd, uint16_t address, uint16_t len) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = ERASE_FLASH;
frame.data_length = (len+ERASE_FLASH_BLOCKSIZE-1)/ERASE_FLASH_BLOCKSIZE;
frame.EE_key_1 = 0x55;
frame.EE_key_2 = 0xaa;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
ssize_t ret = sendReceiveFrame(fd, frame, 0, 1, frame.data_length*5);
if (ret!=0) {
return ret;
}
if (frame.data[0]!=COMMAND_SUCCESS) {
return -frame.data[0]-1;
}
return 0;
}
int calcChecksum(int fd, uint16_t address, uint16_t len) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = CALC_CHECKSUM;
frame.data_length = len;
frame.address_L = address&0xff;
frame.address_H = (address>>8)&0xff;
ssize_t ret = sendReceiveFrame(fd, frame, 0, 2, len*30);
if (ret!=0) {
return ret;
}
return frame.data[0] | (frame.data[1]<<8);
}
int resetDevice(int fd) {
frame_t frame;
memset(frame.buffer, 0, FRAME_MAX_LEN);
frame.command = RESET_DEVICE;
ssize_t ret = sendReceiveFrame(fd, frame, 0, 1);
if (ret!=0) {
return ret;
}
if (frame.data[0]!=COMMAND_SUCCESS) {
return -frame.data[0]-1;
}
return 0;
}
struct termios termios_original;
int openSerial(std::string port) {
// open serial port
int fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // non-blocking IO: | O_NONBLOCK);
if (fd == -1) {
std::cerr<<"unable to open "<<port<<std::endl;
return -1;
}
// backup terminal settings
if (tcgetattr(fd, &termios_original)!=0) {
memset(&termios_original, 0, sizeof(termios_original));
}
// configure terminal settings
struct termios termios = termios_original;
if (cfsetspeed(&termios, BAUDRATE)!=0) {
std::cerr<<"unable to set speed "<<std::endl;
close(fd);
return -1;
}
termios.c_iflag = 0;//IGNPAR;//IGNBRK|IGNPAR|IGNCR;
termios.c_oflag = 0;
termios.c_cflag = CS8 | CREAD | CLOCAL;
termios.c_lflag = 0;
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &termios)!=0) {
std::cerr<<"unable to set serial "<<std::endl;
close(fd);
return -1;
}
return fd;
}
void closeSerial(int fd) {
tcsetattr(fd, TCSANOW, &termios_original);
close(fd);
}
bool flashPic(int fd) {
std::ifstream inStream;
inStream.open(flashFile, ifstream::in);
if (!inStream.good()) {
std::cerr<<"unable to open file"<<std::endl;
return false;
}
intelhex ih;
// if (verbose) {
// ih.verboseOn();
// }
inStream >> ih;
if (ih.getNoErrors()>0 || ih.getNoWarnings()>0) {
std::cerr<<"errors or warnings while reading the file:"<<std::endl;
string str;
while (ih.popNextWarning(str)) {
std::cerr<<"warning: "<<str<<std::endl;
}
while (ih.popNextError(str)) {
std::cerr<<"error: "<<str<<std::endl;
}
return false;
}
unsigned long startAddr = 0, endAddr = 0;
if (!ih.startAddress(&startAddr) || !ih.endAddress(&endAddr)) {
std::cerr<<"unable to read file"<<std::endl;
return false;
}
if (verbose) {
std::cout << "flashing bytes 0x"
<< std::hex << std::setfill('0') << std::setw(4) << static_cast<unsigned>(startAddr)
<< " - 0x"
<< std::hex << std::setfill('0') << std::setw(4) << static_cast<unsigned>(endAddr)
<< std::endl;
}
if (startAddr<0x800 || endAddr>=0x8000 || endAddr<startAddr || (startAddr&0xf)!=0) {
std::cerr<<"invalid address range"<<std::endl;
return false;
}
ih.begin();
uint8_t buf[WRITE_FLASH_BLOCKSIZE];
unsigned long nextAddr = ih.currentAddress();
if (nextAddr!=0x800) {
std::cerr<<"unexpected start address in file: 0x"<<std::hex<<std::setfill('0')<<std::setw(4)<<static_cast<unsigned>(nextAddr)<<std::endl;
return false;
}
unsigned long blockStart = 0x800;
uint16_t checkSum = 0;
int eraseRes = eraseFlash(fd, blockStart/2, (endAddr-blockStart)/2);
if (eraseRes!=0) {
std::cerr << "erasing flash failed: "<< static_cast<signed>(-eraseRes-1)<<std::endl;
return false;
}
std::cout << "erasing flash: done." << std::endl;
std::cout << "flashing:" << std::endl;
size_t blocks = 0;
while (blockStart<endAddr) {
bool blank = true;
for (int pos = 0; pos < WRITE_FLASH_BLOCKSIZE; pos++, nextAddr++) {
unsigned long addr = ih.currentAddress();
uint8_t value = (pos&0x1)==1?0x3f:0xff;
if (addr == nextAddr && ih.getData(&value)) {
ih.incrementAddress();
blank = false;
}
buf[pos] = value;
checkSum += ((uint16_t)value)<<((pos&0x1)*8);
}
if (!blank) {
if (blocks==0) {
std::cout<<std::endl<<"0x"<<std::hex<<std::setfill('0')<<std::setw(4)<<static_cast<unsigned>(blockStart/2)<<" ";
}
if (writeFlash(fd, blockStart/2, WRITE_FLASH_BLOCKSIZE, buf, true)!=0) {
// repeat once silently:
if (writeFlash(fd, blockStart/2, WRITE_FLASH_BLOCKSIZE, buf)!=0) {
std::cerr << "unable to write flash at 0x" << std::hex << std::setfill('0') << std::setw(4)
<< static_cast<unsigned>(blockStart/2) << std::endl;
return false;
}
}
std::cout<<".";
if (++blocks>=64) {
blocks = 0;
}
std::cout.flush();
}
blockStart += WRITE_FLASH_BLOCKSIZE;
}
std::cout<<std::endl<<"flashing finished."<<std::endl;
int picSum = calcChecksum(fd, startAddr/2, blockStart-startAddr);
if (picSum<0) {
std::cout<<"unable to read checksum."<<std::endl;
return false;
}
if (picSum!=checkSum) {
std::cout<<"unexpected checksum."<<std::endl;
return false;
}
std::cout<<"flashing succeeded."<<std::endl;
return true;
}
void readIpSettings(int fd) {
uint8_t mac[] = {0xae, 0xb0, 0x53, 0xef, 0xfe, 0xef}; // "Adapter-eBUS3" + (UserID or MUI)
uint8_t ip[4] = {0, 0, 0, 0};
uint8_t mask[4] = {255, 255, 255, 0};
bool useMUI = true;
uint8_t maskLen = 0;
uint8_t configData[8];
readConfig(fd, 0x0000, 8, false, false, configData); // User ID
useMUI = (configData[1]&0x20)!=0; // if highest bit is set, then use MUI. if cleared, use User ID
maskLen = configData[1]&0x1f;
for (int i=0; i<4; i++) {
ip[i] = configData[i*2];
if (!useMUI && i>0) {
mac[2+i] = configData[i*2];
}
}
if (useMUI) {
// read MUI to build uniqueMAC address
// start with MUI6, end with MUI8 (MUI9 is reserved)
readConfig(fd, 0x0106, 8, true, false, configData); // MUI
for (int i=0; i<3; i++) {
mac[3+i] = configData[i*2];
}
}
std::cout<<"MAC address:";
for (int i=0; i<6; i++) {
std::cout<<(i==0?' ':':')<<std::hex<<std::setw(2)<<std::setfill('0')<<static_cast<unsigned>(mac[i]);
}
std::cout<<std::endl;
if (maskLen==0x1f || (ip[0]|ip[1]|ip[2]|ip[3])==0) {
std::cout<<"IP address: DHCP"<<std::endl;
} else {
std::cout<<"IP address:";
for (int i=0; i<4; i++) {
std::cout<<(i==0?' ':'.')<<std::dec<<static_cast<unsigned>(ip[i]);
}
std::cout<<"/"<<std::dec<<static_cast<unsigned>(maskLen)<<std::endl;
/*
// build gateway
for (uint8_t pos=0; pos<4; pos++) {
mask[pos] = maskLen>=8 ? 255 : maskLen<=0 ? 0 : (255^((1<<(8-maskLen))-1));
ip[pos] &= mask[pos];
maskLen = maskLen>=8 ? maskLen-8 : 0;
}
ip[3] |= 1; // first address in network is used as gateway (not needed anyway))
std::cout<<"IP gateway:";
for (int i=0; i<4; i++) {
std::cout<<(i==0?' ':'.')<<std::dec<<static_cast<unsigned>(ip[i]);
}
std::cout<<std::endl;
*/
}
}
bool writeIpSettings(int fd) {
std::cout << "Writing IP settings: ";
uint8_t configData[] = {0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f};
if (setMacFromIp) {
configData[1] &= ~0x20; // set useMUI
}
configData[1] = (configData[1]&~0x1f) | (setMaskLen&0x1f);
if (setIp) {
for (int i = 0; i < 4; i++) {
configData[i * 2] = setIpAddress[i];
}
}
if (writeConfig(fd, 0x0000, 8, configData) != 0) {
std::cerr << "failed" << std::endl;
return false;
}
std::cout << "done." << std::endl;
return true;
}
int main(int argc, char* argv[]) {
struct argp aargp = { argpoptions, parse_opt, argpargsdoc, argpdoc, nullptr, nullptr, nullptr };
int arg_index = -1;
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, nullptr) != 0) {
std::cerr<<"invalid arguments"<<std::endl;
exit(EXIT_FAILURE);
}
if (setIp != setMask || setMacFromIp && !setIp) {
std::cerr<<"incomplete IP arguments"<<std::endl;
arg_index = argc; // force help output
}
if (argc-arg_index<1) {
argp_help(&aargp, stderr, 0, nullptr);
exit(EXIT_FAILURE);
}
int fd = openSerial(argv[arg_index]);
if (fd<0) {
exit(EXIT_FAILURE);
}
// read version
if (readVersion(fd, verbose)==0) {
uint8_t data[0x10];
if (verbose) {
std::cout << "User ID:" << std::endl;
readConfig(fd, 0x0000, 8); // User ID
std::cout << "Rev ID, Device ID:" << std::endl;
}
readConfig(fd, 0x0005, 4, false, verbose, data); // Rev ID and Device ID
std::cout << "Device revision: " << static_cast<unsigned>(((data[1]&0xf)<<2) | ((data[0]&0xc0)>>6))
<< "." << static_cast<unsigned>(data[0]&0x3f) << std::endl;
if (verbose) {
std::cout << "Configuration words:" << std::endl;
readConfig(fd, 0x0007, 5*2); // Configuration Words
std::cout << "MUI:" << std::endl;
readConfig(fd, 0x0100, 9*2, true); // MUI
std::cout<<"EUI:"<<std::endl;
readConfig(fd, 0x010a, 8*2); // EUI
}
if (verbose) {
std::cout<<"Flash:"<<std::endl;
}
readFlash(fd, 0x0000, false, false, data);
int bootloaderVersion = -1;
if (data[0x2*2]==0xab && data[0x2*2+1]==0x34 && data[0x3*2+1]==0x34) {
bootloaderVersion = data[0x3*2];
std::cout<<"Bootloader version: "<< static_cast<unsigned>(bootloaderVersion) <<std::endl;
} else {
std::cerr<<"Bootloader version not found"<<std::endl;
}
readFlash(fd, 0x0400, false, false, data);
int firmwareVersion = -1;
if (data[0x2*2]==0xae && data[0x2*2+1]==0x34 && data[0x3*2+1]==0x34) {
firmwareVersion = data[0x3*2];
std::cout<<"Firmware version: "<< static_cast<unsigned>(firmwareVersion) <<std::endl;
} else {
std::cout<<"Firmware version not found"<<std::endl;
}
readIpSettings(fd);
bool success = true;
if (flashFile) {
if (!flashPic(fd)) {
success = false;
}
}
if (setIp || setDhcp) {
if (writeIpSettings(fd)) {
readIpSettings(fd);
} else {
success = false;
}
}
if (reset && success) {
std::cout << "resetting device." << std::endl;
resetDevice(fd);
}
}
closeSerial(fd);
return 0;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2012 - Stuart Cording
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 THE
AUTHORS OR COPYRIGHT HOLDERS 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.