result_t BusHandler::handleSymbol() { long timeout = SYN_TIMEOUT; unsigned char sendSymbol = ESC; bool sending = false; BusRequest* startRequest = NULL; // 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 : SIGNAL_TIMEOUT; break; case bs_skip: timeout = SYN_TIMEOUT; break; case bs_ready: if (m_currentRequest != NULL) setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up if (m_remainLockCount == 0 && m_currentRequest == NULL) { startRequest = m_nextRequests.next(false); if (startRequest == NULL && m_pollInterval > 0) { // check for poll/scan time_t now; time(&now); if (m_lastPoll == 0 || difftime(now, m_lastPoll) > m_pollInterval) { Message* message = m_messages->getNextPoll(); if (message != NULL) { m_lastPoll = now; PollRequest* request = new PollRequest(message); result_t ret = request->prepare(m_ownMasterAddress); if (ret != RESULT_OK) { logError(lf_bus, "prepare poll message: %s", getResultCode(ret)); delete request; } else { startRequest = request; m_nextRequests.add(request); } } } } if (startRequest != NULL) { // initiate arbitration sendSymbol = m_ownMasterAddress; sending = true; } } break; case bs_recvCmd: case bs_recvCmdAck: timeout = m_slaveRecvTimeout; break; case bs_recvRes: if (m_response.size() > 0 || m_slaveRecvTimeout > SYN_TIMEOUT) timeout = m_slaveRecvTimeout; else timeout = SYN_TIMEOUT; break; case bs_recvResAck: timeout = m_slaveRecvTimeout; break; case bs_sendCmd: if (m_currentRequest != NULL) { sendSymbol = m_currentRequest->m_master[m_nextSendPos]; // escaped command sending = true; } break; case bs_sendResAck: if (m_currentRequest != NULL) { sendSymbol = m_responseCrcValid ? ACK : NAK; sending = true; } break; case bs_sendCmdAck: if (m_currentRequest != NULL) { sendSymbol = m_commandCrcValid ? ACK : NAK; sending = true; } break; case bs_sendRes: if (m_currentRequest != NULL) { sendSymbol = m_response[m_nextSendPos]; // escaped response sending = true; } break; case bs_sendSyn: sendSymbol = SYN; sending = true; break; } // send symbol if necessary result_t result; if (sending) { result = m_device->send(sendSymbol); if (result == RESULT_OK) if (m_state == bs_ready) timeout = m_busAcquireTimeout; else timeout = SEND_TIMEOUT; else { sending = false; timeout = SYN_TIMEOUT; if (startRequest != NULL && m_nextRequests.remove(startRequest)) { m_currentRequest = startRequest; // force the failed request to be notified } setState(bs_skip, result); } } // receive next symbol (optionally check reception of sent symbol) unsigned char recvSymbol; result = m_device->recv(timeout, recvSymbol); if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 && 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) { recvSymbol = ESC; result = m_device->recv(SEND_TIMEOUT, recvSymbol); 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 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"); } } return setState(bs_skip, result); } time_t now; time(&now); if (result != RESULT_OK) { 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) return setState(bs_noSignal, result); return setState(bs_skip, result); } m_lastReceive = now; if (recvSymbol == SYN) { if (!sending && m_remainLockCount > 0 && m_command.size() != 1) m_remainLockCount--; else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority) return setState(bs_ready, RESULT_ERR_SYN); } unsigned int headerLen, crcPos; switch (m_state) { case bs_noSignal: return setState(bs_skip, RESULT_OK); case bs_skip: return RESULT_OK; case bs_ready: if (startRequest != NULL && sending) { if (!m_nextRequests.remove(startRequest)) { // request already removed (e.g. due to timeout) return setState(bs_skip, RESULT_ERR_TIMEOUT); } m_currentRequest = startRequest; // check arbitration if (recvSymbol == sendSymbol) { // arbitration successful m_nextSendPos = 1; m_repeat = false; return setState(bs_sendCmd, RESULT_OK); } // arbitration lost. if same priority class found, try again after next AUTO-SYN m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) // if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN) m_remainLockCount = m_lockCount; setState(m_state, RESULT_ERR_BUS_LOST); // try again later } result = m_command.push_back(recvSymbol, false); // expect no escaping for master address if (result < RESULT_OK) return setState(bs_skip, result); m_repeat = false; return setState(bs_recvCmd, RESULT_OK); case bs_recvCmd: headerLen = 4; crcPos = m_command.size() > headerLen ? headerLen + 1 + m_command[headerLen] : 0xff; // header symbols are never escaped result = m_command.push_back(recvSymbol, true, m_command.size() < crcPos); if (result < RESULT_OK) return setState(bs_skip, result); if (result == RESULT_OK && crcPos != 0xff && m_command.size() == crcPos + 1) { // CRC received unsigned char dstAddress = m_command[1]; m_commandCrcValid = m_command[headerLen + 1 + m_command[headerLen]] == m_command.getCRC(); // header symbols are never escaped if (m_commandCrcValid) { if (dstAddress == BROADCAST) { receiveCompleted(); return setState(bs_skip, RESULT_OK); } if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) return setState(bs_sendCmdAck, RESULT_OK); return setState(bs_recvCmdAck, RESULT_OK); } if (dstAddress == BROADCAST) return setState(bs_skip, RESULT_ERR_CRC); if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) { return setState(bs_sendCmdAck, RESULT_ERR_CRC); } if (m_repeat) return setState(bs_skip, RESULT_ERR_CRC); return setState(bs_recvCmdAck, RESULT_ERR_CRC); } return RESULT_OK; case bs_recvCmdAck: if (recvSymbol == ACK) { if (!m_commandCrcValid) return setState(bs_skip, RESULT_ERR_ACK); if (m_currentRequest != NULL) { if (isMaster(m_currentRequest->m_master[1])) { return setState(bs_sendSyn, RESULT_OK); } } else if (isMaster(m_command[1])) { // header symbols are never escaped receiveCompleted(); return setState(bs_skip, RESULT_OK); } m_repeat = false; return setState(bs_recvRes, RESULT_OK); } if (recvSymbol == NAK) { if (!m_repeat) { m_repeat = true; m_nextSendPos = 0; m_command.clear(); if (m_currentRequest != NULL) return setState(bs_sendCmd, RESULT_ERR_NAK, true); return setState(bs_recvCmd, RESULT_ERR_NAK); } return setState(bs_skip, RESULT_ERR_NAK); } return setState(bs_skip, RESULT_ERR_ACK); case bs_recvRes: headerLen = 0; crcPos = m_response.size() > headerLen ? headerLen + 1 + m_response[headerLen] : 0xff; result = m_response.push_back(recvSymbol, true, m_response.size() < crcPos); if (result < RESULT_OK) return setState(bs_skip, result); if (result == RESULT_OK && crcPos != 0xff && m_response.size() == crcPos + 1) { // CRC received m_responseCrcValid = m_response[headerLen + 1 + m_response[headerLen]] == m_response.getCRC(); if (m_responseCrcValid) { if (m_currentRequest != NULL) return setState(bs_sendResAck, RESULT_OK); return setState(bs_recvResAck, RESULT_OK); } if (m_repeat) { if (m_currentRequest != NULL) return setState(bs_sendSyn, RESULT_ERR_CRC); return setState(bs_skip, RESULT_ERR_CRC); } if (m_currentRequest != NULL) return setState(bs_sendResAck, RESULT_ERR_CRC); return setState(bs_recvResAck, RESULT_ERR_CRC); } return RESULT_OK; case bs_recvResAck: if (recvSymbol == ACK) { if (!m_responseCrcValid) return setState(bs_skip, RESULT_ERR_ACK); receiveCompleted(); return setState(bs_skip, RESULT_OK); } if (recvSymbol == NAK) { if (!m_repeat) { m_repeat = true; m_response.clear(); return setState(bs_recvRes, RESULT_ERR_NAK, true); } return setState(bs_skip, RESULT_ERR_NAK); } return setState(bs_skip, RESULT_ERR_ACK); case bs_sendCmd: if (m_currentRequest != NULL && sending) { if (recvSymbol == sendSymbol) { // successfully sent m_nextSendPos++; if (m_nextSendPos >= m_currentRequest->m_master.size()) { // master data completely sent if (m_currentRequest->m_master[1] == BROADCAST) return setState(bs_sendSyn, RESULT_OK); m_commandCrcValid = true; return setState(bs_recvCmdAck, RESULT_OK); } return RESULT_OK; } } return setState(bs_skip, RESULT_ERR_INVALID_ARG); case bs_sendResAck: if (m_currentRequest != NULL && sending) { if (recvSymbol == sendSymbol) { // successfully sent if (!m_responseCrcValid) { if (!m_repeat) { m_repeat = true; m_response.clear(); return setState(bs_recvRes, RESULT_ERR_NAK, true); } return setState(bs_sendSyn, RESULT_ERR_ACK); } return setState(bs_sendSyn, RESULT_OK); } } return setState(bs_skip, RESULT_ERR_INVALID_ARG); case bs_sendCmdAck: if (sending && m_answer) { if (recvSymbol == sendSymbol) { // successfully sent if (!m_commandCrcValid) { if (!m_repeat) { m_repeat = true; m_command.clear(); return setState(bs_recvCmd, RESULT_ERR_NAK, true); } return setState(bs_skip, RESULT_ERR_ACK); } if (isMaster(m_command[1])) receiveCompleted(); // decode command and store value return setState(bs_skip, RESULT_OK); m_nextSendPos = 0; m_repeat = false; Message* message = m_messages->find(m_command); if (message == NULL || !message->isPassive() || message->isWrite()) return setState(bs_skip, RESULT_ERR_INVALID_ARG); // don't know this request or definition has wrong direction, deny // build response and store in m_response for sending back to requesting master m_response.clear(true); // escape while sending response result = message->prepareSlave(m_response); if (result != RESULT_OK) return setState(bs_skip, result); return setState(bs_sendRes, RESULT_OK); } } return setState(bs_skip, RESULT_ERR_INVALID_ARG); case bs_sendRes: if (sending && m_answer) { if (recvSymbol == sendSymbol) { // successfully sent m_nextSendPos++; if (m_nextSendPos >= m_response.size()) { // slave data completely sent return setState(bs_recvResAck, RESULT_OK); } return RESULT_OK; } } return setState(bs_skip, RESULT_ERR_INVALID_ARG); case bs_sendSyn: if (sending) { if (recvSymbol == sendSymbol) { // successfully sent return setState(bs_skip, RESULT_OK); } } return setState(bs_skip, RESULT_ERR_INVALID_ARG); } return RESULT_OK; }