/** ****************************************************************************** * @file ymodem.c * @author MCD Application Team * @brief Ymodem module. * This file provides set of firmware functions to manage Ymodem * functionalities. ****************************************************************************** * @attention * * Copyright (c) 2020 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file in * the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /** @addtogroup USER_APP User App Example * @{ */ /** @addtogroup FW_UPDATE Firmware Update Example * @{ */ /* Includes ------------------------------------------------------------------*/ #include "common.h" #include "ymodem.h" #include "string.h" #include "main.h" #include "stm32wlxx_hal.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* @note ATTENTION - please keep this variable 32bit aligned */ static uint8_t m_aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE]; /*!0: packet length * @param uTimeout * @retval HAL_OK: normally return * HAL_BUSY: abort by user */ static HAL_StatusTypeDef ReceivePacket(uint8_t *pData, uint32_t *puLength, uint32_t uTimeout) { uint32_t crc; uint32_t packet_size = 0U; HAL_StatusTypeDef status; uint8_t char1; *puLength = 0U; status = (HAL_StatusTypeDef)COM_Receive(&char1, 1, uTimeout); if (status == HAL_OK) { switch (char1) { case SOH: packet_size = PACKET_SIZE; break; case STX: packet_size = PACKET_1K_SIZE; break; case EOT: break; case CA: if ((COM_Receive(&char1, 1U, uTimeout) == HAL_OK) && (char1 == CA)) { packet_size = 2U; } else { status = HAL_ERROR; } break; case ABORT1: case ABORT2: status = HAL_BUSY; break; case RB: COM_Receive(&char1, 1U, uTimeout); /* Ymodem startup sequence : rb ==> 0x72 + 0x62 + 0x0D */ COM_Receive(&char1, 1U, uTimeout); packet_size = 3U; break; default: status = HAL_ERROR; break; } *pData = char1; if (packet_size >= PACKET_SIZE) { status = COM_Receive(&pData[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, uTimeout); /* Simple packet sanity check */ if (status == HAL_OK) { if (pData[PACKET_NUMBER_INDEX] != ((pData[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE)) { packet_size = 0U; status = HAL_ERROR; } else { /* Check packet CRC */ crc = pData[ packet_size + PACKET_DATA_INDEX ] << 8U; crc += pData[ packet_size + PACKET_DATA_INDEX + 1U ]; if (HAL_CRC_Calculate(&CrcHandle, (uint32_t *)&pData[PACKET_DATA_INDEX], packet_size) != crc) { packet_size = 0U; status = HAL_ERROR; } } } else { packet_size = 0U; } } } *puLength = packet_size; return status; } /** * @brief Init of Ymodem module. * @param None. * @retval None. */ void Ymodem_Init(void) { __HAL_RCC_CRC_CLK_ENABLE(); /* Configure the CRC peripheral */ CrcHandle.Instance = CRC; /* The CRC-16-CCIT polynomial is used */ CrcHandle.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE; CrcHandle.Init.GeneratingPolynomial = 0x1021U; CrcHandle.Init.CRCLength = CRC_POLYLENGTH_16B; /* The zero init value is used */ CrcHandle.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE; CrcHandle.Init.InitValue = 0U; /* The input data are not inverted */ CrcHandle.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; /* The output data are not inverted */ CrcHandle.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE; /* The input data are 32-bit long words */ CrcHandle.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; if (HAL_CRC_Init(&CrcHandle) != HAL_OK) { /* Initialization Error */ while (1U) {} } } /* Public functions ---------------------------------------------------------*/ /** * @brief Receive a file using the ymodem protocol with CRC16. * @param puSize The uSize of the file. * @param uFlashDestination where the file has to be downloaded. * @retval COM_StatusTypeDef result of reception/programming */ COM_StatusTypeDef Ymodem_Receive(uint32_t *puSize, uint32_t uFlashDestination, YMODEM_CallbacksTypeDef *appCb) { uint32_t i; uint32_t packet_length; uint32_t session_done = 0U; uint32_t file_done; uint32_t errors = 0U; uint32_t session_begin = 0U; uint32_t ramsource; uint32_t filesize; uint8_t *file_ptr; uint8_t file_size[FILE_SIZE_LENGTH + 1U]; uint8_t tmp; uint32_t packets_received; COM_StatusTypeDef e_result = COM_OK; while ((session_done == 0U) && (e_result == COM_OK)) { packets_received = 0U; file_done = 0U; while ((file_done == 0U) && (e_result == COM_OK)) { switch (ReceivePacket(m_aPacketData, &packet_length, DOWNLOAD_TIMEOUT)) { case HAL_OK: errors = 0U; switch (packet_length) { case 3U: /* Startup sequence */ break; case 2U: /* Abort by sender */ Serial_PutByte(ACK); e_result = COM_ABORT; break; case 0U: /* End of transmission */ Serial_PutByte(ACK); *puSize = filesize; file_done = 1U; break; default: /* Normal packet */ if (m_aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff)) { /* Serial_PutByte(NAK); */ } else { if (packets_received == 0U) { /* File name packet */ if (m_aPacketData[PACKET_DATA_INDEX] != 0U) { /* File name extraction */ i = 0U; file_ptr = m_aPacketData + PACKET_DATA_INDEX; while ((*file_ptr != 0U) && (i < FILE_NAME_LENGTH)) { m_aFileName[i++] = *file_ptr++; } /* File size extraction */ m_aFileName[i++] = '\0'; i = 0U; file_ptr ++; while ((*file_ptr != ' ') && (i < FILE_SIZE_LENGTH)) { file_size[i++] = *file_ptr++; } file_size[i++] = '\0'; Str2Int(file_size, &filesize); /* Header packet received callback call*/ if (appCb->Ymodem_HeaderPktRxCpltCallback((uint32_t) filesize) == HAL_OK) { Serial_PutByte(ACK); COM_Flush(); Serial_PutByte(CRC16); } else { /* End session */ tmp = CA; COM_Transmit(&tmp, 1U, NAK_TIMEOUT); COM_Transmit(&tmp, 1U, NAK_TIMEOUT); e_result = COM_ABORT; break; } } /* File header packet is empty, end session */ else { Serial_PutByte(ACK); file_done = 1; session_done = 1; break; } } else /* Data packet */ { ramsource = (uint32_t) & m_aPacketData[PACKET_DATA_INDEX]; /* Data packet received callback call*/ if (appCb->Ymodem_DataPktRxCpltCallback((uint8_t *) ramsource, uFlashDestination, (uint32_t) packet_length) == HAL_OK) { Serial_PutByte(ACK); } else /* An error occurred while writing to Flash memory */ { /* End session */ tmp = CA; COM_Transmit(&tmp, 1U, NAK_TIMEOUT); COM_Transmit(&tmp, 1U, NAK_TIMEOUT); e_result = COM_ERROR; } } packets_received ++; session_begin = 1U; } break; } break; case HAL_BUSY: /* Abort actually */ Serial_PutByte(CA); Serial_PutByte(CA); e_result = COM_ABORT; break; default: if (session_begin > 0U) { errors ++; } if (errors > MAX_ERRORS) { /* Abort communication */ Serial_PutByte(CA); Serial_PutByte(CA); e_result = COM_ABORT; } else { Serial_PutByte(CRC16); /* Ask for a packet */ printf("\b."); /* Replace C char by . on display console */ } break; } } } return e_result; } /** * @} */ /** * @} */