#include "RwPollingTask.h" #include #include #include #include #include #include #include "devConf.h" #include "mission/devices/devicedefinitions/rwHelpers.h" RwPollingTask::RwPollingTask(object_id_t objectId, SpiComIF* spiIF) : SystemObject(objectId), spiIF(spiIF) { semaphore = SemaphoreFactory::instance()->createBinarySemaphore(); semaphore->acquire(); ipcLock = MutexFactory::instance()->createMutex(); spiLock = spiIF->getCsMutex(); spiDev = spiIF->getSpiDev().c_str(); } ReturnValue_t RwPollingTask::performOperation(uint8_t operationCode) { while (true) { ipcLock->lockMutex(); state = InternalState::IDLE; ipcLock->unlockMutex(); semaphore->acquire(); int fd = 0; ReturnValue_t result = openSpi(O_RDWR, fd); if (result != returnvalue::OK) { continue; } for (unsigned idx = 0; idx < rwCookies.size(); idx++) { prepareSetSpeedCmd(idx); if (writeOneRwCmd(idx, fd) != returnvalue::OK) { continue; } } closeSpi(fd); usleep(rws::SPI_REPLY_DELAY); if (readAllRws(fd, rws::SET_SPEED) != returnvalue::OK) { continue; } prepareSimpleCommand(rws::GET_LAST_RESET_STATUS); if (writeAndReadAllRws(rws::GET_LAST_RESET_STATUS) != returnvalue::OK) { continue; } prepareSimpleCommand(rws::GET_RW_STATUS); if (writeAndReadAllRws(rws::GET_RW_STATUS) != returnvalue::OK) { continue; } prepareSimpleCommand(rws::GET_TEMPERATURE); if (writeAndReadAllRws(rws::GET_TEMPERATURE) != returnvalue::OK) { continue; } prepareSimpleCommand(rws::CLEAR_LAST_RESET_STATUS); if (writeAndReadAllRws(rws::CLEAR_LAST_RESET_STATUS) != returnvalue::OK) { continue; } } return returnvalue::OK; } ReturnValue_t RwPollingTask::initialize() { if (spiDev == nullptr) { sif::error << "SPI device is invalid" << std::endl; return returnvalue::FAILED; } return returnvalue::OK; } ReturnValue_t RwPollingTask::initializeInterface(CookieIF* cookie) { // We don't need to set the speed because a SPI core is used, but the mode has to be set once // correctly for all RWs if (not modeAndSpeedWasSet) { int fd = open(spiDev, O_RDWR); if (fd < 0) { sif::error << "could not open RW SPI bus" << std::endl; return returnvalue::FAILED; } spiIF->setSpiSpeedAndMode(fd, spi::RW_MODE, spi::RW_SPEED); close(fd); modeAndSpeedWasSet = true; } return returnvalue::OK; } ReturnValue_t RwPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { if (sendLen < 6) { return DeviceHandlerIF::INVALID_DATA; } int32_t speed = 0; uint16_t rampTime = 0; SerializeAdapter::deSerialize(&speed, &sendData, &sendLen, SerializeIF::Endianness::MACHINE); SerializeAdapter::deSerialize(&rampTime, &sendData, &sendLen, SerializeIF::Endianness::MACHINE); rws::SpecialRwRequest specialRequest = rws::SpecialRwRequest::REQUEST_NONE; if (sendLen == 7 and sendData[6] < static_cast(rws::SpecialRwRequest::NUM_REQUESTS)) { specialRequest = static_cast(sendData[6]); } RwCookie* rwCookie = dynamic_cast(cookie); if (rwCookie == nullptr) { return returnvalue::FAILED; } { MutexGuard mg(ipcLock); rwCookie->currentRwSpeed = speed; rwCookie->currentRampTime = rampTime; rwCookie->specialRequest = specialRequest; if (state == InternalState::IDLE and rwCookie->rwIdx == 3) { state = InternalState::BUSY; semaphore->release(); } } return returnvalue::OK; } ReturnValue_t RwPollingTask::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; } ReturnValue_t RwPollingTask::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { return returnvalue::OK; } ReturnValue_t RwPollingTask::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { RwCookie* rwCookie = dynamic_cast(cookie); { MutexGuard mg(ipcLock); *buffer = rwCookie->replyBuf.data(); *size = rwCookie->replyBuf.size(); } return returnvalue::OK; } ReturnValue_t RwPollingTask::writeAndReadAllRws(DeviceCommandId_t id) { // Stopwatch watch; ReturnValue_t result = returnvalue::OK; int fd = 0; result = openSpi(O_RDWR, fd); if (result != returnvalue::OK) { return result; } for (unsigned idx = 0; idx < rwCookies.size(); idx++) { ReturnValue_t result = sendOneMessage(fd, *rwCookies[idx]); if (result != returnvalue::OK) { closeSpi(fd); return returnvalue::FAILED; } } closeSpi(fd); usleep(rws::SPI_REPLY_DELAY); return readAllRws(fd, id); } ReturnValue_t RwPollingTask::openSpi(int flags, int& fd) { fd = open(spiDev, flags); if (fd < 0) { sif::error << "rwSpiCallback::spiCallback: Failed to open device file" << std::endl; return SpiComIF::OPENING_FILE_FAILED; } return returnvalue::OK; } ReturnValue_t RwPollingTask::readNextReply(RwCookie& rwCookie, uint8_t* replyBuf, size_t maxReplyLen) { ReturnValue_t result = returnvalue::OK; int fd = 0; gpioId_t gpioId = rwCookie.getChipSelectPin(); GpioIF& gpioIF = spiIF->getGpioInterface(); pullCsLow(gpioId, spiLock, gpioIF); uint8_t byteRead = 0; for (unsigned idx = 0; idx < MAX_RETRIES_REPLY; idx++) { result = openSpi(O_RDWR, fd); if (result != returnvalue::OK) { return result; } /** * The reaction wheel responds with empty frames while preparing the reply data. * However, receiving more than 5 empty frames will be interpreted as an error. */ for (int idx = 0; idx < 5; idx++) { if (read(fd, &byteRead, 1) != 1) { sif::error << "rwSpiCallback::spiCallback: Read failed" << std::endl; pullCsHigh(gpioId, gpioIF); closeSpi(fd); return rws::SPI_READ_FAILURE; } if (idx == 0) { if (byteRead != FLAG_BYTE) { sif::error << "Invalid data, expected start marker" << std::endl; pullCsHigh(gpioId, gpioIF); closeSpi(fd); return rws::NO_START_MARKER; } } if (byteRead != FLAG_BYTE) { break; } pullCsHigh(gpioId, gpioIF); closeSpi(fd); if (idx == MAX_RETRIES_REPLY - 1) { sif::error << "rwSpiCallback::spiCallback: Empty frame timeout" << std::endl; return rws::NO_REPLY; } TaskFactory::delayTask(5); } } #if FSFW_HAL_SPI_WIRETAPPING == 1 sif::info << "RW start marker detected" << std::endl; #endif size_t decodedFrameLen = 0; while (decodedFrameLen < maxReplyLen) { // First byte already read in if (decodedFrameLen != 0) { byteRead = 0; if (read(fd, &byteRead, 1) != 1) { sif::error << "rwSpiCallback::spiCallback: Read failed" << std::endl; result = rws::SPI_READ_FAILURE; break; } } if (byteRead == FLAG_BYTE) { // Reached end of frame break; } else if (byteRead == 0x7D) { if (read(fd, &byteRead, 1) != 1) { sif::error << "rwSpiCallback::spiCallback: Read failed" << std::endl; result = rws::SPI_READ_FAILURE; break; } if (byteRead == 0x5E) { *(replyBuf + decodedFrameLen) = 0x7E; decodedFrameLen++; continue; } else if (byteRead == 0x5D) { *(replyBuf + decodedFrameLen) = 0x7D; decodedFrameLen++; continue; } else { sif::error << "rwSpiCallback::spiCallback: Invalid substitute" << std::endl; result = rws::INVALID_SUBSTITUTE; break; } } else { *(replyBuf + decodedFrameLen) = byteRead; decodedFrameLen++; continue; } // Check end marker. /** * There might be the unlikely case that each byte in a get-telemetry reply has been * replaced by its substitute. Then the next byte must correspond to the end sign 0x7E. * Otherwise there might be something wrong. */ if (decodedFrameLen == maxReplyLen) { if (read(fd, &byteRead, 1) != 1) { sif::error << "rwSpiCallback::spiCallback: Failed to read last byte" << std::endl; result = rws::SPI_READ_FAILURE; break; } if (byteRead != FLAG_BYTE) { sif::error << "rwSpiCallback::spiCallback: Missing end sign " << static_cast(FLAG_BYTE) << std::endl; decodedFrameLen--; result = rws::MISSING_END_SIGN; break; } } result = returnvalue::OK; } pullCsHigh(gpioId, gpioIF); closeSpi(fd); return result; } ReturnValue_t RwPollingTask::writeOneRwCmd(uint8_t rwIdx, int fd) { ReturnValue_t result = sendOneMessage(fd, *rwCookies[rwIdx]); if (result != returnvalue::OK) { closeSpi(fd); return returnvalue::FAILED; } return returnvalue::OK; } ReturnValue_t RwPollingTask::readAllRws(int fd, DeviceCommandId_t id) { ReturnValue_t result = openSpi(O_RDWR, fd); if (result != returnvalue::OK) { return result; } for (unsigned idx = 0; idx < rwCookies.size(); idx++) { if (spiLock == nullptr) { sif::debug << "rwSpiCallback::spiCallback: Mutex or GPIO interface invalid" << std::endl; return returnvalue::FAILED; } // TODO: Fix buffer to write to uint8_t* replyBuf; size_t maxReadLen = idAndIdxToReadBuffer(id, idx, &replyBuf); readNextReply(*rwCookies[idx], replyBuf, maxReadLen); } closeSpi(fd); return returnvalue::OK; } size_t RwPollingTask::idAndIdxToReadBuffer(DeviceCommandId_t id, uint8_t rwIdx, uint8_t** ptr) { uint8_t* rawStart = rwCookies[rwIdx]->replyBuf.data(); RwReplies replies(rawStart); switch (id) { case (rws::GET_RW_STATUS): { *ptr = replies.rwStatusReply; return rws::SIZE_GET_RW_STATUS; } case (rws::SET_SPEED): { *ptr = replies.setSpeedReply; return rws::SIZE_SET_SPEED_REPLY; } case (rws::CLEAR_LAST_RESET_STATUS): { *ptr = replies.clearLastResetStatusReply; return rws::SIZE_CLEAR_RESET_STATUS; } case (rws::GET_LAST_RESET_STATUS): { *ptr = replies.getLastResetStatusReply; return rws::SIZE_GET_RESET_STATUS; } case (rws::GET_TEMPERATURE): { *ptr = replies.readTemperatureReply; return rws::SIZE_GET_TEMPERATURE_REPLY; } case (rws::GET_TM): { *ptr = replies.hkDataReply; return rws::SIZE_GET_TELEMETRY_REPLY; } case (rws::INIT_RW_CONTROLLER): { *ptr = replies.initRwControllerReply; return rws::SIZE_INIT_RW; } default: { sif::error << "no reply buffer for rw command " << id << std::endl; *ptr = replies.dummyPointer; return 0; } } } void RwPollingTask::encodeHdlc(const uint8_t* sourceBuf, size_t sourceLen, size_t& encodedLen) { encodedBuffer[0] = FLAG_BYTE; encodedLen = 1; for (size_t sourceIdx = 0; sourceIdx < sourceLen; sourceIdx++) { if (sourceBuf[sourceIdx] == 0x7E) { encodedBuffer[encodedLen++] = 0x7D; encodedBuffer[encodedLen++] = 0x5E; } else if (sourceBuf[sourceIdx] == 0x7D) { encodedBuffer[encodedLen++] = 0x7D; encodedBuffer[encodedLen++] = 0x5D; } else { encodedBuffer[encodedLen++] = sourceBuf[sourceIdx]; } } encodedBuffer[encodedLen++] = FLAG_BYTE; } // This closes the SPI void RwPollingTask::closeSpi(int fd) { // This will perform the function to close the SPI close(fd); // The SPI is now closed. } ReturnValue_t RwPollingTask::sendOneMessage(int fd, RwCookie& rwCookie) { gpioId_t gpioId = rwCookie.getChipSelectPin(); GpioIF& gpioIF = spiIF->getGpioInterface(); if (spiLock == nullptr) { sif::debug << "rwSpiCallback::spiCallback: Mutex or GPIO interface invalid" << std::endl; return returnvalue::FAILED; } pullCsLow(gpioId, spiLock, gpioIF); /* //Encoding and sending command size_t idx = 0; while (idx < dataLen) { switch (*(data + idx)) { case 0x7E: writeBuffer[0] = 0x7D; writeBuffer[1] = 0x5E; writeSize = 2; break; case 0x7D: writeBuffer[0] = 0x7D; writeBuffer[1] = 0x5D; writeSize = 2; break; default: writeBuffer[0] = *(data + idx); writeSize = 1; break; } } */ // Add datalinklayer like specified in the datasheet. size_t lenToSend = 0; encodeHdlc(writeBuffer.data(), writeLen, lenToSend); if (write(fd, encodedBuffer.data(), lenToSend) != static_cast(lenToSend)) { sif::error << "rwSpiCallback::spiCallback: Write failed!" << std::endl; pullCsHigh(gpioId, gpioIF); return rws::SPI_WRITE_FAILURE; } pullCsHigh(gpioId, gpioIF); return returnvalue::OK; } ReturnValue_t RwPollingTask::pullCsLow(gpioId_t gpioId, MutexIF* spiLock, GpioIF& gpioIF) { ReturnValue_t result = spiLock->lockMutex(TIMEOUT_TYPE, TIMEOUT_MS); if (result != returnvalue::OK) { sif::debug << "rwSpiCallback::spiCallback: Failed to lock mutex" << std::endl; return result; } // Pull SPI CS low. For now, no support for active high given if (gpioId != gpio::NO_GPIO) { ReturnValue_t result = gpioIF.pullLow(gpioId); if (result != returnvalue::OK) { sif::error << "rwSpiCallback::spiCallback: Failed to pull chip select low" << std::endl; return result; } } return returnvalue::OK; } void RwPollingTask::pullCsHigh(gpioId_t gpioId, GpioIF& gpioIF) { if (gpioId != gpio::NO_GPIO) { if (gpioIF.pullHigh(gpioId) != returnvalue::OK) { sif::error << "closeSpi: Failed to pull chip select high" << std::endl; } } if (spiLock->unlockMutex() != returnvalue::OK) { sif::error << "rwSpiCallback::closeSpi: Failed to unlock mutex" << std::endl; ; } } void RwPollingTask::prepareSimpleCommand(DeviceCommandId_t id) { writeBuffer[0] = static_cast(id); uint16_t crc = CRC::crc16ccitt(writeBuffer.data(), 1, 0xFFFF); writeBuffer[1] = static_cast(crc & 0xFF); writeBuffer[2] = static_cast(crc >> 8 & 0xFF); writeLen = 3; } ReturnValue_t RwPollingTask::prepareSetSpeedCmd(uint8_t rwIdx) { writeBuffer[0] = static_cast(rws::SET_SPEED); uint8_t* serPtr = writeBuffer.data() + 1; int32_t speedToSet = 0; uint16_t rampTimeToSet = 10; { MutexGuard mg(ipcLock); speedToSet = rwCookies[rwIdx]->currentRwSpeed; rampTimeToSet = rwCookies[rwIdx]->currentRampTime; } size_t serLen = 0; SerializeAdapter::serialize(&speedToSet, &serPtr, &serLen, writeBuffer.size(), SerializeIF::Endianness::LITTLE); SerializeAdapter::serialize(&rampTimeToSet, &serPtr, &serLen, writeBuffer.size(), SerializeIF::Endianness::LITTLE); uint16_t crc = CRC::crc16ccitt(writeBuffer.data(), 7, 0xFFFF); writeBuffer[7] = static_cast(crc & 0xFF); writeBuffer[8] = static_cast((crc >> 8) & 0xFF); writeLen = 9; return returnvalue::OK; }