/* * Copyright (C) Roland Jax 2012-2014 * * This file is part of ebusd. * * ebusd is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ebusd is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ebusd. If not, see http://www.gnu.org/licenses/. */ #include "bus.h" #include #include namespace libebus { Bus::Bus(const std::string deviceName, const bool noDeviceCheck, const long recvTimeout, const std::string dumpFile, const long dumpSize, const bool dumpState) : m_sstr(), m_recvTimeout(recvTimeout), m_dumpState(dumpState), m_busLocked(false), m_busPriorRetry(false) { m_port = new Port(deviceName, noDeviceCheck); m_dump = new Dump(dumpFile, dumpSize); } Bus::~Bus() { if (isConnected() == true) disconnect(); delete m_port; delete m_dump; } void Bus::printBytes() const { unsigned char byte; ssize_t bytes_read; bytes_read = m_port->recv(0); for (int i = 0; i < bytes_read; i++) { byte = m_port->byte(); std::cout << std::nouppercase << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); if (byte == SYN) std::cout << std::endl; } } int Bus::proceed() { unsigned char byte; ssize_t nbytes; // fetch new message and get bus if (m_sendBuffer.size() != 0 && m_sstr.size() == 0) { BusCommand* busCommand = m_sendBuffer.front(); return getBus(busCommand->getCommand()[0]); } // wait for new data nbytes = m_port->recv(0); if (nbytes < 0) return RESULT_ERR_DEVICE; for (int i = 0; i < nbytes; i++) { // fetch next byte byte = recvByte(); // store byte return proceedCycData(byte); // TODO what if more than one byte was received? } return RESULT_SYN; } int Bus::proceedCycData(const unsigned char byte) { if (byte != SYN) { m_sstr.push_back(byte, true, false); if (m_busLocked == true) m_busLocked = false; return RESULT_DATA; } if (byte == SYN && m_sstr.size() != 0) { // lock bus after SYN-BYTE-SYN Sequence if (m_sstr.size() == 1 && m_busPriorRetry == false) m_busLocked = true; m_cycBuffer.push(m_sstr); m_sstr.clear(); if (m_busLocked == true) return RESULT_BUS_LOCKED; } return RESULT_SYN; } SymbolString Bus::getCycData() { SymbolString data; if (m_cycBuffer.empty() == false) { data = m_cycBuffer.front(); m_cycBuffer.pop(); } return data; } int Bus::getBus(const unsigned char byte_sent) { unsigned char byte_recv; ssize_t bytes_sent, bytes_recv; // send QQ bytes_sent = m_port->send(&byte_sent, 1); if (bytes_sent <= 0) return RESULT_ERR_SEND; // receive 1 byte - must be QQ bytes_recv = m_port->recv(0, 1); if (bytes_recv < 0) return RESULT_ERR_DEVICE; // fetch next byte byte_recv = recvByte(); // compare sent and received byte if (bytes_recv == 1 && byte_sent == byte_recv) { m_busPriorRetry = false; return RESULT_BUS_ACQUIRED; } // store byte int ret = proceedCycData(byte_recv); if (ret >= 0) return ret; // TODO this needs to be re-designed with above proceedCycData() // compare prior nibble for retry if (bytes_recv == 1 && (byte_sent & 0x0F) == (byte_recv & 0x0F)) { m_busPriorRetry = true; return RESULT_BUS_PRIOR_RETRY; } m_busLocked = true; return RESULT_ERR_BUS_LOST; } BusCommand* Bus::sendCommand() { unsigned char byte_recv; ssize_t bytes_recv; std::string result; SymbolString slaveData; int retval = RESULT_OK; BusCommand* busCommand = m_sendBuffer.front(); m_sendBuffer.pop(); // send ZZ PB SB NN Dx CRC SymbolString command = busCommand->getCommand(); for (size_t i = 1; i < command.size(); i++) { retval = sendByte(command[i]); if (retval < 0) goto on_exit; } // BC -> send SYN if (busCommand->getType() == broadcast) { sendByte(SYN); goto on_exit; } // receive ACK bytes_recv = m_port->recv(m_recvTimeout); if (bytes_recv > 1) { retval = RESULT_ERR_EXTRA_DATA; goto on_exit; } else if (bytes_recv < 0) { retval = RESULT_ERR_TIMEOUT; goto on_exit; } byte_recv = recvByte(); // is received byte SYN? if (byte_recv == SYN) { retval = RESULT_ERR_SYN; goto on_exit; } // is slave ACK negative? if (byte_recv == NAK) { // send QQ ZZ PB SB NN Dx CRC again for (size_t i = 0; i < command.size(); i++) { retval = sendByte(command[i]); if (retval < 0) goto on_exit; } // receive ACK bytes_recv = m_port->recv(m_recvTimeout); if (bytes_recv > 1) { retval = RESULT_ERR_EXTRA_DATA; goto on_exit; } else if (bytes_recv < 0) { retval = RESULT_ERR_TIMEOUT; goto on_exit; } byte_recv = recvByte(); // is received byte SYN? if (byte_recv == SYN) { retval = RESULT_ERR_SYN; goto on_exit; } // is slave ACK negative? if (byte_recv == NAK) { retval = sendByte(SYN); if (retval == 0) retval = RESULT_ERR_NAK; goto on_exit; } } // MM -> send SYN if (busCommand->getType() == masterMaster) { sendByte(SYN); goto on_exit; } // receive NN, Dx, CRC retval = recvSlaveDataAndCRC(slaveData); // are calculated and received CRC equal? if (retval == RESULT_ERR_CRC) { // send NAK retval = sendByte(NAK); if (retval < 0) goto on_exit; // receive NN, Dx, CRC slaveData.clear(); retval = recvSlaveDataAndCRC(slaveData); // are calculated and received CRC equal? if (retval == RESULT_ERR_CRC) { // send NAK retval = sendByte(NAK); if (retval >= 0) retval = RESULT_ERR_CRC; } } if (retval < 0) goto on_exit; // send ACK retval = sendByte(ACK); if (retval == -1) { retval = RESULT_ERR_ACK; goto on_exit; } // MS -> send SYN sendByte(SYN); on_exit: // empty receive buffer while (m_port->size() != 0) byte_recv = recvByte(); busCommand->setResult(slaveData, retval); return busCommand; } BusCommand* Bus::delCommand() { BusCommand* busCommand = m_sendBuffer.front(); m_sendBuffer.pop(); busCommand->setResult(SymbolString(), RESULT_ERR_BUS_LOST); return busCommand; } int Bus::sendByte(const unsigned char byte_sent) { unsigned char byte_recv; ssize_t bytes_sent, bytes_recv; bytes_sent = m_port->send(&byte_sent, 1); // receive 1 byte - must be equal bytes_recv = m_port->recv(RECV_TIMEOUT); if (bytes_sent != bytes_recv) return RESULT_ERR_EXTRA_DATA; byte_recv = recvByte(); if (byte_sent != byte_recv) return RESULT_ERR_SEND; return RESULT_OK; } unsigned char Bus::recvByte() { unsigned char byte_recv; // fetch byte byte_recv = m_port->byte(); if (m_dumpState == true) m_dump->write((const char*) &byte_recv); return byte_recv; } int Bus::recvSlaveDataAndCRC(SymbolString& result) { unsigned char byte_recv, crc_calc = 0; ssize_t bytes_recv; size_t NN = 0; bool updateCrc = true; int retval = 0; for (size_t i = 0, needed = 1; i < needed; i++) { bytes_recv = m_port->recv(RECV_TIMEOUT, 1); if (bytes_recv < 0) return RESULT_ERR_TIMEOUT; byte_recv = recvByte(); retval = result.push_back(byte_recv, true, updateCrc); if (retval < 0) return retval; if (retval == RESULT_IN_ESC) needed++; else if (result.size() == 1) { // NN received NN = result[0]; needed += NN; } else if (NN > 0 && result.size() == 1+NN) {// all data received updateCrc = false; crc_calc = result.getCRC(); needed++; } } if (retval == RESULT_IN_ESC) return RESULT_ERR_ESC; if (updateCrc || crc_calc != result[result.size()-1]) return RESULT_ERR_CRC; return RESULT_OK; } } //namespace