123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /**
- * @file shellmatta_ymodem.c
- * @brief ymodem functions of shellmatta
- * @author Simon Fischer <dev@s-fischer.net>
- */
- #include "shellmatta_ymodem.h"
- #include "shellmatta_crc.h"
- #include "shellmatta_utils.h"
- #include <stddef.h>
- /* current ymodem packet size */
- uint16_t yModemPacketSize = YMODEM_PACKET_SIZE;
- /* packet counter - counts all correctly received packets during one transmission */
- uint16_t shellmatta_ymodem_packet_counter = 0u;
- /* counts total amount of data bytes received during one transmission */
- uint32_t shellmatta_ymodem_total_bytes_received = 0u;
- /* counts total amount of data bytes received during one packet */
- uint16_t shellmatta_ymodem_byte_counter = 0u;
- uint8_t fileNameDelimiterPosition = 255u;
- uint8_t fileSizeDelimiterPosition = 255u;
- /* structure of a packet consisting of data and metadata */
- shellmatta_ymodem_packet_t shellmatta_ymodem_packet;
- /* holds information about which data type is currently actice: YMODEM_NONE, YMODEM_HEADER, YMODEM_BODY, YMODEM_FOOTER */
- shellmatta_ymodem_datatype_t shellmatta_ymodem_current_data_type = YMODEM_NONE;
- /* structure of ymodem callback functions */
- shellmatta_ymodem_callbacks_t shellmatta_ymodem_callbacks;
- /* flag to disable byte processing in ymodem */
- bool ymodem_is_enabled = true;
- /**
- * @brief Initialise the ymodem prior to actually receiving data
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] recvBuffer pointer to the buffer to save the received payload in
- * @param[in] fileSize pointer to the file size variable
- * @param[in] packetSize pointer to the packet size variable
- * @param[in] callbacks callback functions for the ymodem module
- */
- void shellmatta_ymodem_init(shellmatta_handle_t handle,
- uint8_t* recvBuffer,
- uint32_t* fileSize,
- uint16_t* packetSize,
- shellmatta_ymodem_callbacks_t callbacks)
- {
- if ((void*)0u == recvBuffer) return;
- /* init packet structure */
- shellmatta_ymodem_packet.packetNumber = 0u;
- shellmatta_ymodem_packet.reversePacketNumber = 0u;
- shellmatta_ymodem_packet.packetCrc = 0u;
- shellmatta_ymodem_packet.packetSize = packetSize;
- shellmatta_ymodem_packet.fileSize = fileSize;
- shellmatta_ymodem_packet.packetData = recvBuffer;
- /* init callbacks */
- shellmatta_ymodem_callbacks = callbacks;
- /* init ymodem state */
- shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
- /* init current data type */
- shellmatta_ymodem_current_data_type = YMODEM_NONE;
- }
- /**
- * @brief change the current state of the ymodem module
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] newState the state the ymodem module should be changed to
- */
- void shellmatta_ymodem_set_state(shellmatta_handle_t handle, shellmatta_ymodem_state_t newState)
- {
- ((shellmatta_instance_t*)handle)->ymodemState = newState;
- }
- /**
- * @brief Returns the current state of the ymodem module
- * @param[in, out] handle shellmatta handle of the instance
- * @return the current state of the ymodem module
- */
- shellmatta_ymodem_state_t shellmatta_ymodem_get_state(shellmatta_handle_t handle)
- {
- return ((shellmatta_instance_t*)handle)->ymodemState;
- }
- /**
- * @brief State machine that processes bytewise input during active ymodem transmission
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] byteIn the byte to be processed
- * @note won't do anything if ymodem module is not initialized
- */
- void shellmatta_ymodem_receive_packet(shellmatta_handle_t handle, uint8_t byteIn)
- {
- static uint32_t fileSize = 0u;
- static char fileSizeStr[7u]; /* hopefully no more bytes than a million will ever be transmitted */
- shellmatta_ymodem_rcv_retcode_t recvRetCode;
- /* skip byte processing if ymodem is not enabled */
- if (!ymodem_is_enabled)
- {
- return;
- }
- recvRetCode = shellmatta_ymodem_receive_byte(handle, byteIn);
- switch (shellmatta_ymodem_get_state(handle))
- {
- case INACTIVE:
- /* cant do anything if ymodem module was not correctly initialized */
- return;
- case WAIT_FOR_START:
- /* wait for start symbol of a packet */
- switch (shellmatta_ymodem_current_data_type)
- {
- case YMODEM_NONE: /* go here if the header packet is to be expected */
- if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
- {
- /* reset packet counter */
- shellmatta_ymodem_packet_counter = 0u;
- shellmatta_ymodem_byte_counter = 0u;
- shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
- shellmatta_ymodem_current_data_type = YMODEM_HEADER;
- }
- break;
-
- case YMODEM_HEADER: /* go here if the first body packet is to be expected */
- if (recvRetCode == STX_RECEIVED || SOH_RECEIVED)
- {
- shellmatta_ymodem_byte_counter = 0u;
- shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
- shellmatta_ymodem_current_data_type = YMODEM_BODY;
- }
- break;
- case YMODEM_BODY: /* go here if the data transmission loop is active */
- if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
- {
- shellmatta_ymodem_byte_counter = 0u;
- /* stay in body when SOH was received */
- shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
- shellmatta_ymodem_current_data_type = YMODEM_BODY;
- }
- else /* go here if the end of transmission symbol is received */
- if (recvRetCode == EOT_RECEIVED)
- {
- shellmatta_ymodem_byte_counter = 0u;
- /* answer with ACK */
- shellmatta_ymodem_ack(handle);
- /* then send 0x43 symbol */
- shellmatta_ymodem_control(handle, YMODEM_CRC);
- /* go to footer when EOT was received */
- shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
- /* then wait for SOH */
- }
- break;
- case YMODEM_FOOTER: /* go here if the end of transmission packet is to be expected */
- /* it _may_ be, that EOT is sent multiple times, repeat previous procedure */
- if (recvRetCode == EOT_RECEIVED)
- {
- /* answer with ACK */
- shellmatta_ymodem_ack(handle);
- /* then send 0x43 symbol */
- shellmatta_ymodem_control(handle, YMODEM_CRC);
- }
- if (recvRetCode == SOH_RECEIVED)
- {
- /* reset packet counter */
- shellmatta_ymodem_packet_counter = 0u;
- /* reset byte counter to avoid error by one caused by previously received EOT symbol */
- shellmatta_ymodem_byte_counter = 0u;
- shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
- }
- break;
- default:
- break;
- }
- break;
- case RECEIVE_PACKET:
- /* receiving data */
- if (recvRetCode == DATA_RECEIVED)
- {
- switch (shellmatta_ymodem_current_data_type)
- {
- case YMODEM_HEADER:
- switch (shellmatta_ymodem_byte_counter)
- {
- /* first two bytes are packet number and reverse packet number */
- case 0:
- shellmatta_ymodem_packet.packetNumber = byteIn;
- break;
- case 1:
- shellmatta_ymodem_packet.reversePacketNumber = byteIn;
- break;
- default:
- break;
- }
- /* after packet numbers, data field begins */
- if ((shellmatta_ymodem_byte_counter >= YMODEM_HEADER_DATA_OFFSET) &&
- (shellmatta_ymodem_byte_counter < YMODEM_HEADER_CRC_POSITION))
- {
- shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
- /* check for filename */
- if (shellmatta_ymodem_byte_counter < fileNameDelimiterPosition)
- {
- /* find NULL-Terminator in filename */
- if (byteIn == YMODEM_NULL)
- {
- fileNameDelimiterPosition = shellmatta_ymodem_byte_counter + 1;
- }
- }
- /* after filename the filesize begins */
- else
- if (shellmatta_ymodem_byte_counter >= fileNameDelimiterPosition &&
- (shellmatta_ymodem_byte_counter < fileSizeDelimiterPosition))
- {
- /* find space as delimiter after filesize */
- if (byteIn == YMODEM_SPACE)
- {
- fileSizeDelimiterPosition = shellmatta_ymodem_byte_counter;
- /* convert file size string to actual number */
- fileSize = utils_shellAsciiToUInt32(fileSizeStr, fileSizeDelimiterPosition - fileNameDelimiterPosition);
- *shellmatta_ymodem_packet.fileSize = fileSize;
- }
- /* save characters in string otherwise */
- else
- {
- fileSizeStr[shellmatta_ymodem_byte_counter - fileNameDelimiterPosition] = byteIn;
- }
- }
- else
- {
- /* some meta data from sender, possibly file date */
- /* not needed for now */
- }
- }
- /* after data field, crc begins */
- if (shellmatta_ymodem_byte_counter >= YMODEM_HEADER_CRC_POSITION)
- {
- if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION)
- {
- /* upper byte */
- shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
- }
- if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION + 1)
- {
- /* lower byte */
- shellmatta_ymodem_packet.packetCrc |= byteIn;
- /* change ymodem state to check packet */
- shellmatta_ymodem_check_packet(handle);
- }
- }
- shellmatta_ymodem_byte_counter++;
- break;
- case YMODEM_BODY:
- switch (shellmatta_ymodem_byte_counter)
- {
- case 0:
- shellmatta_ymodem_packet.packetNumber = byteIn;
- break;
- case 1:
- shellmatta_ymodem_packet.reversePacketNumber = byteIn;
- break;
- default:
- break;
- }
- /* after data field, crc begins */
- if (shellmatta_ymodem_byte_counter >= yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
- {
- if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
- {
- /* upper byte */
- shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
- }
- if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET + 1)
- {
- /* lower byte */
- shellmatta_ymodem_packet.packetCrc |= byteIn;
- /* change ymodem state to check packet */
- shellmatta_ymodem_check_packet(handle);
- }
- }
- else
- if ((shellmatta_ymodem_byte_counter > 1) &&
- (shellmatta_ymodem_byte_counter < (yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)))
- {
- shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
- }
- shellmatta_ymodem_byte_counter++;
- break;
- case YMODEM_FOOTER:
- /* reset packet counter */
- shellmatta_ymodem_packet_counter = 0;
- switch (shellmatta_ymodem_byte_counter)
- {
- case 0:
- shellmatta_ymodem_packet.packetNumber = byteIn;
- break;
- case 1:
- shellmatta_ymodem_packet.reversePacketNumber = byteIn;
- break;
- default:
- break;
- }
- /* after data field, crc begins */
- if (shellmatta_ymodem_byte_counter >= yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
- {
- if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
- {
- /* upper byte */
- shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
- }
- if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET + 1)
- {
- /* lower byte */
- shellmatta_ymodem_packet.packetCrc |= byteIn;
- /* change ymodem state to check packet */
- shellmatta_ymodem_check_packet(handle);
- }
- }
- else
- if ((shellmatta_ymodem_byte_counter > 1) &&
- (shellmatta_ymodem_byte_counter < (yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)))
- {
- /* save data */
- shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
- }
- shellmatta_ymodem_byte_counter++;
- break;
-
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- }
- /**
- * @brief Gives a return code depending on symbol received at different states of the ymodem module
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] byteIn currently processed input byte
- */
- shellmatta_ymodem_rcv_retcode_t shellmatta_ymodem_receive_byte(shellmatta_handle_t handle, uint8_t byteIn)
- {
- shellmatta_ymodem_rcv_retcode_t ret;
- if (WAIT_FOR_START == shellmatta_ymodem_get_state(handle))
- {
- switch (byteIn)
- {
- case YMODEM_SOH:
- /* start of header -> default packet size */
- yModemPacketSize = YMODEM_PACKET_SIZE;
- ret = SOH_RECEIVED;
- break;
-
- case YMODEM_STX:
- /* start of header -> extended packet size */
- yModemPacketSize = YMODEM_PACKET_SIZE_1K;
- ret = STX_RECEIVED;
- break;
- case YMODEM_EOT:
- /* end of transmission -> data transmission complete */
- ret = EOT_RECEIVED;
- break;
-
- case YMODEM_CA:
- /* cancel transmssion -> reset state machine */
- ret = CA_RECEIVED;
- break;
- default:
- /* no other data should be received in this state */
- ret = ERROR;
- break;
- }
- /* give packet size back to upper layer */
- *shellmatta_ymodem_packet.packetSize = yModemPacketSize;
- }
- else
- if (RECEIVE_PACKET == shellmatta_ymodem_get_state(handle))
- {
- ret = DATA_RECEIVED;
- }
- return ret;
- }
- /**
- * @brief Checks packet data for validity and sends ACK or NAK accordingly
- * @param[in, out] handle shellmatta handle of the instance
- * @note Only to be called after full packet is received
- * @note Will call yModemRecvPacketCallback when packet is received correctly
- * @note Will call ymodemTransmissionCompleteCallback when transmission is completed
- */
- void shellmatta_ymodem_check_packet(shellmatta_handle_t handle)
- {
- uint16_t computedCrc = 0u;
- if (shellmatta_ymodem_packet.packetNumber != (0xFF - shellmatta_ymodem_packet.reversePacketNumber))
- {
- shellmatta_ymodem_nak(handle);
- return;
- }
- /* compare to internal packet counter */
- if (shellmatta_ymodem_packet.packetNumber != ((uint8_t)shellmatta_ymodem_packet_counter)) /* uint8_t cast to cause overflow */
- {
- shellmatta_ymodem_nak(handle);
- return;
- }
- /* compare packet crc only after other checks succeeded */
- computedCrc = crc16Calc((const char*)shellmatta_ymodem_packet.packetData, yModemPacketSize);
- //! uncomment to ignore crc check for debug purposes
- if (shellmatta_ymodem_packet.packetCrc != computedCrc)
- {
- shellmatta_ymodem_nak(handle);
- return;
- }
- /* if previous checks passed, packet is acknowledged */
- (void) computedCrc;
- shellmatta_ymodem_total_bytes_received += shellmatta_ymodem_byte_counter;
- shellmatta_ymodem_byte_counter = 0u;
- shellmatta_ymodem_ack(handle);
- if (shellmatta_ymodem_current_data_type == YMODEM_HEADER)
- {
- /* send additional CRC symbol if the packet was a header packet */
- shellmatta_ymodem_control(handle, YMODEM_CRC);
- }
- /* callback handling */
- switch (shellmatta_ymodem_current_data_type)
- {
- case YMODEM_NONE:
- /* shouldn't happen */
- break;
-
- case YMODEM_HEADER:
- /* nothing to do after receiving the header packet */
- break;
- case YMODEM_BODY:
- /* call the ymodem receive packet callback */
- if (NULL != shellmatta_ymodem_callbacks.yModemRecvPacketCallback)
- {
- shellmatta_ymodem_callbacks.yModemRecvPacketCallback();
- }
- break;
- case YMODEM_FOOTER:
- /* call the ymodem transmission complete callback */
- if (NULL != shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback)
- {
- shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback();
- }
- break;
- default:
- break;
- }
- }
- /**
- * @brief Acknowledge handling. Will send ACK and set ymodem state back to WAIT_FOR_START
- * @param[in, out] handle shellmatta handle of the instance
- */
- void shellmatta_ymodem_ack(shellmatta_handle_t handle)
- {
- shellmatta_ymodem_control(handle, YMODEM_ACK);
- shellmatta_ymodem_packet_counter++;
- shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
- }
- /**
- * @brief Not-Acknowledge handling. Will send NAK and set ymodem state back to WAIT_FOR_START
- * @param[in, out] handle shellmatta handle of the instance
- */
- void shellmatta_ymodem_nak(shellmatta_handle_t handle)
- {
- shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
- /* set back current data type to prevent false forward stepping */
- switch (shellmatta_ymodem_current_data_type)
- {
- case YMODEM_NONE:
- /* no handling needed */
- break;
-
- case YMODEM_HEADER:
- shellmatta_ymodem_control(handle, YMODEM_NAK);
- break;
- case YMODEM_BODY:
- /* YMODEM_BODY stays in YMODEM_BODY */
- shellmatta_ymodem_control(handle, YMODEM_NAK);
- shellmatta_ymodem_current_data_type = YMODEM_BODY;
- break;
- case YMODEM_FOOTER:
- /* YMODEM_FOOTER as well */
- shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
- break;
- default:
- break;
- }
- }
- /**
- * @brief forwards the given character to write-function without formatting
- * @param[in] handle shellmatta handle of the instance
- * @param[in] c character to be sent
- */
- void shellmatta_ymodem_control(shellmatta_handle_t handle, const char c)
- {
- ((shellmatta_instance_t*)handle)->write(&c, 1);
- }
- /**
- * @brief reset function for the ymodem module
- * @param[in, out] handle shellmatta handle of the instance
- * @param[in] doCancel flag to execute the cancel-callback
- */
- void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
- {
- /* call cancel callback function */
- if (doCancel)
- {
- /* send cancel symbol */
- shellmatta_ymodem_control(handle, YMODEM_CA);
- if (NULL != shellmatta_ymodem_callbacks.yModemCancelCallback)
- {
- shellmatta_ymodem_callbacks.yModemCancelCallback();
- }
- }
- /* reset global variables */
- shellmatta_ymodem_byte_counter = 0u;
- shellmatta_ymodem_packet_counter = 0u;
- shellmatta_ymodem_total_bytes_received = 0u;
- fileNameDelimiterPosition = 255u;
- fileSizeDelimiterPosition = 255u;
- /* reset all ymodem_packet data */
- shellmatta_ymodem_packet.packetNumber = 0u;
- shellmatta_ymodem_packet.reversePacketNumber = 0u;
- shellmatta_ymodem_packet.packetCrc = 0u;
- shellmatta_ymodem_packet.packetSize = 0u;
- shellmatta_ymodem_packet.fileSize = 0u;
- shellmatta_ymodem_packet.packetData = 0u;
- /* set ymodem to inactive */
- shellmatta_ymodem_set_state(handle, INACTIVE);
- shellmatta_ymodem_current_data_type = YMODEM_NONE;
- #ifdef SHELLMATTA_TRANSPORT
- /* re-enable transport layer */
- ((shellmatta_instance_t*)handle)->transportEnabled = true;
- #endif
- }
- shellmatta_ymodem_datatype_t shellmatta_ymodem_get_current_datatype(void)
- {
- return shellmatta_ymodem_current_data_type;
- }
- void shellmatta_ymodem_enable(void)
- {
- ymodem_is_enabled = true;
- }
- void shellmatta_ymodem_disable(void)
- {
- ymodem_is_enabled = false;
- }
|