prj to mdks

This commit is contained in:
hwq
2025-09-17 09:11:22 +08:00
parent ea8dcada90
commit 31a048e428
44 changed files with 268 additions and 2966 deletions
+569
View File
@@ -0,0 +1,569 @@
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)
File diff suppressed because it is too large Load Diff
+862
View File
@@ -0,0 +1,862 @@
#include "firmware.h"
static uint8_t _fw_start_write_flag; /* 固件开始写入的标志位 */
static uint16_t _update_progress; /* 固件更新的进度, 10000 制 */
static uint16_t _update_progress_step_num; /* 固件更新进度的步进单位 */
static uint16_t _storage_data_size; /* 固件包写入时记录暂存的固件分包大小,单位 byte */
static uint32_t _write_part_addr; /* 固件包写入时记录写入 flash 的相对地址 */
static uint16_t _write_last_pkg_size; /* 固件包写入时最后一个分包的大小,单位 byte */
static uint8_t _fw_first_bytes[CONFIG_WRITE_BYTES_LEASET]; /* 固件包的前几个字节 */
static uint8_t _upk_min_handle_buff[UPK_LEAST_HANDLE_BYTE]; /* upk 固件最小处理单位的缓存区,多次使用以降低系统资源开销 */
static struct upk_head_t upkHdr; /* 用于存放 upk 固件包头 */
#if (CONFIG_DECRYPT)
static struct AES_ctx aesCtx; /* AES 对象 */
#endif
static struct BSP_FLASH _flash_app_part; /* APP 分区 */
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
static struct BSP_FLASH _flash_download_part; /* download 分区 */
#if (CONFIG_CHIP_PARTS == USE_TRIPLE_PARTITION)
static struct BSP_FLASH _flash_factory_part; /* factory 分区 */
#endif
#endif
/* Extern function prototypes ------------------------------------------------*/
extern void Firmware_OperateCallback(uint16_t progress);
/* Private function prototypes -----------------------------------------------*/
static eErrCode _Write_FirmwareSubPackage( const struct BSP_FLASH *part,
uint8_t *data,
uint16_t pkg_size,
uint8_t decrypt,
FM_FIRMWARE_WRITE_DIR write_dir);
static void _Reset_Write(void);
/* Exported functions ---------------------------------------------------------*/
/**
* @brief 初始化接口
* @note
* @retval None
*/
void FM_Init (void)
{
BSP_Flash_Init(&_flash_app_part, NAME_PART_APPLICATION, BASE_APPLICATION, SIZE_PART_APPLICATION);
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
BSP_Flash_Init(&_flash_download_part, NAME_PART_DOWNLOAD, BASE_DOWNLOAD, SIZE_PART_DOWNLOAD);
#if (CONFIG_CHIP_PARTS > USE_DOUBLE_PARTITION)
BSP_Flash_Init(&_flash_factory_part, NAME_PART_FACTORY, BASE_FACTORY, SIZE_PART_FACTORY);
#endif
#endif
printf("onchip size : 0x%lX\r\n", SIZE_ONCHIP_FLASH);
printf("0x%08lX+0x%lX 'parameters'.\r\n", BASE_PARAMETERS, SIZE_PART_PARAMETERS);
printf("0x%08X+0x%X 'app'.\r\n", _flash_app_part.addr, _flash_app_part.len);
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
printf("0x%08X+0x%X 'download'.\r\n", _flash_download_part.addr, _flash_app_part.len);
#if (CONFIG_CHIP_PARTS > USE_DOUBLE_PARTITION)
printf("0x%08X+0x%X 'factory'.\r\n", _flash_factory_part.addr, _flash_app_part.len);
#endif
#endif
#if (CONFIG_DECRYPT)
AES_init_ctx_iv(&aesCtx, (uint8_t *)AES256_KEY, (uint8_t *)AES256_IV);
#endif
}
/**
* @brief 固件包是否有加密
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 0: 未加密。1: 有加密
*/
inline uint8_t FM_IsEncrypt(void)
{
/* 读取加密选项 */
if (upkHdr.config[1] == 0x01)
return 1;
return 0;
}
/**
* @brief 检测某个分区是否为空
* @note ERR_OK: 分区数据空
* @param[in] part_name: 分区名
* @retval eErrCode
*/
eErrCode FM_IsEmpty(const char *part_name)
{
int read_len = 0;
uint16_t i = 0;
uint16_t need_read_size = UPK_LEAST_HANDLE_BYTE;
uint32_t *p_data = (uint32_t *)_upk_min_handle_buff;
uint32_t read_posit = 0;
ASSERT(part_name != NULL);
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL) {
printf("%s: '%s' not found.\r\n", __func__, part_name);
return ERR_NO_THIS_PART;
}
for (read_posit = 0; read_posit < part->len; ) {
if ((part->len - read_posit) < UPK_LEAST_HANDLE_BYTE) {
need_read_size = part->len - read_posit;
}
read_len = BSP_Flash_Read(part, read_posit, _upk_min_handle_buff, need_read_size);
if (read_len < 0) {
printf("%s: read error (%d).\r\n", __func__, __LINE__);
return ERR_READ_IS_EMPTY_ERR;
}
for (i = 0; i < (UPK_LEAST_HANDLE_BYTE / sizeof(p_data)); i++) {
if (p_data[i] != 0xFFFFFFFF) {
printf("part '%s' not empty.\r\n", part_name);
return ERR_FLASH_NO_EMPTY;
}
}
read_posit += read_len;
}
//printf("%s: '%s' part empty.\r\n", __func__, part_name);
return ERR_OK;
}
/**
* @brief 获取固件包的固件版本
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 固件新版本
*/
inline char * FM_GetNewFirmwareVersion(void)
{
return upkHdr.fw_new_ver;
}
/**
* @brief 获取源固件的 CRC32 值
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 源固件的 CRC32 值
*/
inline uint32_t FM_GetRawCRC32(void)
{
return upkHdr.raw_crc;
}
/**
* @brief 暂存固件包头
* @note 会同时校验固件包头
* @param[in] part_name: 分区名
* @param[in] data: 数据
* @retval eErrCode
*/
eErrCode FM_StorageFirmwareHead(const char *part_name, uint8_t *data)
{
uint32_t head_crc = 0xFFFFFFFF;
uint8_t *pHdr = (uint8_t *)&upkHdr;
ASSERT(part_name != NULL);
ASSERT(data != NULL);
_Reset_Write();
memcpy(pHdr, data, UPK_HEAD_SIZE);
#if (!CONFIG_DECRYPT)
/* 若固件包加密,检查是否有解密组件 */
if (FM_IsEncrypt()) {
printf("%s: no decrypt component\r\n", __func__);
return ERR_NO_DECRYPT_COMPONENT;
}
#endif
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL) {
printf("%s: not found.\r\n", __func__);
return ERR_NO_THIS_PART;
}
//printf("%s: '%s' part.\r\n", __func__, part_name);
if (strncmp(upkHdr.name, "upk", sizeof("upk")) != 0) {
return ERR_FAULT_FIRMWARE;
}
if ((upkHdr.pkg_size > part->len)
|| (upkHdr.raw_size > part->len)) {
return ERR_FIRMWARE_OVERSIZE;
}
head_crc = crc32_step(head_crc, pHdr, UPK_HEAD_SIZE - 4);
if (head_crc != upkHdr.head_crc) {
printf("%s: head crc verify failed. (%.8X - %.8X)\r\n", __func__, upkHdr.head_crc, head_crc);
return ERR_FIRMWARE_HEAD_VERIFY_ERR;
}
printf("\n\r--------------------\n\r");
printf("head: \t\t%s\n\r", upkHdr.name);
printf("config: \t%d %d %d %d\n\r", upkHdr.config[0], upkHdr.config[1], upkHdr.config[2], upkHdr.config[3]);
printf("old_ver: \t%d.%d.%d.%d\n\r", upkHdr.fw_old_ver[0], upkHdr.fw_old_ver[1], upkHdr.fw_old_ver[2], upkHdr.fw_old_ver[3]);
printf("new_ver: \t%d.%d.%d.%d\n\r", upkHdr.fw_new_ver[0], upkHdr.fw_new_ver[1], upkHdr.fw_new_ver[2], upkHdr.fw_new_ver[3]);
printf("string: \t%s\n\r",upkHdr.user_string);
printf("partition: \t%s\n\r", upkHdr.part_name);
printf("raw_size: \t%d\n\r", upkHdr.raw_size);
printf("pkg_size: \t%d\n\r", upkHdr.pkg_size);
printf("timestamp: \t%d\n\r", upkHdr.timestamp);
printf("raw_crc: \t%.8X\n\r", upkHdr.raw_crc);
printf("pkg_crc: \t%.8X\n\r", upkHdr.pkg_crc);
printf("head_crc: \t%.8X\n\r", upkHdr.head_crc);
printf("--------------------\n\r");
/* 计算固件更新进度的最小单位,降低过程计算量,无更新进度需求可删除 */
_update_progress_step_num = upkHdr.pkg_size / UPK_LEAST_HANDLE_BYTE;
_update_progress_step_num += upkHdr.pkg_size % UPK_LEAST_HANDLE_BYTE;
_update_progress_step_num = 10000 / _update_progress_step_num;
//printf("%s: progress unit: %d\r\n", __func__, _update_progress_step_num);
return ERR_OK;
}
/**
* @brief 校验已放置在分区的固件包的包体数据的正确性
* @note 一般要先校验包头,注意各分区包体的偏移地址有区别
* @param[in] part_name: 分区名称
* @param[in] crc32: 需进行比对的 CRC32 校验值
* @param[in] is_auto_fill: 是否自动填充固件的首地址数据
* @retval eErrCode
*/
eErrCode FM_VerifyFirmware (const char *part_name, uint32_t crc32, uint8_t is_auto_fill)
{
int read_len = 0;
uint8_t app_part_flag = 0;
uint32_t pkg_size = 0;
uint32_t body_crc = 0xFFFFFFFF;
uint32_t read_posit = 0;
uint32_t read_posit_temp = 0;
uint16_t need_read_size = UPK_LEAST_HANDLE_BYTE;
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
uint8_t first_flag = 0;
#endif
ASSERT(part_name != NULL);
#if (!CONFIG_DECRYPT)
/* 若固件包加密,检查是否有解密组件 */
if (FM_IsEncrypt()) {
printf("%s: no decrypt component\r\n", __func__);
return ERR_NO_DECRYPT_COMPONENT;
}
#endif
if (strncmp(part_name, NAME_PART_APPLICATION, MAX_NAME_LEN) == 0) {
app_part_flag = 1;
}
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL) {
printf("%s: not found.\r\n", part_name);
return ERR_NO_THIS_PART;
}
hex_printf((uint8_t *)&upkHdr, UPK_HEAD_SIZE);
printf("--------------------\n\r");
printf("head: \t\t%s\n\r", upkHdr.name);
printf("config: \t%d %d %d %d\n\r", upkHdr.config[0], upkHdr.config[1], upkHdr.config[2], upkHdr.config[3]);
printf("old_ver: \t%d.%d.%d.%d\n\r", upkHdr.fw_old_ver[0], upkHdr.fw_old_ver[1], upkHdr.fw_old_ver[2], upkHdr.fw_old_ver[3]);
printf("new_ver: \t%d.%d.%d.%d\n\r", upkHdr.fw_new_ver[0], upkHdr.fw_new_ver[1], upkHdr.fw_new_ver[2], upkHdr.fw_new_ver[3]);
printf("string: \t%s\n\r",upkHdr.user_string);
printf("partition: \t%s\n\r", upkHdr.part_name);
printf("raw_size: \t%d\n\r", upkHdr.raw_size);
printf("pkg_size: \t%d\n\r", upkHdr.pkg_size);
printf("timestamp: \t%d\n\r", upkHdr.timestamp);
printf("raw_crc: \t%.8X\n\r", upkHdr.raw_crc);
printf("pkg_crc: \t%.8X\n\r", upkHdr.pkg_crc);
printf("head_crc: \t%.8X\n\r", upkHdr.head_crc);
printf("--------------------\n\r");
if (app_part_flag) {
pkg_size = upkHdr.raw_size;
} else {
pkg_size = upkHdr.pkg_size;
}
/* 校验固件包体的数据正确性 */
for (; read_posit < pkg_size; ) {
/* 剩余的数据数小于最小处理单位时,按剩余字节数处理 */
if ((pkg_size - read_posit) < UPK_LEAST_HANDLE_BYTE) {
need_read_size = pkg_size - read_posit;
}
/* 非 APP 分区,需要偏移包头的地址才是包体 */
if (app_part_flag) {
read_posit_temp = read_posit;
} else {
read_posit_temp = read_posit + UPK_HEAD_SIZE;
}
read_len = BSP_Flash_Read(part, read_posit_temp, _upk_min_handle_buff, need_read_size);
if (read_len < 0) {
printf("%s: read error (%d).\r\n", __func__, __LINE__);
return ERR_VERIFY_READ_ERR;
}
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
if (is_auto_fill) {
if (first_flag == 0) {
first_flag = 1;
for (uint8_t i = 0; i < CONFIG_WRITE_BYTES_LEASET; i++) {
_upk_min_handle_buff[i] = _fw_first_bytes[i];
}
}
}
#endif
body_crc = crc32_step(body_crc, _upk_min_handle_buff, read_len);
read_posit += read_len;
}
if (body_crc != crc32) {
printf("%s: '%s' body crc verify failed. (%08X vs %08X)\r\n", __func__, part_name, crc32, body_crc);
if (app_part_flag) {
return ERR_RAW_BODY_VERIFY_ERR;
} else {
return ERR_PKG_BODY_VERIFY_ERR;
}
}
return ERR_OK;
}
/**
* @brief 擦除某个分区的固件
* @note
* @param[in] part_name: 分区名称
* @retval eErrCode
*/
eErrCode FM_EraseFirmware(const char *part_name)
{
ASSERT(part_name != NULL);
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL)
{
printf("%s: not found %s part.\r\n", __func__, part_name);
return ERR_NO_THIS_PART;
}
if (BSP_Flash_Erase(part, 0, part->len) < 0)
{
printf("%s: %s part erase failed.\r\n", __func__, part_name);
return ERR_ERASE_PART_ERR;
}
return ERR_OK;
}
/**
* @brief 固件写入分区的最终阶段,将分区首地址的几个字节数据写入 flash
* @note 程序调用 _Write_FirmwareSubPackage 函数时已暂存进 _fw_first_bytes
* @param[in] part_name: 分区名称
* @retval eErrCode
*/
eErrCode FM_WriteFirmwareDone (const char *part_name)
{
ASSERT(part_name != NULL);
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL) {
printf("%s: not found part.\r\n", __func__);
return ERR_NO_THIS_PART;
}
if (BSP_Flash_Write(part, 0, _fw_first_bytes, CONFIG_WRITE_BYTES_LEASET) < 0) {
printf("%s: write error (%d).\r\n", __func__, __LINE__);
return ERR_WRITE_FIRST_ADDR_ERR;
}
_Reset_Write();
Firmware_OperateCallback(10000);
return ERR_OK;
}
/**
* @brief 将固件分包按顺序写入分区
* @note 由于固件包头已经写入,这里写入的是固件包体,需要注意在 flash 的偏移位置
* @param[in] part_name: 分区名称
* @param[in] data: 数据包
* @param[in] pkg_size: 数据包大小,单位 byte
* @retval eErrCode
*/
eErrCode FM_WriteFirmwareSubPackage(const char *part_name, uint8_t *data, uint16_t pkg_size)
{
ASSERT(part_name != NULL);
ASSERT(data != NULL);
ASSERT(pkg_size != 0);
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL)
{
printf("%s: not found %s part.\r\n", __func__, part_name);
return ERR_NO_THIS_PART;
}
printf(",");
#if (CONFIG_CHIP_PARTS == USE_SINGLE_PARTITION)
uint8_t decrypt = 0;
/* 读取加密选项 */
decrypt = FM_IsEncrypt();
return _Write_FirmwareSubPackage(part, data, pkg_size, decrypt, FM_DIR_HOST_TO_APP); /* 写入前解密 */
#else
return _Write_FirmwareSubPackage(part, data, pkg_size, 0, FM_DIR_HOST_TO_DOWNLOAD); /* 写入前不解密 */
#endif
}
/**
* @brief 检查固件的完整性
* @note 通过首地址数据最后写入的机制,判断首地址 4 个字节是否有正确的数据
* @param[in] addr: 分区首地址
* @retval eErrCode
*/
eErrCode FM_CheckFirmwareIntegrity(uint32_t addr)
{
uint32_t value = *(volatile uint32_t *)addr;
eErrCode fw_integrity = ERR_JUMP_TO_APP_ERR;
printf("\r\nvalue = 0x%08X @ offset 0x%.8X", value, addr);
if (BASE_APPLICATION == addr) {
fw_integrity = ((value & 0x2FF00000) == 0x20000000) ? ERR_OK : ERR_JUMP_TO_APP_ERR;
}
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
else if ((BASE_DOWNLOAD == addr)||(BASE_FACTORY == addr)) {
if (UPK_IDENTIFIER == value) {
fw_integrity = ERR_OK;
}
}
#endif
return fw_integrity;
}
#if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION)
/**
* @brief 判断是否要进行固件自动更新
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 0: 无须更新。1: 需自动更新
*/
uint8_t FM_IsNeedAutoUpdate(void)
{
#if (CONFIG_AUTO_UPDATE_MODE == MODE_APPEND_TO_APP)
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(NAME_PART_APPLICATION);
if (part == NULL) {
printf("%s: not found app part.\r\n", __func__);
return ERR_NO_THIS_PART;
}
if (BSP_Flash_Read(part, (SIZE_PART_APPLICATION - UPK_VERSION_SIZE), (uint8_t *)&upkHdr.fw_old_ver[0], UPK_VERSION_SIZE) < 0)
{
printf("%s: read error.\r\n", __func__);
return ERR_READ_VER_ERR;
}
#endif
printf("fw old ver: V%d.%d.%d.%d\r\n", upkHdr.fw_old_ver[0], upkHdr.fw_old_ver[1], upkHdr.fw_old_ver[2], upkHdr.fw_old_ver[3]);
printf("fw new ver: V%d.%d.%d.%d\r\n", upkHdr.fw_new_ver[0], upkHdr.fw_new_ver[1], upkHdr.fw_new_ver[2], upkHdr.fw_new_ver[3]);
if (upkHdr.fw_old_ver[0] != upkHdr.fw_new_ver[0]
|| upkHdr.fw_old_ver[1] != upkHdr.fw_new_ver[1]
|| upkHdr.fw_old_ver[2] != upkHdr.fw_new_ver[2]
|| upkHdr.fw_old_ver[3] != upkHdr.fw_new_ver[3]) {
printf("Need to update.\r\n");
return 1;
}
return 0;
}
/**
* @brief 获取当前操作的固件包的分区名称
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 分区名称
*/
inline char * FM_GetPartName(void)
{
return upkHdr.part_name;
}
/**
* @brief 获取旧的固件版本,即 APP 分区正在运行的固件版本
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 固件旧版本
*/
inline char * FM_GetOldFirmwareVersion(void)
{
return upkHdr.fw_old_ver;
}
/**
* @brief 获取打包后固件的 CRC32 值
* @note 调用前需确保 upkHdr 已经读入了数据
* @retval 打包后固件的CRC32值
*/
inline uint32_t FM_GetPackageCRC32(void)
{
return upkHdr.pkg_crc;
}
/**
* @brief 将分区内的固件包头读出
* @note 读出后的数据将放在 upkHdr 中
* @param[in] part_name: 分区名称
* @retval eErrCode
*/
eErrCode FM_ReadFirmwareHead(const char *part_name)
{
ASSERT(part_name != NULL);
_Reset_Write();
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL)
{
printf("%s: not found.\r\n", __func__);
return ERR_NO_THIS_PART;
}
if (BSP_Flash_Read(part, 0, (uint8_t *)&upkHdr, UPK_HEAD_SIZE) < 0)
{
printf("%s: read error.\r\n", __func__);
return ERR_READ_FIRMWARE_HEAD_ERR;
}
return ERR_OK;
}
/**
* @brief 从某个分区将固件包更新至 APP 分区
* @note 读取 -> 解密 -> 写入
* @param[in] from_part_name: 放置需要更新至 APP 分区的固件包的分区
* @retval eErrCode
*/
eErrCode FM_UpdateToAPP(const char *from_part_name)
{
int read_len = 0;
uint8_t decrypt = 0;
uint32_t read_posit = 0;
uint32_t write_posit = 0;
uint32_t need_read_size = UPK_LEAST_HANDLE_BYTE;
eErrCode result = ERR_OK;
ASSERT(from_part_name != NULL);
const struct BSP_FLASH *app_part = NULL;
const struct BSP_FLASH *firmware_part = NULL;
app_part = BSP_Flash_GetHandle(NAME_PART_APPLICATION);
if (app_part == NULL) {
printf("%s: not found.\r\n", __func__);
return ERR_NO_THIS_PART;
}
firmware_part = BSP_Flash_GetHandle(from_part_name);
if (firmware_part == NULL) {
printf("%s: not found %s part.\r\n", __func__, from_part_name);
return ERR_NO_THIS_PART;
}
_Reset_Write();
printf("part '%s' write to 'app'.\r\n", from_part_name);
/* 读取加密选项 */
decrypt = FM_IsEncrypt();
for (write_posit = 0; write_posit < upkHdr.pkg_size; ) {
if ((upkHdr.pkg_size - read_posit) < UPK_LEAST_HANDLE_BYTE) {
need_read_size = upkHdr.pkg_size - read_posit;
}
read_len = BSP_Flash_Read(firmware_part, (read_posit + UPK_HEAD_SIZE), _upk_min_handle_buff, need_read_size);
if (read_len < 0) {
printf("%s: read error (%d).\r\n", __func__, __LINE__);
return ERR_UPDATE_READ_ERR;
}
result = _Write_FirmwareSubPackage(app_part, _upk_min_handle_buff, read_len, decrypt, FM_DIR_DOWNLOAD_TO_APP);
if (result) {
printf("%s: write error (%d).\r\n", __func__, __LINE__);
return result;
}
read_posit += read_len;
write_posit += read_len;
}
return ERR_OK;
}
#if (CONFIG_AUTO_UPDATE_MODE == MODE_UPDATE_DOWNLOAD_HEAD)
/**
* @brief 更新固件包中的版本信息
* @note
* @param[in] part_name: 分区名称
* @retval eErrCode
*/
eErrCode FM_UpdateFirmwareVersion(const char *part_name)
{
ASSERT(part_name != NULL);
#if (ONCHIP_FLASH_ERASE_GRANULARITY > UPK_LEAST_HANDLE_BYTE)
#error "erase granularity oversize than _upk_min_handle_buff array"
#endif
/* 将 download 分区首地址的数据读出,长度为片内 flash 最小擦除粒度 */
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(part_name);
if (part == NULL)
{
printf("%s: not found.\r\n", __func__);
return ERR_NO_THIS_PART;
}
printf("%s: part name: %s\r\n", __func__, part_name);
if (BSP_Flash_Read(part, 0, &_upk_min_handle_buff[0], ONCHIP_FLASH_ERASE_GRANULARITY) < 0)
{
printf("%s: read error.\r\n", __func__);
return ERR_UPDATE_VER_READ_ERR;
}
/* 修改固件包头中旧版本字段的版本信息为新的固件版本 */
struct upk_head_t *p_pkg_head = (struct upk_head_t *)&_upk_min_handle_buff[0];
p_pkg_head->fw_old_ver[0] = p_pkg_head->fw_new_ver[0];
p_pkg_head->fw_old_ver[1] = p_pkg_head->fw_new_ver[1];
p_pkg_head->fw_old_ver[2] = p_pkg_head->fw_new_ver[2];
p_pkg_head->fw_old_ver[3] = p_pkg_head->fw_new_ver[3];
/* 将读出数据的区域擦除 */
if (BSP_Flash_Erase(part, 0, ONCHIP_FLASH_ERASE_GRANULARITY) < 0)
{
printf("%s: %s part erase failed.\r\n", __func__, part_name);
return ERR_UPDATE_VER_ERASE_ERR;
}
/* 将新的数据写入擦除的区域 */
if (BSP_Flash_Write(part, 0, &_upk_min_handle_buff[0], ONCHIP_FLASH_ERASE_GRANULARITY) < 0)
{
printf("%s: write error (%d).\r\n", __func__, __LINE__);
return ERR_UPDATE_VER_WRITE_ERR;
}
memcpy((uint8_t *)&upkHdr, &_upk_min_handle_buff[0], UPK_HEAD_SIZE);
printf("fw old ver: V%d.%d.%d.%d\r\n", p_pkg_head->fw_old_ver[0], p_pkg_head->fw_old_ver[1], p_pkg_head->fw_old_ver[2], p_pkg_head->fw_old_ver[3]);
printf("fw new ver: V%d.%d.%d.%d\r\n", p_pkg_head->fw_new_ver[0], p_pkg_head->fw_new_ver[1], p_pkg_head->fw_new_ver[2], p_pkg_head->fw_new_ver[3]);
return ERR_OK;
}
#elif (CONFIG_AUTO_UPDATE_MODE == MODE_APPEND_TO_APP)
/**
* @brief 更新固件包中的版本信息
* @note
* @param[in] part_name: 分区名称
* @retval eErrCode
*/
eErrCode FM_UpdateFirmwareVersion(const char *part_name)
{
const struct BSP_FLASH *part = NULL;
part = BSP_Flash_GetHandle(NAME_PART_APPLICATION);
if (part == NULL)
{
printf("%s: not found APP part.\r\n", __func__);
return ERR_NO_THIS_PART;
}
if (BSP_Flash_Read(part, (SIZE_PART_APPLICATION - UPK_VERSION_SIZE), (uint8_t *)&upkHdr.fw_old_ver[0], UPK_VERSION_SIZE) < 0)
{
printf("%s: read error.\r\n", __func__);
return ERR_READ_VER_ERR;
}
if (upkHdr.fw_old_ver[0] != 0xFF
|| upkHdr.fw_old_ver[1] != 0xFF
|| upkHdr.fw_old_ver[2] != 0xFF
|| upkHdr.fw_old_ver[3] != 0xFF)
{
printf("%s: version area no erase.\r\n", __func__);
return ERR_VER_AREA_NO_ERASE;
}
if (BSP_Flash_Write(part, (SIZE_PART_APPLICATION - UPK_VERSION_SIZE), (uint8_t *)&upkHdr.fw_new_ver[0], UPK_VERSION_SIZE) < 0)
{
printf("%s: write error.\r\n", __func__);
return ERR_WRITE_VER_ERR;
}
return ERR_OK;
}
#endif /* #if (CONFIG_AUTO_UPDATE_MODE == MODE_UPDATE_DOWNLOAD_HEAD) */
#endif /* #if (CONFIG_CHIP_PARTS > USE_SINGLE_PARTITION) */
/* Private functions ---------------------------------------------------------*/
/**
* @brief 复位写固件的一些记录信息
* @note
* @retval None
*/
static void _Reset_Write(void)
{
_fw_start_write_flag = 0; /* 固件开始写入的标志位 */
_update_progress = 0; /* 固件更新的进度, 10000 制 */
_storage_data_size = 0; /* 固件包写入时记录暂存的固件分包大小,单位 byte */
_write_part_addr = 0; /* 固件包写入时记录写入 flash 的相对地址 */
_write_last_pkg_size = 0; /* 固件包写入时最后一个分包的大小,单位 byte */
}
uint32_t wcrc32 = 0xFFFFFFFF;
uint32_t wlen = 0;
/**
* @brief 将固件分包按顺序写入某个分区
* @note 循环调用本函数,无须指定写入地址,函数内部自行记录已写入的大小
* @param[in] part: 分区对象
* @param[in] data: 数据
* @param[in] pkg_size: 数据大小,单位 byte
* @param[in] decrypt: 0: 固件包无加密。1: 固件包有加密
* @retval eErrCode
*/
static eErrCode _Write_FirmwareSubPackage( const struct BSP_FLASH *part,
uint8_t *data,
uint16_t pkg_size,
uint8_t decrypt,
FM_FIRMWARE_WRITE_DIR write_dir)
{
uint8_t *fw_4096byte_buff = data;
/* 主机直接下发的固件分包不满 UPK_LEAST_HANDLE_BYTE 个字节,需要先暂存至满足后再写入 */
if (write_dir == FM_DIR_HOST_TO_APP) {
fw_4096byte_buff = &_upk_min_handle_buff[0];
/* 判断是否是最后一个包 */
if (_storage_data_size == 0) {
if (upkHdr.pkg_size - _write_part_addr < UPK_LEAST_HANDLE_BYTE)
{
/* 最后一个固件分包的标志 */
_write_last_pkg_size = upkHdr.pkg_size - _write_part_addr;
printf("_write_last_pkg_size : %d\r\n", _write_last_pkg_size);
}
}
/* 小于最小处理单位时,暂存 */
if (_storage_data_size < UPK_LEAST_HANDLE_BYTE) {
memcpy(&fw_4096byte_buff[_storage_data_size], data, pkg_size);
_storage_data_size += pkg_size;
if (_write_last_pkg_size) {
if (_storage_data_size < _write_last_pkg_size) {
return ERR_OK;
}
} else if (_storage_data_size < UPK_LEAST_HANDLE_BYTE) {
return ERR_OK;
}
}
}
/* 从 download 或 factory 更新至 APP ,因可以直接读取 UPK_LEAST_HANDLE_BYTE 个字节,所以无须暂存 */
else {
if (_fw_start_write_flag == 0) {
wlen = upkHdr.pkg_size;
_storage_data_size = pkg_size;
} else {
if (wlen >= pkg_size) {
wlen -= pkg_size;
_storage_data_size = pkg_size;
} else {
_storage_data_size = wlen;
}
}
}
#if (CONFIG_DECRYPT)
if (decrypt) {
AES_CBC_decrypt_buffer(&aesCtx, fw_4096byte_buff, _storage_data_size);
}
#endif
/* 保存首地址的几个字节数据,等待最后写入 */
if (_fw_start_write_flag == 0) {
for (uint8_t i = 0; i < CONFIG_WRITE_BYTES_LEASET; i++) {
_fw_first_bytes[i] = fw_4096byte_buff[i];
}
fw_4096byte_buff += CONFIG_WRITE_BYTES_LEASET;
_storage_data_size -= CONFIG_WRITE_BYTES_LEASET;
_write_part_addr = CONFIG_WRITE_BYTES_LEASET;
}
if (BSP_Flash_Write(part, _write_part_addr, fw_4096byte_buff, _storage_data_size) < 0) {
printf("%s: write error (%d) 0x%08X + 0x%08X, len %d.\r\n", __func__, __LINE__,
part->addr,_write_part_addr, _storage_data_size);
_Reset_Write();
return ERR_WRITE_PART_ERR;
}
if (_storage_data_size!=92) {
wcrc32 = crc32_step(wcrc32, fw_4096byte_buff, _storage_data_size);
}
_write_part_addr += _storage_data_size;
_storage_data_size = 0;
_fw_start_write_flag = 1;
_update_progress += _update_progress_step_num;
Firmware_OperateCallback(_update_progress);
return ERR_OK;
}
@@ -0,0 +1,47 @@
/**
* 此文件用于关闭 semihosting
*/
#include "common.h"
/* 告诉编译器若没有使用 MicroLIB ,则 main() 函数不需要入口参数 */
#if __IS_COMPILER_ARM_COMPILER_6__
#ifndef __MICROLIB
__asm(".global __ARM_use_no_argv\n\t");
#endif
#endif
/* 关闭 semihosting */
#if __IS_COMPILER_ARM_COMPILER_6__
__asm(".global __use_no_semihosting");
/* AC6 会因为关闭 semihosting 缺这个函数,所以要补上 */
void _sys_exit(int ret)
{
(void)ret;
while(1) {}
}
#elif __IS_COMPILER_ARM_COMPILER_5__
#pragma import(__use_no_semihosting)
#endif
/* AC5 和 AC6 都会因为关闭 semihosting 缺这个函数,所以要补上 */
#if __IS_COMPILER_ARM_COMPILER__
void _ttywrch(int ch)
{
(void)ch;
}
#endif
/* 当使用 AC6 开启 MicroLIB 时,若有使用 assert() 的需求,需要自己实现 __aeabi_assert() */
#if __IS_COMPILER_ARM_COMPILER_6__ && defined(__MICROLIB)
void __aeabi_assert(const char *chCond, const char *chLine, int wErrCode)
{
(void)chCond;
(void)chLine;
(void)wErrCode;
while(1) {
__NOP();
}
}
#endif
+100
View File
@@ -0,0 +1,100 @@
#include "transfer.h"
#if (DT_ENABLE_BROKEN_FRAME_DETECT)
static uint8_t timeo_frame = 0;
static struct drv_timer_t timerFrameBroken;
static void timerFrameBrokenCallback (void *user_data)
{
timeo_frame = 1;
//printf("frame detect clock time up!\r\n");
}
#endif
static uint8_t DT_Port_IsRecvData (struct transfer_t *xfer)
{
#if (DT_ENABLE_BROKEN_FRAME_DETECT)
if (timeo_frame) {
timeo_frame = 0;
return BSP_UART_ERR_OK;
} else if (BSP_UART_IsFrameEnd((BSP_UART_ID)xfer->if_id) == BSP_UART_ERR_OK) {
drv_timer_linkUserData(&timerFrameBroken, xfer);
drv_timer_restart(&timerFrameBroken);
return BSP_UART_ERR_NO_RECV_FRAME;
}
return BSP_UART_ERR_NO_RECV_FRAME;
#else
return BSP_UART_IsFrameEnd((BSP_UART_ID)xfer->if_id);
#endif
}
void DT_Send (struct transfer_t *xfer, uint8_t *data, uint32_t len)
{
BSP_UART_Send((BSP_UART_ID)xfer->if_id, data, len, 0xFFFF);
}
/**
* @brief 检测是否接收到一帧数据的轮询接口
* @note
* @param[in] xfer: 传输控制块对象
* @retval exRESULT
*/
exRESULT DT_PollingReceive (struct transfer_t *xfer)
{
exRESULT state = X_RESULT_NO_DATA;
if (DT_Port_IsRecvData(xfer) == 0) {
state = X_RESULT_RECV_FRAME;
}
return state;
}
/**
* @brief 数据传输层初始化
* @note
* @param[in] xfer: 传输控制块对象
* @param[in] if_id: 传输接口 ID
* @param[in] buff: 用于接收数据的缓冲池,单位 byte
* @param[in] len: 指示接收到的数据长度,单位 byte
* @param[in] buff_size: 数据池最大容量,单位 byte
* @retval None
*/
void DT_Init (struct transfer_t *xfer, uint8_t if_id, uint8_t *buff, uint16_t *len, uint32_t buff_size)
{
ASSERT(xfer != NULL);
xfer->if_id = if_id;
xfer->rx_buff = buff;
xfer->rx_len = len;
xfer->rx_buff_size = buff_size;
BSP_UART_Init((BSP_UART_ID)xfer->if_id);
BSP_UART_LinkUserData((BSP_UART_ID)xfer->if_id, xfer);
BSP_UART_EnableReceive( (BSP_UART_ID)xfer->if_id,
xfer->rx_buff,
xfer->rx_len,
xfer->rx_buff_size);
#if (DT_ENABLE_BROKEN_FRAME_DETECT)
drv_timer_init(&timerFrameBroken,
timerFrameBrokenCallback,
BROKEN_FRAME_INTERVAL_TIME,
TIMER_RUN_ONE_SHOT);
#endif
}
+104
View File
@@ -0,0 +1,104 @@
/* Includes ------------------------------------------------------------------*/
#include "utils.h"
#include "bsp_common.h"
const char Hex2Ascii[17] = "0123456789ABCDEF";
int hex_printf (const uint8_t *buff, int count)
{
uint32_t i = 0, j = 0;
char str_val[75] = {0};
uint32_t index = 0;
uint32_t str_index = 0;
uint32_t cnt = (count+15)>>4;
if (count < 0) {
return -1;
}
if (cnt > 16) {
cnt = 16;
//BSP_printf("!!! TOO LONG. ONLY SHOW HEAD 256 bytes !!!\n");
}
for (i = 0; i < cnt; i++) {
index = 0;
str_index = 0;
for (j = 0; j < 16; j++) {
if (j + (i << 4) < count) {
str_val[index++] = Hex2Ascii[(buff[j + (i << 4)] & 0xF0) >> 4];
str_val[index++] = Hex2Ascii[buff[j + (i << 4)] & 0xF];
str_val[index++] = ' ';
} else {
str_val[index++] = ' ';
str_val[index++] = ' ';
str_val[index++] = ' ';
}
}
index += str_index;
str_val[index++] = '\0';
str_val[index++] = '\0';
printf("%s\r\n", str_val);
}
return 0;
}
/******************************************************************************
* Name: CRC-16/XMODEM x16+x12+x5+1
* Poly: 0x1021
* Init: 0x0000
* Refin: False
* Refout: False
* Xorout: 0x0000
* Alias: CRC-16/ZMODEM,CRC-16/ACORN
*****************************************************************************/
uint16_t crc16_xmodem (uint8_t *data, uint16_t length)
{
uint8_t i;
uint16_t crc = 0; // Initial value
while(length--)
{
crc ^= (uint16_t)(*data++) << 8; // crc ^= (uint16_t)(*data)<<8; data++;
for (i = 0; i < 8; ++i)
{
if ( crc & 0x8000 )
crc = (crc << 1) ^ 0x1021;
else
crc <<= 1;
}
}
return crc;
}
uint32_t crc32_step (uint32_t in_crc, const void *buf, uint32_t size)
{
static uint32_t crc_table[256] = {0, 0,};
uint32_t i = 0, j = 0, crc = 0;
if (!crc_table[1]) {
for (i = 0; i < 256; i++) {
crc = i;
for (j = 0; j < 8; j++) {
crc = (crc & 1) ? ((crc>>1)^0xEDB88320ul) : (crc>>1);
}
crc_table[i] = crc;
}
}
crc = in_crc ^ 0xFFFFFFFFul;
for (i = 0; i < size; i++) {
crc = crc_table[(crc ^ ((const uint8_t *)buf)[i]) & 0xFF]^(crc>>8);
}
return crc ^ 0xFFFFFFFFul;
}
+336
View File
@@ -0,0 +1,336 @@
#include "ymodem.h"
struct ymodem_info_t {
uint8_t isBusy; /* 正在处理主机数据的标志位 */
uint8_t enRecv; /* 使能接收主机的指令包 */
uint16_t rxLen; /* 接收到的数据长度 */
uint8_t *rxData; /* 协议解析的数据来源 */
uint8_t numPkt; /* 记录 YModem 协议的 packet number */
eYmFLOW flow; /* 记录协议的执行流程 */
union message_raw_t *msg; /* 接收主机数据包的缓存池,称为主机消息 */
struct tx_raw_t txPkt; /* 用于存放设备上发数据组包的部分参数 */
ymSend send; /* 数据发送接口 */
ymPrepareCallback prepare; /* 收到主机指令时的预备处理接口 */
ymReplyCallback reply; /* 正在执行指令时响应主机查询执行过程和结果的接口 */
};
static struct ymodem_info_t yM;
static struct drv_timer_t timerSendC; /* 用于定时向主机发送数据的定时器 */
static void timerSendCCallback (void *user_data)
{
static uint8_t c[1] = {YMODEM_C};
yM.send(c, 1, HAL_MAX_DELAY);
}
/**
* @brief 复位一些全局变量和标志信息
* @note
* @retval None
*/
static void ymodemReset (void)
{
yM.isBusy = 0;
yM.enRecv = 1;
yM.numPkt = 0;
yM.flow = YMODEM_FLOW_NONE;
}
/**
* @brief 对主机下发的心跳包的处理
* @note 对主机下发的数据帧进行回复, ymodem 没有心跳包,此处只是模拟
* @retval None
*/
static void ymodemHeartBeatProcess (void)
{
static eRESULT result;
yM.reply((eYmCMD)yM.msg->pkg.header, &result, NULL, NULL);
switch (result) {
case YM_RESULT_OK:
//yM.txPkt.response = YMODEM_ACK;
break;
case YM_RESULT_FAILED: {
/* 因前面已经加 1 ,此处是由于对数据处理有问题,非协议本身问题,因此需要减回 */
yM.numPkt--;
yM.txPkt.response = YMODEM_NAK;
break;
}
case YM_RESULT_CANCEL:
yM.txPkt.response = YMODEM_CAN;
break;
default:
/* 业务层还在处理数据,暂时不回复主机 */
return;
}
yM.isBusy = 0;
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
}
/**
* @brief 对主机下发指令的处理
* @note
* @retval None
*/
static void ymodemCommandProcess (void)
{
uint16_t data_len = 0;
yM.isBusy = 1;
if ((yM.msg->pkg.header == YMODEM_SOH)||(yM.msg->pkg.header == YMODEM_STX)) {
data_len = yM.rxLen - YMODEM_FRAME_FIXED_LEN;
}
yM.prepare((eYmCMD)yM.msg->pkg.header, yM.msg->pkg.data, data_len);
}
/**
* @brief 设置协议执行流程
* @note
* @param[in] cmd: 主机的指令
* @retval eErrCode
*/
static eErrCode ymodemExeFlow (eYmCMD cmd)
{
switch (cmd) {
case YM_CMD_SOH: {
if (yM.flow == YMODEM_FLOW_NONE) { /* 第一个 SOH 数据帧 */
yM.flow = YMODEM_FLOW_START;
yM.txPkt.response = YMODEM_ACK;
} else if (yM.flow == YMODEM_FLOW_SECOND_EOT) { /* 最后一个空的 SOH 数据帧 */
drv_timer_pause(&timerSendC);
yM.flow = YMODEM_FLOW_SUCCESS;
yM.numPkt = 0;
yM.txPkt.response = YMODEM_ACK;
} else { /* 正在传输数据的 SOH 数据帧 */
/* 暂停发送字符“ C ”的定时器 */
drv_timer_pause(&timerSendC);
yM.txPkt.response = YMODEM_ACK;
}
break;
}
case YM_CMD_STX: {
/* 因为第一个有数据的数据帧可能是 STX ,因此此处有必要暂停发送字符“ C ”的定时器 */
drv_timer_pause(&timerSendC);
yM.txPkt.response = YMODEM_ACK;
break;
}
case YM_CMD_EOT: {
if (yM.flow == YMODEM_FLOW_START) { /* 第一个 EOT */
yM.flow = YMODEM_FLOW_FIRST_EOT;
yM.txPkt.response = YMODEM_NAK;
} else if (yM.flow == YMODEM_FLOW_FIRST_EOT) { /* 第二个 EOT */
yM.flow = YMODEM_FLOW_SECOND_EOT;
yM.numPkt = 0;
yM.txPkt.response = YMODEM_ACK;
/* 发完 ACK 需要继续发“ C ” */
drv_timer_restart(&timerSendC);
} else {
return ERR_EXE_FLOW;
}
break;
}
case YM_CMD_CAN: {
yM.flow = YMODEM_FLOW_CANCEL;
yM.txPkt.response = YMODEM_ACK;
break;
}
default:
break;
}
return ERR_OK;
}
/**
* @brief 协议解析处理函数
* @note 需要循环调用
* @retval eErrCode
*/
eErrCode ymodemHandler (uint8_t *data, uint16_t len)
{
eErrCode err_code;
/* 有数据 */
if (data && len && yM.enRecv) {
/* 暂存和格式化 */
yM.rxData = data;
yM.rxLen = len;
yM.msg = (union message_raw_t *)yM.rxData;
//hex_printf(data, len);
/* 只有数据帧才做以下错误检查 */
if (yM.msg->pkg.header == YMODEM_SOH || yM.msg->pkg.header == YMODEM_STX) {
/* 判断序列号是否符合顺序 */
if (yM.msg->pkg.pkt_num != yM.numPkt) {
if (yM.msg->pkg.pkt_num == (yM.numPkt - 1)) {
printf("error: duplicate frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
err_code = ERR_DUPLICATE_FRAME;
} else {
printf("error: omission frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
err_code = ERR_OMISSION_FRAME;
}
goto __error_exit;
}
/* 判断正反序列号是否正确 */
uint8_t pkt_num = ~(yM.msg->pkg.pkt_num);
if (yM.msg->pkg.not_pkt_num != pkt_num) {
printf("error: packet number: %.2X %.2X\r\n", yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
err_code = ERR_PKT_NUM_ERR;
goto __error_exit;
}
/* 获取协议帧中的数据字段长度 */
uint16_t data_len;
if (yM.msg->pkg.header == YMODEM_SOH) {
data_len = YMODEM_SOH_DATA_LEN;
} else if (yM.msg->pkg.header == YMODEM_STX) {
data_len = YMODEM_STX_DATA_LEN;
}
/* 帧长度判断 */
/* 奇怪的是, Xshell 在发送最后一个空 SOH 数据帧时会附加两个字节的 0x4F ,原因未知。
* 为了处理这个问题,避免误判为数据帧长度有误,此处嵌套了“ ymodem_pkt_num != 0 ”的判断 */
if (yM.numPkt != 0) {
if (yM.rxLen != (data_len + YMODEM_FRAME_FIXED_LEN)) {
//printf("error: _dev_rx_len: %d\r\n", yM.rxLen);
err_code = ERR_FRAME_LENGTH;
goto __error_exit;
}
}
/* 校验数据是否正确 */
uint16_t crc16 = crc16_xmodem(yM.msg->pkg.data, data_len);
printf("%04X", crc16);
uint16_t raw_crc16 = (yM.msg->pkg.data[data_len] << 8) | yM.msg->pkg.data[data_len + 1];
if (crc16 != raw_crc16) {
printf("error: crc16: %.4X\r\n", crc16);
printf("error: raw crc16: %.4X\r\n", raw_crc16);
err_code = ERR_FRAME_VERIFY_ERR;
goto __error_exit;
}
//printf("crc16 ok.\r\n");
/* 执行到此处记录序列号加1 */
yM.numPkt++;
}
//printf("Ymodem recv len: %d (%02X %02X %02X)\r\n",
// yM.rxLen, yM.msg->pkg.header, yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
/* 设置流程 */
if (ymodemExeFlow((eYmCMD)yM.msg->pkg.header)) {
//printf("error: flow illegal\r\n");
err_code = ERR_EXE_FLOW;
goto __error_exit;
}
/* 除了最后一个空的SOH数据帧,其他都会执行 ,包括 EOT CAN */
/* 需要注意是, 执行完后仍未回复主机,回复部分由 ymodemHeartBeatProcess 处理 */
ymodemCommandProcess();
return ERR_OK;
__error_exit:
yM.txPkt.response = YMODEM_NAK;
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
return err_code;
}
if (yM.isBusy) {
/* 模拟主机的心跳处理,查询是否对协议包处理完毕,以向主机回复 */
ymodemHeartBeatProcess();
}
return ERR_OK;
}
/**
* @brief 配置协议析构层的参数
* @note
* @param[in] para: 需要进行配置的选型或参数
* @param[in] value: 对应的数据或值
* @retval None
*/
void ymodemConfig (eYmMODE mode, void *value)
{
switch (mode) {
case YM_MODE_RESET:
ymodemReset();
drv_timer_restart(&timerSendC);
break;
case YM_MODE_RECV: {
uint8_t *enable = (uint8_t *)value;
yM.enRecv = *enable;
if (yM.enRecv == 0) {
drv_timer_pause(&timerSendC);
}
break;
}
default:
break;
}
}
/**
* @brief 协议析构层的初始化
* @note
* @param[in] Send: 底层数据发送接口
* @param[in] HeartbeatCallback: 心跳包的响应接口
* @param[in] PrepareCallback: 指令包的处理接口
* @param[in] Set_ReplyInfo: 查询指令执行结果的处理接口
* @retval None
*/
void ymodem_init (ymSend Send,
ymPrepareCallback PrepareCallback,
ymReplyCallback Set_ReplyInfo)
{
yM.send = Send;
yM.prepare = PrepareCallback;
yM.reply = Set_ReplyInfo;
ymodemReset();
drv_timer_init(&timerSendC,
timerSendCCallback,
1000,
TIMER_RUN_FOREVER);
drv_timer_start(&timerSendC);
}