Example #1
0
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;
}