123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- /*
- * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
- /**
- * @file shellmatta_transport.c
- * @brief transport layer functions of shellmatta
- * @author Simon Fischer <fischer.simon.1991@gmail.com>
- */
- #ifdef SHELLMATTA_TRANSPORT
- #include "shellmatta_transport.h"
- #include "shellmatta.h"
- #include "shellmatta_crc.h"
- #include "shellmatta_utils.h"
- #include <string.h>
- /**
- * @brief create a secured packet and pass it to the shellmatta write function
- *
- * @note The packetType, payloadLength and payload have to be preset!
- *
- * @param[in, out] transportLayer transport layer instance to work on
- * @param[in] packetType type of packet to send
- * @param[in] packet packet to send
- * @return errorcode
- */
- static shellmatta_retCode_t shellmatta_transport_send(shellmatta_transport_layer_t *transportLayer,
- shellmatta_transport_packet_t *packet)
- {
- uint32_t crc;
- char *rawPacket = (char*)packet;
- shellmatta_transport_header_t *header = &packet->header;
- /** -# fill header */
- header->startOfHeader = SHELLMATTA_TRANSPORT_START_OF_HEADER;
- header->protocolVersion = SHELLMATTA_TRANSPORT_PROTOCOL_VERSION;
- header->source = 0;
- header->destination = 0;
- header->sequenceH2S = transportLayer->sequenceH2S;
- header->sequenceS2H = ++transportLayer->sequenceS2H;
- crc = crc32Calc((char*) packet, SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
- /** -# append crc to end of payload */
- rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength] = (uint8_t)(crc >> 24u);
- rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 1u] = (uint8_t)(crc >> 16u);
- rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 2u] = (uint8_t)(crc >> 8u);
- rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 3u] = (uint8_t)(crc);
- /** -# use original write function to send full buffer */
- return transportLayer->writeFct((char*) rawPacket,
- SHELLMATTA_TRANSPORT_LENGTH_STATIC +
- header->payloadLength);
- }
- /**
- * @brief Initializes the transportLayerInst
- * @param[in, out] transportLayer transport layer instance to work on
- * @param[in] writeFct function pointer to output function
- * @return errorcode #SHELLMATTA_OK
- */
- shellmatta_retCode_t shellmatta_transport_init( shellmatta_transport_layer_t *transportLayer,
- shellmatta_write_t writeFct)
- {
- /** -# clear instance and store write function */
- memset(transportLayer, 0u, sizeof(shellmatta_transport_layer_t));
- transportLayer->writeFct = writeFct;
- return SHELLMATTA_OK;
- }
- /**
- * @brief Configures the transport layer
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] mandatory enforce using the transport layer
- * @param[in] disableAutoFlush enforce manual flushing of the output packet
- * @param[in] customCrcFct use a custom crc generation (default NULL)
- * @return errorcode
- */
- shellmatta_retCode_t shellmatta_transport_configure(shellmatta_handle_t handle,
- bool mandatory,
- bool disableAutoFlush,
- shellmatta_transport_crc_t customCrcFct)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- shellmatta_transport_layer_t *transportLayer = &inst->transportLayer;
- transportLayer->mandatory = mandatory;
- transportLayer->disableAutoFlush = disableAutoFlush;
- transportLayer->customCrcFct = customCrcFct;
- /** -# set the transport layer active when configured as mandatory */
- if(true == mandatory)
- {
- transportLayer->active = true;
- }
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief Resets all values of tranportLayerInst except for sequence counters
- * @param[in, out] handle shellmatta handle of the instance
- * @return errorcode
- */
- shellmatta_retCode_t shellmatta_transport_reset(shellmatta_handle_t handle)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- /** -# set transport layer back to initial state */
- shellmatta_transport_layer_t *transportLayer = &inst->transportLayer;
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_WAIT;
- transportLayer->active = transportLayer->mandatory;
- transportLayer->headerIndex = 0u;
- transportLayer->payloadIndex = 0u;
- transportLayer->crcIndex = 0u;
- memset(&transportLayer->inPacket, 0, sizeof(transportLayer->inPacket));
- memset(&transportLayer->outPacket, 0, sizeof(transportLayer->outPacket));
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief processes the passed amount of data
- * @param[in, out] transportLayer transport layer instance to work on
- * @param[in] byte byte to process
- * @param[out] data payload of processed packet - or manual mode
- * @param[out] length length of processed packet
- * @return errorcode #SHELLMATTA_OK data is returned\n
- * #SHELLMATTA_BUSY packet reception ongoing\n
- * #SHELLMATTA_ERROR in case of crc error
- */
- shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t *transportLayer,
- char byte,
- char **data,
- uint32_t *length)
- {
- shellmatta_retCode_t ret = SHELLMATTA_BUSY;
- char *rawPacket = (char*)&transportLayer->inPacket;
- shellmatta_transport_header_t *header = &transportLayer->inPacket.header;
- shellmatta_transport_packet_int_t intPacket;
- uint32_t refCrc;
- switch (transportLayer->state)
- {
- /** -# look for start of header */
- case SHELLMATTA_TRANSPORT_STATE_WAIT:
- /** -# if start of header is found, continue transport layer fsm */
- if (SHELLMATTA_TRANSPORT_START_OF_HEADER == byte)
- {
- memset(&transportLayer->inPacket, 0, sizeof(transportLayer->inPacket));
- transportLayer->headerIndex = 1u;
- transportLayer->payloadIndex = 0u;
- transportLayer->crcIndex = 0u;
- header->startOfHeader = byte;
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_GET_HEADER;
- }
- else if((true == transportLayer->mandatory))
- {
- /** -# if transport layer is mandatory - throw away unrecognized bytes */
- *data = transportLayer->inPacket.payload;
- *length = 0;
- transportLayer->active = true;
- ret = SHELLMATTA_OK;
- }
- else
- {
- /** -# otherwise just pass the payload as is */
- transportLayer->inPacket.payload[0] = byte;
- *data = transportLayer->inPacket.payload;
- *length = 1;
- transportLayer->active = false;
- ret = SHELLMATTA_OK;
- }
- break;
- case SHELLMATTA_TRANSPORT_STATE_GET_HEADER:
- rawPacket[transportLayer->headerIndex] = byte;
- transportLayer->headerIndex ++;
- if (transportLayer->headerIndex == SHELLMATTA_TRANSPORT_LENGTH_HEADER)
- {
- //TODO check for errors in header data
- if (0u != header->payloadLength)
- {
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD;
- }
- else
- {
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_GET_CRC;
- }
- }
- break;
- /** -# read payload for previously read length of payload */
- case SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD:
- transportLayer->inPacket.payload[transportLayer->payloadIndex] = byte;
- transportLayer->payloadIndex ++;
- if (transportLayer->payloadIndex >= header->payloadLength)
- {
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_GET_CRC;
- }
- break;
- /** -# read crc32 for four bytes */
- case SHELLMATTA_TRANSPORT_STATE_GET_CRC:
- transportLayer->crcIndex ++;
- transportLayer->inPacket.crc |= ((uint8_t)byte << ((SHELLMATTA_TRANSPORT_LENGTH_CRC - transportLayer->crcIndex) * 8u));
- if (transportLayer->crcIndex >= SHELLMATTA_TRANSPORT_LENGTH_CRC)
- {
- refCrc = SHELLMATTA_TRANSPORT_CALC_CRC(transportLayer,
- rawPacket,
- SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
- /** -# if crc is correct, further handling of data depends on type of packet */
- if (transportLayer->inPacket.crc == refCrc)
- {
- transportLayer->active = true;
- /* crc is correct */
- transportLayer->sequenceH2S++;
- switch (header->packetType)
- {
- case SHELLMATTA_TRANSPORT_PACKET_DATA:
- /** -# return pointer to payload and length */
- *data = (char*)&transportLayer->inPacket.payload;
- *length = header->payloadLength;
- ret = SHELLMATTA_OK;
- break;
-
- case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_REQUEST:
- /** -# send out packet with no payload */
- intPacket.header.packetType = SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND;
- intPacket.header.payloadLength = 0u;
- (void)shellmatta_transport_send(transportLayer, (shellmatta_transport_packet_t *)&intPacket);
- break;
- case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND:
- /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND - nothing to do */
- break;
-
- case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_REQUEST:
- /** @todo implement */
- break;
- case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_RESPOND:
- /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_RESPOND - nothing to do */
- break;
-
- case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_REQUEST:
- /** @todo implement */
- break;
-
- case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_RESPOND:
- /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_RESPOND - nothing to do */
- break;
- case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_REQUEST:
- /** @todo implement */
- break;
- case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_RESPOND:
- /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_RESPOND - nothing to do */
- break;
- default:
- /** -# undo sequence counter increment on wrong packet type */
- transportLayer->sequenceH2S--;
- break;
- }
- }
- else
- {
- /** -# return error if crc is incorrect */
- ret = SHELLMATTA_ERROR;
- }
- /** -# reset state machine */
- transportLayer->state = SHELLMATTA_TRANSPORT_STATE_WAIT;
- }
- break;
-
- default:
- break;
- }
- return ret;
- }
- /**
- * @brief Wrapper function for the write-function of shellmatta handle
- *
- * This function is used to transmit data with the tranport layer protocol.\n
- * The input data is copied into a new array along with the header and crc32.\n
- * The resulting buffer is the forwarded to the original write function.\n
- *
- * @note If length of data exceeds #UINT8_MAX, data is sent in multiple packets.\n
- *
- * @param[in, out] transportLayer transport layer instance to work on
- * @param[in] data pointer to input data to process
- * @param[in] length length of data to process
- * @return errorcode #SHELLMATTA_OK
- */
- shellmatta_retCode_t shellmatta_transport_write(shellmatta_transport_layer_t *transportLayer,
- const char* data,
- uint32_t length)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- uint32_t outPayloadLength = length;
- shellmatta_transport_packet_t *packet = &transportLayer->outPacket;
- shellmatta_transport_header_t *header = &transportLayer->outPacket.header;
- /** -# handle data with length bigger than max length */
- uint32_t processedLength = 0u;
- uint32_t piledUpPayload;
- uint32_t splitLength;
- uint32_t restOfPayload;
- /** -# foot-controlled loop to send packets without payload length */
- do
- {
- piledUpPayload = header->payloadLength;
- /* compute length of next payload split */
- restOfPayload = (outPayloadLength - processedLength);
- if (restOfPayload > (SHELLMATTA_TRANPORT_PAYLOAD_MAXLENGTH - piledUpPayload))
- {
- splitLength = SHELLMATTA_TRANPORT_PAYLOAD_MAXLENGTH - piledUpPayload;
- }
- else
- {
- splitLength = restOfPayload;
- }
- (void)memcpy(&packet->payload[piledUpPayload], &data[processedLength], splitLength);
- header->payloadLength += splitLength;
- processedLength += splitLength;
- if(header->payloadLength >= SHELLMATTA_TRANPORT_PAYLOAD_MAXLENGTH)
- {
- /** -# packet is full - send */
- header->packetType = SHELLMATTA_TRANSPORT_PACKET_DATA;
- ret = shellmatta_transport_send(transportLayer, packet);
- header->payloadLength = 0u;
- }
- }
- while (processedLength < outPayloadLength);
- return ret;
- }
- /**
- * @brief Send out piled up payload
- *
- * @param[in, out] handle shellmatta handle of the instance
- * @return errorcode
- */
- shellmatta_retCode_t shellmatta_transport_flush(shellmatta_handle_t handle)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- shellmatta_transport_packet_t *packet = &inst->transportLayer.outPacket;
- if(0 != packet->header.payloadLength)
- {
- packet->header.packetType = SHELLMATTA_TRANSPORT_PACKET_DATA;
- ret = shellmatta_transport_send(&inst->transportLayer, packet);
- packet->header.payloadLength = 0u;
- }
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- #endif
|