From 1a6dd44d0302138565fdbc9465f4fb156db2055a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 19 Sep 2016 18:00:03 +1000 Subject: [PATCH 001/131] hwcrypto bignum: Use mbedtls_mpi data structures for all bignum data Still doesn't solve the problem of multiplying two numbers where one is >2048 bits, needed for RSA support. --- components/mbedtls/port/esp_bignum.c | 135 ++++++++---------- .../mbedtls/port/include/mbedtls/esp_config.h | 4 +- 2 files changed, 58 insertions(+), 81 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 59bdc87260..caae0161f0 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -207,98 +207,80 @@ static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint m return( mpi_montmul( A, &U, N, mm, T ) ); } +#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ -/* Allocate parameters used by hardware MPI multiply, - and copy mbedtls_mpi structures into them */ -static int mul_pram_alloc(const mbedtls_mpi *A, const mbedtls_mpi *B, char **pA, char **pB, char **pX, size_t *bites) +/* Number of words used to hold 'mpi', rounded up to nearest + 16 words (512 bits) to match hardware support +*/ +static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { - char *sa, *sb, *sx; -// int algn; - int words, bytes; - int abytes, bbytes; - - if (A->n > B->n) - words = A->n; - else - words = B->n; - - bytes = (words / 16 + ((words % 16) ? 1 : 0 )) * 16 * 4 * 2; - - abytes = A->n * 4; - bbytes = B->n * 4; - - sa = malloc(bytes); - if (!sa) { - return -1; - } - - sb = malloc(bytes); - if (!sb) { - free(sa); - return -1; - } - - sx = malloc(bytes); - if (!sx) { - free(sa); - free(sb); - return -1; - } - - memcpy(sa, A->p, abytes); - memset(sa + abytes, 0, bytes - abytes); - - memcpy(sb, B->p, bbytes); - memset(sb + bbytes, 0, bytes - bbytes); - - *pA = sa; - *pB = sb; - - *pX = sx; - - *bites = bytes * 4; - - return 0; + size_t res; + for(res = mpi->n; res > 0; res-- ) { + if( mpi->p[res - 1] != 0 ) + break; + } + res = (res + 0xF) & ~0xF; + return res; } -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) - int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) { int ret = -1; - size_t i, j; - char *s1 = NULL, *s2 = NULL, *dest = NULL; - size_t bites; + size_t words_a, words_b, words_x, words_mult; mbedtls_mpi TA, TB; mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); - if( X == A ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); A = &TA; } - if( X == B ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); B = &TB; } + /* Count words needed for A & B in hardware */ + words_a = hardware_words_needed(A); + words_b = hardware_words_needed(B); - for( i = A->n; i > 0; i-- ) - if( A->p[i - 1] != 0 ) - break; + /* Take a copy of A if either X == A OR if A isn't long enough + to hold the number of words needed for hardware. - for( j = B->n; j > 0; j-- ) - if( B->p[j - 1] != 0 ) - break; + (can't grow A directly as it is const) - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + j ) ); + TODO: growing the input operands is only necessary because the + ROM functions only take one length argument. It should be + possible for us to just copy the used data only into the + hardware buffers, and set the remaining bits to zero - saving + RAM. But we need to reimplement ets_bigint_mult_prepare() in + software for this. + */ + if( X == A || A->n < words_a) { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_a) ); + A = &TA; + } + /* Same for B */ + if( X == B || B->n < words_b ) { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_b) ); + B = &TB; + } + + /* Result X has to have room for double the larger operand */ + words_mult = (words_a > words_b ? words_a : words_b); + words_x = words_mult * 2; + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); + /* TODO: check if lset here is necessary, hardware should zero */ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - if (mul_pram_alloc(A, B, &s1, &s2, &dest, &bites)) { - goto cleanup; - } - esp_mpi_acquire_hardware(); - if (ets_bigint_mult_prepare((uint32_t *)s1, (uint32_t *)s2, bites)){ - ets_bigint_wait_finish(); - if (ets_bigint_mult_getz((uint32_t *)dest, bites) == true) { - memcpy(X->p, dest, (i + j) * 4); - ret = 0; - } else { + + if(words_mult * 32 > 2048) { + printf("WARNING: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); + } + + if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { + ets_bigint_wait_finish(); + /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is + copied to output X->p. + */ + if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { + ret = 0; + } else { printf("ets_bigint_mult_getz failed\n"); } } else{ @@ -307,11 +289,6 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi esp_mpi_release_hardware(); X->s = A->s * B->s; - - free(s1); - free(s2); - free(dest); - cleanup: mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 5a69ff78e4..4ddd9821c4 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -253,8 +253,8 @@ Disabled as number of limbs limited by bug. Internal TW#7112. */ -//#define MBEDTLS_MPI_EXP_MOD_ALT -//#define MBEDTLS_MPI_MUL_MPI_ALT +#define MBEDTLS_MPI_EXP_MOD_ALT +#define MBEDTLS_MPI_MUL_MPI_ALT /** * \def MBEDTLS_MD2_PROCESS_ALT From 6b3bc4d8c5bb3eacf1076417074bccdca73971d5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Sep 2016 21:02:07 +1000 Subject: [PATCH 002/131] hwcrypto bignum: Implement multiplication modulo Fixes case where hardware bignum multiplication fails due to either operand >2048 bits. --- components/mbedtls/port/esp_bignum.c | 245 ++++++++++++++++-- .../mbedtls/port/include/mbedtls/esp_config.h | 4 +- 2 files changed, 223 insertions(+), 26 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index caae0161f0..d6b79e32f5 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -52,6 +52,140 @@ static void esp_mpi_release_hardware( void ) _lock_release(&mpi_lock); } +/* Given a & b, determine u & v such that + + gcd(a,b) = d = au + bv + + Underlying algorithm comes from: + http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf + http://www.hackersdelight.org/hdcodetxt/mont64.c.txt + */ +static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, + mbedtls_mpi *u, mbedtls_mpi *v) +{ + mbedtls_mpi ta, tb; + + mbedtls_mpi_init(&ta); + mbedtls_mpi_copy(&ta, a); + mbedtls_mpi_init(&tb); + mbedtls_mpi_copy(&tb, b); + + mbedtls_mpi_lset(u, 1); + mbedtls_mpi_lset(v, 0); + + /* Loop invariant: + ta = u*2*a - v*b. */ + while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { + mbedtls_mpi_shift_r(&ta, 1); + if (mbedtls_mpi_get_bit(u, 0) == 0) { + // Remove common factor of 2 in u & v + mbedtls_mpi_shift_r(u, 1); + mbedtls_mpi_shift_r(v, 1); + } + else { + /* u = (u + b) >> 1 */ + mbedtls_mpi_add_mpi(u, u, b); + mbedtls_mpi_shift_r(u, 1); + /* v = (v >> 1) + a */ + mbedtls_mpi_shift_r(v, 1); + mbedtls_mpi_add_mpi(v, v, a); + } + } + mbedtls_mpi_free(&ta); + mbedtls_mpi_free(&tb); + + /* u = u * 2, so 1 = u*a - v*b */ + mbedtls_mpi_shift_l(u, 1); +} + +/* inner part of MPI modular multiply, after Rinv & Mprime are calculated */ +static int mpi_mul_mpi_mod_inner(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, mbedtls_mpi *Rinv, uint32_t Mprime, size_t num_words) +{ + int ret; + mbedtls_mpi TA, TB; + size_t num_bits = num_words * 32; + + mbedtls_mpi_grow(Rinv, num_words); + + /* TODO: fill memory blocks directly so this isn't needed */ + mbedtls_mpi_init(&TA); + mbedtls_mpi_copy(&TA, A); + mbedtls_mpi_grow(&TA, num_words); + A = &TA; + mbedtls_mpi_init(&TB); + mbedtls_mpi_copy(&TB, B); + mbedtls_mpi_grow(&TB, num_words); + B = &TB; + + esp_mpi_acquire_hardware(); + + if(ets_bigint_mod_mult_prepare(A->p, B->p, M->p, Mprime, + Rinv->p, num_bits, false)) { + mbedtls_mpi_grow(X, num_words); + ets_bigint_wait_finish(); + if(ets_bigint_mod_mult_getz(M->p, X->p, num_bits)) { + X->s = A->s * B->s; + ret = 0; + } else { + printf("ets_bigint_mod_mult_getz failed\n"); + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + } else { + printf("ets_bigint_mod_mult_prepare failed\n"); + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + esp_mpi_release_hardware(); + + /* unclear why this is necessary, but the result seems + to come back rotated 32 bits to the right... */ + uint32_t last_word = X->p[num_words-1]; + X->p[num_words-1] = 0; + mbedtls_mpi_shift_l(X, 32); + X->p[0] = last_word; + + mbedtls_mpi_free(&TA); + mbedtls_mpi_free(&TB); + + return ret; +} + +/* X = (A * B) mod M + + Not an mbedTLS function + + num_bits guaranteed to be a multiple of 512 already. + + TODO: ensure M is odd + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, size_t num_bits) +{ + int ret = 0; + mbedtls_mpi RR, Rinv, Mprime; + uint32_t Mprime_int; + size_t num_words = num_bits / 32; + + /* Rinv & Mprime are calculated via extended binary gcd + algorithm, see references on extended_binary_gcd above. + */ + mbedtls_mpi_init(&Rinv); + mbedtls_mpi_init(&RR); + mbedtls_mpi_set_bit(&RR, num_bits+32, 1); + mbedtls_mpi_init(&Mprime); + extended_binary_gcd(&RR, M, &Rinv, &Mprime); + + /* M' is mod 2^32 */ + Mprime_int = Mprime.p[0]; + + ret = mpi_mul_mpi_mod_inner(X, A, B, M, &Rinv, Mprime_int, num_words); + + mbedtls_mpi_free(&RR); + mbedtls_mpi_free(&Mprime); + mbedtls_mpi_free(&Rinv); + + return ret; +} + + /* * Helper for mbedtls_mpi multiplication * copied/trimmed from mbedtls bignum.c @@ -223,6 +357,53 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } + +/* Special-case multiply, where we use hardware montgomery mod + multiplication to solve the case where A or B are >2048 bits so + can't do standard multiplication. + + the modulus here is chosen with M=(2^num_bits-1) + to guarantee the output isn't actually modulo anything. This means + we don't need to calculate M' and Rinv, they are predictable + as follows: + M' = 1 + Rinv = (1 << (num_bits - 32) + + (See RSA Accelerator section in Technical Reference for derivation + of M', Rinv) +*/ +static int esp_mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, size_t num_words) + { + mbedtls_mpi M, Rinv; + int ret; + size_t mprime; + size_t num_bits = num_words * 32; + + mbedtls_mpi_init(&M); + mbedtls_mpi_init(&Rinv); + + /* TODO: it may be faster to just use 4096-bit arithmetic every time, + and make these constants rather than runtime derived + derived. */ + /* M = (2^num_words)-1 */ + mbedtls_mpi_grow(&M, num_words); + for(int i = 0; i < num_words*32; i++) { + mbedtls_mpi_set_bit(&M, i, 1); + } + + /* Rinv = (2^num_words-32) */ + mbedtls_mpi_grow(&Rinv, num_words); + mbedtls_mpi_set_bit(&Rinv, num_bits - 32, 1); + + mprime = 1; + + ret = mpi_mul_mpi_mod_inner(X, A, B, &M, &Rinv, mprime, num_words); + + mbedtls_mpi_free(&M); + mbedtls_mpi_free(&Rinv); + return ret; + } + int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) { int ret = -1; @@ -236,6 +417,8 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi words_a = hardware_words_needed(A); words_b = hardware_words_needed(B); + words_mult = (words_a > words_b ? words_a : words_b); + /* Take a copy of A if either X == A OR if A isn't long enough to hold the number of words needed for hardware. @@ -248,47 +431,63 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi RAM. But we need to reimplement ets_bigint_mult_prepare() in software for this. */ - if( X == A || A->n < words_a) { + if( X == A || A->n < words_mult) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_a) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_mult) ); A = &TA; } /* Same for B */ - if( X == B || B->n < words_b ) { + if( X == B || B->n < words_mult ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_b) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_mult) ); B = &TB; } /* Result X has to have room for double the larger operand */ - words_mult = (words_a > words_b ? words_a : words_b); words_x = words_mult * 2; MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); /* TODO: check if lset here is necessary, hardware should zero */ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - esp_mpi_acquire_hardware(); + /* If either operand is over 2048 bits, we can't use the standard hardware multiplier + (it assumes result is double longest operand, and result is max 4096 bits.) + However, we can fail over to mod_mult for up to 4096 bits. + */ if(words_mult * 32 > 2048) { - printf("WARNING: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); - } - - if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { - ets_bigint_wait_finish(); - /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is - copied to output X->p. + /* TODO: check if there's an overflow condition if words_a & words_b are both + the bit lengths of the operands, result could be 1 bit longer */ - if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { - ret = 0; - } else { - printf("ets_bigint_mult_getz failed\n"); - } - } else{ - printf("Baseline multiplication failed\n"); - } - esp_mpi_release_hardware(); + if((words_a + words_b) * 32 > 4096) { + printf("ERROR: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + else { + ret = esp_mpi_mult_mpi_failover_mod_mult(X, A, B, words_a + words_b); + } + } + else { - X->s = A->s * B->s; + /* normal mpi multiplication */ + esp_mpi_acquire_hardware(); + if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { + ets_bigint_wait_finish(); + /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is + copied to output X->p. + */ + if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { + X->s = A->s * B->s; + ret = 0; + } else { + printf("ets_bigint_mult_getz failed\n"); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + } else{ + printf("Baseline multiplication failed\n"); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + esp_mpi_release_hardware(); + } cleanup: mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 4ddd9821c4..e4f4af271a 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -250,10 +250,8 @@ /* The following MPI (bignum) functions have ESP32 hardware support, Uncommenting these macros will use the hardware-accelerated implementations. - - Disabled as number of limbs limited by bug. Internal TW#7112. */ -#define MBEDTLS_MPI_EXP_MOD_ALT +//#define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT /** From 9632c8e56c49db6351dd8a1b964d61ac67cff342 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Sep 2016 21:24:58 +1000 Subject: [PATCH 003/131] RSA Accelerator: Add mod_exp, refactor to avoid memory allocation & copying Not fully working at the moment, mod_exp has a bug. --- components/esp32/include/soc/hwcrypto_reg.h | 37 + components/esp32/include/soc/soc.h | 1 + components/mbedtls/port/esp_bignum.c | 1073 +++++++---------- .../mbedtls/port/include/mbedtls/esp_config.h | 2 +- 4 files changed, 502 insertions(+), 611 deletions(-) create mode 100644 components/esp32/include/soc/hwcrypto_reg.h diff --git a/components/esp32/include/soc/hwcrypto_reg.h b/components/esp32/include/soc/hwcrypto_reg.h new file mode 100644 index 0000000000..4f38b1ba93 --- /dev/null +++ b/components/esp32/include/soc/hwcrypto_reg.h @@ -0,0 +1,37 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __HWCRYPTO_REG_H__ +#define __HWCRYPTO_REG_H__ + +#include "soc.h" + +/* registers for RSA acceleration via Multiple Precision Integer ops */ +#define RSA_MEM_M_BLOCK_BASE ((DR_REG_RSA_BASE)+0x000) +/* RB & Z use the same memory block, depending on phase of operation */ +#define RSA_MEM_RB_BLOCK_BASE ((DR_REG_RSA_BASE)+0x200) +#define RSA_MEM_Z_BLOCK_BASE ((DR_REG_RSA_BASE)+0x200) +#define RSA_MEM_Y_BLOCK_BASE ((DR_REG_RSA_BASE)+0x400) +#define RSA_MEM_X_BLOCK_BASE ((DR_REG_RSA_BASE)+0x600) + +#define RSA_M_DASH_REG (DR_REG_RSA_BASE + 0x800) +#define RSA_MODEXP_MODE_REG (DR_REG_RSA_BASE + 0x804) +#define RSA_START_MODEXP_REG (DR_REG_RSA_BASE + 0x808) +#define RSA_MULT_MODE_REG (DR_REG_RSA_BASE + 0x80c) +#define RSA_MULT_START_REG (DR_REG_RSA_BASE + 0x810) + +#define RSA_INTERRUPT_REG (DR_REG_RSA_BASE + 0X814) + +#define RSA_CLEAN_ADDR (DR_REG_RSA_BASE + 0X818) + +#endif diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 4ffdfb069e..65698ec856 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -141,6 +141,7 @@ //}} #define DR_REG_DPORT_BASE 0x3ff00000 +#define DR_REG_RSA_BASE 0x3ff02000 #define DR_REG_UART_BASE 0x3ff40000 #define DR_REG_SPI1_BASE 0x3ff42000 #define DR_REG_SPI0_BASE 0x3ff43000 diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index d6b79e32f5..55e70c47ac 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -23,9 +23,19 @@ #include #include #include +#include +#include #include "mbedtls/bignum.h" #include "mbedtls/bn_mul.h" #include "rom/bigint.h" +#include "soc/hwcrypto_reg.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "bignum"; #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) @@ -35,6 +45,38 @@ static _lock_t mpi_lock; +/* Temporary debugging function to print an MPI number to + stdout. Happens to be in a format compatible with Python. +*/ +void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) +{ + static char buf[1024]; + size_t n; + memset(buf, 0, sizeof(buf)); + printf("%s = 0x", name); + mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); + if(n) { + puts(buf); + } else { + puts("TOOLONG"); + } +} + +/* Temporary debug function to dump a memory block's contents to stdout + TODO remove + */ +static void __attribute__((unused)) dump_memory_block(const char *label, uint32_t addr) +{ + printf("Dumping %s @ %08x\n", label, addr); + for(int i = 0; i < (4096 / 8); i += 4) { + if(i % 32 == 0) { + printf("\n %04x:", i); + } + printf("%08x ", REG_READ(addr + i)); + } + printf("Done\n"); +} + /* At the moment these hardware locking functions aren't exposed publically for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. @@ -52,299 +94,11 @@ static void esp_mpi_release_hardware( void ) _lock_release(&mpi_lock); } -/* Given a & b, determine u & v such that - - gcd(a,b) = d = au + bv - - Underlying algorithm comes from: - http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf - http://www.hackersdelight.org/hdcodetxt/mont64.c.txt - */ -static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, - mbedtls_mpi *u, mbedtls_mpi *v) -{ - mbedtls_mpi ta, tb; - - mbedtls_mpi_init(&ta); - mbedtls_mpi_copy(&ta, a); - mbedtls_mpi_init(&tb); - mbedtls_mpi_copy(&tb, b); - - mbedtls_mpi_lset(u, 1); - mbedtls_mpi_lset(v, 0); - - /* Loop invariant: - ta = u*2*a - v*b. */ - while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { - mbedtls_mpi_shift_r(&ta, 1); - if (mbedtls_mpi_get_bit(u, 0) == 0) { - // Remove common factor of 2 in u & v - mbedtls_mpi_shift_r(u, 1); - mbedtls_mpi_shift_r(v, 1); - } - else { - /* u = (u + b) >> 1 */ - mbedtls_mpi_add_mpi(u, u, b); - mbedtls_mpi_shift_r(u, 1); - /* v = (v >> 1) + a */ - mbedtls_mpi_shift_r(v, 1); - mbedtls_mpi_add_mpi(v, v, a); - } - } - mbedtls_mpi_free(&ta); - mbedtls_mpi_free(&tb); - - /* u = u * 2, so 1 = u*a - v*b */ - mbedtls_mpi_shift_l(u, 1); -} - -/* inner part of MPI modular multiply, after Rinv & Mprime are calculated */ -static int mpi_mul_mpi_mod_inner(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, mbedtls_mpi *Rinv, uint32_t Mprime, size_t num_words) -{ - int ret; - mbedtls_mpi TA, TB; - size_t num_bits = num_words * 32; - - mbedtls_mpi_grow(Rinv, num_words); - - /* TODO: fill memory blocks directly so this isn't needed */ - mbedtls_mpi_init(&TA); - mbedtls_mpi_copy(&TA, A); - mbedtls_mpi_grow(&TA, num_words); - A = &TA; - mbedtls_mpi_init(&TB); - mbedtls_mpi_copy(&TB, B); - mbedtls_mpi_grow(&TB, num_words); - B = &TB; - - esp_mpi_acquire_hardware(); - - if(ets_bigint_mod_mult_prepare(A->p, B->p, M->p, Mprime, - Rinv->p, num_bits, false)) { - mbedtls_mpi_grow(X, num_words); - ets_bigint_wait_finish(); - if(ets_bigint_mod_mult_getz(M->p, X->p, num_bits)) { - X->s = A->s * B->s; - ret = 0; - } else { - printf("ets_bigint_mod_mult_getz failed\n"); - ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - } else { - printf("ets_bigint_mod_mult_prepare failed\n"); - ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - esp_mpi_release_hardware(); - - /* unclear why this is necessary, but the result seems - to come back rotated 32 bits to the right... */ - uint32_t last_word = X->p[num_words-1]; - X->p[num_words-1] = 0; - mbedtls_mpi_shift_l(X, 32); - X->p[0] = last_word; - - mbedtls_mpi_free(&TA); - mbedtls_mpi_free(&TB); - - return ret; -} - -/* X = (A * B) mod M - - Not an mbedTLS function - - num_bits guaranteed to be a multiple of 512 already. - - TODO: ensure M is odd - */ -int esp_mpi_mul_mpi_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, size_t num_bits) -{ - int ret = 0; - mbedtls_mpi RR, Rinv, Mprime; - uint32_t Mprime_int; - size_t num_words = num_bits / 32; - - /* Rinv & Mprime are calculated via extended binary gcd - algorithm, see references on extended_binary_gcd above. - */ - mbedtls_mpi_init(&Rinv); - mbedtls_mpi_init(&RR); - mbedtls_mpi_set_bit(&RR, num_bits+32, 1); - mbedtls_mpi_init(&Mprime); - extended_binary_gcd(&RR, M, &Rinv, &Mprime); - - /* M' is mod 2^32 */ - Mprime_int = Mprime.p[0]; - - ret = mpi_mul_mpi_mod_inner(X, A, B, M, &Rinv, Mprime_int, num_words); - - mbedtls_mpi_free(&RR); - mbedtls_mpi_free(&Mprime); - mbedtls_mpi_free(&Rinv); - - return ret; -} - - -/* - * Helper for mbedtls_mpi multiplication - * copied/trimmed from mbedtls bignum.c - */ -static void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b ) -{ - mbedtls_mpi_uint c = 0, t = 0; - - for( ; i >= 16; i -= 16 ) - { - MULADDC_INIT - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_STOP - } - - for( ; i >= 8; i -= 8 ) - { - MULADDC_INIT - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_STOP - } - - - for( ; i > 0; i-- ) - { - MULADDC_INIT - MULADDC_CORE - MULADDC_STOP - } - - t++; - - do { - *d += c; c = ( *d < c ); d++; - } - while( c != 0 ); -} - - -/* - * Helper for mbedtls_mpi subtraction - * Copied/adapter from mbedTLS bignum.c - */ -static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d ) -{ - size_t i; - mbedtls_mpi_uint c, z; - - for( i = c = 0; i < n; i++, s++, d++ ) - { - z = ( *d < c ); *d -= c; - c = ( *d < *s ) + z; *d -= *s; - } - - while( c != 0 ) - { - z = ( *d < c ); *d -= c; - c = z; i++; d++; - } -} - - -/* The following 3 Montgomery arithmetic function are - copied from mbedTLS bigint.c verbatim as they are static. - - TODO: find a way to support making the versions in mbedtls - non-static. -*/ - -/* - * Fast Montgomery initialization (thanks to Tom St Denis) - */ -static void mpi_montg_init( mbedtls_mpi_uint *mm, const mbedtls_mpi *N ) -{ - mbedtls_mpi_uint x, m0 = N->p[0]; - unsigned int i; - - x = m0; - x += ( ( m0 + 2 ) & 4 ) << 1; - - for( i = biL; i >= 8; i /= 2 ) - x *= ( 2 - ( m0 * x ) ); - - *mm = ~x + 1; -} - -/* - * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) - */ -static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm, - const mbedtls_mpi *T ) -{ - size_t i, n, m; - mbedtls_mpi_uint u0, u1, *d; - - if( T->n < N->n + 1 || T->p == NULL ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); - - memset( T->p, 0, T->n * ciL ); - - d = T->p; - n = N->n; - m = ( B->n < n ) ? B->n : n; - - for( i = 0; i < n; i++ ) - { - /* - * T = (T + u0*B + u1*N) / 2^biL - */ - u0 = A->p[i]; - u1 = ( d[0] + u0 * B->p[0] ) * mm; - - mpi_mul_hlp( m, B->p, d, u0 ); - mpi_mul_hlp( n, N->p, d, u1 ); - - *d++ = u0; d[n + 1] = 0; - } - - memcpy( A->p, d, ( n + 1 ) * ciL ); - - if( mbedtls_mpi_cmp_abs( A, N ) >= 0 ) - mpi_sub_hlp( n, N->p, A->p ); - else - /* prevent timing attacks */ - mpi_sub_hlp( n, A->p, T->p ); - - return( 0 ); -} - -/* - * Montgomery reduction: A = A * R^-1 mod N - */ -static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint mm, const mbedtls_mpi *T ) -{ - mbedtls_mpi_uint z = 1; - mbedtls_mpi U; - - U.n = U.s = (int) z; - U.p = &z; - - return( mpi_montmul( A, &U, N, mm, T ) ); -} - -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ - /* Number of words used to hold 'mpi', rounded up to nearest - 16 words (512 bits) to match hardware support + 16 words (512 bits) to match hardware support. + + Note that mpi->N (size of memory buffer) may be higher than this + number, if the high bits are mostly zeroes. */ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { @@ -357,356 +111,455 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } +/* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'. -/* Special-case multiply, where we use hardware montgomery mod - multiplication to solve the case where A or B are >2048 bits so - can't do standard multiplication. - - the modulus here is chosen with M=(2^num_bits-1) - to guarantee the output isn't actually modulo anything. This means - we don't need to calculate M' and Rinv, they are predictable - as follows: - M' = 1 - Rinv = (1 << (num_bits - 32) - - (See RSA Accelerator section in Technical Reference for derivation - of M', Rinv) + If num_words is higher than the number of words in the bignum then + these additional words will be zeroed in the memory buffer. */ -static int esp_mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, size_t num_words) - { - mbedtls_mpi M, Rinv; - int ret; - size_t mprime; - size_t num_bits = num_words * 32; - - mbedtls_mpi_init(&M); - mbedtls_mpi_init(&Rinv); - - /* TODO: it may be faster to just use 4096-bit arithmetic every time, - and make these constants rather than runtime derived - derived. */ - /* M = (2^num_words)-1 */ - mbedtls_mpi_grow(&M, num_words); - for(int i = 0; i < num_words*32; i++) { - mbedtls_mpi_set_bit(&M, i, 1); - } - - /* Rinv = (2^num_words-32) */ - mbedtls_mpi_grow(&Rinv, num_words); - mbedtls_mpi_set_bit(&Rinv, num_bits - 32, 1); - - mprime = 1; - - ret = mpi_mul_mpi_mod_inner(X, A, B, &M, &Rinv, mprime, num_words); - - mbedtls_mpi_free(&M); - mbedtls_mpi_free(&Rinv); - return ret; - } - -int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t num_words) { - int ret = -1; - size_t words_a, words_b, words_x, words_mult; - - mbedtls_mpi TA, TB; - - mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); - - /* Count words needed for A & B in hardware */ - words_a = hardware_words_needed(A); - words_b = hardware_words_needed(B); - - words_mult = (words_a > words_b ? words_a : words_b); - - /* Take a copy of A if either X == A OR if A isn't long enough - to hold the number of words needed for hardware. - - (can't grow A directly as it is const) - - TODO: growing the input operands is only necessary because the - ROM functions only take one length argument. It should be - possible for us to just copy the used data only into the - hardware buffers, and set the remaining bits to zero - saving - RAM. But we need to reimplement ets_bigint_mult_prepare() in - software for this. - */ - if( X == A || A->n < words_mult) { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_mult) ); - A = &TA; + for(size_t i = 0; i < mpi->n && i < num_words; i++) { + REG_WRITE(mem_base + i * 4, mpi->p[i]); } - /* Same for B */ - if( X == B || B->n < words_mult ) { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_mult) ); - B = &TB; + for(size_t i = mpi->n; i < num_words; i++) { + REG_WRITE(mem_base + i * 4, 0); } - - /* Result X has to have room for double the larger operand */ - words_x = words_mult * 2; - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); - /* TODO: check if lset here is necessary, hardware should zero */ - MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - - /* If either operand is over 2048 bits, we can't use the standard hardware multiplier - (it assumes result is double longest operand, and result is max 4096 bits.) - - However, we can fail over to mod_mult for up to 4096 bits. - */ - if(words_mult * 32 > 2048) { - /* TODO: check if there's an overflow condition if words_a & words_b are both - the bit lengths of the operands, result could be 1 bit longer - */ - if((words_a + words_b) * 32 > 4096) { - printf("ERROR: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - else { - ret = esp_mpi_mult_mpi_failover_mod_mult(X, A, B, words_a + words_b); - } - } - else { - - /* normal mpi multiplication */ - esp_mpi_acquire_hardware(); - if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { - ets_bigint_wait_finish(); - /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is - copied to output X->p. - */ - if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { - X->s = A->s * B->s; - ret = 0; - } else { - printf("ets_bigint_mult_getz failed\n"); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - } else{ - printf("Baseline multiplication failed\n"); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - esp_mpi_release_hardware(); - } -cleanup: - - mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); - - return( ret ); } -#endif /* MBEDTLS_MPI_MUL_MPI_ALT */ +/* Read mbedTLS MPI bignum back from hardware memory block. -#if defined(MBEDTLS_MPI_EXP_MOD_ALT) -/* - * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + Reads num_words words from block. + + Can return a failure result if fails to grow the MPI result. +*/ +static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) +{ + int ret = 0; + size_t x_n = x->n; + + /* this code is written in non-intuitive way, to only grow the + result if it is absolutely necessary - ie if all the high bits + are zero, the bignum won't be grown to fit them. */ + for(int i = num_words - 1; i >= 0; i--) { + uint32_t value = REG_READ(mem_base + i * 4); + if(value != 0 && x_n <= i) { + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, i+1) ); + x_n = i+1; + } + if(x_n > i) { + x->p[i] = value; + } + } + /* Zero any remaining limbs in the bignum, if the buffer was + always bigger than num_words */ + for(size_t i = num_words; i < x->n; i++) { + x->p[i] = 0; + } + + cleanup: + return ret; +} + +/* Given a & b, determine u & v such that + + gcd(a,b) = d = au - bv + + This is suitable for calculating values for montgomery multiplication: + + gcd(R, M) = R * Rinv - M * Mprime = 1 + + Conditions which must be true: + - argument 'a' (R) is a power of 2. + - argument 'b' (M) is odd. + + Underlying algorithm comes from: + http://www.hackersdelight.org/hdcodetxt/mont64.c.txt + http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf */ -int mbedtls_mpi_exp_mod( mbedtls_mpi* X, const mbedtls_mpi* A, const mbedtls_mpi* E, const mbedtls_mpi* N, mbedtls_mpi* _RR ) +static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, + mbedtls_mpi *u, mbedtls_mpi *v) +{ + mbedtls_mpi a_, ta; + + /* These checks degrade performance, TODO remove them... */ + assert(b->p[0] & 1); + assert(mbedtls_mpi_bitlen(a) == mbedtls_mpi_lsb(a)+1); + assert(mbedtls_mpi_cmp_mpi(a, b) > 0); + + mbedtls_mpi_lset(u, 1); + mbedtls_mpi_lset(v, 0); + + /* 'a' needs to be half its real value for this algorithm + TODO see if we can halve the number in the caller to avoid + allocating a bignum here. + */ + mbedtls_mpi_init(&a_); + mbedtls_mpi_copy(&a_, a); + mbedtls_mpi_shift_r(&a_, 1); + + mbedtls_mpi_init(&ta); + mbedtls_mpi_copy(&ta, &a_); + + //mbedtls_mpi_printf("a", &a_); + //mbedtls_mpi_printf("b", b); + + /* Loop invariant: + 2*ta = u*2*a - v*b. + + Loop until ta == 0 + */ + while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { + //mbedtls_mpi_printf("ta", &ta); + //mbedtls_mpi_printf("u", u); + //mbedtls_mpi_printf("v", v); + //printf("2*ta == u*2*a - v*b\n"); + + mbedtls_mpi_shift_r(&ta, 1); + if (mbedtls_mpi_get_bit(u, 0) == 0) { + // Remove common factor of 2 in u & v + mbedtls_mpi_shift_r(u, 1); + mbedtls_mpi_shift_r(v, 1); + } + else { + /* u = (u + b) >> 1 */ + mbedtls_mpi_add_mpi(u, u, b); + mbedtls_mpi_shift_r(u, 1); + /* v = (v - a) >> 1 */ + mbedtls_mpi_shift_r(v, 1); + mbedtls_mpi_add_mpi(v, v, &a_); + } + } + mbedtls_mpi_free(&ta); + mbedtls_mpi_free(&a_); +} + +/* Execute RSA operation. op_reg specifies which 'START' register + to write to. +*/ +static inline void execute_op(uint32_t op_reg) +{ + /* Clear interrupt status, start operation */ + REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); + + /* TODO: use interrupt instead of busywaiting */ + while(REG_READ(RSA_INTERRUPT_REG) != 1) + { } + + /* clear the interrupt */ + REG_WRITE(RSA_INTERRUPT_REG, 1); +} + +/* Sub-stages of modulo multiplication/exponentiation operations */ +static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words); +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = (X * Y) mod M + + Not an mbedTLS function + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) { int ret; - size_t wbits, wsize, one = 1; - size_t i, j, nblimbs; - size_t bufsize, nbits; - mbedtls_mpi_uint ei, mm, state; - mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos; - int neg; + size_t num_words = hardware_words_needed(M); - if( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + /* Calculate and load the first stage montgomery multiplication */ + MBEDTLS_MPI_CHK( modular_op_prepare(X, M, num_words) ); - if( mbedtls_mpi_cmp_int( E, 0 ) < 0 ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + execute_op(RSA_MULT_START_REG); - /* - * Init temps and window size - */ - mpi_montg_init( &mm, N ); - mbedtls_mpi_init( &RR ); mbedtls_mpi_init( &T ); - mbedtls_mpi_init( &Apos ); - memset( W, 0, sizeof( W ) ); + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - i = mbedtls_mpi_bitlen( E ); + esp_mpi_release_hardware(); - wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : - ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + cleanup: + return ret; +} - if( wsize > MBEDTLS_MPI_WINDOW_SIZE ) - wsize = MBEDTLS_MPI_WINDOW_SIZE; +#if defined(MBEDTLS_MPI_EXP_MOD_ALT) - j = N->n + 1; - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1], j ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T, j * 2 ) ); +/* + * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +{ + int ret; + size_t z_words = hardware_words_needed(Z); + size_t x_words = hardware_words_needed(X); + size_t y_words = hardware_words_needed(Y); + size_t m_words = hardware_words_needed(M); + size_t num_words; - /* - * Compensate for negative A (and correct at the end) - */ - neg = ( A->s == -1 ); - if( neg ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Apos, A ) ); - Apos.s = 1; - A = &Apos; + mbedtls_mpi_printf("X",X); + mbedtls_mpi_printf("Y",Y); + mbedtls_mpi_printf("M",M); + + /* "all numbers must be the same length", so choose longest number + as cardinal length of operation... + */ + num_words = z_words; + if (x_words > num_words) { + num_words = x_words; + } + if (y_words > num_words) { + num_words = y_words; + } + if (m_words > num_words) { + num_words = m_words; + } + printf("num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); + + /* TODO: _RR parameter currently ignored */ + + ret = modular_op_prepare(X, M, num_words); + if (ret != 0) { + return ret; } - /* - * If 1st call, pre-compute R^2 mod N - */ - if( _RR == NULL || _RR->p == NULL ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &RR, 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &RR, N->n * 2 * biL ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &RR, &RR, N ) ); + mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); - if( _RR != NULL ) - memcpy( _RR, &RR, sizeof( mbedtls_mpi) ); - } - else - memcpy( &RR, _RR, sizeof( mbedtls_mpi) ); + //dump_memory_block("X_BLOCK", RSA_MEM_X_BLOCK_BASE); + //dump_memory_block("Y_BLOCK", RSA_MEM_Y_BLOCK_BASE); + //dump_memory_block("M_BLOCK", RSA_MEM_M_BLOCK_BASE); - /* - * W[1] = A * R^2 * R^-1 mod N = A * R mod N - */ - if( mbedtls_mpi_cmp_mpi( A, N ) >= 0 ) - MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &W[1], A, N ) ); - else - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) ); + REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); - mpi_montmul( &W[1], &RR, N, mm, &T ); + execute_op(RSA_START_MODEXP_REG); - /* - * X = R^2 * R^-1 mod N = R mod N - */ - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) ); - mpi_montred( X, N, mm, &T ); + //dump_memory_block("Z_BLOCK", RSA_MEM_Z_BLOCK_BASE); - if( wsize > 1 ) - { - /* - * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) - */ - j = one << ( wsize - 1 ); + /* TODO: only need to read m_words not num_words, provided result is correct... */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[j], N->n + 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) ); + esp_mpi_release_hardware(); - for( i = 0; i < wsize - 1; i++ ) - mpi_montmul( &W[j], &W[j], N, mm, &T ); + mbedtls_mpi_printf("Z",Z); + printf("print (Z == (X ** Y) %% M)\n"); - /* - * W[i] = W[i - 1] * W[1] - */ - for( i = j + 1; i < ( one << wsize ); i++ ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) ); - - mpi_montmul( &W[i], &W[1], N, mm, &T ); - } - } - - nblimbs = E->n; - bufsize = 0; - nbits = 0; - wbits = 0; - state = 0; - - while( 1 ) - { - if( bufsize == 0 ) - { - if( nblimbs == 0 ) - break; - - nblimbs--; - - bufsize = sizeof( mbedtls_mpi_uint ) << 3; - } - - bufsize--; - - ei = (E->p[nblimbs] >> bufsize) & 1; - - /* - * skip leading 0s - */ - if( ei == 0 && state == 0 ) - continue; - - if( ei == 0 && state == 1 ) - { - /* - * out of window, square X - */ - mpi_montmul( X, X, N, mm, &T ); - continue; - } - - /* - * add ei to current window - */ - state = 2; - - nbits++; - wbits |= ( ei << ( wsize - nbits ) ); - - if( nbits == wsize ) - { - /* - * X = X^wsize R^-1 mod N - */ - for( i = 0; i < wsize; i++ ) - mpi_montmul( X, X, N, mm, &T ); - - /* - * X = X * W[wbits] R^-1 mod N - */ - mpi_montmul( X, &W[wbits], N, mm, &T ); - - state--; - nbits = 0; - wbits = 0; - } - } - - /* - * process the remaining bits - */ - for( i = 0; i < nbits; i++ ) - { - mpi_montmul( X, X, N, mm, &T ); - - wbits <<= 1; - - if( ( wbits & ( one << wsize ) ) != 0 ) - mpi_montmul( X, &W[1], N, mm, &T ); - } - - /* - * X = A^E * R * R^-1 mod N = A^E mod N - */ - mpi_montred( X, N, mm, &T ); - - if( neg ) - { - X->s = -1; - MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( X, N, X ) ); - } - -cleanup: - - for( i = ( one << ( wsize - 1 ) ); i < ( one << wsize ); i++ ) - mbedtls_mpi_free( &W[i] ); - - mbedtls_mpi_free( &W[1] ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &Apos ); - - if( _RR == NULL || _RR->p == NULL ) - mbedtls_mpi_free( &RR ); - - return( ret ); + return ret; } #endif /* MBEDTLS_MPI_EXP_MOD_ALT */ + +/* The common parts of modulo multiplication and modular sliding + * window exponentiation: + * + * @param X first multiplication factor and/or base of exponent. + * @param M modulo value for result + * @param num_words size of modulo operation, in words (limbs). + * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. + * + * Steps: + * Calculate Rinv & Mprime based on M & num_words + * Load all coefficients to memory + * Set mode register + * + * @note This function calls esp_mpi_acquire_hardware. If successful, + * returns 0 and it becomes the callers responsibility to call + * esp_mpi_release_hardware(). If failure is returned, the caller does + * not need to call esp_mpi_release_hardware(). + */ +static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words) +{ + int ret = 0; + mbedtls_mpi RR, Rinv, Mprime; + size_t num_bits; + + /* Calculate number of bits */ + num_bits = num_words * 32; + + if(num_bits > 4096) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + /* Rinv & Mprime are calculated via extended binary gcd + algorithm, see references on extended_binary_gcd() above. + */ + mbedtls_mpi_init(&Rinv); + mbedtls_mpi_init(&RR); + mbedtls_mpi_init(&Mprime); + + mbedtls_mpi_set_bit(&RR, num_bits, 1); /* R = b^n where b = 2^32, n=num_words, + ie R = 2^N (where N=num_bits) */ + /* calculate Rinv & Mprime */ + extended_binary_gcd(&RR, M, &Rinv, &Mprime); + + /* Block of debugging data, output suitable to paste into Python + TODO remove + */ + mbedtls_mpi_printf("R", &RR); + mbedtls_mpi_printf("M", M); + mbedtls_mpi_printf("Rinv", &Rinv); + mbedtls_mpi_printf("Mprime", &Mprime); + printf("print (R * Rinv - M * Mprime == 1)\n"); + printf("print (Rinv == (R * R) %% M)\n"); + + esp_mpi_acquire_hardware(); + + /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, Mprime.p[0]); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + mbedtls_mpi_free(&Rinv); + mbedtls_mpi_free(&RR); + mbedtls_mpi_free(&Mprime); + + return ret; +} + +/* Second & final step of a modular multiply - load second multiplication + * factor Y, run the multiply, read back the result into Z. + * + * @param Z result value + * @param X first multiplication factor (used to set sign of result). + * @param Y second multiplication factor. + * @param num_words size of modulo operation, in words (limbs). + * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. + * + * Caller must have already called esp_mpi_acquire_hardware(). + */ +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) +{ + int ret; + /* Load Y to X input memory block, rerun */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, num_words); + + execute_op(RSA_MULT_START_REG); + + /* Read result into Z */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + + Z->s = X->s * Y->s; + + return ret; +} + +#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ + +static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = X * Y */ +int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) +{ + int ret; + size_t words_x, words_y, words_mult, words_z; + + /* Count words needed for X & Y in hardware */ + words_x = hardware_words_needed(X); + words_y = hardware_words_needed(Y); + + words_mult = (words_x > words_y ? words_x : words_y); + + /* Result Z has to have room for double the larger factor */ + words_z = words_mult * 2; + + /* If either factor is over 2048 bits, we can't use the standard hardware multiplier + (it assumes result is double longest factor, and result is max 4096 bits.) + + However, we can fail over to mod_mult for up to 4096 bits of result (modulo + multiplication doesn't have the same restriction, so result is simply the + number of bits in X plus number of bits in in Y.) + */ + if (words_mult * 32 > 2048) { + /* Calculate new length of Z */ + words_z = words_x + words_y; + if (words_z * 32 > 4096) { + ESP_LOGE(TAG, "ERROR: %d bit result (%d bits * %d bits) too large for hardware unit\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + else { + return mpi_mult_mpi_failover_mod_mult(Z, X, Y, words_z); + } + } + + /* Otherwise, we can use the (faster) multiply hardware unit */ + + esp_mpi_acquire_hardware(); + + /* Copy X (right-extended) & Y (left-extended) to memory block */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, words_mult); + mpi_to_mem_block(RSA_MEM_Z_BLOCK_BASE + words_mult * 4, Y, words_mult); + /* NB: as Y is left-extended, we don't zero the bottom words_mult words of Y block. + This is OK for now because zeroing is done by hardware when we do esp_mpi_acquire_hardware(). + */ + + REG_WRITE(RSA_M_DASH_REG, 0); + + /* "mode" register loaded with number of 512-bit blocks in result, + plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8)) + */ + REG_WRITE(RSA_MULT_MODE_REG, (words_z / 16) + 7); + + execute_op(RSA_MULT_START_REG); + + /* Read back the result */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, words_z); + + Z->s = X->s * Y->s; + + esp_mpi_release_hardware(); + + return ret; +} + +/* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod + multiplication to solve the case where A or B are >2048 bits so + can't use the standard multiplication method. + + This case is simpler than esp_mpi_mul_mpi_mod() as we control the arguments: + + * Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output + isn't actually modulo anything. + * Therefore of of M' and Rinv are predictable as follows: + M' = 1 + Rinv = 1 + + (See RSA Accelerator section in Technical Reference * + extended_binary_gcd() function above for more about M', Rinv) +*/ +static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) + { + int ret = 0; + + /* Load coefficients to hardware */ + esp_mpi_acquire_hardware(); + + /* M = 2^num_words - 1, so block is entirely FF */ + for(int i = 0; i < num_words; i++) { + REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); + } + /* Mprime = 1 */ + REG_WRITE(RSA_M_DASH_REG, 1); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + /* Load X */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + + /* Rinv = 1 */ + REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); + for(int i = 1; i < num_words; i++) { + REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); + } + + execute_op(RSA_MULT_START_REG); + + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + + esp_mpi_release_hardware(); + + cleanup: + return ret; +} + +#endif /* MBEDTLS_MPI_MUL_MPI_ALT */ + #endif /* MBEDTLS_MPI_MUL_MPI_ALT || MBEDTLS_MPI_EXP_MOD_ALT */ diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index e4f4af271a..2b47d84ea4 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -251,7 +251,7 @@ Uncommenting these macros will use the hardware-accelerated implementations. */ -//#define MBEDTLS_MPI_EXP_MOD_ALT +#define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT /** From ce7b8059de18fd53a78848f2987716045c126f05 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Oct 2016 17:08:05 +1100 Subject: [PATCH 004/131] RSA Accelerator: Remove timing-sensitive optimisations Avoid potentially leaking timing information about number of bits set in MPI values. --- components/mbedtls/port/esp_bignum.c | 37 +++++++++++++--------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 55e70c47ac..0a835c9e8d 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -97,15 +97,21 @@ static void esp_mpi_release_hardware( void ) /* Number of words used to hold 'mpi', rounded up to nearest 16 words (512 bits) to match hardware support. - Note that mpi->N (size of memory buffer) may be higher than this + Note that mpi->n (size of memory buffer) may be higher than this number, if the high bits are mostly zeroes. + + This implementation may cause the caller to leak a small amount of + timing information when an operation is performed (length of a + given mpi value, rounded to nearest 512 bits), but not all mbedTLS + RSA operations succeed if we use mpi->N as-is (buffers are too long). */ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { - size_t res; - for(res = mpi->n; res > 0; res-- ) { - if( mpi->p[res - 1] != 0 ) - break; + size_t res = 1; + for(size_t i = 0; i < mpi->n; i++) { + if( mpi->p[i] != 0 ) { + res = i + 1; + } } res = (res + 0xF) & ~0xF; return res; @@ -135,23 +141,14 @@ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, s static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) { int ret = 0; - size_t x_n = x->n; - /* this code is written in non-intuitive way, to only grow the - result if it is absolutely necessary - ie if all the high bits - are zero, the bignum won't be grown to fit them. */ - for(int i = num_words - 1; i >= 0; i--) { - uint32_t value = REG_READ(mem_base + i * 4); - if(value != 0 && x_n <= i) { - MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, i+1) ); - x_n = i+1; - } - if(x_n > i) { - x->p[i] = value; - } + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, num_words) ); + + for(int i = 0; i < num_words; i++) { + x->p[i] = REG_READ(mem_base + i * 4); } - /* Zero any remaining limbs in the bignum, if the buffer was - always bigger than num_words */ + /* Zero any remaining limbs in the bignum, if the buffer is bigger + than num_words */ for(size_t i = num_words; i < x->n; i++) { x->p[i] = 0; } From 288f4f63f0a108003e29c3a041b86de3bb64976a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 09:17:10 +0800 Subject: [PATCH 005/131] Add UART driver 1. add uart.h and uart.c 2. add ESP_ERR_TIMEOUT in esp_err.h 3. add UART AHB FIFO address in uart_reg.h 4. modify xRingbufferSendFromISR return value in ringbuffer.c 5. add #include "soc/gpio_sig_map.h" in gpio.h --- components/driver/include/driver/gpio.h | 1 + components/driver/include/driver/uart.h | 688 +++++++++++++++++ components/driver/uart.c | 938 ++++++++++++++++++++++++ components/esp32/include/esp_err.h | 1 + components/esp32/include/soc/uart_reg.h | 4 +- components/freertos/ringbuf.c | 3 +- 6 files changed, 1632 insertions(+), 3 deletions(-) create mode 100644 components/driver/include/driver/uart.h create mode 100644 components/driver/uart.c diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 9b47c88e69..c821b640eb 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -20,6 +20,7 @@ #include "soc/gpio_struct.h" #include "soc/rtc_io_reg.h" #include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" #include "rom/gpio.h" #include "esp_attr.h" diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h new file mode 100644 index 0000000000..cd0725d2ce --- /dev/null +++ b/components/driver/include/driver/uart.h @@ -0,0 +1,688 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_UART_H_ +#define _DRIVER_UART_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" +#include "esp_err.h" +#include "driver/periph_ctrl.h" +#include + +extern const char* UART_TAG; +#define UART_FIFO_LEN (128) //Do Not Change it +#define UART_INTR_MASK 0x1ff +#define UART_LINE_INV_MASK (0x3f << 19) + +typedef enum { + UART_DATA_5_BITS = 0x0, //word length: 5bits + UART_DATA_6_BITS = 0x1, //word length: 6bits + UART_DATA_7_BITS = 0x2, //word length: 7bits + UART_DATA_8_BITS = 0x3, //word length: 8bits + UART_DATA_MAX_BITS = 0X4, +} uart_word_length_t; + +typedef enum { + UART_STOP_BITS_1 = 0x1, //stop bit: 1bit + UART_STOP_BITS_1_5 = 0x2, //stop bit: 1.5bits + UART_STOP_BITS_2 = 0x3, //stop bit: 2bits + UART_STOP_BITS_MAX = 0x4, +} uart_stop_bits_t; + +typedef enum { + UART_NUM_0 = 0x0, //base address 0x3ff40000 + UART_NUM_1 = 0x1, //base address 0x3ff50000 + UART_NUM_2 = 0x2, //base address 0x3ff6E000 + UART_NUM_MAX, +} uart_port_t; + +typedef enum { + UART_PARITY_DISABLE = 0x0, //Disable UART parity + UART_PARITY_EVEN = 0x10, //Enable UART even parity + UART_PARITY_ODD = 0x11 //Enable UART odd parity +} uart_parity_t; + +typedef enum { + UART_BITRATE_300 = 300, + UART_BITRATE_600 = 600, + UART_BITRATE_1200 = 1200, + UART_BITRATE_2400 = 2400, + UART_BITRATE_4800 = 4800, + UART_BITRATE_9600 = 9600, + UART_BITRATE_19200 = 19200, + UART_BITRATE_38400 = 38400, + UART_BITRATE_57600 = 57600, + UART_BITRATE_74880 = 74880, + UART_BITRATE_115200 = 115200, + UART_BITRATE_230400 = 230400, + UART_BITRATE_460800 = 460800, + UART_BITRATE_921600 = 921600, + UART_BITRATE_1843200 = 1843200, + UART_BITRATE_3686400 = 3686400, + UART_BITRATE_MAX = 5000000, +} uart_baudrate_t; //you can set any rate you need in this range + +typedef enum { + UART_HW_FLOWCTRL_DISABLE = 0x0, //disable hardware flow control + UART_HW_FLOWCTRL_RTS = 0x1, //enable RX hardware flow control (rts) + UART_HW_FLOWCTRL_CTS = 0x2, //enable TX hardware flow control (cts) + UART_HW_FLOWCTRL_CTS_RTS = 0x3, //enable hardware flow control + UART_HW_FLOWCTRL_MAX = 0x4, +} uart_hw_flowcontrol_t; + +typedef enum { + UART_INVERSE_DISABLE = 0x0, //Disable UART wire output inverse + UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, //UART RXD input inverse + UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, //UART CTS input inverse + UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, //UART TXD output inverse + UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, //UART RTS output inverse +} uart_inverse_t; + +typedef struct { + uart_baudrate_t baud_rate; //UART baudrate + uart_word_length_t data_bits; //UART byte size + uart_parity_t parity; //UART parity mode + uart_stop_bits_t stop_bits; //UART stop bits + uart_hw_flowcontrol_t flow_ctrl; //UART hw flow control mode(cts/rts) + uint8_t rx_flow_ctrl_thresh ; //UART hw RTS threshold +} uart_config_t; + +typedef struct { + uint32_t intr_enable_mask; //UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator + uint8_t rx_timeout_thresh; //UART timeout interrupt threshold(unit: time of sending one byte) + uint8_t txfifo_empty_intr_thresh; //UART TX empty interrupt threshold. + uint8_t rxfifo_full_thresh; //UART RX full interrupt threshold. +} uart_intr_config_t; + + +typedef enum { + UART_DATA, + UART_BREAK, + UART_BUFFER_FULL, + UART_FIFO_OVF, + UART_FRAME_ERR, + UART_PARITY_ERR, + UART_EVENT_MAX, +} uart_event_type_t; + +typedef struct { + uart_event_type_t type; + union { + struct { + size_t size; + } data; + + }; +} uart_event_t; + + + +/** + * @brief Set UART data bits. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_word_length_t data_bit : UART data bits + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); + +/** + * @brief Get UART data bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1) : Parameter error + * UART_DATA_5_BITS (0): UART word length: 5 bits. + * UART_DATA_6_BITS (1): UART word length: 6 bits. + * UART_DATA_7_BITS (2): UART word length: 7 bits. + * UART_DATA_8_BITS (3): UART word length: 8 bits. + */ +int uart_get_word_length(uart_port_t uart_num); + +/** + * @brief Set UART stop bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_stop_bits_t bit_num : UART stop bits + * + * @return ESP_OK : Success + * ESP_FAIL: Fail + */ +esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); + +/** + * @brief Set UART stop bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_STOP_BITS_1 (1): 1 stop bit + * UART_STOP_BITS_1_5 (2): 1.5 stop bits + * UART_STOP_BITS_1 (3): 2 stop bits + */ +int uart_get_stop_bits(uart_port_t uart_num); + +/** + * @brief Set UART parity. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_parity_t parity_mode : the enum of uart parity configuration + * + * @return null + */ +esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); + +/** + * @brief Get UART parity mode. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_PARITY_ODD (0x11): Odd parity check mode + * UART_PARITY_EVEN (0x10): Even parity check mode + * UART_PARITY_DISABLE(0x0) : parity check disabled + * + */ +int uart_get_parity(uart_port_t uart_num); + +/** + * @brief Set UART baud rate. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t baud_rate : UART baud-rate, we can choose one from uart_baudrate_t, or set a value. + * + * @return null + */ +esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); + +/** + * @brief Get UART bit-rate. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL(-1): Parameter error + * Others (>0): UART baud-rate + * + */ +int uart_get_baudrate(uart_port_t uart_num); + +/** + * @brief Set UART line inverse mode + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t inverse_mask : Choose the wires that need to be inversed + * (Should be chosen from uart_inverse_t, combine with OR-OPERATION) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; + + +/** + * @brief Set hardware flow control. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_hw_flowcontrol_t flow_ctrl : Hardware flow control mode + * @param uint8_t rx_thresh : Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); + +/** + * @brief Get hardware flow control mode + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_HW_FLOWCTRL_DISABLE (0): UART hw flow control disabled + * UART_HW_FLOWCTRL_RTS (1): UART RX flow control enabled + * UART_HW_FLOWCTRL_CTS (2): UART TX flow control enabled + * UART_HW_FLOWCTRL_CTS_RTS (3): UART TX/RX flow control enabled + */ +int uart_get_hw_flow_ctrl(uart_port_t uart_num); + +/** + * @brief Clear UART interrupt status + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t clr_mask : Bit mask of the status that to be cleared. + * enable_mask should be chosen from the fields of register UART_INT_CLR_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); + +/** + * @brief Set UART interrupt enable + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t enable_mask : Bit mask of the enable bits. + * enable_mask should be chosen from the fields of register UART_INT_ENA_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); + +/** + * @brief Clear UART interrupt enable bits + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t disable_mask : Bit mask of the disable bits. + * Disable_mask should be chosen from the fields of register UART_INT_ENA_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); + + +/** + * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_tx_intr(uart_port_t uart_num); + +/** + * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int enable : 1: enable; 0: disable + * @param int thresh : Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); + +/** +* @brief register UART interrupt handler(ISR). + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param void (* fn)(void* ) : Interrupt handler function. + * Note that the handler function MUST be defined with attribution of "IRAM_ATTR" for now. + * @param void * arg : parameter for handler function + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); + +/** + * @brief Set UART pin number + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param tx_io_num : UART TX pin GPIO number + * @param rx_io_num : UART RX pin GPIO number + * @param rts_io_num : UART RTS pin GPIO number + * @param cts_io_num : UART CTS pin GPIO number + * + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); + +/** + * @brief UART set RTS level (before inverse) + * UART rx hardware flow control should not be set. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int level : 1: RTS output low(active) + * 0: RTS output high(block) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_rts(uart_port_t uart_num, int level); + +/** + * @brief UART set DTR level (before inverse) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int level : 1: DTR output low + * 0: DTR output high + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_dtr(uart_port_t uart_num, int level); + +/** +* @brief UART parameter configure + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_config_t *uart_config: UART parameter settings + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); + +/** +* @brief UART interrupt configure + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_config_t *p_intr_conf: UART interrupt settings + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); + +/** + * @brief Install UART driver. + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int buffer_size : UART ring buffer size + * @param int queue_size : UART event queue size/depth. + * @param int uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue); + +/** + * @brief Uninstall UART driver. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_driver_delete(uart_port_t uart_num); + +/** + * @brief Wait UART TX FIFO empty + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * + * @return ESP_OK : Success + * ESP_ERR_TIMEOUT: Timeout + */ +esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* buffer : data buffer address + * @param uint32_t len : data length to send + * + * @return The number of data that pushed to the TX FIFO + */ +int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* src : data buffer address + * @param size_t size : data length to send + * + * @return The number of data that sent out. + */ +int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not return until all the data and the break signal have been sent out. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* src : data buffer address + * @param size_t size : data length to send + * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) + * + * @return The number of data that sent out. + */ +int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); + +/** +* @brief UART read one char + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param TickType_t ticks_to_wait : ticks to wait. + * + * @return -1 : Error + * Others : return a char data from uart fifo. + */ +int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); + +/** +* @brief UART read bytes from UART buffer + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t* buf : pointer to the buffer. + * @param uint32_t length : data length + * @param TickType_t ticks_to_wait: timeout time( FreeRTOS ti c + * + * @return -1 : Error + * Others : return a char data from uart fifo. + */ +int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); + +/** + * @brief UART ring buffer flush + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_flush(uart_port_t uart_num); + +/** + * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error, or UART driver not installed. + */ +esp_err_t uart_set_print_port(uart_port_t uart_no); + +/** + * @brief Get the current serial port for ets_printf function + * + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return null + */ +int uart_get_print_port(); + +/***************************EXAMPLE********************************** + * + * + * ----------------EXAMPLE OF UART SETTING --------------------- + * //1. Setup UART + * #include "freertos/queue.h" + * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h + * //a. Set UART parameter + * int uart_num = 0; //uart port number + * uart_config_t uart_config = { + * .baud_rate = UART_BITRATE_115200, //baudrate + * .data_bits = UART_DATA_8_BITS, //data bit mode + * .parity = UART_PARITY_DISABLE, //parity mode + * .stop_bits = UART_STOP_BITS_1, //stop bit mode + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) + * .rx_flow_ctrl_thresh = 120, //flow control threshold + * }; + * uart_param_config(uart_num, &uart_config); + * //b1. Setup UART driver(with UART queue) + * QueueHandle_t uart_queue; + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //b2. Setup UART driver(without UART queue) + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + * + *-----------------------------------------------------------------------------* + * //2. Set UART pin + * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * + *-----------------------------------------------------------------------------* + * //3. Read data from UART. + * uint8_t data[128]; + * int length = 0; + * length = uart_read_bytes(uart_num, data, sizeof(data), 100); + * + *-----------------------------------------------------------------------------* + * //4. Write data to UART. + * char* test_str = "This is a test string.\n" + * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * + *-----------------------------------------------------------------------------* + * //5. Write data to UART, end with a break signal. + * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * + *-----------------------------------------------------------------------------* + * + * //6. an example of echo test with hardware flow control on UART1 + * void uart_loop_back_test() + * { + * int uart_num = 1; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Config UART1 parameters + * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * uart_driver_install(uart_num, 1024 * 2, 10, 17, NULL); //Install UART driver( We don't need an event queue here) + * uint8_t data[1000]; + * while(1) { + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART + * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART + * } + * } + * + *-----------------------------------------------------------------------------* + * //7. An example of using UART event queue on UART0. + * + * #include "freertos/queue.h" + * QueueHandle_t uart0_queue; //A queue to handle UART event. + * void uart_task(void *pvParameters) + * { + * int uart_num = (int)pvParameters; + * uart_event_t event; + * uint8_t dtmp[1000]; + * for(;;) { + * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { //Waiting for UART event. + * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * switch(event.type) { + * case UART_DATA: //Event of UART receving data + * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); + * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * break; + * case UART_FIFO_OVF: //Event of HW FIFO overflow detected + * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * while(1); + * break; + * case UART_BUFFER_FULL: //Event of UART ring buffer full + * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * break; + * case UART_BREAK: + * ESP_LOGI(UART_TAG, "uart rx break\n"); //Event of UART RX break detected + * break; + * case UART_PARITY_ERR: //Event of UART parity check error + * ESP_LOGI(UART_TAG, "uart parity error\n"); + * break; + * case UART_FRAME_ERR: //Event of UART frame error + * ESP_LOGI(UART_TAG, "uart frame error\n"); + * break; + * default: //Others + * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * break; + * } + * } + * } + * vTaskDelete(NULL); + * } + * + * void uart_queue_test() + * { + * int uart_num = 0; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Set UART parameters + * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * uart_driver_install(uart_num, 1024 * 2, 10, 17, &uart0_queue); //Install UART driver, and get the queue. + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * } + * + * + ***************************END OF EXAMPLE**********************************/ + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_UART_H_*/ diff --git a/components/driver/uart.c b/components/driver/uart.c new file mode 100644 index 0000000000..157e0c49c7 --- /dev/null +++ b/components/driver/uart.c @@ -0,0 +1,938 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "rom/ets_sys.h" +#include "soc/uart_struct.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "soc/uart_struct.h" + +const char* UART_TAG = "UART"; +#define UART_CHECK(a, str) if (!(a)) { \ + ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return ESP_FAIL; \ + } +#define DEFAULT_EMPTY_THRESH 10 +#define DEFAULT_FULL_THRESH 120 +#define DEFAULT_TOUT_THRESH 10 +#define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +typedef struct { + uart_port_t uart_num; + SemaphoreHandle_t tx_fifo_sem; + SemaphoreHandle_t tx_mutex; + SemaphoreHandle_t tx_done_sem; + SemaphoreHandle_t tx_brk_sem; + SemaphoreHandle_t rx_sem; + QueueHandle_t xQueueUart; + int queue_size; + int intr_num; + RingbufHandle_t ring_buffer; + bool buffer_full_flg; + bool tx_waiting; + int cur_remain; + uint8_t* rd_ptr; + uint8_t* head_ptr; + uint8_t data_buf[UART_FIFO_LEN]; + uint8_t data_len; +} uart_obj_t; + +static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; +static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; +static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; + +//Fill UART tx_fifo and return a number, +//This function by itself is not thread-safe, always call from within a muxed section. +static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +{ + uint8_t i = 0; + uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; + uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); + uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); + for(i = 0; i < copy_cnt; i++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); + } + return copy_cnt; +} + +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.bit_num = data_bit; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_word_length(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + return UART[uart_num]->conf0.bit_num; +} + +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.stop_bit_num = stop_bit; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_stop_bits(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + return UART[uart_num]->conf0.stop_bit_num; +} + +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.parity = parity_mode & 0x1; + UART[uart_num]->conf0.parity_en = (parity_mode >> 1) & 0x1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_parity(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + int val = UART[uart_num]->conf0.val; + if(val & UART_PARITY_EN_M) { + if(val & UART_PARITY_M) { + return UART_PARITY_ODD; + } else { + return UART_PARITY_EVEN; + } + } else { + return UART_PARITY_DISABLE; + } +} + +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error"); + uint32_t clk_div = (((UART_CLK_FREQ) << 4) / baud_rate); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->clk_div.div_int = clk_div >> 4; + UART[uart_num]->clk_div.div_frag = clk_div & 0xf; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_baudrate(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uint32_t baudrate = ((UART_CLK_FREQ) << 4) / clk_div; + return baudrate; +} + +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_CONF0_REG(uart_num), UART_LINE_INV_MASK); + SET_PERI_REG_MASK(UART_CONF0_REG(uart_num), inverse_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +//only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error"); + UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + if(flow_ctrl & UART_HW_FLOWCTRL_RTS) { + UART[uart_num]->conf1.rx_flow_thrhd = rx_thresh; + UART[uart_num]->conf1.rx_flow_en = 1; + } else { + UART[uart_num]->conf1.rx_flow_en = 0; + } + if(flow_ctrl & UART_HW_FLOWCTRL_CTS) { + UART[uart_num]->conf0.tx_flow_en = 1; + } else { + UART[uart_num]->conf0.tx_flow_en = 0; + } + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_hw_flow_ctrl(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; + if(UART[uart_num]->conf1.rx_flow_en) { + val |= UART_HW_FLOWCTRL_RTS; + } + if(UART[uart_num]->conf0.tx_flow_en) { + val |= UART_HW_FLOWCTRL_CTS; + } + return val; +} + +static esp_err_t uart_reset_fifo(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.rxfifo_rst = 1; + UART[uart_num]->conf0.rxfifo_rst = 0; + UART[uart_num]->conf0.txfifo_rst = 1; + UART[uart_num]->conf0.txfifo_rst = 0; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + //intr_clr register is write-only + UART[uart_num]->int_clr.val = clr_mask; + return ESP_OK; +} + +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + SET_PERI_REG_MASK(UART_INT_CLR_REG(uart_num), enable_mask); + SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), enable_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), disable_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_enable_rx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_rx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_tx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_ena.txfifo_empty = 0; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_clr.txfifo_empty = 1; + UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; + UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num); + return ESP_OK; +} + +esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + ESP_INTR_DISABLE(uart_intr_num); + switch(uart_num) { + case UART_NUM_1: + intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num); + break; + case UART_NUM_2: + intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num); + break; + case UART_NUM_0: + default: + intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num); + break; + } + xt_set_interrupt_handler(uart_intr_num, fn, arg); + ESP_INTR_ENABLE(uart_intr_num); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +//internal signal can be output to multiple GPIO pads +//only one GPIO pad can connect with input signal +esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((tx_io_num < 0 || tx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[tx_io_num] != 0), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || rx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rx_io_num] != 0), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || rts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rts_io_num] != 0), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || cts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[cts_io_num] != 0), "cts_io_num error"); + + int tx_sig, rx_sig, rts_sig, cts_sig; + switch(uart_num) { + case UART_NUM_0: + tx_sig = U0TXD_OUT_IDX; + rx_sig = U0RXD_IN_IDX; + rts_sig = U0RTS_OUT_IDX; + cts_sig = U0CTS_IN_IDX; + break; + case UART_NUM_1: + tx_sig = U1TXD_OUT_IDX; + rx_sig = U1RXD_IN_IDX; + rts_sig = U1RTS_OUT_IDX; + cts_sig = U1CTS_IN_IDX; + break; + case UART_NUM_2: + tx_sig = U2TXD_OUT_IDX; + rx_sig = U2RXD_IN_IDX; + rts_sig = U2RTS_OUT_IDX; + cts_sig = U2CTS_IN_IDX; + break; + case UART_NUM_MAX: + default: + tx_sig = U0TXD_OUT_IDX; + rx_sig = U0RXD_IN_IDX; + rts_sig = U0RTS_OUT_IDX; + cts_sig = U0CTS_IN_IDX; + break; + } + if(tx_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[tx_io_num], PIN_FUNC_GPIO); + gpio_set_direction(tx_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(tx_io_num, tx_sig, 0, 0); + } + + if(rx_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_io_num], PIN_FUNC_GPIO); + gpio_set_direction(rx_io_num, GPIO_MODE_INPUT); + gpio_matrix_in(rx_io_num, rx_sig, 0); + } + if(rts_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rts_io_num], PIN_FUNC_GPIO); + gpio_set_direction(rts_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(rts_io_num, rts_sig, 0, 0); + } + if(cts_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cts_io_num], PIN_FUNC_GPIO); + gpio_set_direction(cts_io_num, GPIO_MODE_INPUT); + gpio_matrix_in(cts_io_num, cts_sig, 0); + } + return ESP_OK; +} + +esp_err_t uart_set_rts(uart_port_t uart_num, int level) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.sw_rts = level & 0x1; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_set_dtr(uart_port_t uart_num, int level) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.sw_dtr = level & 0x1; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_config), "param null\n"); + if(uart_num == UART_NUM_0) { + periph_module_enable(PERIPH_UART0_MODULE); + } else if(uart_num == UART_NUM_1) { + periph_module_enable(PERIPH_UART1_MODULE); + } else if(uart_num == UART_NUM_2) { + periph_module_enable(PERIPH_UART2_MODULE); + } + uart_set_hw_flow_ctrl(uart_num, uart_config->flow_ctrl, uart_config->rx_flow_ctrl_thresh); + uart_set_baudrate(uart_num, uart_config->baud_rate); + UART[uart_num]->conf0.val = ( + (uart_config->parity << UART_PARITY_S) + | (uart_config->stop_bits << UART_STOP_BIT_NUM_S) + | (uart_config->data_bits << UART_BIT_NUM_S) + | ((uart_config->flow_ctrl & UART_HW_FLOWCTRL_CTS) ? UART_TX_FLOW_EN : 0x0) + | UART_TICK_REF_ALWAYS_ON_M); + return ESP_OK; +} + +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_intr_conf), "param null\n"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_clr.val = UART_INTR_MASK; + if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { + UART[uart_num]->conf1.rx_tout_thrhd = ((p_intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); + UART[uart_num]->conf1.rx_tout_en = 1; + } else { + UART[uart_num]->conf1.rx_tout_en = 0; + } + if(p_intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { + UART[uart_num]->conf1.rxfifo_full_thrhd = p_intr_conf->rxfifo_full_thresh; + } + if(p_intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { + UART[uart_num]->conf1.txfifo_empty_thrhd = p_intr_conf->txfifo_empty_intr_thresh; + } + UART[uart_num]->int_ena.val = p_intr_conf->intr_enable_mask; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_FAIL; +} + +//internal isr handler for default driver code. +static void IRAM_ATTR uart_rx_intr_handler_default(void *param) +{ + uart_obj_t *p_uart = (uart_obj_t*) param; + uint8_t uart_num = p_uart->uart_num; + uart_dev_t* uart_reg = UART[uart_num]; + + uint8_t buf_idx = 0; + uint32_t uart_intr_status = UART[uart_num]->int_st.val; + static int rx_fifo_len = 0; + uart_event_t uart_event; + portBASE_TYPE HPTaskAwoken = 0; + while(uart_intr_status != 0x0) { + buf_idx = 0; + uart_event.type = UART_EVENT_MAX; + if(uart_intr_status & UART_TXFIFO_EMPTY_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.txfifo_empty = 0; + uart_reg->int_clr.txfifo_empty = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + if(p_uart->tx_waiting == true) { + p_uart->tx_waiting = false; + xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); + } + } + else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { + if(p_uart->buffer_full_flg == false) { + //Get the buffer from the FIFO + rx_fifo_len = uart_reg->status.rxfifo_cnt; + p_uart->data_len = rx_fifo_len; + memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); + while(buf_idx < rx_fifo_len) { + p_uart->data_buf[buf_idx++] = uart_reg->fifo.rw_byte; + } + //After Copying the Data From FIFO ,Clear intr_status + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_clr.rxfifo_tout = 1; + uart_reg->int_clr.rxfifo_full = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_DATA; + uart_event.data.size = rx_fifo_len; + if(pdFALSE == xRingbufferSendFromISR(p_uart->ring_buffer, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.rxfifo_full = 0; + uart_reg->int_ena.rxfifo_tout = 0; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + p_uart->buffer_full_flg = true; + uart_event.type = UART_BUFFER_FULL; + } else { + uart_event.type = UART_DATA; + } + } else { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.rxfifo_full = 0; + uart_reg->int_ena.rxfifo_tout = 0; + uart_reg->int_clr.val = UART_RXFIFO_FULL_INT_CLR_M | UART_RXFIFO_TOUT_INT_CLR_M; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_BUFFER_FULL; + } + } else if(uart_intr_status & UART_RXFIFO_OVF_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->conf0.rxfifo_rst = 1; + uart_reg->conf0.rxfifo_rst = 0; + uart_reg->int_clr.rxfifo_ovf = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_FIFO_OVF; + } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) { + uart_reg->int_clr.brk_det = 1; + uart_event.type = UART_BREAK; + } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { + uart_reg->int_clr.parity_err = 1; + uart_event.type = UART_FRAME_ERR; + } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { + uart_reg->int_clr.frm_err = 1; + uart_event.type = UART_PARITY_ERR; + } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->conf0.txd_brk = 0; + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->int_clr.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_idle_done = 0; + uart_reg->int_clr.tx_brk_idle_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + } else if(uart_intr_status & UART_TX_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_done = 0; + uart_reg->int_clr.tx_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); + } + else { + uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ + uart_event.type = UART_EVENT_MAX; + } + + if(uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) { + xQueueSendFromISR(p_uart->xQueueUart, (void * )&uart_event, &HPTaskAwoken); + } + uart_intr_status = uart_reg->int_st.val; + } +} + +/**************************************************************/ +esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_INTR_DISABLE(uart_intr_num); + p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + return ESP_FAIL; + } + p_uart_obj[uart_num]->uart_num = uart_num; + p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); + + p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->rx_sem = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->intr_num = uart_intr_num; + p_uart_obj[uart_num]->queue_size = queue_size; + + if(uart_queue) { + p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); + *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + } else { + p_uart_obj[uart_num]->xQueueUart = NULL; + } + p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting = false; + p_uart_obj[uart_num]->rd_ptr = NULL; + p_uart_obj[uart_num]->cur_remain = 0; + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->ring_buffer = xRingbufferCreate(buffer_size, 0); + } else { + ESP_LOGE(UART_TAG, "UART driver already installed\n"); + return ESP_FAIL; + } + uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_intr_config_t uart_intr = { + .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M + | UART_RXFIFO_TOUT_INT_ENA_M + | UART_FRM_ERR_INT_ENA_M + | UART_RXFIFO_OVF_INT_ENA_M + | UART_BRK_DET_INT_ENA_M, + .rxfifo_full_thresh = DEFAULT_FULL_THRESH, + .rx_timeout_thresh = DEFAULT_TOUT_THRESH, + .txfifo_empty_intr_thresh = DEFAULT_EMPTY_THRESH + }; + uart_intr_config(uart_num, &uart_intr); + ESP_INTR_ENABLE(uart_intr_num); + return ESP_OK; +} + +//Make sure no other tasks are still using UART before you call this function +esp_err_t uart_driver_delete(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + return ESP_OK; + } + ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + uart_disable_rx_intr(uart_num); + uart_disable_tx_intr(uart_num); + uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); + + if(p_uart_obj[uart_num]->tx_fifo_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_fifo_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_done_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_done_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_brk_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); + p_uart_obj[uart_num]->tx_brk_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); + p_uart_obj[uart_num]->tx_mutex = NULL; + } + if(p_uart_obj[uart_num]->rx_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->rx_sem); + p_uart_obj[uart_num]->rx_sem = NULL; + } + if(p_uart_obj[uart_num]->xQueueUart) { + vQueueDelete(p_uart_obj[uart_num]->xQueueUart); + p_uart_obj[uart_num]->xQueueUart = NULL; + } + if(p_uart_obj[uart_num]->ring_buffer) { + vRingbufferDelete(p_uart_obj[uart_num]->ring_buffer); + p_uart_obj[uart_num]->ring_buffer = NULL; + } + free(p_uart_obj[uart_num]); + p_uart_obj[uart_num] = NULL; + return ESP_OK; +} + +esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + BaseType_t res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + //Take tx_mutex + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + //take 1st tx_done_sem + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + ESP_LOGE(UART_TAG, "take uart done sem error, should not get here.\n"); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_ERR_TIMEOUT; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if(UART[uart_num]->status.txfifo_cnt == 0) { + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_OK; + } + uart_enable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); + //take 2nd tx_done_sem, wait given from ISR + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_ERR_TIMEOUT; + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_OK; +} + +static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) +{ + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->idle_conf.tx_brk_num = break_num; + UART[uart_num]->conf0.txd_brk = 1; + UART[uart_num]->int_clr.tx_brk_done = 1; + UART[uart_num]->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(buffer, "buffer null"); + if(len == 0) { + return 0; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + int tx_len = uart_fill_fifo(uart_num, buffer, len); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return tx_len; +} + +static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool brk_en, int brk_len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(src, "buffer null"); + if(size == 0) { + return 0; + } + //lock for uart_tx + xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + size_t original_size = size; + while(size) { + //semaphore for tx_fifo available + if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { + size_t sent = uart_fill_fifo(uart_num, (char*) src, size); + if(sent < size) { + p_uart_obj[uart_num]->tx_waiting = true; + uart_enable_tx_intr(uart_num, 1, DEFAULT_EMPTY_THRESH); + } + size -= sent; + src += sent; + } + } + if(brk_en) { + uart_set_break(uart_num, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return original_size; +} + +int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(src, "buffer null"); + return uart_tx_all(uart_num, src, size, 0, 0); +} + +int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((size > 0), "uart size error"); + UART_CHECK((src), "uart data null"); + UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + return uart_tx_all(uart_num, src, size, 1, brk_len); +} + +int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + uint8_t* data; + size_t size; + int val; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + return -1; + } + if(p_uart_obj[uart_num]->cur_remain == 0) { + ticks_to_wait = ticks_end - xTaskGetTickCount(); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + if(data) { + p_uart_obj[uart_num]->head_ptr = data; + p_uart_obj[uart_num]->rd_ptr = data; + p_uart_obj[uart_num]->cur_remain = size; + } else { + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return -1; + } + } + val = *(p_uart_obj[uart_num]->rd_ptr); + p_uart_obj[uart_num]->rd_ptr++; + p_uart_obj[uart_num]->cur_remain--; + if(p_uart_obj[uart_num]->cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rd_ptr = NULL; + if(p_uart_obj[uart_num]->buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + if(res == pdTRUE) { + p_uart_obj[uart_num]->buffer_full_flg = false; + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + } + } + } + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return val; +} + +int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((buf), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + + uint8_t* data = NULL; + size_t size; + size_t copy_len = 0; + int len_tmp; + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + return -1; + } + while(length) { + if(p_uart_obj[uart_num]->cur_remain == 0) { + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + if(data) { + p_uart_obj[uart_num]->head_ptr = data; + p_uart_obj[uart_num]->rd_ptr = data; + p_uart_obj[uart_num]->cur_remain = size; + } else { + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return copy_len; + } + } + if(p_uart_obj[uart_num]->cur_remain > length) { + len_tmp = length; + } else { + len_tmp = p_uart_obj[uart_num]->cur_remain; + } + memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); + p_uart_obj[uart_num]->rd_ptr += len_tmp; + p_uart_obj[uart_num]->cur_remain -= len_tmp; + copy_len += len_tmp; + length -= len_tmp; + if(p_uart_obj[uart_num]->cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rd_ptr = NULL; + if(p_uart_obj[uart_num]->buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + if(res == pdTRUE) { + p_uart_obj[uart_num]->buffer_full_flg = false; + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + } + } + } + } + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return copy_len; +} + +esp_err_t uart_flush(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + + uart_obj_t* p_uart = p_uart_obj[uart_num]; + uint8_t* data; + size_t size; + //rx sem protect the ring buffer read related functions + xSemaphoreTake(p_uart->rx_sem, (portTickType)portMAX_DELAY); + while(true) { + if(p_uart->head_ptr) { + vRingbufferReturnItem(p_uart->ring_buffer, p_uart->head_ptr); + p_uart->rd_ptr = NULL; + p_uart->cur_remain = 0; + p_uart->head_ptr = NULL; + } + data = (uint8_t*) xRingbufferReceive(p_uart->ring_buffer, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->ring_buffer, data); + } + p_uart->rd_ptr = NULL; + p_uart->cur_remain = 0; + p_uart->head_ptr = NULL; + xSemaphoreGive(p_uart->rx_sem); + uart_wait_tx_fifo_empty(uart_num, portMAX_DELAY); + uart_reset_fifo(uart_num); + return ESP_OK; +} + +//----------------------------------- +//Should not enable hw flow control the debug print port. +//Use uart_tx_all_chars() as a thread-safe function to send data. +static int s_uart_print_nport = UART_NUM_0; +static void uart2_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_2, (const char*)&chr, 1); +} + +static void uart1_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_1, (const char*)&chr, 1); +} + +static void uart0_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_0, (const char*)&chr, 1); +} + +static void uart_ignore_char(char chr) +{ + +} + +//Only effective to ets_printf function, not ESP_LOGX macro. +esp_err_t uart_set_print_port(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); + + s_uart_print_nport = uart_num; + switch(s_uart_print_nport) { + case UART_NUM_0: + ets_install_putc1(uart0_write_char); + break; + case UART_NUM_1: + ets_install_putc1(uart1_write_char); + break; + case UART_NUM_2: + ets_install_putc1(uart2_write_char); + break; + case UART_NUM_MAX: + default: + ets_install_putc1(uart_ignore_char); + break; + } + return ESP_OK; +} + +int uart_get_print_port() +{ + return s_uart_print_nport; +} + diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index 4f013f91ab..5504c3d682 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -31,6 +31,7 @@ typedef int32_t esp_err_t; #define ESP_ERR_NO_MEM 0x101 #define ESP_ERR_INVALID_ARG 0x102 #define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_TIMEOUT 0x104 /** * Macro which can be used to check the error code, diff --git a/components/esp32/include/soc/uart_reg.h b/components/esp32/include/soc/uart_reg.h index 155700b293..8cac4bb832 100644 --- a/components/esp32/include/soc/uart_reg.h +++ b/components/esp32/include/soc/uart_reg.h @@ -18,8 +18,10 @@ #include "soc.h" #define REG_UART_BASE( i ) (DR_REG_UART_BASE + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) ) - +#define REG_UART_AHB_BASE(i) (0x60000000 + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) ) +#define UART_FIFO_AHB_REG(i) (REG_UART_AHB_BASE(i) + 0x0) #define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0) + /* UART_RXFIFO_RD_BYTE : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: This register stores one byte data read by rx fifo.*/ #define UART_RXFIFO_RD_BYTE 0x000000FF diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 6045c5a695..3651753147 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -371,8 +371,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da //Does not fit in the remaining space in the ringbuffer. write_succeeded=pdFALSE; } else { - copyItemToRingbuf(rb, data, dataSize); - write_succeeded=pdTRUE; + write_succeeded = copyItemToRingbuf(rb, data, dataSize); } portEXIT_CRITICAL_ISR(&rb->mux); if (write_succeeded) { From 9098e64398e1af7418e7c27c6bba23f80b55810f Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 09:38:34 +0800 Subject: [PATCH 006/131] modify GPIO number check --- components/driver/uart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 157e0c49c7..f585c0965d 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -312,10 +312,10 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || tx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[tx_io_num] != 0), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || rx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rx_io_num] != 0), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || rts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rts_io_num] != 0), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || cts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[cts_io_num] != 0), "cts_io_num error"); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { From 74aff2b9d2762578f1da7ff736e402c909755620 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 15:57:23 +0800 Subject: [PATCH 007/131] Update UART driver 1. Use esp_log API for LEDC and GPIO code. 2. Modify some API return value. 3. Add ledc_set_pin() for LEDC 4. Modify typo in uart.h Questions: In uart driver ISR handler, I used xxxFromISR , like xSemaphoreGiveFromISR, do those FromISR functions need to be put in IRAM? --- components/driver/gpio.c | 123 ++++--------- components/driver/include/driver/gpio.h | 2 + components/driver/include/driver/ledc.h | 33 +++- components/driver/include/driver/uart.h | 71 ++++---- components/driver/ledc.c | 223 ++++++------------------ components/driver/uart.c | 40 +++-- 6 files changed, 169 insertions(+), 323 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 14dfc00b43..da0fedb895 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -18,34 +18,13 @@ #include "freertos/xtensa_api.h" #include "driver/gpio.h" #include "soc/soc.h" +#include "esp_log.h" -//TODO: move debug options to menuconfig -#define GPIO_DBG_ENABLE (0) -#define GPIO_WARNING_ENABLE (0) -#define GPIO_ERROR_ENABLE (0) -#define GPIO_INFO_ENABLE (0) -//DBG INFOR -#if GPIO_INFO_ENABLE -#define GPIO_INFO ets_printf -#else -#define GPIO_INFO(...) -#endif -#if GPIO_WARNING_ENABLE -#define GPIO_WARNING(format,...) do{\ - ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define GPIO_WARNING(...) -#endif -#if GPIO_ERROR_ENABLE -#define GPIO_ERROR(format,...) do{\ - ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define GPIO_ERROR(...) -#endif +const char* GPIO_TAG = "GPIO"; +#define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ + ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret_val); \ + } const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_0, @@ -90,33 +69,17 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; -static int is_valid_gpio(int gpio_num) -{ - if(gpio_num >= GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[gpio_num] == 0) { - GPIO_ERROR("GPIO io_num=%d does not exist\n",gpio_num); - return 0; - } - return 1; -} - esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } - if(intr_type >= GPIO_INTR_MAX) { - GPIO_ERROR("Unknown GPIO intr:%u\n",intr_type); - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_type = intr_type; return ESP_OK; } esp_err_t gpio_intr_enable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(xPortGetCoreID() == 0) { GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr } else { @@ -127,18 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num) esp_err_t gpio_intr_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr return ESP_OK; } static esp_err_t gpio_output_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1tc = (0x1 << gpio_num); } else { @@ -149,13 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num) static esp_err_t gpio_output_enable(gpio_num_t gpio_num) { - if(gpio_num >= 34) { - GPIO_ERROR("io_num=%d can only be input\n",gpio_num); - return ESP_ERR_INVALID_ARG; - } - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error\n", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1ts = (0x1 << gpio_num); } else { @@ -166,9 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - if(!GPIO_IS_VALID_GPIO(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(level) { if(gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); @@ -196,9 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num) esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: @@ -218,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); break; default: - GPIO_ERROR("Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); + ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); ret = ESP_ERR_INVALID_ARG; break; } @@ -227,11 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) { - GPIO_ERROR("io_num=%d can only be input\n",gpio_num); + ESP_LOGE(GPIO_TAG, "io_num=%d can only be input\n",gpio_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -268,52 +216,51 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) uint32_t io_num = 0; uint64_t bit_valid = 0; if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) { - GPIO_ERROR("GPIO_PIN mask error \n"); + ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error \n"); return ESP_ERR_INVALID_ARG; } if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) { //GPIO 34/35/36/37/38/39 can only be used as input mode; if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) { - GPIO_ERROR("GPIO34-39 can only be used as input mode\n"); + ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode\n"); return ESP_ERR_INVALID_ARG; } } do { io_reg = GPIO_PIN_MUX_REG[io_num]; if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) { - GPIO_INFO("Gpio%02d |Mode:",io_num); + ESP_LOGI(GPIO_TAG, "Gpio%02d |Mode:",io_num); if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) { - GPIO_INFO("INPUT "); + ESP_LOGI(GPIO_TAG, "INPUT "); PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]); } else { PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]); } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) { - GPIO_INFO("OD "); + ESP_LOGI(GPIO_TAG, "OD "); GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */ } else { GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */ } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) { - GPIO_INFO("OUTPUT "); + ESP_LOGI(GPIO_TAG, "OUTPUT "); gpio_output_enable(io_num); } else { gpio_output_disable(io_num); } - GPIO_INFO("|"); if(pGPIOConfig->pull_up_en) { - GPIO_INFO("PU "); + ESP_LOGI(GPIO_TAG, "PU "); PIN_PULLUP_EN(io_reg); } else { PIN_PULLUP_DIS(io_reg); } if(pGPIOConfig->pull_down_en) { - GPIO_INFO("PD "); + ESP_LOGI(GPIO_TAG, "PD "); PIN_PULLDWN_EN(io_reg); } else { PIN_PULLDWN_DIS(io_reg); } - GPIO_INFO("Intr:%d |\n",pGPIOConfig->intr_type); + ESP_LOGI(GPIO_TAG, "Intr:%d |\n",pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); if(pGPIOConfig->intr_type) { gpio_intr_enable(io_num); @@ -322,7 +269,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) } PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */ } else if(bit_valid && (io_reg == 0)) { - GPIO_WARNING("io_num=%d does not exist\n",io_num); + ESP_LOGW(GPIO_TAG, "io_num=%d does not exist\n",io_num); } io_num++; } while(io_num < GPIO_PIN_COUNT); @@ -331,9 +278,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) { - if(fn == NULL) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(fn, "GPIO ISR null\n", ESP_ERR_INVALID_ARG); ESP_INTR_DISABLE(gpio_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); xt_set_interrupt_handler(gpio_intr_num, fn, arg); @@ -344,15 +289,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar /*only level interrupt can be used for wake-up function*/ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { - GPIO_ERROR("GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); + ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); ret = ESP_ERR_INVALID_ARG; } return ret; @@ -360,9 +303,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].wakeup_enable = 0; return ESP_OK; } diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index c821b640eb..5ed99e5976 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -28,6 +28,8 @@ extern "C" { #endif +extern const char* GPIO_TAG; + #define GPIO_SEL_0 (BIT(0)) /* Pin 0 selected */ #define GPIO_SEL_1 (BIT(1)) /* Pin 1 selected */ #define GPIO_SEL_2 (BIT(2)) /* Pin 2 selected */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 79a6c7f9f3..d6ce9b86c9 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -26,6 +26,7 @@ extern "C" { #endif +extern const char* LEDC_TAG; #define LEDC_APB_CLK_HZ (APB_CLK_FREQ) #define LEDC_REF_CLK_HZ (1*1000000) @@ -338,6 +339,22 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); */ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); +/** + * @brief Set LEDC output signal to GPIO + * + * @param[in] gpio_num : GPIO number for LEDC signal output + * + * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * + * @param[in] channel : LEDC channel index(0-7), select from ledc_channel_t + * + * + * @return ESP_ERR_INVALID_ARG: parameter error + * ESP_OK: success + * + */ +esp_err_t ledc_set_pin(int gpio_num,ledc_mode_t speed_mode, ledc_channel_t ledc_channel); + /***************************EXAMPLE********************************** * * @@ -349,21 +366,21 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * ledc_timer_config_t timer_conf = { * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE //timer mode, + * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, * .timer_num = LEDC_TIMER_0, //timer number * }; * ledc_timer_config(&timer_conf); //setup timer. * * //3. set LEDC channel * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same + * .channel = LEDC_CHANNEL_0, //set LEDC channel 0 + * .duty = 1000, //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + * .gpio_num = 16, //GPIO number + * .intr_type = LEDC_INTR_FADE_END, //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + * .speed_mode = LEDC_HIGH_SPEED_MODE, //set LEDC mode, from ledc_mode_t + * .timer_sel = LEDC_TIMER_0, //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same * } - * ledc_channel_config(&ledc_conf); //setup the configuration + * ledc_channel_config(&ledc_conf); //setup the configuration * * ----------------EXAMPLE OF SETTING DUTY --- ----------------- * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index cd0725d2ce..dafdf54a84 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -27,7 +27,7 @@ extern "C" { #include extern const char* UART_TAG; -#define UART_FIFO_LEN (128) //Do Not Change it +#define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) @@ -150,13 +150,10 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1) : Parameter error - * UART_DATA_5_BITS (0): UART word length: 5 bits. - * UART_DATA_6_BITS (1): UART word length: 6 bits. - * UART_DATA_7_BITS (2): UART word length: 7 bits. - * UART_DATA_8_BITS (3): UART word length: 8 bits. + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*data_bit) */ -int uart_get_word_length(uart_port_t uart_num); +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** * @brief Set UART stop bits. @@ -174,12 +171,10 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_STOP_BITS_1 (1): 1 stop bit - * UART_STOP_BITS_1_5 (2): 1.5 stop bits - * UART_STOP_BITS_1 (3): 2 stop bits + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*stop_bit) */ -int uart_get_stop_bits(uart_port_t uart_num); +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** * @brief Set UART parity. @@ -196,13 +191,11 @@ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_PARITY_ODD (0x11): Odd parity check mode - * UART_PARITY_EVEN (0x10): Even parity check mode - * UART_PARITY_DISABLE(0x0) : parity check disabled + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*parity_mode) * */ -int uart_get_parity(uart_port_t uart_num); +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** * @brief Set UART baud rate. @@ -219,11 +212,11 @@ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL(-1): Parameter error - * Others (>0): UART baud-rate + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*baudrate) * */ -int uart_get_baudrate(uart_port_t uart_num); +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode @@ -253,13 +246,10 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ * @brief Get hardware flow control mode * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_HW_FLOWCTRL_DISABLE (0): UART hw flow control disabled - * UART_HW_FLOWCTRL_RTS (1): UART RX flow control enabled - * UART_HW_FLOWCTRL_CTS (2): UART TX flow control enabled - * UART_HW_FLOWCTRL_CTS_RTS (3): UART TX/RX flow control enabled + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*flow_ctrl) */ -int uart_get_hw_flow_ctrl(uart_port_t uart_num); +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** * @brief Clear UART interrupt status @@ -453,6 +443,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks * * @return ESP_OK : Success + * ESP_FAIL : Parameter error * ESP_ERR_TIMEOUT: Timeout */ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); @@ -465,7 +456,8 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait * @param char* buffer : data buffer address * @param uint32_t len : data length to send * - * @return The number of data that pushed to the TX FIFO + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); @@ -477,7 +469,8 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * @param char* src : data buffer address * @param size_t size : data length to send * - * @return The number of data that sent out. + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); @@ -490,7 +483,8 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * @param size_t size : data length to send * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) * - * @return The number of data that sent out. + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); @@ -498,20 +492,20 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s * @brief UART read one char * * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait : ticks to wait. + * @param TickType_t ticks_to_wait : Timeout, count in RTOS ticks * * @return -1 : Error - * Others : return a char data from uart fifo. + * Others : return a char data from UART. */ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief UART read bytes from UART buffer * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t* buf : pointer to the buffer. - * @param uint32_t length : data length - * @param TickType_t ticks_to_wait: timeout time( FreeRTOS ti c + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t* buf : pointer to the buffer. + * @param uint32_t length : data length + * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks * * @return -1 : Error * Others : return a char data from uart fifo. @@ -542,9 +536,9 @@ esp_err_t uart_set_print_port(uart_port_t uart_no); * @brief Get the current serial port for ets_printf function * * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @return null + * @return current print port: 0: UART0; + * 1: UART1; + * 2: UART2; */ int uart_get_print_port(); @@ -637,7 +631,6 @@ int uart_get_print_port(); * break; * case UART_FIFO_OVF: //Event of HW FIFO overflow detected * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); - * while(1); * break; * case UART_BUFFER_FULL: //Event of UART ring buffer full * ESP_LOGI(UART_TAG, "ring buffer full\n"); diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 386c93dfa6..771c4a17df 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -18,86 +18,19 @@ #include "freertos/xtensa_api.h" #include "soc/gpio_sig_map.h" #include "driver/ledc.h" +#include "esp_log.h" -//TODO: to use APIs in esp_log.h. -#define LEDC_DBG_WARING_ENABLE (0) -#define LEDC_DBG_ERROR_ENABLE (0) -#define LEDC_INFO_ENABLE (0) -#define LEDC_DBG_ENABLE (0) - -//DBG INFOR -#if LEDC_DBG_ENABLE -#define LEDC_DBG(format,...) do{\ - ets_printf("[dbg][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_DBG(...) -#endif - -#if LEDC_INFO_ENABLE -#define LEDC_INFO(format,...) do{\ - ets_printf("[info][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_INFO(...) -#endif - -#if LEDC_DBG_WARING_ENABLE -#define LEDC_WARING(format,...) do{\ - ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_WARING(...) -#endif -#if LEDC_DBG_ERROR_ENABLE -#define LEDC_ERROR(format,...) do{\ - ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_ERROR(...) -#endif - +const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; - -static bool ledc_is_valid_channel(uint32_t channel) -{ - if(channel > LEDC_CHANNEL_7) { - LEDC_ERROR("LEDC CHANNEL ERR: %d\n",channel); - return false; - } - return true; -} - -static bool ledc_is_valid_mode(uint32_t mode) -{ - if(mode >= LEDC_SPEED_MODE_MAX) { - LEDC_ERROR("LEDC MODE ERR: %d\n",mode); - return false; - } - return true; -} - -static bool ledc_is_valid_timer(int timer) -{ - if(timer > LEDC_TIMER_3) { - LEDC_ERROR("LEDC TIMER ERR: %d\n", timer); - return false; - } - return true; -} +#define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ + ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret_val); \ + } esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; @@ -125,12 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_idx)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -139,12 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -154,12 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -168,12 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -182,9 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); @@ -200,9 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) { - if(fn == NULL) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(fn, "ledc isr null\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); ESP_INTR_DISABLE(ledc_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); @@ -218,16 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { - LEDC_ERROR("freq_hz=%u bit_num=%u\n", freq_hz, bit_num); + ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u\n", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } if(timer_num > LEDC_TIMER_3) { - LEDC_ERROR("Time Select %u\n", timer_num); + ESP_LOGE(LEDC_TAG, "Time Select %u\n", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -239,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) /*Selet the reference tick*/ div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - LEDC_ERROR("div param err,div_param=%u\n", div_param); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", (uint32_t)div_param); ret = ESP_FAIL; } timer_clk_src = LEDC_REF_TICK; @@ -254,6 +164,21 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) return ret; } +esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) +{ + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); + gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); + if(speed_mode == LEDC_HIGH_SPEED_MODE) { + gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0); + } else { + + } + return ESP_OK; +} + esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) { uint32_t speed_mode = ledc_conf->speed_mode; @@ -262,21 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - - if(!ledc_is_valid_channel(ledc_channel)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!GPIO_IS_VALID_OUTPUT_GPIO(gpio_num)) { - LEDC_ERROR("GPIO number error: IO%d\n ", gpio_num); - return ESP_ERR_INVALID_ARG; - } - if(timer_select > LEDC_TIMER_3) { - LEDC_ERROR("Time Select %u\n", timer_select); - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; /*set channel parameters*/ /* channel parameters decide how the waveform looks like in one period*/ @@ -288,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - LEDC_INFO("LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", + ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -300,12 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -315,12 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -331,18 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } - if(fade_direction > LEDC_DUTY_DIR_INCREASE) { - LEDC_ERROR("Duty direction err\n"); - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error\n", ESP_ERR_INVALID_ARG); if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { - LEDC_ERROR("step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); + ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } ledc_duty_config(speed_mode, @@ -359,12 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -379,18 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - if(!ledc_is_valid_mode(speed_mode)) { - return -1; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; @@ -403,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; } if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { - LEDC_ERROR("div param err,div_param=%u\n", div_num); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", div_num); ret = ESP_FAIL; } LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num; @@ -413,9 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - if(!ledc_is_valid_mode(speed_mode)) { - return 0; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (0)); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; diff --git a/components/driver/uart.c b/components/driver/uart.c index f585c0965d..29e4522d6d 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -90,10 +90,11 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit return ESP_OK; } -int uart_get_word_length(uart_port_t uart_num) +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - return UART[uart_num]->conf0.bit_num; + *(data_bit) = UART[uart_num]->conf0.bit_num; + return ESP_OK; } esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) @@ -106,10 +107,11 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) return ESP_OK; } -int uart_get_stop_bits(uart_port_t uart_num) +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - return UART[uart_num]->conf0.stop_bit_num; + (*stop_bit) = UART[uart_num]->conf0.stop_bit_num; + return ESP_OK; } esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) @@ -122,19 +124,20 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) return ESP_OK; } -int uart_get_parity(uart_port_t uart_num) +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); int val = UART[uart_num]->conf0.val; if(val & UART_PARITY_EN_M) { if(val & UART_PARITY_M) { - return UART_PARITY_ODD; + (*parity_mode) = UART_PARITY_ODD; } else { - return UART_PARITY_EVEN; + (*parity_mode) = UART_PARITY_EVEN; } } else { - return UART_PARITY_DISABLE; + (*parity_mode) = UART_PARITY_DISABLE; } + return ESP_OK; } esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) @@ -149,14 +152,14 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) return ESP_OK; } -int uart_get_baudrate(uart_port_t uart_num) +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - uint32_t baudrate = ((UART_CLK_FREQ) << 4) / clk_div; - return baudrate; + (*baudrate) = ((UART_CLK_FREQ) << 4) / clk_div; + return ESP_OK; } esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) @@ -192,7 +195,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow return ESP_OK; } -int uart_get_hw_flow_ctrl(uart_port_t uart_num) +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; @@ -202,7 +205,8 @@ int uart_get_hw_flow_ctrl(uart_port_t uart_num) if(UART[uart_num]->conf0.tx_flow_en) { val |= UART_HW_FLOWCTRL_CTS; } - return val; + (*flow_ctrl) = val; + return ESP_OK; } static esp_err_t uart_reset_fifo(uart_port_t uart_num) @@ -311,11 +315,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); +// UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); +// UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); +// UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); +// UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); +// UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { From d9005e739dbf1e32aab0b2cbce01eae100d43ef6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 24 Oct 2016 21:18:02 +0800 Subject: [PATCH 008/131] Add bytebuffer support to ringbuf.c --- .../freertos/include/freertos/ringbuf.h | 50 ++- components/freertos/ringbuf.c | 344 +++++++++++++----- 2 files changed, 303 insertions(+), 91 deletions(-) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 7884e9856e..93ba30758e 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -18,22 +18,34 @@ to this bit of memory will block. The requirement for items to be contiguous is slightly problematic when the only way to place the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can -be solved in two ways: -- allow_split_items = pdTRUE: The insertion code will split the item in two items; one which fits +be solved (or not) in a few ways: +- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits in the space left at the end of the ringbuffer, one that contains the remaining data which is placed in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data. -- allow_split_items = pdFALSE: The insertion code will leave the room at the end of the ringbuffer +- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer unused and instead will put the entire item at the start of the ringbuffer, as soon as there is enough free space. +- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no +overhead, but it has no item contiguousness either: a read will just give you the entire written +buffer space, or the space up to the end of the buffer, and writes can be broken up in any way +possible. Note that this type cannot do a 2nd read before returning the memory of the 1st. The maximum size of an item will be affected by this decision. When split items are allowed, it's acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the -maximum size is (buffer_size/2)-8 bytes. +maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has +no overhead. */ //An opaque handle for a ringbuff object. typedef void * RingbufHandle_t; +//The various types of buffer +typedef enum { + RINGBUF_TYPE_NOSPLIT = 0, + RINGBUF_TYPE_ALLOWSPLIT, + RINGBUF_TYPE_BYTEBUF +} ringbuf_type_t; + /** * @brief Create a ring buffer @@ -45,7 +57,7 @@ typedef void * RingbufHandle_t; * * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error. */ -RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items); +RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); /** @@ -120,6 +132,34 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size); +/** + * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes + * to return + * + * @param ringbuf - Ring buffer to retrieve the item from + * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param xTicksToWait - Ticks to wait for items in the ringbuffer. + * + * @return Pointer to the retrieved item on success; *item_size filled with the length of the + * item. NULL on timeout, *item_size is untouched in that case. + */ +void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size); + + +/** + * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes + * to return. Call this from an ISR. + * + * @param ringbuf - Ring buffer to retrieve the item from + * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * + * @return Pointer to the retrieved item on success; *item_size filled with the length of the + * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + */ +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size); + + + /** * @brief Return a previously-retrieved item to the ringbuffer * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 3651753147..77eb362843 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -18,6 +18,7 @@ #include "freertos/queue.h" #include "freertos/xtensa_api.h" #include "freertos/ringbuf.h" +#include "esp_attr.h" #include #include #include @@ -25,6 +26,7 @@ typedef enum { flag_allowsplit = 1, + flag_bytebuf = 2, } rbflag_t; typedef enum { @@ -33,8 +35,10 @@ typedef enum { } itemflag_t; +typedef struct ringbuf_t ringbuf_t; + //The ringbuffer structure -typedef struct { +struct ringbuf_t { SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark. size_t size; //Size of the data storage @@ -44,7 +48,12 @@ typedef struct { uint8_t *data; //Data storage portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification rbflag_t flags; -} ringbuf_t; + size_t maxItemSize; + //The following keep function pointers to hold different implementations for ringbuffer management. + BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size); + uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length); + void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item); +}; @@ -73,14 +82,16 @@ static int ringbufferFreeMem(ringbuf_t *rb) return free_size-1; } -//Copies a single item to the ring buffer. Assumes there is space in the ringbuffer and + +//Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on //success, pdFALSE if it can't make the item fit and the calling routine needs to retry //later or fail. //This function by itself is not threadsafe, always call from within a muxed section. -static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) { - size_t rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value + size_t rbuffer_size; + rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size //of a header to the end of the ringbuff @@ -88,65 +99,28 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe //See if we have enough contiguous space to write the buffer. if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) { - //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do - //that depending on how the ringbuffer is configured. - //The code here is also expected to check if the buffer, mangled in whatever way is implemented, - //will still fit, and return pdFALSE if that is not the case. - if (rb->flags & flag_allowsplit) { - //Buffer plus header is not going to fit in the room from wr_pos to the end of the - //ringbuffer... we need to split the write in two. - //First, see if this will fit at all. - if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) { - //Will not fit. - return pdFALSE; - } - //Because the code at the end of the function makes sure we always have - //room for a header, this should never assert. - configASSERT(rem_len>=sizeof(buf_entry_hdr_t)); - //Okay, it should fit. Write everything. - //First, place bit of buffer that does fit. Write header first... - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; - hdr->flags=0; - hdr->len=rem_len-sizeof(buf_entry_hdr_t); - rb->write_ptr+=sizeof(buf_entry_hdr_t); - rem_len-=sizeof(buf_entry_hdr_t); - if (rem_len!=0) { - //..then write the data bit that fits. - memcpy(rb->write_ptr, buffer, rem_len); - //Update vars so the code later on will write the rest of the data. - buffer+=rem_len; - rbuffer_size-=rem_len; - buffer_size-=rem_len; - } else { - //Huh, only the header fit. Mark as dummy so the receive function doesn't receive - //an useless zero-byte packet. - hdr->flags|=iflag_dummydata; - } - rb->write_ptr=rb->data; - } else { - //Buffer plus header is not going to fit in the room from wr_pos to the end of the - //ringbuffer... but we're not allowed to split the buffer. We need to fill the - //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of - //the ringbuffer.. - //First, find out if we actually have enough space at the start of the ringbuffer to - //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr) - if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) { - //Will not fit. - return pdFALSE; - } - //If the read buffer hasn't wrapped around yet, there's no way this will work either. - if (rb->free_ptr > rb->write_ptr) { - //No luck. - return pdFALSE; - } - - //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet. - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; - hdr->flags=iflag_dummydata; - //Reset the write pointer to the start of the ringbuffer so the code later on can - //happily write the data. - rb->write_ptr=rb->data; + //Buffer plus header is not going to fit in the room from wr_pos to the end of the + //ringbuffer... but we're not allowed to split the buffer. We need to fill the + //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of + //the ringbuffer.. + //First, find out if we actually have enough space at the start of the ringbuffer to + //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr) + if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) { + //Will not fit. + return pdFALSE; } + //If the read buffer hasn't wrapped around yet, there's no way this will work either. + if (rb->free_ptr > rb->write_ptr) { + //No luck. + return pdFALSE; + } + + //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet. + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->flags=iflag_dummydata; + //Reset the write pointer to the start of the ringbuffer so the code later on can + //happily write the data. + rb->write_ptr=rb->data; } else { //No special handling needed. Checking if it's gonna fit probably still is a good idea. if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) { @@ -174,9 +148,117 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe return pdTRUE; } +//Copies a single item to the ring buffer; allows split items. Assumes there is space in the ringbuffer and +//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on +//success, pdFALSE if it can't make the item fit and the calling routine needs to retry +//later or fail. +//This function by itself is not threadsafe, always call from within a muxed section. +static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +{ + size_t rbuffer_size; + rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value + configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned + configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size + //of a header to the end of the ringbuff + size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer + + //See if we have enough contiguous space to write the buffer. + if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) { + //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do + //that depending on how the ringbuffer is configured. + //The code here is also expected to check if the buffer, mangled in whatever way is implemented, + //will still fit, and return pdFALSE if that is not the case. + //Buffer plus header is not going to fit in the room from wr_pos to the end of the + //ringbuffer... we need to split the write in two. + //First, see if this will fit at all. + if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) { + //Will not fit. + return pdFALSE; + } + //Because the code at the end of the function makes sure we always have + //room for a header, this should never assert. + configASSERT(rem_len>=sizeof(buf_entry_hdr_t)); + //Okay, it should fit. Write everything. + //First, place bit of buffer that does fit. Write header first... + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->flags=0; + hdr->len=rem_len-sizeof(buf_entry_hdr_t); + rb->write_ptr+=sizeof(buf_entry_hdr_t); + rem_len-=sizeof(buf_entry_hdr_t); + if (rem_len!=0) { + //..then write the data bit that fits. + memcpy(rb->write_ptr, buffer, rem_len); + //Update vars so the code later on will write the rest of the data. + buffer+=rem_len; + rbuffer_size-=rem_len; + buffer_size-=rem_len; + } else { + //Huh, only the header fit. Mark as dummy so the receive function doesn't receive + //an useless zero-byte packet. + hdr->flags|=iflag_dummydata; + } + rb->write_ptr=rb->data; + } else { + //No special handling needed. Checking if it's gonna fit probably still is a good idea. + if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) { + //Buffer is not going to fit, period. + return pdFALSE; + } + } + + //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer. + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->len=buffer_size; + hdr->flags=0; + rb->write_ptr+=sizeof(buf_entry_hdr_t); + memcpy(rb->write_ptr, buffer, buffer_size); + rb->write_ptr+=rbuffer_size; + + //The buffer will wrap around if we don't have room for a header anymore. + if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) { + //'Forward' the write buffer until we are at the start of the ringbuffer. + //The read pointer will always be at the start of a full header, which cannot + //exist at the point of the current write pointer, so there's no chance of overtaking + //that. + rb->write_ptr=rb->data; + } + return pdTRUE; +} + + +//Copies a bunch of daya to the ring bytebuffer. Assumes there is space in the ringbuffer and +//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on +//success, pdFALSE if it can't make the item fit and the calling routine needs to retry +//later or fail. +//This function by itself is not threadsafe, always call from within a muxed section. +static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +{ + size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer + + //See if we have enough contiguous space to write the buffer. + if (rem_len < buffer_size) { + //...Nope. Write the data bit that fits. + memcpy(rb->write_ptr, buffer, rem_len); + //Update vars so the code later on will write the rest of the data. + buffer+=rem_len; + buffer_size-=rem_len; + rb->write_ptr=rb->data; + } + + //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer. + memcpy(rb->write_ptr, buffer, buffer_size); + rb->write_ptr+=buffer_size; + //The buffer will wrap around if we're at the end. + if ((rb->data+rb->size)==rb->write_ptr) { + rb->write_ptr=rb->data; + } + return pdTRUE; +} + //Retrieves a pointer to the data of the next item, or NULL if this is not possible. //This function by itself is not threadsafe, always call from within a muxed section. -static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length) +//Because we always return one item, this function ignores the wanted_length variable. +static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length) { uint8_t *ret; configASSERT(((int)rb->read_ptr&3)==0); @@ -210,10 +292,48 @@ static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length) return ret; } +//Retrieves a pointer to the data in the buffer, or NULL if this is not possible. +//This function by itself is not threadsafe, always call from within a muxed section. +//This function honours the wanted_length and will never return more data than this. +static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length) +{ + uint8_t *ret; + if (rb->read_ptr != rb->free_ptr) { + //This type of ringbuff does not support multiple outstanding buffers. + return NULL; + } + if (rb->read_ptr == rb->write_ptr) { + //No data available. + return NULL; + } + ret=rb->read_ptr; + if (rb->read_ptr > rb->write_ptr) { + //Available data wraps around. Give data until the end of the buffer. + *length=rb->size-(rb->read_ptr - rb->data); + if (wanted_length != 0 && *length > wanted_length) { + *length=wanted_length; + rb->read_ptr+=wanted_length; + } else { + rb->read_ptr=rb->data; + } + } else { + //Return data up to write pointer. + *length=rb->write_ptr -rb->read_ptr; + if (wanted_length != 0 && *length > wanted_length) { + *length=wanted_length; + rb->read_ptr+=wanted_length; + } else { + rb->read_ptr=rb->write_ptr; + } + } + return ret; +} + + //Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. -static void returnItemToRingbuf(ringbuf_t *rb, void *item) { +static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -249,6 +369,17 @@ static void returnItemToRingbuf(ringbuf_t *rb, void *item) { } +//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer +//can be increase. +//This function by itself is not threadsafe, always call from within a muxed section. +static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) { + uint8_t *data=(uint8_t*)item; + configASSERT(data >= rb->data); + configASSERT(data < rb->data+rb->size); + //Free the read memory. + rb->free_ptr=rb->read_ptr; +} + void xRingbufferPrintInfo(RingbufHandle_t ringbuf) { ringbuf_t *rb=(ringbuf_t *)ringbuf; @@ -259,7 +390,7 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) -RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items) +RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) { ringbuf_t *rb = malloc(sizeof(ringbuf_t)); if (rb==NULL) goto err; @@ -273,9 +404,35 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_item rb->free_space_sem = xSemaphoreCreateBinary(); rb->items_buffered_sem = xSemaphoreCreateBinary(); rb->flags=0; - if (allow_split_items) rb->flags|=flag_allowsplit; + if (type==RINGBUF_TYPE_ALLOWSPLIT) { + rb->flags|=flag_allowsplit; + rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit; + rb->getItemFromRingbufImpl=getItemFromRingbufDefault; + rb->returnItemToRingbufImpl=returnItemToRingbufDefault; + //Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead. + rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4; + } else if (type==RINGBUF_TYPE_BYTEBUF) { + rb->flags|=flag_bytebuf; + rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf; + rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf; + rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf; + //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one. + rb->maxItemSize=rb->size-1; + } else if (type==RINGBUF_TYPE_NOSPLIT) { + rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit; + rb->getItemFromRingbufImpl=getItemFromRingbufDefault; + rb->returnItemToRingbufImpl=returnItemToRingbufDefault; + //Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free + //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill + //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill + //with the real item. (item size being header+data) + rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4; + } else { + configASSERT(0); + } if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err; vPortCPUInitializeMutex(&rb->mux); + return (RingbufHandle_t)rb; err: @@ -303,18 +460,7 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf) { ringbuf_t *rb=(ringbuf_t *)ringbuf; configASSERT(rb); - //In both cases, we return 4 bytes less than what we actually can have. If the ringbuffer is - //indeed entirely filled, read_ptr==free_ptr, which throws off the free space calculation. - if (rb->flags & flag_allowsplit) { - //Worst case, we need to split an item into two, which means two headers of overhead. - return rb->size-(sizeof(buf_entry_hdr_t)*2)-4; - } else { - //Worst case, we have the write ptr in such a position that we are lacking four bytes of free - //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill - //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill - //with the real item. (item size being header+data) - return (rb->size/2)-sizeof(buf_entry_hdr_t)-4; - } + return rb->maxItemSize; } BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait) @@ -352,7 +498,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry //everything if this is the case. Otherwise, we can write and are done. - done=copyItemToRingbuf(rb, data, dataSize); + done=rb->copyItemToRingbufImpl(rb, data, dataSize); portEXIT_CRITICAL(&rb->mux); } xSemaphoreGive(rb->items_buffered_sem); @@ -371,7 +517,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da //Does not fit in the remaining space in the ringbuffer. write_succeeded=pdFALSE; } else { - write_succeeded = copyItemToRingbuf(rb, data, dataSize); + write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize); } portEXIT_CRITICAL_ISR(&rb->mux); if (write_succeeded) { @@ -381,7 +527,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da } -void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) +static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; uint8_t *itemData; @@ -398,7 +544,7 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t } //Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there. portENTER_CRITICAL(&rb->mux); - itemData=getItemFromRingbuf(rb, item_size); + itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size); portEXIT_CRITICAL(&rb->mux); if (itemData) { //We managed to get an item. @@ -408,6 +554,11 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t return (void*)itemData; } +void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) +{ + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); +} + void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) { @@ -415,7 +566,28 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) uint8_t *itemData; configASSERT(rb); portENTER_CRITICAL_ISR(&rb->mux); - itemData=getItemFromRingbuf(rb, item_size); + itemData=rb->getItemFromRingbufImpl(rb, item_size, 0); + portEXIT_CRITICAL_ISR(&rb->mux); + return (void*)itemData; +} + +void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { + ringbuf_t *rb=(ringbuf_t *)ringbuf; + if (wanted_size == 0) return NULL; + configASSERT(rb); + configASSERT(rb->flags & flag_bytebuf); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); +} + +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size) +{ + ringbuf_t *rb=(ringbuf_t *)ringbuf; + uint8_t *itemData; + if (wanted_size == 0) return NULL; + configASSERT(rb); + configASSERT(rb->flags & flag_bytebuf); + portENTER_CRITICAL_ISR(&rb->mux); + itemData=rb->getItemFromRingbufImpl(rb, item_size, 0); portEXIT_CRITICAL_ISR(&rb->mux); return (void*)itemData; } @@ -425,7 +597,7 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item) { ringbuf_t *rb=(ringbuf_t *)ringbuf; portENTER_CRITICAL_ISR(&rb->mux); - returnItemToRingbuf(rb, item); + rb->returnItemToRingbufImpl(rb, item); portEXIT_CRITICAL_ISR(&rb->mux); xSemaphoreGive(rb->free_space_sem); } @@ -435,7 +607,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_ { ringbuf_t *rb=(ringbuf_t *)ringbuf; portENTER_CRITICAL_ISR(&rb->mux); - returnItemToRingbuf(rb, item); + rb->returnItemToRingbufImpl(rb, item); portEXIT_CRITICAL_ISR(&rb->mux); xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken); } From d7ea61734b2474460fde113833a382a81ba88fb6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 24 Oct 2016 21:25:48 +0800 Subject: [PATCH 009/131] Tabs -> spaces --- components/freertos/ringbuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 77eb362843..ce5504596a 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -556,7 +556,7 @@ static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_siz void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) { - return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); } @@ -573,17 +573,17 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; - if (wanted_size == 0) return NULL; + if (wanted_size == 0) return NULL; configASSERT(rb); configASSERT(rb->flags & flag_bytebuf); - return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); } void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; uint8_t *itemData; - if (wanted_size == 0) return NULL; + if (wanted_size == 0) return NULL; configASSERT(rb); configASSERT(rb->flags & flag_bytebuf); portENTER_CRITICAL_ISR(&rb->mux); From 8d6b78232728d7a987a2cae690fdd820cf8cf36a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 1 Nov 2016 09:22:09 +0800 Subject: [PATCH 010/131] Modify UART driver: 1. Add a ring buffer for UART TX. If the buffer size is set to zero, driver will not use a buffer. But we need a task to send data from buffer to fifo. I tried directly copy data in ISR, but the code looked too long for ISR. 2. Modify the format in uart.h --- components/driver/include/driver/uart.h | 712 +++++++++++++----------- components/driver/uart.c | 421 ++++++++------ 2 files changed, 633 insertions(+), 500 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index dafdf54a84..3ea77b2d02 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -24,175 +24,168 @@ extern "C" { #include "soc/uart_struct.h" #include "esp_err.h" #include "driver/periph_ctrl.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" #include extern const char* UART_TAG; #define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) +#define UART_BITRATE_MAX 5000000 typedef enum { - UART_DATA_5_BITS = 0x0, //word length: 5bits - UART_DATA_6_BITS = 0x1, //word length: 6bits - UART_DATA_7_BITS = 0x2, //word length: 7bits - UART_DATA_8_BITS = 0x3, //word length: 8bits + UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ + UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ + UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/ + UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/ UART_DATA_MAX_BITS = 0X4, } uart_word_length_t; typedef enum { - UART_STOP_BITS_1 = 0x1, //stop bit: 1bit - UART_STOP_BITS_1_5 = 0x2, //stop bit: 1.5bits - UART_STOP_BITS_2 = 0x3, //stop bit: 2bits + UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ + UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/ + UART_STOP_BITS_2 = 0x3, /*!< stop bit: 2bits*/ UART_STOP_BITS_MAX = 0x4, } uart_stop_bits_t; typedef enum { - UART_NUM_0 = 0x0, //base address 0x3ff40000 - UART_NUM_1 = 0x1, //base address 0x3ff50000 - UART_NUM_2 = 0x2, //base address 0x3ff6E000 + UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/ + UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/ + UART_NUM_2 = 0x2, /*!< UART base address 0x3ff6E000*/ UART_NUM_MAX, } uart_port_t; typedef enum { - UART_PARITY_DISABLE = 0x0, //Disable UART parity - UART_PARITY_EVEN = 0x10, //Enable UART even parity - UART_PARITY_ODD = 0x11 //Enable UART odd parity + UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ + UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/ + UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/ } uart_parity_t; typedef enum { - UART_BITRATE_300 = 300, - UART_BITRATE_600 = 600, - UART_BITRATE_1200 = 1200, - UART_BITRATE_2400 = 2400, - UART_BITRATE_4800 = 4800, - UART_BITRATE_9600 = 9600, - UART_BITRATE_19200 = 19200, - UART_BITRATE_38400 = 38400, - UART_BITRATE_57600 = 57600, - UART_BITRATE_74880 = 74880, - UART_BITRATE_115200 = 115200, - UART_BITRATE_230400 = 230400, - UART_BITRATE_460800 = 460800, - UART_BITRATE_921600 = 921600, - UART_BITRATE_1843200 = 1843200, - UART_BITRATE_3686400 = 3686400, - UART_BITRATE_MAX = 5000000, -} uart_baudrate_t; //you can set any rate you need in this range - -typedef enum { - UART_HW_FLOWCTRL_DISABLE = 0x0, //disable hardware flow control - UART_HW_FLOWCTRL_RTS = 0x1, //enable RX hardware flow control (rts) - UART_HW_FLOWCTRL_CTS = 0x2, //enable TX hardware flow control (cts) - UART_HW_FLOWCTRL_CTS_RTS = 0x3, //enable hardware flow control + UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/ + UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/ + UART_HW_FLOWCTRL_CTS = 0x2, /*!< enable TX hardware flow control (cts)*/ + UART_HW_FLOWCTRL_CTS_RTS = 0x3, /*!< enable hardware flow control*/ UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; typedef enum { - UART_INVERSE_DISABLE = 0x0, //Disable UART wire output inverse - UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, //UART RXD input inverse - UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, //UART CTS input inverse - UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, //UART TXD output inverse - UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, //UART RTS output inverse + UART_INVERSE_DISABLE = 0x0, /*!< Disable UART wire output inverse*/ + UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, /*!< UART RXD input inverse*/ + UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, /*!< UART CTS input inverse*/ + UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, /*!< UART TXD output inverse*/ + UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, /*!< UART RTS output inverse*/ } uart_inverse_t; typedef struct { - uart_baudrate_t baud_rate; //UART baudrate - uart_word_length_t data_bits; //UART byte size - uart_parity_t parity; //UART parity mode - uart_stop_bits_t stop_bits; //UART stop bits - uart_hw_flowcontrol_t flow_ctrl; //UART hw flow control mode(cts/rts) - uint8_t rx_flow_ctrl_thresh ; //UART hw RTS threshold + int baud_rate; /*!< UART baudrate*/ + uart_word_length_t data_bits; /*!< UART byte size*/ + uart_parity_t parity; /*!< UART parity mode*/ + uart_stop_bits_t stop_bits; /*!< UART stop bits*/ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART hw flow control mode(cts/rts)*/ + uint8_t rx_flow_ctrl_thresh ; /*!< UART hw RTS threshold*/ } uart_config_t; typedef struct { - uint32_t intr_enable_mask; //UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator - uint8_t rx_timeout_thresh; //UART timeout interrupt threshold(unit: time of sending one byte) - uint8_t txfifo_empty_intr_thresh; //UART TX empty interrupt threshold. - uint8_t rxfifo_full_thresh; //UART RX full interrupt threshold. + uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/ + uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/ + uint8_t txfifo_empty_intr_thresh; /*!< UART TX empty interrupt threshold.*/ + uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/ } uart_intr_config_t; - typedef enum { - UART_DATA, - UART_BREAK, - UART_BUFFER_FULL, - UART_FIFO_OVF, - UART_FRAME_ERR, - UART_PARITY_ERR, - UART_EVENT_MAX, + UART_DATA, /*!< UART data event*/ + UART_BREAK, /*!< UART break event*/ + UART_BUFFER_FULL, /*!< UART RX buffer full event*/ + UART_FIFO_OVF, /*!< UART FIFO overflow event*/ + UART_FRAME_ERR, /*!< UART RX frame error event*/ + UART_PARITY_ERR, /*!< UART RX parity event*/ + UART_DATA_BREAK, /*!< UART TX data and break event*/ + UART_EVENT_MAX, /*!< UART event max index*/ } uart_event_type_t; typedef struct { uart_event_type_t type; union { struct { + int brk_len; size_t size; + uint8_t data[]; } data; - }; } uart_event_t; - - /** * @brief Set UART data bits. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_word_length_t data_bit : UART data bits + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param data_bit UART data bits * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); /** * @brief Get UART data bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*data_bit) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*data_bit) */ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** * @brief Set UART stop bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_stop_bits_t bit_num : UART stop bits + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param bit_num UART stop bits * - * @return ESP_OK : Success - * ESP_FAIL: Fail + * @return + * - ESP_OK Success + * - ESP_FAIL Fail */ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); /** * @brief Set UART stop bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*stop_bit) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*stop_bit) */ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** * @brief Set UART parity. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_parity_t parity_mode : the enum of uart parity configuration + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param parity_mode the enum of uart parity configuration * - * @return null + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success */ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); /** * @brief Get UART parity mode. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*parity_mode) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*parity_mode) * */ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); @@ -200,32 +193,37 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** * @brief Set UART baud rate. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t baud_rate : UART baud-rate, we can choose one from uart_baudrate_t, or set a value. + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param baud_rate UART baud-rate. * - * @return null + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success */ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); /** * @brief Get UART bit-rate. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*baudrate) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*baudrate) * */ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t inverse_mask : Choose the wires that need to be inversed - * (Should be chosen from uart_inverse_t, combine with OR-OPERATION) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param inverse_mask Choose the wires that need to be inversed. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; @@ -233,57 +231,65 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; /** * @brief Set hardware flow control. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_hw_flowcontrol_t flow_ctrl : Hardware flow control mode - * @param uint8_t rx_thresh : Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param flow_ctrl Hardware flow control mode + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); /** * @brief Get hardware flow control mode - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*flow_ctrl) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*flow_ctrl) */ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** * @brief Clear UART interrupt status * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t clr_mask : Bit mask of the status that to be cleared. - * enable_mask should be chosen from the fields of register UART_INT_CLR_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param clr_mask Bit mask of the status that to be cleared. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); /** * @brief Set UART interrupt enable * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t enable_mask : Bit mask of the enable bits. - * enable_mask should be chosen from the fields of register UART_INT_ENA_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param enable_mask Bit mask of the enable bits. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); /** * @brief Clear UART interrupt enable bits * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t disable_mask : Bit mask of the disable bits. - * Disable_mask should be chosen from the fields of register UART_INT_ENA_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param disable_mask Bit mask of the disable bits. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); @@ -291,42 +297,46 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); /** * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_rx_intr(uart_port_t uart_num); /** * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_rx_intr(uart_port_t uart_num); /** * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); /** * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int enable : 1: enable; 0: disable - * @param int thresh : Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param enable 1: enable; 0: disable + * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); @@ -337,29 +347,31 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * We can find the information of INUM and interrupt level in soc.h. * * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * @param void (* fn)(void* ) : Interrupt handler function. - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR" for now. - * @param void * arg : parameter for handler function + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param fn Interrupt handler function. + * @attention + * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. + * @param arg parameter for handler function * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); /** * @brief Set UART pin number * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param tx_io_num : UART TX pin GPIO number - * @param rx_io_num : UART RX pin GPIO number - * @param rts_io_num : UART RTS pin GPIO number - * @param cts_io_num : UART CTS pin GPIO number + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param tx_io_num UART TX pin GPIO number + * @param rx_io_num UART RX pin GPIO number + * @param rts_io_num UART RTS pin GPIO number + * @param cts_io_num UART CTS pin GPIO number * - * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); @@ -367,97 +379,107 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * @brief UART set RTS level (before inverse) * UART rx hardware flow control should not be set. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int level : 1: RTS output low(active) - * 0: RTS output high(block) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param level 1: RTS output low(active); 0: RTS output high(block) * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_rts(uart_port_t uart_num, int level); /** * @brief UART set DTR level (before inverse) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int level : 1: DTR output low - * 0: DTR output high + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param level 1: DTR output low; 0: DTR output high * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); /** * @brief UART parameter configure * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_config_t *uart_config: UART parameter settings + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_config UART parameter settings * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); /** * @brief UART interrupt configure * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_intr_config_t *p_intr_conf: UART interrupt settings + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param p_intr_conf UART interrupt settings * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); /** * @brief Install UART driver. + * * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int buffer_size : UART ring buffer size - * @param int queue_size : UART event queue size/depth. - * @param int uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param rx_buffer_size UART RX ring buffer size + * @param tx_buffer_size UART TX ring buffer size, if set to zero, driver will not use TX buffer and TX task. + * @param queue_size UART event queue size/depth. + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param buf_type UART RX ring_buffer type * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type); /** * @brief Uninstall UART driver. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_driver_delete(uart_port_t uart_num); /** * @brief Wait UART TX FIFO empty * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param ticks_to_wait Timeout, count in RTOS ticks * - * @return ESP_OK : Success - * ESP_FAIL : Parameter error - * ESP_ERR_TIMEOUT: Timeout + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + * - ESP_ERR_TIMEOUT Timeout */ -esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); +esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* buffer : data buffer address - * @param uint32_t len : data length to send + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buffer data buffer address + * @param len data length to send * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); @@ -465,12 +487,13 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * @brief Send data to the UART port from a given buffer and length, * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* src : data buffer address - * @param size_t size : data length to send + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param src data buffer address + * @param size data length to send * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); @@ -478,57 +501,62 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * @brief Send data to the UART port from a given buffer and length, * This function will not return until all the data and the break signal have been sent out. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* src : data buffer address - * @param size_t size : data length to send - * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param src data buffer address + * @param size data length to send + * @param brk_len break signal length (unit: one bit's time@current_baudrate) * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read one char * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait : Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param ticks_to_wait Timeout, count in RTOS ticks * - * @return -1 : Error - * Others : return a char data from UART. + * @return + * - (-1) Error + * - Others return a char data from UART. */ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief UART read bytes from UART buffer * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t* buf : pointer to the buffer. - * @param uint32_t length : data length - * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buf pointer to the buffer. + * @param length data length + * @param ticks_to_wait: Timeout, count in RTOS ticks * - * @return -1 : Error - * Others : return a char data from uart fifo. + * @return + * - (-1) Error + * - Others return a char data from uart fifo. */ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); /** * @brief UART ring buffer flush * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_flush(uart_port_t uart_num); /** * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error, or UART driver not installed. + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error, or UART driver not installed. */ esp_err_t uart_set_print_port(uart_port_t uart_no); @@ -536,141 +564,155 @@ esp_err_t uart_set_print_port(uart_port_t uart_no); * @brief Get the current serial port for ets_printf function * * - * @return current print port: 0: UART0; - * 1: UART1; - * 2: UART2; + * @return current print port(0: UART0; 1: UART1; 2: UART2) */ -int uart_get_print_port(); +int uart_get_print_port(void); /***************************EXAMPLE********************************** * * * ----------------EXAMPLE OF UART SETTING --------------------- - * //1. Setup UART - * #include "freertos/queue.h" - * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h - * //a. Set UART parameter - * int uart_num = 0; //uart port number + * @code{c} + * //1. Setup UART + * #include "freertos/queue.h" + * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h + * //a. Set UART parameter + * int uart_num = 0; //uart port number + * uart_config_t uart_config = { + * .baud_rate = UART_BITRATE_115200, //baudrate + * .data_bits = UART_DATA_8_BITS, //data bit mode + * .parity = UART_PARITY_DISABLE, //parity mode + * .stop_bits = UART_STOP_BITS_1, //stop bit mode + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) + * .rx_flow_ctrl_thresh = 120, //flow control threshold + * }; + * uart_param_config(uart_num, &uart_config); + * //b1. Setup UART driver(with UART queue) + * QueueHandle_t uart_queue; + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //b2. Setup UART driver(without UART queue) + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + *@endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //2. Set UART pin + * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //3. Read data from UART. + * uint8_t data[128]; + * int length = 0; + * length = uart_read_bytes(uart_num, data, sizeof(data), 100); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //4. Write data to UART. + * char* test_str = "This is a test string.\n" + * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //5. Write data to UART, end with a break signal. + * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //6. an example of echo test with hardware flow control on UART1 + * void uart_loop_back_test() + * { + * int uart_num = 1; * uart_config_t uart_config = { - * .baud_rate = UART_BITRATE_115200, //baudrate - * .data_bits = UART_DATA_8_BITS, //data bit mode - * .parity = UART_PARITY_DISABLE, //parity mode - * .stop_bits = UART_STOP_BITS_1, //stop bit mode - * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) - * .rx_flow_ctrl_thresh = 120, //flow control threshold + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); - * //b1. Setup UART driver(with UART queue) - * QueueHandle_t uart_queue; - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example - * //b2. Setup UART driver(without UART queue) - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example - * - *-----------------------------------------------------------------------------* - * //2. Set UART pin - * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. - * - *-----------------------------------------------------------------------------* - * //3. Read data from UART. - * uint8_t data[128]; - * int length = 0; - * length = uart_read_bytes(uart_num, data, sizeof(data), 100); - * - *-----------------------------------------------------------------------------* - * //4. Write data to UART. - * char* test_str = "This is a test string.\n" - * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); - * - *-----------------------------------------------------------------------------* - * //5. Write data to UART, end with a break signal. - * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); - * - *-----------------------------------------------------------------------------* - * - * //6. an example of echo test with hardware flow control on UART1 - * void uart_loop_back_test() - * { - * int uart_num = 1; - * uart_config_t uart_config = { - * .baud_rate = 115200, - * .data_bits = UART_DATA_8_BITS, - * .parity = UART_PARITY_DISABLE, - * .stop_bits = UART_STOP_BITS_1, - * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, - * .rx_flow_ctrl_thresh = 122, - * }; - * uart_param_config(uart_num, &uart_config); //Config UART1 parameters - * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level - * uart_driver_install(uart_num, 1024 * 2, 10, 17, NULL); //Install UART driver( We don't need an event queue here) - * uint8_t data[1000]; - * while(1) { - * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART - * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART - * } + * uart_param_config(uart_num, &uart_config); //Config UART1 parameters + * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * //Install UART driver( We don't need an event queue here) + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); + * uint8_t data[1000]; + * while(1) { + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART + * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART * } - * + * } + * @endcode *-----------------------------------------------------------------------------* - * //7. An example of using UART event queue on UART0. - * - * #include "freertos/queue.h" - * QueueHandle_t uart0_queue; //A queue to handle UART event. - * void uart_task(void *pvParameters) - * { - * int uart_num = (int)pvParameters; - * uart_event_t event; - * uint8_t dtmp[1000]; - * for(;;) { - * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { //Waiting for UART event. - * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); - * switch(event.type) { - * case UART_DATA: //Event of UART receving data - * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); - * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(UART_TAG, "uart read: %d\n", len); - * break; - * case UART_FIFO_OVF: //Event of HW FIFO overflow detected - * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); - * break; - * case UART_BUFFER_FULL: //Event of UART ring buffer full - * ESP_LOGI(UART_TAG, "ring buffer full\n"); - * break; - * case UART_BREAK: - * ESP_LOGI(UART_TAG, "uart rx break\n"); //Event of UART RX break detected - * break; - * case UART_PARITY_ERR: //Event of UART parity check error - * ESP_LOGI(UART_TAG, "uart parity error\n"); - * break; - * case UART_FRAME_ERR: //Event of UART frame error - * ESP_LOGI(UART_TAG, "uart frame error\n"); - * break; - * default: //Others - * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); - * break; - * } - * } - * } - * vTaskDelete(NULL); - * } - * - * void uart_queue_test() - * { - * int uart_num = 0; - * uart_config_t uart_config = { - * .baud_rate = 115200, - * .data_bits = UART_DATA_8_BITS, - * .parity = UART_PARITY_DISABLE, - * .stop_bits = UART_STOP_BITS_1, - * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - * .rx_flow_ctrl_thresh = 122, - * }; - * uart_param_config(uart_num, &uart_config); //Set UART parameters - * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level - * uart_driver_install(uart_num, 1024 * 2, 10, 17, &uart0_queue); //Install UART driver, and get the queue. - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * @code{c} + * //7. An example of using UART event queue on UART0. + * #include "freertos/queue.h" + * //A queue to handle UART event. + * QueueHandle_t uart0_queue; + * void uart_task(void *pvParameters) + * { + * int uart_num = (int)pvParameters; + * uart_event_t event; + * uint8_t dtmp[1000]; + * for(;;) { + * //Waiting for UART event. + * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { + * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * switch(event.type) { + * //Event of UART receving data + * case UART_DATA: + * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); + * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * break; + * //Event of HW FIFO overflow detected + * case UART_FIFO_OVF: + * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * break; + * //Event of UART ring buffer full + * case UART_BUFFER_FULL: + * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * break; + * //Event of UART RX break detected + * case UART_BREAK: + * ESP_LOGI(UART_TAG, "uart rx break\n"); + * break; + * //Event of UART parity check error + * case UART_PARITY_ERR: + * ESP_LOGI(UART_TAG, "uart parity error\n"); + * break; + * //Event of UART frame error + * case UART_FRAME_ERR: + * ESP_LOGI(UART_TAG, "uart frame error\n"); + * break; + * //Others + * default: + * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * break; + * } * } + * } + * vTaskDelete(NULL); + * } * + * void uart_queue_test() + * { + * int uart_num = 0; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Set UART parameters + * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * //Install UART driver, and get the queue. + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * } + * @endcode * ***************************END OF EXAMPLE**********************************/ diff --git a/components/driver/uart.c b/components/driver/uart.c index 29e4522d6d..eeb2c64208 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -30,13 +30,15 @@ #include "soc/uart_struct.h" const char* UART_TAG = "UART"; -#define UART_CHECK(a, str) if (!(a)) { \ +#define UART_CHECK(a, str) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return ESP_FAIL; \ } -#define DEFAULT_EMPTY_THRESH 10 -#define DEFAULT_FULL_THRESH 120 -#define DEFAULT_TOUT_THRESH 10 +#define UART_EMPTY_THRESH_DEFAULT (10) +#define UART_FULL_THRESH_DEFAULT (120) +#define UART_TOUT_THRESH_DEFAULT (10) +#define UART_TX_TASK_DEPTH_DEFAULT (256*2+64) +#define UART_TX_TASK_PRIO_DEFAULT (10) #define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) #define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -46,13 +48,19 @@ typedef struct { uart_port_t uart_num; SemaphoreHandle_t tx_fifo_sem; SemaphoreHandle_t tx_mutex; + SemaphoreHandle_t tx_buffer_mutex; SemaphoreHandle_t tx_done_sem; SemaphoreHandle_t tx_brk_sem; - SemaphoreHandle_t rx_sem; + SemaphoreHandle_t rx_mux; QueueHandle_t xQueueUart; int queue_size; int intr_num; - RingbufHandle_t ring_buffer; + int rx_buf_size; + ringbuf_type_t rx_buf_type; + RingbufHandle_t rx_ring_buf; + int tx_buf_size; + RingbufHandle_t tx_ring_buf; + TaskHandle_t tx_task_handle; bool buffer_full_flg; bool tx_waiting; int cur_remain; @@ -66,20 +74,6 @@ static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; -//Fill UART tx_fifo and return a number, -//This function by itself is not thread-safe, always call from within a muxed section. -static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) -{ - uint8_t i = 0; - uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; - uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); - uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); - for(i = 0; i < copy_cnt; i++) { - WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); - } - return copy_cnt; -} - esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); @@ -315,11 +309,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { -// UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); -// UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); -// UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); -// UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); -// UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { @@ -443,7 +437,6 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_obj_t *p_uart = (uart_obj_t*) param; uint8_t uart_num = p_uart->uart_num; uart_dev_t* uart_reg = UART[uart_num]; - uint8_t buf_idx = 0; uint32_t uart_intr_status = UART[uart_num]->int_st.val; static int rx_fifo_len = 0; @@ -478,7 +471,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; uart_event.data.size = rx_fifo_len; - if(pdFALSE == xRingbufferSendFromISR(p_uart->ring_buffer, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; uart_reg->int_ena.rxfifo_tout = 0; @@ -544,108 +537,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } /**************************************************************/ -esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - if(p_uart_obj[uart_num] == NULL) { - ESP_INTR_DISABLE(uart_intr_num); - p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); - if(p_uart_obj[uart_num] == NULL) { - ESP_LOGE(UART_TAG, "UART driver malloc error\n"); - return ESP_FAIL; - } - p_uart_obj[uart_num]->uart_num = uart_num; - p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); - p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); - - p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->rx_sem = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->intr_num = uart_intr_num; - p_uart_obj[uart_num]->queue_size = queue_size; - - if(uart_queue) { - p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); - *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; - ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); - } else { - p_uart_obj[uart_num]->xQueueUart = NULL; - } - p_uart_obj[uart_num]->buffer_full_flg = false; - p_uart_obj[uart_num]->tx_waiting = false; - p_uart_obj[uart_num]->rd_ptr = NULL; - p_uart_obj[uart_num]->cur_remain = 0; - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->ring_buffer = xRingbufferCreate(buffer_size, 0); - } else { - ESP_LOGE(UART_TAG, "UART driver already installed\n"); - return ESP_FAIL; - } - uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); - uart_intr_config_t uart_intr = { - .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M - | UART_RXFIFO_TOUT_INT_ENA_M - | UART_FRM_ERR_INT_ENA_M - | UART_RXFIFO_OVF_INT_ENA_M - | UART_BRK_DET_INT_ENA_M, - .rxfifo_full_thresh = DEFAULT_FULL_THRESH, - .rx_timeout_thresh = DEFAULT_TOUT_THRESH, - .txfifo_empty_intr_thresh = DEFAULT_EMPTY_THRESH - }; - uart_intr_config(uart_num, &uart_intr); - ESP_INTR_ENABLE(uart_intr_num); - return ESP_OK; -} - -//Make sure no other tasks are still using UART before you call this function -esp_err_t uart_driver_delete(uart_port_t uart_num) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - if(p_uart_obj[uart_num] == NULL) { - ESP_LOGI(UART_TAG, "ALREADY NULL\n"); - return ESP_OK; - } - ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); - uart_disable_rx_intr(uart_num); - uart_disable_tx_intr(uart_num); - uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); - - if(p_uart_obj[uart_num]->tx_fifo_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); - p_uart_obj[uart_num]->tx_fifo_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_done_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); - p_uart_obj[uart_num]->tx_done_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_brk_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); - p_uart_obj[uart_num]->tx_brk_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); - p_uart_obj[uart_num]->tx_mutex = NULL; - } - if(p_uart_obj[uart_num]->rx_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->rx_sem); - p_uart_obj[uart_num]->rx_sem = NULL; - } - if(p_uart_obj[uart_num]->xQueueUart) { - vQueueDelete(p_uart_obj[uart_num]->xQueueUart); - p_uart_obj[uart_num]->xQueueUart = NULL; - } - if(p_uart_obj[uart_num]->ring_buffer) { - vRingbufferDelete(p_uart_obj[uart_num]->ring_buffer); - p_uart_obj[uart_num]->ring_buffer = NULL; - } - free(p_uart_obj[uart_num]); - p_uart_obj[uart_num] = NULL; - return ESP_OK; -} - -esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait) +esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); @@ -657,17 +549,9 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait return ESP_ERR_TIMEOUT; } ticks_to_wait = ticks_end - xTaskGetTickCount(); - //take 1st tx_done_sem - res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); - if(res == pdFALSE) { - ESP_LOGE(UART_TAG, "take uart done sem error, should not get here.\n"); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); - return ESP_ERR_TIMEOUT; - } + xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0); ticks_to_wait = ticks_end - xTaskGetTickCount(); if(UART[uart_num]->status.txfifo_cnt == 0) { - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_OK; } @@ -676,11 +560,9 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); if(res == pdFALSE) { uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_ERR_TIMEOUT; } - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_OK; } @@ -696,6 +578,20 @@ static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) return ESP_OK; } +//Fill UART tx_fifo and return a number, +//This function by itself is not thread-safe, always call from within a muxed section. +static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +{ + uint8_t i = 0; + uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; + uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); + uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); + for(i = 0; i < copy_cnt; i++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); + } + return copy_cnt; +} + int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); @@ -727,7 +623,7 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool size_t sent = uart_fill_fifo(uart_num, (char*) src, size); if(sent < size) { p_uart_obj[uart_num]->tx_waiting = true; - uart_enable_tx_intr(uart_num, 1, DEFAULT_EMPTY_THRESH); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); } size -= sent; src += sent; @@ -742,12 +638,55 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return original_size; } +static void uart_tx_task(void* arg) +{ + uart_obj_t* p_uart = (uart_obj_t*) arg; + size_t size; + uart_event_t evt; + for(;;) { + char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); + if(data == NULL) { + continue; + } + memcpy(&evt, data, sizeof(evt)); + if(evt.type == UART_DATA) { + uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); + } else if(evt.type == UART_DATA_BREAK) { + uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); + } + vRingbufferReturnItem(p_uart->tx_ring_buf, data); + } + vTaskDelete(NULL); +} + int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); UART_CHECK(src, "buffer null"); - return uart_tx_all(uart_num, src, size, 0, 0); + if(p_uart_obj[uart_num]->tx_buf_size > 0) { + if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { + uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); + if(evt == NULL) { + ESP_LOGE(UART_TAG, "UART EVT MALLOC ERROR\n"); + return -1; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + evt->type = UART_DATA; + evt->data.size = size; + memcpy(evt->data.data, src, size); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); + free(evt); + evt = NULL; + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + return size; + } else { + ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); + return uart_tx_all(uart_num, src, size, 0, 0); + } + } else { + return uart_tx_all(uart_num, src, size, 0, 0); + } } int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) @@ -757,7 +696,29 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((size > 0), "uart size error"); UART_CHECK((src), "uart data null"); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); - return uart_tx_all(uart_num, src, size, 1, brk_len); + if(p_uart_obj[uart_num]->tx_buf_size > 0) { + if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { + uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); + if(evt == NULL) { + return -1; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + evt->type = UART_DATA_BREAK; + evt->data.size = size; + evt->data.brk_len = brk_len; + memcpy(evt->data.data, src, size); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); + free(evt); + evt = NULL; + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + return size; + } else { + ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); + return uart_tx_all(uart_num, src, size, 1, brk_len); + } + } else { + return uart_tx_all(uart_num, src, size, 1, brk_len); + } } int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) @@ -768,18 +729,18 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) size_t size; int val; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } if(p_uart_obj[uart_num]->cur_remain == 0) { ticks_to_wait = ticks_end - xTaskGetTickCount(); - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return -1; } } @@ -787,18 +748,18 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) p_uart_obj[uart_num]->rd_ptr++; p_uart_obj[uart_num]->cur_remain--; if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } } - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return val; } @@ -807,23 +768,22 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((buf), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - uint8_t* data = NULL; size_t size; size_t copy_len = 0; int len_tmp; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } while(length) { if(p_uart_obj[uart_num]->cur_remain == 0) { - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } } @@ -838,11 +798,11 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp copy_len += len_tmp; length -= len_tmp; if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); @@ -850,7 +810,7 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } } } - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -858,30 +818,38 @@ esp_err_t uart_flush(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - uart_obj_t* p_uart = p_uart_obj[uart_num]; uint8_t* data; size_t size; //rx sem protect the ring buffer read related functions - xSemaphoreTake(p_uart->rx_sem, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); while(true) { if(p_uart->head_ptr) { - vRingbufferReturnItem(p_uart->ring_buffer, p_uart->head_ptr); + vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->head_ptr); p_uart->rd_ptr = NULL; p_uart->cur_remain = 0; p_uart->head_ptr = NULL; } - data = (uint8_t*) xRingbufferReceive(p_uart->ring_buffer, &size, (portTickType) 0); + data = (uint8_t*) xRingbufferReceive(p_uart->rx_ring_buf, &size, (portTickType) 0); if(data == NULL) { break; } - vRingbufferReturnItem(p_uart->ring_buffer, data); + vRingbufferReturnItem(p_uart->rx_ring_buf, data); } p_uart->rd_ptr = NULL; p_uart->cur_remain = 0; p_uart->head_ptr = NULL; - xSemaphoreGive(p_uart->rx_sem); - uart_wait_tx_fifo_empty(uart_num, portMAX_DELAY); + xSemaphoreGive(p_uart->rx_mux); + xSemaphoreTake(p_uart->tx_mutex, (portTickType)portMAX_DELAY); + do { + data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->rx_ring_buf, data); + } while(1); + xSemaphoreGive(p_uart->tx_mutex); + uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; } @@ -915,7 +883,6 @@ esp_err_t uart_set_print_port(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); - s_uart_print_nport = uart_num; switch(s_uart_print_nport) { case UART_NUM_0: @@ -940,3 +907,127 @@ int uart_get_print_port() return s_uart_print_nport; } +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); + if(p_uart_obj[uart_num] == NULL) { + ESP_INTR_DISABLE(uart_intr_num); + p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + return ESP_FAIL; + } + p_uart_obj[uart_num]->uart_num = uart_num; + p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); + p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); + p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->tx_buffer_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->intr_num = uart_intr_num; + p_uart_obj[uart_num]->queue_size = queue_size; + + if(uart_queue) { + p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); + *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + } else { + p_uart_obj[uart_num]->xQueueUart = NULL; + } + p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting = false; + p_uart_obj[uart_num]->rd_ptr = NULL; + p_uart_obj[uart_num]->cur_remain = 0; + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; + p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); + if(tx_buffer_size > 0) { + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; + xTaskCreate(uart_tx_task, "uart_tx_task", UART_TX_TASK_DEPTH_DEFAULT, (void*)p_uart_obj[uart_num], UART_TX_TASK_PRIO_DEFAULT, &p_uart_obj[uart_num]->tx_task_handle); + + } else { + p_uart_obj[uart_num]->tx_ring_buf = NULL; + p_uart_obj[uart_num]->tx_buf_size = 0; + p_uart_obj[uart_num]->tx_task_handle = NULL; + } + } else { + ESP_LOGE(UART_TAG, "UART driver already installed\n"); + return ESP_FAIL; + } + uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_intr_config_t uart_intr = { + .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M + | UART_RXFIFO_TOUT_INT_ENA_M + | UART_FRM_ERR_INT_ENA_M + | UART_RXFIFO_OVF_INT_ENA_M + | UART_BRK_DET_INT_ENA_M, + .rxfifo_full_thresh = UART_FULL_THRESH_DEFAULT, + .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, + .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT + }; + uart_intr_config(uart_num, &uart_intr); + ESP_INTR_ENABLE(uart_intr_num); + return ESP_OK; +} + +//Make sure no other tasks are still using UART before you call this function +esp_err_t uart_driver_delete(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + return ESP_OK; + } + ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + uart_disable_rx_intr(uart_num); + uart_disable_tx_intr(uart_num); + uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); + + if(p_uart_obj[uart_num]->tx_task_handle) { + vTaskDelete(p_uart_obj[uart_num]->tx_task_handle); + p_uart_obj[uart_num]->tx_task_handle = NULL; + } + if(p_uart_obj[uart_num]->tx_fifo_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_fifo_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_done_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_done_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_brk_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); + p_uart_obj[uart_num]->tx_brk_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); + p_uart_obj[uart_num]->tx_mutex = NULL; + } + if(p_uart_obj[uart_num]->tx_buffer_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mutex); + p_uart_obj[uart_num]->tx_buffer_mutex = NULL; + } + if(p_uart_obj[uart_num]->rx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); + p_uart_obj[uart_num]->rx_mux = NULL; + } + if(p_uart_obj[uart_num]->xQueueUart) { + vQueueDelete(p_uart_obj[uart_num]->xQueueUart); + p_uart_obj[uart_num]->xQueueUart = NULL; + } + if(p_uart_obj[uart_num]->rx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->rx_ring_buf); + p_uart_obj[uart_num]->rx_ring_buf = NULL; + } + if(p_uart_obj[uart_num]->tx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->tx_ring_buf); + p_uart_obj[uart_num]->tx_ring_buf = NULL; + } + + free(p_uart_obj[uart_num]); + p_uart_obj[uart_num] = NULL; + return ESP_OK; +} From f10cc7dc8eb09bcdd3075b7f8ef9303e3dc3da28 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Oct 2016 14:55:35 +1100 Subject: [PATCH 011/131] bootloader: Refactor secure boot digest generation --- .../bootloader/src/main/bootloader_config.h | 4 +- .../bootloader/src/main/bootloader_start.c | 15 +- components/bootloader/src/main/secure_boot.c | 135 +++++++++++++----- 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 8a837693c2..1655280dc7 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -36,7 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 - #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 #define PART_SUBTYPE_OTA_FLAG 0x10 @@ -66,8 +65,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ); uint32_t get_bin_len(uint32_t pos); bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot(void); - +bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 5b1e152070..a4b27702c4 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -256,6 +256,7 @@ void bootloader_main() bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); @@ -329,16 +330,20 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); + if(fhdr.secure_boot_flag == 0x01) { - /* protect the 2nd_boot */ - if(false == secure_boot()){ - ESP_LOGE(TAG, "secure boot failed"); - return; + /* Generate secure digest from this bootloader to protect future + modifications */ + if (secure_boot_generate_bootloader_digest() == false){ + ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } } if(fhdr.encrypt_flag == 0x01) { - /* encrypt flash */ + /* encrypt flash */ if (false == flash_encrypt(&bs)) { ESP_LOGE(TAG, "flash encrypt failed"); return; diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 3dfdbd1412..9247f2cbd9 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -40,7 +40,7 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t bin_len){ SpiFlashOpResult spiRet; uint16_t i; uint32_t buf[32]; @@ -87,41 +87,112 @@ bool secure_boot_generate(uint32_t bin_len){ return true; } +/* Burn values written to the efuse write registers */ +static inline void burn_efuses() +{ + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +} /** - * @function : secure_boot - * @description: protect boot code in flash + * @function : secure_boot_generate_bootloader_digest * - * @inputs: bool + * @description: Called if the secure boot flag is set on the + * bootloader image in flash. If secure boot is not yet enabled for + * bootloader, this will generate the secure boot digest and enable + * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * @return true if secure boot is enabled (either was already enabled, + * or is freshly enabled as a result of calling this function.) */ -bool secure_boot(void){ - uint32_t bin_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) - { - ESP_LOGD(TAG, "already secure boot !"); - return true; - } else { - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "boot len is error"); - return false; - } - if (false == secure_boot_generate(bin_len)){ - ESP_LOGE(TAG, "secure boot generate failed"); - return false; - } - } +bool secure_boot_generate_bootloader_digest(void) { + uint32_t bin_len = 0; + if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + { + ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); + return true; + } - REG_SET_BIT(EFUSE_BLK0_WDATA6_REG, EFUSE_RD_ABS_DONE_0); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn abstract_done_0"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - ESP_LOGI(TAG, "read EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - return true; + boot_cache_redirect( 0, 64*1024); + bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); + if (bin_len == 0) { + ESP_LOGE(TAG, "Invalid bootloader image length zero."); + return false; + } + if (bin_len > 0x100000) { + ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + return false; + } + uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); + bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2; + bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2; + if (efuse_key_read_protected == false + && efuse_key_write_protected == false + && REG_READ(EFUSE_BLK2_RDATA0_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA1_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA2_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA3_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA4_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA5_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new secure boot key..."); + /* reuse the secure boot IV generation function to generate + the key, as this generator uses the hardware RNG. */ + uint32_t buf[32]; + ets_secure_boot_rd_iv(buf); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + burn_efuses(); + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); + burn_efuses(); + efuse_key_read_protected = true; + efuse_key_write_protected = true; + + } else { + ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); + } + + ESP_LOGI(TAG, "Generating secure boot digest..."); + if (false == secure_boot_generate(bin_len)){ + ESP_LOGE(TAG, "secure boot generation failed"); + return false; + } + ESP_LOGI(TAG, "Digest generation complete."); + + if (!efuse_key_read_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); + return false; + } + if (!efuse_key_write_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); + return false; + } + + ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); + REG_WRITE(EFUSE_BLK0_WDATA6_REG, + EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + burn_efuses(); + uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG); + ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); + if (after & EFUSE_RD_ABS_DONE_0) { + ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); + return true; + } else { + ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); + return false; + } } From cf732bb663a4c8690107e354d958042ef3ea5bfb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 31 Oct 2016 16:27:26 +1100 Subject: [PATCH 012/131] esp32: efuse_reg add bit values for read & write disable flags --- components/esp32/include/soc/efuse_reg.h | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/esp32/include/soc/efuse_reg.h b/components/esp32/include/soc/efuse_reg.h index a0f0a07da6..291e3984ee 100644 --- a/components/esp32/include/soc/efuse_reg.h +++ b/components/esp32/include/soc/efuse_reg.h @@ -29,6 +29,16 @@ #define EFUSE_RD_EFUSE_RD_DIS_M ((EFUSE_RD_EFUSE_RD_DIS_V)<<(EFUSE_RD_EFUSE_RD_DIS_S)) #define EFUSE_RD_EFUSE_RD_DIS_V 0xF #define EFUSE_RD_EFUSE_RD_DIS_S 16 + +/* Read disable bits for efuse blocks 1-3 */ +#define EFUSE_RD_DIS_BLK1 (1<<16) +#define EFUSE_RD_DIS_BLK2 (1<<17) +#define EFUSE_RD_DIS_BLK3 (1<<18) +/* Read disable FLASH_CRYPT_CONFIG, CODING_SCHEME & KEY_STATUS + in efuse block 0 +*/ +#define EFUSE_RD_DIS_BLK0_PARTIAL (1<<19) + /* EFUSE_RD_EFUSE_WR_DIS : RO ;bitpos:[15:0] ;default: 16'b0 ; */ /*description: read for efuse_wr_disable*/ #define EFUSE_RD_EFUSE_WR_DIS 0x0000FFFF @@ -36,6 +46,22 @@ #define EFUSE_RD_EFUSE_WR_DIS_V 0xFFFF #define EFUSE_RD_EFUSE_WR_DIS_S 0 +/* Write disable bits */ +#define EFUSE_WR_DIS_RD_DIS (1<<0) /*< disable writing read disable reg */ +#define EFUSE_WR_DIS_WR_DIS (1<<1) /*< disable writing write disable reg */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CNT (1<<2) +#define EFUSE_WR_DIS_MAC_SPI_CONFIG_HD (1<<3) /*< disable writing MAC & SPI config hd efuses */ +#define EFUSE_WR_DIS_XPD_SDIO (1<<5) /*< disable writing SDIO config efuses */ +#define EFUSE_WR_DIS_SPI_PAD_CONFIG (1<<6) /*< disable writing SPI_PAD_CONFIG efuses */ +#define EFUSE_WR_DIS_BLK1 (1<<7) /*< disable writing BLK1 efuses */ +#define EFUSE_WR_DIS_BLK2 (1<<8) /*< disable writing BLK2 efuses */ +#define EFUSE_WR_DIS_BLK3 (1<<9) /*< disable writing BLK3 efuses */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CODING_SCHEME (1<<10) /*< disable writing FLASH_CRYPT_CONFIG and CODING_SCHEME efuses */ +#define EFUSE_WR_DIS_ABS_DONE_0 (1<<12) /*< disable writing ABS_DONE_0 efuse */ +#define EFUSE_WR_DIS_ABS_DONE_1 (1<<13) /*< disable writing ABS_DONE_1 efuse */ +#define EFUSE_WR_DIS_JTAG_DISABLE (1<<14) /*< disable writing JTAG_DISABLE efuse */ +#define EFUSE_WR_DIS_CONSOLE_DL_DISABLE (1<<15) /*< disable writing CONSOLE_DEBUG_DISABLE, DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT and DISABLE_DL_CACHE efuses */ + #define EFUSE_BLK0_RDATA1_REG (DR_REG_EFUSE_BASE + 0x004) /* EFUSE_RD_WIFI_MAC_CRC_LOW : RO ;bitpos:[31:0] ;default: 32'b0 ; */ /*description: read for low 32bit WIFI_MAC_Address*/ From 4ba1b73eba78893d2117a94a6e65c6ad63e6705a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 10:50:16 +1100 Subject: [PATCH 013/131] build system: Add espefuse/espsecure support for secure boot --- components/bootloader/Kconfig.projbuild | 67 ++++++++++++++++++-- components/bootloader/Makefile.projbuild | 57 ++++++++++++++++- components/bootloader/src/main/secure_boot.c | 2 + components/esptool_py/Makefile.projbuild | 17 ++++- components/esptool_py/esptool | 2 +- make/project.mk | 23 +++++-- 6 files changed, 147 insertions(+), 21 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 394bcc1bd1..d6736b33db 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -20,12 +20,65 @@ config LOG_BOOTLOADER_LEVEL_VERBOSE endchoice config LOG_BOOTLOADER_LEVEL - int - default 0 if LOG_BOOTLOADER_LEVEL_NONE - default 1 if LOG_BOOTLOADER_LEVEL_ERROR - default 2 if LOG_BOOTLOADER_LEVEL_WARN - default 3 if LOG_BOOTLOADER_LEVEL_INFO - default 4 if LOG_BOOTLOADER_LEVEL_DEBUG - default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + int + default 0 if LOG_BOOTLOADER_LEVEL_NONE + default 1 if LOG_BOOTLOADER_LEVEL_ERROR + default 2 if LOG_BOOTLOADER_LEVEL_WARN + default 3 if LOG_BOOTLOADER_LEVEL_INFO + default 4 if LOG_BOOTLOADER_LEVEL_DEBUG + default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + +choice SECURE_BOOTLOADER + bool "Secure bootloader" + default SECURE_BOOTLOADER_DISABLED + help + Build a bootloader with the secure boot flag enabled. + + Secure bootloader can be one-time-flash (chip will only ever + boot that particular bootloader), or a digest key can be used + to allow the secure bootloader to be re-flashed with + modifications. Secure boot also permanently disables JTAG. + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_DISABLED + bool "Disabled" + +config SECURE_BOOTLOADER_ONE_TIME_FLASH + bool "One-time flash" + help + On first boot, the bootloader will generate a key which is not readable externally or by software. A digest is generated from the bootloader image itself. This digest will be verified on each subsequent boot. + + Enabling this option means that the bootloader cannot be changed after the first time it is booted. + +config SECURE_BOOTLOADER_REFLASHABLE + bool "Reflashable" + help + Generate the bootloader digest key on the computer instead of inside + the chip. Allows the secure bootloader to be re-flashed by using the + same key. + + This option is less secure than one-time flash, because a leak of the digest key allows reflashing of any device that uses it. + +endchoice + +config SECURE_BOOTLOADER_KEY_FILE + string "Secure bootloader digest key file" + depends on SECURE_BOOTLOADER_REFLASHABLE + default secure_boot_key.bin + help + Path to the key file for a reflashable secure boot digest. + File must contain 32 randomly generated bytes. + + Path is evaluated relative to the project directory. + + You can generate a new key by running the following command: + espsecure.py generate_key secure_boot_key.bin + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_ENABLED + bool + default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE endmenu diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 50c95f9fec..8edabdb6ef 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,6 +15,8 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig +SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) + # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ @@ -31,18 +33,67 @@ bootloader-clean: clean: bootloader-clean +ifdef CONFIG_SECURE_BOOTLOADER_DISABLED +# If secure boot disabled, bootloader flashing is integrated +# with 'make flash' and no warnings are printed. + bootloader: $(BOOTLOADER_BIN) + @echo "$(SEPARATOR)" @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" -all_binaries: $(BOOTLOADER_BIN) - ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) -# bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash +else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH +# One time flashing requires user to run esptool.py command themselves, +# and warning is printed about inability to reflash. + +bootloader: $(BOOTLOADER_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built. One-time flash command is:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" + +else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE +# Reflashable secure bootloader (recommended for testing only) +# generates a digest binary (bootloader + digest) and prints +# instructions for reflashing. User must run commands manually. + +BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin + +bootloader: $(BOOTLOADER_DIGEST_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built and secure digest generated. First time flash command is:" + @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOT_KEYFILE)" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "To reflash the bootloader after initial flash:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" + @echo $(SEPARATOR) + @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." + @echo "* NOT RECOMMENDED TO RE-FLASH the same bootloader-reflash-digest.bin to multiple production devices" + +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) + @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOT_KEYFILE) -o $@ $< + +$(SECURE_BOOT_KEYFILE): + @echo $(SEPARATOR) + @echo "Need to generate secure boot digest key first. Run following command:" + @echo "$(ESPSECUREPY) generate_key $@" + @echo "Keep key file safe after generating." + @exit 1 + +else +$(error Bad sdkconfig - one of CONFIG_SECURE_BOOTLOADER_DISABLED, CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH, CONFIG_SECURE_BOOTLOADER_REFLASHABLE must be set) +endif + +all_binaries: $(BOOTLOADER_BIN) + # synchronise the project level config to the bootloader's # config $(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 9247f2cbd9..070fbf24d3 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -148,7 +148,9 @@ bool secure_boot_generate_bootloader_digest(void) { /* reuse the secure boot IV generation function to generate the key, as this generator uses the hardware RNG. */ uint32_t buf[32]; + ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); + ets_secure_boot_finish(); for (int i = 0; i < 8; i++) { ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 69c01e1e7f..dfb9dfc4c2 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -11,23 +11,34 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # two commands that can be used from other components # to invoke esptool.py (with or without serial port args) # -# NB: esptool.py lives in the sdk/bin directory not the component directory ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +# Supporting esptool command line tools +ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py +ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py + ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) -# the no-stub argument is temporary until esptool.py fully supports compressed uploads +ESPTOOL_ELF2IMAGE_OPTIONS := + +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ESPTOOL_ELF2IMAGE_OPTIONS += "--set-secure-boot-flag" +endif + ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" +endif $(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 5c6962e894..95dae1651e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 +Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 diff --git a/make/project.mk b/make/project.mk index 67e2e92bdb..17fb83e0d8 100644 --- a/make/project.mk +++ b/make/project.mk @@ -11,13 +11,13 @@ # .PHONY: build-components menuconfig defconfig all build clean all_binaries -all: all_binaries # other components will add dependencies to 'all_binaries' - @echo "To flash all build output, run 'make flash' or:" - @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) - -# (the reason all_binaries is used instead of 'all' is so that the flash target -# can build everything without triggering the per-component "to flash..." -# output targets.) +all: all_binaries +# see below for recipe of 'all' target +# +# # other components will add dependencies to 'all_binaries'. The +# reason all_binaries is used instead of 'all' is so that the flash +# target can build everything without triggering the per-component "to +# flash..." output targets.) help: @echo "Welcome to Espressif IDF build system. Some useful make targets:" @@ -135,6 +135,15 @@ export PROJECT_PATH #Include functionality common to both project & component -include $(IDF_PATH)/make/common.mk +all: +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" + @echo "To flash app & partition table, run 'make flash' or:" +else + @echo "To flash all build output, run 'make flash' or:" +endif + @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + # Set default LDFLAGS LDFLAGS ?= -nostdlib \ From 04beb8baba3c087622558851285ccd038f8b59d0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 17:41:27 +1100 Subject: [PATCH 014/131] Add documentation for bootloader secure boot stage --- components/bootloader/Kconfig.projbuild | 4 +- components/bootloader/Makefile.projbuild | 2 +- docs/security/secure-boot.rst | 146 +++++++++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/security/secure-boot.rst diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index d6736b33db..55c2eebd14 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -63,11 +63,11 @@ config SECURE_BOOTLOADER_REFLASHABLE endchoice config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader digest key file" + string "Secure bootloader key file" depends on SECURE_BOOTLOADER_REFLASHABLE default secure_boot_key.bin help - Path to the key file for a reflashable secure boot digest. + Path to the key file for a reflashable secure bootloader digest. File must contain 32 randomly generated bytes. Path is evaluated relative to the project directory. diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 8edabdb6ef..b9dab3e093 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -75,7 +75,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" @echo $(SEPARATOR) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." - @echo "* NOT RECOMMENDED TO RE-FLASH the same bootloader-reflash-digest.bin to multiple production devices" + @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst new file mode 100644 index 0000000000..5e33367b49 --- /dev/null +++ b/docs/security/secure-boot.rst @@ -0,0 +1,146 @@ +Secure Boot +=========== + +Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. + +Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. + +Background +---------- + +- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. + +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. + +- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. + +Secure Boot Process Overview +---------------------------- + +This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: + +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". + +2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. + +2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. + +3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: + - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - The secure digest is flashed at offset 0x0 in the flash. + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader also disables JTAG via efuse. + +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. + +5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. + +Keys +---- + +The following keys are used by the secure boot process: + +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. + +- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + +- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. + +How To Enable Secure Boot +------------------------- + +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) + +2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. + +3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration + +4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. + + **IMPORTANT** The key pair is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + +5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. + +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. + +7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. + + *NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled. + +8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occured due to the build configuration. + +**IMPORTANT** Secure boot won't ever be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. + +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). + +Re-Flashable Software Bootloader +-------------------------------- + +The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. + +However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. + +1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". + +2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. + +3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. + + **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. + +4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). + +5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. + + +Technical Details +----------------- + +The following sections contain low-level descriptions of various technical functions: + +Hardware Secure Boot Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Secure Boot support hardware can perform three basic operations: + +1. Generate a random sequence of bytes from a hardware random number generator. + +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. + +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. + +Secure Bootloader Digest Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Starting with an "image" of binary data as input, this algorithm generates a digest as output. + +For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. + +Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. + +1. Prefix the image with a 128 byte randomly generated IV. +2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +3. For each 16 byte block of the input image: + - Reverse the byte order of the block (^) + - Use the AES256 algorithm in ECB mode to encrypt the block. + - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Append to the overall ciphertext output. +4. Byte-swap each 4 byte word of the ciphertext (^) +5. Calculate SHA-512 of the ciphertext. + +Output digest is 192 bytes of data: The 128 byte IV, followed by the 64 byte SHA-512 digest. + +Image Signing Algorithm +~~~~~~~~~~~~~~~~~~~~~~~ + +Deterministic ECDSA as specified by `RFC6979`. + +Curve is TBD. +Key format is TBD. +Output format is TBD. + + +.. _esp-idf boot process: ../boot-process.rst +.. _RFC6979: https://tools.ietf.org/html/rfc6979 From aceb6517c05e807c7f9ad27d31e9b8aa12568172 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 10:41:58 +1100 Subject: [PATCH 015/131] Refactor existing bootloader common functionality into bootloader_support component --- components/bootloader/Makefile.projbuild | 6 +- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_config.h | 5 - .../bootloader/src/main/bootloader_start.c | 276 ++++++++---------- .../bootloader/src/main/flash_encrypt.c | 158 +++++----- components/bootloader/src/main/secure_boot.c | 72 +++-- components/bootloader_support/README.rst | 9 + components/bootloader_support/component.mk | 11 + .../include/esp_image_format.h | 133 +++++++++ .../include/esp_secureboot.h | 51 ++++ .../include_priv/bootloader_flash.h | 69 +++++ .../bootloader_support/src/bootloader_flash.c | 122 ++++++++ .../bootloader_support/src/esp_image_format.c | 155 ++++++++++ .../bootloader_support/src/secureboot.c | 7 + .../esp32/include/esp_flash_data_types.h | 48 --- components/esp32/include/rom/secure_boot.h | 2 +- components/log/include/esp_log.h | 4 + make/project.mk | 2 +- 18 files changed, 813 insertions(+), 319 deletions(-) create mode 100644 components/bootloader_support/README.rst create mode 100755 components/bootloader_support/component.mk create mode 100644 components/bootloader_support/include/esp_image_format.h create mode 100644 components/bootloader_support/include/esp_secureboot.h create mode 100644 components/bootloader_support/include_priv/bootloader_flash.h create mode 100644 components/bootloader_support/src/bootloader_flash.c create mode 100644 components/bootloader_support/src/esp_image_format.c create mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index b9dab3e093..3799e913c8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) @@ -89,7 +89,9 @@ $(SECURE_BOOT_KEYFILE): @exit 1 else -$(error Bad sdkconfig - one of CONFIG_SECURE_BOOTLOADER_DISABLED, CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH, CONFIG_SECURE_BOOTLOADER_REFLASHABLE must be set) +bootloader: + @echo "Invalid bootloader target: bad sdkconfig?" + @exit 1 endif all_binaries: $(BOOTLOADER_BIN) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index add9c15d61..278e0e9afb 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 1655280dc7..0823581824 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -25,8 +25,6 @@ extern "C" #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 -#define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) -#define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -61,9 +59,6 @@ typedef struct { uint32_t selected_subtype; } bootloader_state_t; -void boot_cache_redirect( uint32_t pos, size_t size ); -uint32_t get_bin_len(uint32_t pos); - bool flash_encrypt(bootloader_state_t *bs); bool secure_boot_generate_bootloader_digest(void); diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a4b27702c4..3bc2696a4b 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -33,6 +33,8 @@ #include "soc/timer_group_reg.h" #include "sdkconfig.h" +#include "esp_image_format.h" +#include "bootloader_flash.h" #include "bootloader_config.h" @@ -49,7 +51,7 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const esp_partition_pos_t *app_node); +static void unpack_load_app(const esp_partition_pos_t *app_node); void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, @@ -94,53 +96,6 @@ void IRAM_ATTR call_start_cpu0() bootloader_main(); } -/** - * @function : get_bin_len - * @description: get bin's length - * - * @inputs: pos bin locate address in flash - * @return: uint32 length of bin,if bin MAGIC error return 0 - */ - -uint32_t get_bin_len(uint32_t pos) -{ - uint32_t len = 8 + 16; - uint8_t i; - ESP_LOGD(TAG, "pos %d %x",pos,*(uint8_t *)pos); - if(0xE9 != *(uint8_t *)pos) { - return 0; - } - for (i = 0; i < *(uint8_t *)(pos + 1); i++) { - len += *(uint32_t *)(pos + len + 4) + 8; - } - if (len % 16 != 0) { - len = (len / 16 + 1) * 16; - } else { - len += 16; - } - ESP_LOGD(TAG, "bin length = %d", len); - return len; -} - -/** - * @function : boot_cache_redirect - * @description: Configure several pages in flash map so that `size` bytes - * starting at `pos` are mapped to 0x3f400000. - * This sets up mapping only for PRO CPU. - * - * @inputs: pos address in flash - * size size of the area to map, in bytes - */ -void boot_cache_redirect( uint32_t pos, size_t size ) -{ - uint32_t pos_aligned = pos & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; - Cache_Read_Disable( 0 ); - Cache_Flush( 0 ); - ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", pos_aligned, count ); - cache_flash_mmu_set( 0, 0, 0x3f400000, pos_aligned, 64, count ); - Cache_Read_Enable( 0 ); -} /** * @function : load_partition_table @@ -154,79 +109,86 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - esp_partition_info_t partition; - uint32_t end = addr + 0x1000; - int index = 0; + const esp_partition_info_t *partitions; + const int PARTITION_TABLE_SIZE = 0x1000; + const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); - while (addr < end) { - ESP_LOGD(TAG, "load partition table entry from %x(%08x)", addr, MEM_CACHE(addr)); - memcpy(&partition, MEM_CACHE(addr), sizeof(partition)); - ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); + partitions = bootloader_mmap(addr, 0x1000); + if (!partitions) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + return false; + } + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + + for(int i = 0; i < MAX_PARTITIONS; i++) { + const esp_partition_info_t *partition = &partitions[i]; + ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); + ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ - switch(partition.type) { - case PART_TYPE_APP: /* app partition */ - switch(partition.subtype) { - case PART_SUBTYPE_FACTORY: /* factory binary */ - bs->factory = partition.pos; - partition_usage = "factory app"; - break; - case PART_SUBTYPE_TEST: /* test binary */ - bs->test = partition.pos; - partition_usage = "test app"; - break; - default: - /* OTA binary */ - if ((partition.subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { - bs->ota[partition.subtype & PART_SUBTYPE_OTA_MASK] = partition.pos; - ++bs->app_count; - partition_usage = "OTA app"; - } - else { - partition_usage = "Unknown app"; - } - break; + if (partition->magic != ESP_PARTITION_MAGIC) { + /* invalid partition definition indicates end-of-table */ + break; + } + + /* valid partition table */ + switch(partition->type) { + case PART_TYPE_APP: /* app partition */ + switch(partition->subtype) { + case PART_SUBTYPE_FACTORY: /* factory binary */ + bs->factory = partition->pos; + partition_usage = "factory app"; + break; + case PART_SUBTYPE_TEST: /* test binary */ + bs->test = partition->pos; + partition_usage = "test app"; + break; + default: + /* OTA binary */ + if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { + bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos; + ++bs->app_count; + partition_usage = "OTA app"; } - break; /* PART_TYPE_APP */ - case PART_TYPE_DATA: /* data partition */ - switch(partition.subtype) { - case PART_SUBTYPE_DATA_OTA: /* ota data */ - bs->ota_info = partition.pos; - partition_usage = "OTA data"; - break; - case PART_SUBTYPE_DATA_RF: - partition_usage = "RF data"; - break; - case PART_SUBTYPE_DATA_WIFI: - partition_usage = "WiFi data"; - break; - default: - partition_usage = "Unknown data"; - break; + else { + partition_usage = "Unknown app"; } - break; /* PARTITION_USAGE_DATA */ - default: /* other partition type */ break; } - } - /* invalid partition magic number */ - else { - break; /* todo: validate md5 */ + break; /* PART_TYPE_APP */ + case PART_TYPE_DATA: /* data partition */ + switch(partition->subtype) { + case PART_SUBTYPE_DATA_OTA: /* ota data */ + bs->ota_info = partition->pos; + partition_usage = "OTA data"; + break; + case PART_SUBTYPE_DATA_RF: + partition_usage = "RF data"; + break; + case PART_SUBTYPE_DATA_WIFI: + partition_usage = "WiFi data"; + break; + default: + partition_usage = "Unknown data"; + break; + } + break; /* PARTITION_USAGE_DATA */ + default: /* other partition type */ + break; } /* print partition type info */ - ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", index, partition.label, partition_usage, - partition.type, partition.subtype, - partition.pos.offset, partition.pos.size); - index++; - addr += sizeof(partition); + ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage, + partition->type, partition->subtype, + partition->pos.offset, partition->pos.size); } + bootloader_unmap(partitions); + ESP_LOGI(TAG,"End of partition table"); return true; } @@ -254,8 +216,9 @@ void bootloader_main() esp_image_header_t fhdr; bootloader_state_t bs; - SpiFlashOpResult spiRet1,spiRet2; + SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + const esp_ota_select_entry_t *ota_select_map; memset(&bs, 0, sizeof(bs)); @@ -264,10 +227,11 @@ void bootloader_main() REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); - /*register first sector in drom0 page 0 */ - boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); + if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) { + ESP_LOGE(TAG, "failed to load bootloader header!"); + return; + } print_flash_info(&fhdr); @@ -282,9 +246,19 @@ void bootloader_main() if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); - boot_cache_redirect(bs.ota_info.offset, bs.ota_info.size ); - memcpy(&sa,MEM_CACHE(bs.ota_info.offset & 0x0000ffff),sizeof(sa)); - memcpy(&sb,MEM_CACHE((bs.ota_info.offset + 0x1000)&0x0000ffff) ,sizeof(sb)); + if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) { + ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t)); + return; + } + ota_select_map = bootloader_mmap(bs.ota_info.offset, bs.ota_info.size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size); + return; + } + sa = ota_select_map[0]; + sb = ota_select_map[1]; + bootloader_unmap(ota_select_map); + if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash load_part_pos = bs.ota[0]; @@ -355,14 +329,14 @@ void bootloader_main() } -void unpack_load_app(const esp_partition_pos_t* partition) +static void unpack_load_app(const esp_partition_pos_t* partition) { - boot_cache_redirect(partition->offset, partition->size); - - uint32_t pos = 0; esp_image_header_t image_header; - memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); - pos += sizeof(image_header); + + if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { + ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); + return; + } uint32_t drom_addr = 0; uint32_t drom_load_addr = 0; @@ -376,19 +350,22 @@ void unpack_load_app(const esp_partition_pos_t* partition) bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, - image_header.blocks, + image_header.segment_count, image_header.spi_mode, image_header.spi_size, (unsigned)image_header.entry_addr); - for (uint32_t section_index = 0; - section_index < image_header.blocks; - ++section_index) { - esp_image_section_header_t section_header = {0}; - memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); - pos += sizeof(section_header); + for (int segment = 0; segment < image_header.segment_count; segment++) { + esp_image_segment_header_t segment_header; + uint32_t data_offs; + if(esp_image_load_segment_header(segment, partition->offset, + &image_header, &segment_header, + &data_offs) != ESP_OK) { + ESP_LOGE(TAG, "failed to load segment header #%d", segment); + return; + } - const uint32_t address = section_header.load_addr; + const uint32_t address = segment_header.load_addr; bool load = true; bool map = false; if (address == 0x00000000) { // padding, ignore block @@ -400,47 +377,50 @@ void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", pos, - section_header.load_addr); - drom_addr = partition->offset + pos - sizeof(section_header); - drom_load_addr = section_header.load_addr; - drom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + drom_addr = data_offs; + drom_load_addr = segment_header.load_addr; + drom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", pos, - section_header.load_addr); - irom_addr = partition->offset + pos - sizeof(section_header); - irom_load_addr = section_header.load_addr; - irom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + irom_addr = data_offs; + irom_load_addr = segment_header.load_addr; + irom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); load = false; } - ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos, - section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":""); + ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", segment, data_offs - sizeof(esp_image_segment_header_t), + segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":""); - if (!load) { - pos += section_header.data_len; - continue; + if (load) { + const void *data = bootloader_mmap(data_offs, segment_header.data_len); + if(!data) { + ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed", + data_offs, segment_header.data_len); + return; + } + memcpy((void *)segment_header.load_addr, data, segment_header.data_len); + bootloader_unmap(data); } - - memcpy((void*) section_header.load_addr, MEM_CACHE(pos), section_header.data_len); - pos += section_header.data_len; } - + set_cache_and_start_app(drom_addr, drom_load_addr, drom_size, @@ -526,7 +506,7 @@ void print_flash_info(const esp_image_header_t* phdr) #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) ESP_LOGD(TAG, "magic %02x", phdr->magic ); - ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "segments %02x", phdr->segment_count ); ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 2fb57a987d..2b1479097d 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -17,6 +17,7 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_log.h" +#include "esp_err.h" #include "rom/cache.h" #include "rom/ets_sys.h" @@ -30,6 +31,7 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "esp_image_format.h" static const char* TAG = "flash_encrypt"; @@ -90,103 +92,97 @@ bool flash_encrypt_write(uint32_t pos, uint32_t len) Cache_Read_Enable(0); return true; } + + /** * @function : flash_encrypt * @description: encrypt 2nd boot ,partition table ,factory bin ��test bin (if use)��ota bin * ��OTA info sector. * * @inputs: bs bootloader state structure used to save the data - * + * * @return: return true, if the encrypt flash success - * + * */ bool flash_encrypt(bootloader_state_t *bs) { - uint32_t bin_len = 0; - uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); - uint8_t count = bitcount(flash_crypt_cnt); - int i = 0; - ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); + esp_err_t err; + uint32_t image_len = 0; + uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); + uint8_t count = bitcount(flash_crypt_cnt); + ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); - if ((count % 2) == 0) { - boot_cache_redirect( 0, 64*1024); - /* encrypt iv and abstruct */ - if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt iv and abstract error"); - return false; - } + if ((count % 2) == 0) { + /* encrypt iv and abstract */ + if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt iv and abstract error"); + return false; + } + + /* encrypt bootloader image */ + err = esp_image_basic_verify(0x1000, &image_len); + if(err == ESP_OK && image_len != 0) { + if (false == flash_encrypt_write(0x1000, image_len)) { + ESP_LOGE(TAG, "encrypt 2nd boot error"); + return false; + } + } else { + ESP_LOGE(TAG, "2nd boot len error"); + return false; + } - /* encrypt write boot bin*/ - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if(bin_len != 0) { - if (false == flash_encrypt_write(0x1000, bin_len)) { - ESP_LOGE(TAG, "encrypt 2nd boot error"); - return false; - } - } else { - ESP_LOGE(TAG, "2nd boot len error"); - return false; - } /* encrypt partition table */ - if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt partition table error"); - return false; - } + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt partition table error"); + return false; + } /* encrypt write factory bin */ - if(bs->factory.offset != 0x00) { - ESP_LOGD(TAG, "have factory bin"); - boot_cache_redirect(bs->factory.offset, bs->factory.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->factory.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->factory.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt factory bin error"); - return false; - } - } - } + if(bs->factory.offset != 0 && bs->factory.size != 0) { + ESP_LOGD(TAG, "have factory bin"); + if (false == flash_encrypt_write(bs->factory.offset, bs->factory.size)) { + ESP_LOGE(TAG, "encrypt factory bin error"); + return false; + } + } + /* encrypt write test bin */ - if(bs->test.offset != 0x00) { - ESP_LOGD(TAG, "have test bin"); - boot_cache_redirect(bs->test.offset, bs->test.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->test.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->test.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt test bin error"); - return false; - } - } - } + if(bs->test.offset != 0 && bs->test.size != 0) { + ESP_LOGD(TAG, "have test bin"); + if (false == flash_encrypt_write(bs->test.offset, bs->test.size)) { + ESP_LOGE(TAG, "encrypt test bin error"); + return false; + } + } + /* encrypt write ota bin */ - for (i = 0;i<16;i++) { - if(bs->ota[i].offset != 0x00) { - ESP_LOGD(TAG, "have ota[%d] bin",i); - boot_cache_redirect(bs->ota[i].offset, bs->ota[i].size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->ota[i].offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->ota[i].offset, bin_len)) { - ESP_LOGE(TAG, "encrypt ota bin error"); - return false; - } - } - } - } + for (int i = 0; i < 16; i++) { + if(bs->ota[i].offset != 0 && bs->ota[i].size != 0) { + ESP_LOGD(TAG, "have ota[%d] bin",i); + if (false == flash_encrypt_write(bs->ota[i].offset, bs->ota[i].size)) { + ESP_LOGE(TAG, "encrypt ota bin error"); + return false; + } + } + } + /* encrypt write ota info bin */ - if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt ota info error"); - return false; - } - REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn flash_crypt_cnt"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - return true; - } else { - ESP_LOGI(TAG, "flash already encrypted."); - return true; - } + if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt ota info error"); + return false; + } + + REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + ESP_LOGW(TAG, "burn flash_crypt_cnt"); + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ + return true; + } else { + ESP_LOGI(TAG, "flash already encrypted."); + return true; + } } diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 070fbf24d3..2b1b8573fc 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -31,6 +31,8 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "bootloader_flash.h" +#include "esp_image_format.h" static const char* TAG = "secure_boot"; @@ -40,43 +42,52 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -static bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t image_len){ SpiFlashOpResult spiRet; - uint16_t i; uint32_t buf[32]; - if (bin_len % 128 != 0) { - bin_len = (bin_len / 128 + 1) * 128; - } + const void *image; + + if (image_len % 128 != 0) { + image_len = (image_len / 128 + 1) * 128; + } ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); ets_secure_boot_hash(NULL); Cache_Read_Disable(0); - /* iv stored in sec 0 */ + /* iv stored in sec 0 */ spiRet = SPIEraseSector(0); if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); return false; } - ESP_LOGD(TAG, "write iv to flash."); Cache_Read_Enable(0); - /* read 4K code image from flash, for test */ - for (i = 0; i < bin_len; i+=128) { - ets_secure_boot_hash((uint32_t *)(0x3f400000 + 0x1000 + i)); + + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + ESP_LOGD(TAG, "write iv to flash."); + spiRet = SPIWrite(0, buf, 128); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, SPI_ERROR_LOG); + return false; } + /* generate digest from image contents */ + image = bootloader_mmap(0x1000, image_len); + if (!image) { + ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); + return false; + } + for (int i = 0; i < image_len; i+=128) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } + bootloader_unmap(image); + ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); ets_secure_boot_finish(); - Cache_Read_Disable(0); - /* write abstract to flash, 0x0080, 64 bytes (512 bits) */ + + ESP_LOGD(TAG, "write abstract to flash."); spiRet = SPIWrite(0x80, buf, 64); if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); @@ -99,9 +110,9 @@ static inline void burn_efuses() } /** - * @function : secure_boot_generate_bootloader_digest + * @brief Enable secure boot if it is not already enabled. * - * @description: Called if the secure boot flag is set on the + * Called if the secure boot flag is set on the * bootloader image in flash. If secure boot is not yet enabled for * bootloader, this will generate the secure boot digest and enable * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. @@ -110,24 +121,21 @@ static inline void burn_efuses() * ROM bootloader does this.) * * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) + * or is freshly enabled as a result of calling this function.) false + * implies an error occured (possibly secure boot is part-enabled.) */ bool secure_boot_generate_bootloader_digest(void) { - uint32_t bin_len = 0; + esp_err_t err; + uint32_t image_len = 0; if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); return true; } - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "Invalid bootloader image length zero."); - return false; - } - if (bin_len > 0x100000) { - ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + err = esp_image_basic_verify(0x1000, &image_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); return false; } @@ -168,7 +176,7 @@ bool secure_boot_generate_bootloader_digest(void) { } ESP_LOGI(TAG, "Generating secure boot digest..."); - if (false == secure_boot_generate(bin_len)){ + if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); return false; } diff --git a/components/bootloader_support/README.rst b/components/bootloader_support/README.rst new file mode 100644 index 0000000000..54ac861c9b --- /dev/null +++ b/components/bootloader_support/README.rst @@ -0,0 +1,9 @@ +Bootloader Support Component +============================ + +Overview +-------- + +"Bootloader support" contains APIs which are used by the bootloader but are also needed for the main app. + +Code in this component needs to be aware of being executed in a bootloader environment (no RTOS available, BOOTLOADER_BUILD macro set) or in an esp-idf app environment (RTOS running, need locking support.) diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk new file mode 100755 index 0000000000..2988fe287e --- /dev/null +++ b/components/bootloader_support/component.mk @@ -0,0 +1,11 @@ +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := include_priv + +ifdef IS_BOOTLOADER_BUILD +# share "private" headers with the bootloader component +COMPONENT_ADD_INCLUDEDIRS += include_priv +endif + +COMPONENT_SRCDIRS := src + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h new file mode 100644 index 0000000000..a8b1739d73 --- /dev/null +++ b/components/bootloader_support/include/esp_image_format.h @@ -0,0 +1,133 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP32_IMAGE_FORMAT_H +#define __ESP32_IMAGE_FORMAT_H + +#include +#include + +#define ESP_ERR_IMAGE_BASE 0x2000 +#define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1) +#define ESP_ERR_IMAGE_INVALID (ESP_ERR_IMAGE_BASE + 2) + +/* Support for app/bootloader image parsing + Can be compiled as part of app or bootloader code. +*/ + +/* SPI flash mode, used in esp_image_header_t */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* SPI flash clock frequency */ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/* Supported SPI flash sizes */ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +#define ESP_IMAGE_HEADER_MAGIC 0xE9 + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t segment_count; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* Header of binary image segment */ +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_segment_header_t; + + +/** + * @brief Read an ESP image header from flash. + * + * @param src_addr Address in flash to load image header. Must be 4 byte aligned. + * @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined. + * + * @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL + * if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header + * appears invalid. + */ +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header); + +/** + * @brief Read the segment header and data offset of a segment in the image. + * + * @param index Index of the segment to load information for. + * @param src_addr Base address in flash of the image. + * @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header(). + * @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined. + * @param[out] segment_data_offset Pointer to the data offset of the segment. + * + * @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid. + */ +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); + + +/** + * @brief Return length of an image in flash. Non-cryptographically validates image integrity in the process. + * + * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result. + * + * Image validation checks: + * - Magic byte + * - No single section longer than 16MB + * - Total image no longer than 16MB + * - 8 bit image checksum is valid + * + * @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned. + * @param[out] length Length of the image, set to a value if the image is valid. Can be null. + * + * @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors. + * + */ +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *length); + + +typedef struct { + uint32_t drom_addr; + uint32_t drom_load_addr; + uint32_t drom_size; + uint32_t irom_addr; + uint32_t irom_load_addr; + uint32_t irom_size; +} esp_image_flash_mapping_t; + +#endif diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secureboot.h new file mode 100644 index 0000000000..b0097df8a6 --- /dev/null +++ b/components/bootloader_support/include/esp_secureboot.h @@ -0,0 +1,51 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP32_SECUREBOOT_H +#define __ESP32_SECUREBOOT_H + +#include +#include + +/* Support functions for secure boot features. + + Can be compiled as part of app or bootloader code. +*/ + +/** @brief Is secure boot currently enabled in hardware? + * + * Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means + * that the ROM bootloader code will only boot a verified secure + * bootloader digest from now on. + * + * @return true if secure boot is enabled. + */ +bool esp_secure_boot_enabled(void); + + +/** @brief Enable secure boot if it isw not already enabled. + * + * @important If this function succeeds, secure boot is permanentl + * enabled on the chip via efuse. + * + * This function is intended to be called from bootloader code. + * + * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow + * secure boot to be enabled cleanly. ESP_OK if secure boot + * is enabled on this chip from now on. + */ +esp_err_t esp_secure_boot_enable(void); + + + +#endif diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h new file mode 100644 index 0000000000..d70ec22d56 --- /dev/null +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -0,0 +1,69 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __BOOTLOADER_FLASH_H +#define __BOOTLOADER_FLASH_H + +#include +#include +#include +#include + +/* Provide a Flash API for bootloader_support code, + that can be used from bootloader or app code. + + This header is available to source code in the bootloader & + bootloader_support components only. +*/ + +/** + * @brief Map a region of flash to data memory + * + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * + * @important In app code, these functions are not thread safe. + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + * + * In esp-idf app, this function maps directly to spi_flash_mmap. + * + * @param offset - Starting flash offset to map to memory. + * @param length - Length of data to map. + * + * @return Pointer to mapped data memory (at src_addr), or NULL + * if an allocation error occured. + */ +const void *bootloader_mmap(uint32_t src_addr, uint32_t size); + + +/** + * @brief Unmap a previously mapped region of flash + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + */ +void bootloader_unmap(const void *mapping); + +/** + * @brief Read data from Flash. + * + * @note Both src and dest have to be 4-byte aligned. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size); + +#endif diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c new file mode 100644 index 0000000000..a50cd157e9 --- /dev/null +++ b/components/bootloader_support/src/bootloader_flash.c @@ -0,0 +1,122 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include +#include +#include /* including in bootloader for error values */ + +#ifndef BOOTLOADER_BUILD +/* Normal app version maps to esp_spi_flash.h operations... + */ +static const char *TAG = "bootloader_mmap"; + +static spi_flash_mmap_memory_t map; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (map) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* existing mapping in use... */ + } + const void *result = NULL; + esp_err_t err = spi_flash_mmap(src_addr, size, SPI_FLASH_MMAP_DATA, &result, &map); + if (err != ESP_OK) { + result = NULL; + } + return result; +} + +void bootloader_unmap(const void *mapping) +{ + if(mapping && map) { + spi_flash_munmap(map); + } + map = 0; +} + +esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size) +{ + return spi_flash_read(src, dest, size); +} + +#else +/* Bootloader version, uses ROM functions only */ +#include +#include + +static const char *TAG = "bootloader_flash"; + +static bool mapped; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (mapped) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* can't map twice */ + } + + uint32_t src_addr_aligned = src_addr & 0xffff0000; + uint32_t count = (size + 0xffff) / 0x10000; + Cache_Read_Disable(0); + Cache_Flush(0); + ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); + cache_flash_mmu_set( 0, 0, 0x3f400000, src_addr_aligned, 64, count ); + Cache_Read_Enable( 0 ); + + mapped = true; + + return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); +} + +void bootloader_unmap(const void *mapping) +{ + if (mapped) { + /* Full MMU reset */ + Cache_Read_Disable(0); + Cache_Flush(0); + mmu_init(0); + mapped = false; + } +} + +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) +{ + if(src_addr & 3) { + ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + return ESP_FAIL; + } + if((intptr_t)dest & 3) { + ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + return ESP_FAIL; + } + + Cache_Read_Disable(0); + Cache_Flush(0); + SpiFlashOpResult r = SPIRead(src_addr, dest, size); + Cache_Read_Enable(0); + + switch(r) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_ERR: + return ESP_ERR_FLASH_OP_FAIL; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + default: + return ESP_FAIL; + } +} + +#endif diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c new file mode 100644 index 0000000000..ad3cd33f13 --- /dev/null +++ b/components/bootloader_support/src/esp_image_format.c @@ -0,0 +1,155 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include +#include +#include + +const static char *TAG = "esp_image"; + +#define SIXTEEN_MB 0x1000000 +#define ESP_ROM_CHECKSUM_INITIAL 0xEF + +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header) +{ + esp_err_t err; + ESP_LOGD(TAG, "reading image header @ 0x%x", src_addr); + + err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t)); + + if (err == ESP_OK) { + if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) { + ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); + err = ESP_ERR_IMAGE_INVALID; + } + if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode); + } + if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed); + } + if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size); + } + } + + if (err != ESP_OK) { + bzero(image_header, sizeof(esp_image_header_t)); + } + return err; +} + +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset) +{ + esp_err_t err = ESP_OK; + uint32_t next_addr = src_addr + sizeof(esp_image_header_t); + + if(index >= image_header->segment_count) { + ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count); + return ESP_ERR_INVALID_ARG; + } + + for(int i = 0; i <= index && err == ESP_OK; i++) { + err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); + if (err == ESP_OK) { + if ((segment_header->data_len & 3) != 0 + || segment_header->data_len >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); + err = ESP_ERR_IMAGE_INVALID; + } + next_addr += sizeof(esp_image_segment_header_t); + *segment_data_offset = next_addr; + next_addr += segment_header->data_len; + } + } + + if (err != ESP_OK) { + *segment_data_offset = 0; + bzero(segment_header, sizeof(esp_image_segment_header_t)); + } + + return err; +} + +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) +{ + esp_err_t err; + uint8_t buf[16]; + uint8_t checksum = ESP_ROM_CHECKSUM_INITIAL; + esp_image_header_t image_header; + esp_image_segment_header_t segment_header = { 0 }; + uint32_t segment_data_offs = 0; + const uint8_t *segment_data; + uint32_t end_addr; + uint32_t length; + + if (p_length != NULL) { + *p_length = 0; + } + + err = esp_image_load_header(src_addr, &image_header); + if (err != ESP_OK) { + return err; + } + + ESP_LOGD(TAG, "reading %d image segments", image_header.segment_count); + + /* Checksum each segment's data */ + for (int i = 0; i < image_header.segment_count; i++) { + err = esp_image_load_segment_header(i, src_addr, &image_header, + &segment_header, &segment_data_offs); + if (err != ESP_OK) { + return err; + } + + segment_data = bootloader_mmap(segment_data_offs, segment_header.data_len); + if (segment_data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", segment_data_offs, segment_header.data_len); + return ESP_FAIL; + } + for(int i = 0; i < segment_header.data_len; i++) { + checksum ^= segment_data[i]; + } + bootloader_unmap(segment_data); + } + + /* End of image, verify checksum */ + end_addr = segment_data_offs + segment_header.data_len; + + if (end_addr < src_addr) { + ESP_LOGE(TAG, "image offset has wrapped"); + return ESP_ERR_IMAGE_INVALID; + } + + length = end_addr - src_addr; + if (length >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid total length 0x%x", length); + return ESP_ERR_IMAGE_INVALID; + } + + /* image padded to next full 16 byte block, with checksum byte at very end */ + length += 15 - (length % 16); + bootloader_flash_read(src_addr + length - 16, buf, 16); + if (checksum != buf[15]) { + ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", + checksum, buf[15]); + return ESP_ERR_IMAGE_INVALID; + } + + if (p_length != NULL) { + *p_length = length; + } + return ESP_OK; +} diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c new file mode 100644 index 0000000000..e55c1f33fa --- /dev/null +++ b/components/bootloader_support/src/secureboot.c @@ -0,0 +1,7 @@ +#include +#include + +#include "esp_log.h" + + + diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index 4bf886c842..ce4acb7bc9 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -24,54 +24,6 @@ extern "C" #define ESP_PARTITION_TABLE_ADDR 0x4000 #define ESP_PARTITION_MAGIC 0x50AA -/* SPI flash mode, used in esp_image_header_t */ -typedef enum { - ESP_IMAGE_SPI_MODE_QIO, - ESP_IMAGE_SPI_MODE_QOUT, - ESP_IMAGE_SPI_MODE_DIO, - ESP_IMAGE_SPI_MODE_DOUT, - ESP_IMAGE_SPI_MODE_FAST_READ, - ESP_IMAGE_SPI_MODE_SLOW_READ -} esp_image_spi_mode_t; - -/* SPI flash clock frequency */ -enum { - ESP_IMAGE_SPI_SPEED_40M, - ESP_IMAGE_SPI_SPEED_26M, - ESP_IMAGE_SPI_SPEED_20M, - ESP_IMAGE_SPI_SPEED_80M = 0xF -} esp_image_spi_freq_t; - -/* Supported SPI flash sizes */ -typedef enum { - ESP_IMAGE_FLASH_SIZE_1MB = 0, - ESP_IMAGE_FLASH_SIZE_2MB, - ESP_IMAGE_FLASH_SIZE_4MB, - ESP_IMAGE_FLASH_SIZE_8MB, - ESP_IMAGE_FLASH_SIZE_16MB, - ESP_IMAGE_FLASH_SIZE_MAX -} esp_image_flash_size_t; - -/* Main header of binary image */ -typedef struct { - uint8_t magic; - uint8_t blocks; - uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ - uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ - uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ - uint32_t entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -} esp_image_header_t; - -/* Header of binary image segment */ -typedef struct { - uint32_t load_addr; - uint32_t data_len; -} esp_image_section_header_t; - - /* OTA selection structure (two copies in the OTA data partition.) Size of 32 bytes is friendly to flash encryption */ typedef struct { diff --git a/components/esp32/include/rom/secure_boot.h b/components/esp32/include/rom/secure_boot.h index cfeda08933..bd4f32ed95 100644 --- a/components/esp32/include/rom/secure_boot.h +++ b/components/esp32/include/rom/secure_boot.h @@ -25,7 +25,7 @@ void ets_secure_boot_start(void); void ets_secure_boot_finish(void); -void ets_secure_boot_hash(uint32_t *buf); +void ets_secure_boot_hash(const uint32_t *buf); void ets_secure_boot_obtain(void); diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 6716f6e10f..f4b9aa2885 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -19,6 +19,10 @@ #include #include "sdkconfig.h" +#ifdef BOOTLOADER_BUILD +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/make/project.mk b/make/project.mk index 17fb83e0d8..7e8dc46d54 100644 --- a/make/project.mk +++ b/make/project.mk @@ -100,7 +100,7 @@ COMPONENT_LDFLAGS := # # Debugging this? Replace $(shell with $(error and you'll see the full command as-run. define GetVariable -$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" ) +$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) IS_BOOTLOADER_BUILD=$(IS_BOOTLOADER_BUILD) | sed -En "s/^$(2)=(.+)/\1/p" ) endef COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \ From 8282c73ac25a3b6e8895719eadab0eec92f1e1b3 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 3 Nov 2016 18:28:36 +0800 Subject: [PATCH 016/131] debug ring buffer error. --- components/driver/include/driver/uart.h | 2 +- components/driver/uart.c | 205 +++++++++++++++++------- components/freertos/ringbuf.c | 29 +++- 3 files changed, 177 insertions(+), 59 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 3ea77b2d02..445d71b685 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -530,7 +530,7 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @param buf pointer to the buffer. * @param length data length - * @param ticks_to_wait: Timeout, count in RTOS ticks + * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return * - (-1) Error diff --git a/components/driver/uart.c b/components/driver/uart.c index eeb2c64208..d6585405f2 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -37,8 +37,6 @@ const char* UART_TAG = "UART"; #define UART_EMPTY_THRESH_DEFAULT (10) #define UART_FULL_THRESH_DEFAULT (120) #define UART_TOUT_THRESH_DEFAULT (10) -#define UART_TX_TASK_DEPTH_DEFAULT (256*2+64) -#define UART_TX_TASK_PRIO_DEFAULT (10) #define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) #define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -60,7 +58,6 @@ typedef struct { RingbufHandle_t rx_ring_buf; int tx_buf_size; RingbufHandle_t tx_ring_buf; - TaskHandle_t tx_task_handle; bool buffer_full_flg; bool tx_waiting; int cur_remain; @@ -439,8 +436,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_dev_t* uart_reg = UART[uart_num]; uint8_t buf_idx = 0; uint32_t uart_intr_status = UART[uart_num]->int_st.val; - static int rx_fifo_len = 0; + int rx_fifo_len = 0; uart_event_t uart_event; + + static uint8_t * tx_ptr; + static uart_event_t* tx_head; + static int tx_len_tot = 0; + static int brk_flg = 0; + static int tx_brk_len = 0; + static int wait_brk = 0; + + portBASE_TYPE HPTaskAwoken = 0; while(uart_intr_status != 0x0) { buf_idx = 0; @@ -450,14 +456,99 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.txfifo_empty = 0; uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + if(wait_brk) { + return; + } if(p_uart->tx_waiting == true) { p_uart->tx_waiting = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } + else { + int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; + bool en_tx_flg = false; + if(tx_len_tot == 0) { + size_t size; +// ets_printf("dbg1,tot=0,get 1st head\n"); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + if(tx_head) { //enable empty intr +// tx_ptr = (uint8_t*)tx_head + sizeof(uart_event_t); + tx_ptr = NULL; +// en_tx_flg = true; + tx_len_tot = tx_head->data.size; + if(tx_head->type == UART_DATA_BREAK) { + tx_len_tot = tx_head->data.size; + brk_flg = 1; + tx_brk_len = tx_head->data.brk_len; + } +// ets_printf("ret1,tot: %d\n", tx_len_tot); + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + } + else { + return; + } + } + if(tx_ptr == NULL) { + size_t size; +// ets_printf("dbg2, tx ptr null, get 2nd tx ptr\n"); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + if(tx_ptr) { + tx_head = (void*) tx_ptr; +// ets_printf("get size: %d ; h size: %d\n", size, tx_len_tot); + en_tx_flg = true; + } else { + return; + } + } +// else + if(tx_len_tot > 0 && tx_ptr) { //tx + int send_len = tx_len_tot > tx_fifo_rem ? tx_fifo_rem : tx_len_tot; + for(buf_idx = 0; buf_idx < send_len; buf_idx++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(tx_ptr++) & 0xff); + } + tx_len_tot -= send_len; +// ets_printf("tot: %d\n", tx_len_tot); + if(tx_len_tot == 0) { + if(brk_flg == 1) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->idle_conf.tx_brk_num = tx_brk_len; + uart_reg->conf0.txd_brk = 1; + uart_reg->int_clr.tx_brk_done = 1; + uart_reg->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + wait_brk = 1; + } else { + en_tx_flg = true; + } +// ets_printf("ret2\n"); + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_head = NULL; + tx_ptr = NULL; + } else { + en_tx_flg = true; + } + } + if(en_tx_flg) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_clr.txfifo_empty = 1; + uart_reg->int_ena.txfifo_empty = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + } + } } else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { if(p_uart->buffer_full_flg == false) { //Get the buffer from the FIFO +// ESP_LOGE(UART_TAG, "FULL\n"); rx_fifo_len = uart_reg->status.rxfifo_cnt; p_uart->data_len = rx_fifo_len; memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); @@ -506,12 +597,22 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { +// ESP_LOGE(UART_TAG, "UART TX BRK DONE\n"); + ets_printf("tx brk done\n"); UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->conf0.txd_brk = 0; uart_reg->int_ena.tx_brk_done = 0; uart_reg->int_clr.tx_brk_done = 1; + if(brk_flg == 1) { + uart_reg->int_ena.txfifo_empty = 1; + } UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + if(brk_flg == 1) { + brk_flg = 0; + wait_brk = 0; + } else { + xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + } } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.tx_brk_idle_done = 0; @@ -638,26 +739,26 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return original_size; } -static void uart_tx_task(void* arg) -{ - uart_obj_t* p_uart = (uart_obj_t*) arg; - size_t size; - uart_event_t evt; - for(;;) { - char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); - if(data == NULL) { - continue; - } - memcpy(&evt, data, sizeof(evt)); - if(evt.type == UART_DATA) { - uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); - } else if(evt.type == UART_DATA_BREAK) { - uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); - } - vRingbufferReturnItem(p_uart->tx_ring_buf, data); - } - vTaskDelete(NULL); -} +//static void uart_tx_task(void* arg) +//{ +// uart_obj_t* p_uart = (uart_obj_t*) arg; +// size_t size; +// uart_event_t evt; +// for(;;) { +// char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); +// if(data == NULL) { +// continue; +// } +// memcpy(&evt, data, sizeof(evt)); +// if(evt.type == UART_DATA) { +// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); +// } else if(evt.type == UART_DATA_BREAK) { +// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); +// } +// vRingbufferReturnItem(p_uart->tx_ring_buf, data); +// } +// vTaskDelete(NULL); +//} int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { @@ -666,19 +767,18 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) UART_CHECK(src, "buffer null"); if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { - uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); - if(evt == NULL) { - ESP_LOGE(UART_TAG, "UART EVT MALLOC ERROR\n"); - return -1; - } + uart_event_t evt; xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); - evt->type = UART_DATA; - evt->data.size = size; - memcpy(evt->data.data, src, size); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); - free(evt); - evt = NULL; + evt.type = UART_DATA; + evt.data.size = size; + ets_printf("-----1st send-----\n"); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); + ets_printf("====2nd send====\n"); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); + xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); @@ -698,19 +798,15 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { - uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); - if(evt == NULL) { - return -1; - } + uart_event_t evt; xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); - evt->type = UART_DATA_BREAK; - evt->data.size = size; - evt->data.brk_len = brk_len; - memcpy(evt->data.data, src, size); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); - free(evt); - evt = NULL; + evt.type = UART_DATA_BREAK; + evt.data.size = size; + evt.data.brk_len = brk_len; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); @@ -782,8 +878,10 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; +// ets_printf("dbg0\n"); } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); +// ets_printf("dbg1\n"); return copy_len; } } @@ -792,17 +890,20 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } else { len_tmp = p_uart_obj[uart_num]->cur_remain; } +// ets_printf("dbga\n"); memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); p_uart_obj[uart_num]->rd_ptr += len_tmp; p_uart_obj[uart_num]->cur_remain -= len_tmp; copy_len += len_tmp; length -= len_tmp; +// ets_printf("dbgb\n"); if(p_uart_obj[uart_num]->cur_remain == 0) { vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); +// ets_printf("dbg2\n"); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); @@ -810,6 +911,7 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } } } +// ets_printf("dbg3\n"); xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -944,14 +1046,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); if(tx_buffer_size > 0) { - p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; - xTaskCreate(uart_tx_task, "uart_tx_task", UART_TX_TASK_DEPTH_DEFAULT, (void*)p_uart_obj[uart_num], UART_TX_TASK_PRIO_DEFAULT, &p_uart_obj[uart_num]->tx_task_handle); - } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; - p_uart_obj[uart_num]->tx_task_handle = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -986,10 +1085,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) uart_disable_tx_intr(uart_num); uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); - if(p_uart_obj[uart_num]->tx_task_handle) { - vTaskDelete(p_uart_obj[uart_num]->tx_task_handle); - p_uart_obj[uart_num]->tx_task_handle = NULL; - } if(p_uart_obj[uart_num]->tx_fifo_sem) { vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); p_uart_obj[uart_num]->tx_fifo_sem = NULL; diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index ce5504596a..560eb5fdd9 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -77,9 +77,13 @@ static int ringbufferFreeMem(ringbuf_t *rb) { int free_size = rb->free_ptr-rb->write_ptr; if (free_size <= 0) free_size += rb->size; + //If we free the last dummy item in the buffer, free_ptr will point to rb->data + //In this case, after we write the last some bytes, the buffer might wrap around if we don't have room for a header anymore. +// if (free_size == 0 && rb->read_ptr == rb->write_ptr) free_size += rb->size; //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation - //where read_ptr == free_ptr, messing up the next calculation. - return free_size-1; + //where write_ptr == free_ptr, messing up the next calculation. +// return free_size == 0 ? 0 : free_size - 1; + return free_size - 1; } @@ -334,6 +338,10 @@ static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wan //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { + ets_printf("in returnItemToRingbufDefault\n"); + xRingbufferPrintInfo(rb); + + uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -350,9 +358,16 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { hdr=(buf_entry_hdr_t *)rb->free_ptr; //basically forward free_ptr until we run into either a block that is still in use or the write pointer. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) { + if (hdr->flags & iflag_dummydata) { + ets_printf("hrd len: %d; flg: 0x%02x\n",hdr->len,hdr->flags); //Rest is dummy data. Reset to start of ringbuffer. rb->free_ptr=rb->data; + //If the read_ptr is pointing to this dummy item, + //we should also move the read pointer to data, in case we overwrite the read hdr. +// if(rb->read_ptr == (uint8_t*)hdr) { +// rb->read_ptr = rb->data; +// } } else { //Skip past item size_t len=(hdr->len+3)&~3; @@ -363,8 +378,10 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) { rb->free_ptr=rb->data; } + if(rb->free_ptr == rb->read_ptr) break; //Next header hdr=(buf_entry_hdr_t *)rb->free_ptr; + } } @@ -386,6 +403,12 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) configASSERT(rb); ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n", rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data); + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr; + if(rb->write_ptr == rb->read_ptr) { + ets_printf("write que read\n"); + } else { + ets_printf("hdr len: %d; flg: 0x%08x\n", hdr->len, hdr->flags); + } } @@ -493,7 +516,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ticks_to_wait = ticks_end - xTaskGetTickCount(); } } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry From e314f42b0c71fd96913e0f4f8b79d4e16e69982a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 20:18:30 +0800 Subject: [PATCH 017/131] nvs: fix Page::findItem and Storage::findItem regression When read caching was added, Page::findItem started modifying itemIndex reference argument even if item wasn't found. Incidentally, Storage::findItem reused itemIndex when starting search at next page. So, - if the first page had a cached index (findItem was called for that page), and it pointed to a non-zero index, - first page has a few empty items at the end (but is marked full), - next search looked up the item on the second page, - index of the item on the second page was less than the cached index on the first page, then the search would fail because cached starting index was reused. This change fixes both sides of the problem: - Page::findItem shouldn't modify itemIndex argument if item is not found - Storage::findItem should not reuse itemIndex between pages Two tests have been added. --- components/nvs_flash/src/nvs_page.cpp | 11 ++--- components/nvs_flash/src/nvs_storage.cpp | 2 +- components/nvs_flash/test/test_nvs.cpp | 54 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 23cefd1aad..d2ca225352 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -656,19 +656,20 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) { return ESP_ERR_NVS_NOT_FOUND; } - - if (itemIndex >= ENTRY_COUNT) { + + size_t findBeginIndex = itemIndex; + if (findBeginIndex >= ENTRY_COUNT) { return ESP_ERR_NVS_NOT_FOUND; } CachedFindInfo findInfo(nsIndex, datatype, key); if (mFindInfo == findInfo) { - itemIndex = mFindInfo.itemIndex(); + findBeginIndex = mFindInfo.itemIndex(); } size_t start = mFirstUsedEntry; - if (itemIndex > mFirstUsedEntry && itemIndex < ENTRY_COUNT) { - start = itemIndex; + if (findBeginIndex > mFirstUsedEntry && findBeginIndex < ENTRY_COUNT) { + start = findBeginIndex; } size_t end = mNextFreeEntry; diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index eb90cac5bc..cacfbd4022 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -71,8 +71,8 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item) { - size_t itemIndex = 0; for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + size_t itemIndex = 0; auto err = it->findItem(nsIndex, datatype, key, itemIndex, item); if (err == ESP_OK) { page = it; diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 528c9df686..81bf7fd216 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -300,6 +300,27 @@ TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_OK); } +TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]") +{ + SpiFlashEmulator emu(3); + Storage storage; + CHECK(storage.init(0, 3) == ESP_OK); + int bar = 0; + uint8_t bigdata[100 * 32] = {0}; + // write one big chunk of data + ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "first", bigdata, sizeof(bigdata))); + + // write second one; it will not fit into the first page + ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "second", bigdata, sizeof(bigdata))); + + size_t size; + ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "first", size)); + CHECK(size == sizeof(bigdata)); + ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "second", size)); + CHECK(size == sizeof(bigdata)); +} + + TEST_CASE("can write and read variable length data lots of times", "[nvs]") { SpiFlashEmulator emu(8); @@ -1055,6 +1076,39 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") } +TEST_CASE("read/write failure (TW8406)", "[nvs]") +{ + SpiFlashEmulator emu(3); + nvs_flash_init_custom(0, 3); + for (int attempts = 0; attempts < 3; ++attempts) { + int i = 0; + nvs_handle light_handle = 0; + char key[15] = {0}; + char data[76] = {12, 13, 14, 15, 16}; + uint8_t number = 20; + size_t data_len = sizeof(data); + + ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle)); + ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number)); + for (i = 0; i < number; ++i) { + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data))); + } + nvs_commit(light_handle); + + uint8_t get_number = 0; + ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number)); + REQUIRE(number == get_number); + for (i = 0; i < number; ++i) { + char data[76] = {0}; + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len)); + } + nvs_close(light_handle); + } +} + + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; From 9ed7c4f8bc7f0c8d1f26bf68877b95099b6703ef Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 3 Nov 2016 23:30:54 +0800 Subject: [PATCH 018/131] fix ringbuffer bug. --- components/driver/include/driver/uart.h | 3 +- components/driver/uart.c | 427 ++++++++++++------------ components/freertos/ringbuf.c | 30 +- 3 files changed, 214 insertions(+), 246 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 445d71b685..0d9ab3c563 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -437,13 +437,12 @@ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf * @param queue_size UART event queue size/depth. * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. - * @param buf_type UART RX ring_buffer type * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); /** * @brief Uninstall UART driver. diff --git a/components/driver/uart.c b/components/driver/uart.c index d6585405f2..9eaf783438 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -43,28 +43,35 @@ const char* UART_TAG = "UART"; #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) typedef struct { - uart_port_t uart_num; - SemaphoreHandle_t tx_fifo_sem; - SemaphoreHandle_t tx_mutex; - SemaphoreHandle_t tx_buffer_mutex; - SemaphoreHandle_t tx_done_sem; - SemaphoreHandle_t tx_brk_sem; - SemaphoreHandle_t rx_mux; - QueueHandle_t xQueueUart; - int queue_size; - int intr_num; - int rx_buf_size; - ringbuf_type_t rx_buf_type; - RingbufHandle_t rx_ring_buf; - int tx_buf_size; - RingbufHandle_t tx_ring_buf; - bool buffer_full_flg; - bool tx_waiting; - int cur_remain; - uint8_t* rd_ptr; - uint8_t* head_ptr; - uint8_t data_buf[UART_FIFO_LEN]; - uint8_t data_len; + uart_port_t uart_num; /*!< UART port number*/ + int queue_size; /*!< UART event queue size*/ + QueueHandle_t xQueueUart; /*!< UART queue handler*/ + int intr_num; /*!< UART interrupt number*/ + //rx parameters + SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/ + int rx_buf_size; /*!< RX ring buffer size */ + RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler*/ + bool rx_buffer_full_flg; /*!< RX ring buffer full flag. */ + int rx_cur_remain; /*!< Data number that waiting to be read out in ring buffer item*/ + uint8_t* rx_ptr; /*!< pointer to the current data in ring buffer*/ + uint8_t* rx_head_ptr; /*!< pointer to the head of RX item*/ + uint8_t rx_data_buf[UART_FIFO_LEN]; /*!< Data buffer to stash FIFO data*/ + uint8_t rx_stash_len; /*!< stashed data length.(When using flow control, after reading out FIFO data, if we fail to push to buffer, we can just stash them.) */ + //tx parameters + SemaphoreHandle_t tx_fifo_sem; /*!< UART TX FIFO semaphore*/ + SemaphoreHandle_t tx_mux; /*!< UART TX mutex*/ + SemaphoreHandle_t tx_buffer_mux; /*!< UART TX buffer semaphore*/ + SemaphoreHandle_t tx_done_sem; /*!< UART TX done semaphore*/ + SemaphoreHandle_t tx_brk_sem; /*!< UART TX send break done semaphore*/ + int tx_buf_size; /*!< TX ring buffer size */ + RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler*/ + bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/ + uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ + uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ + uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ + uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ + uint8_t tx_brk_len; /*!< TX break signal cycle length/number */ + uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ } uart_obj_t; static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; @@ -438,16 +445,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uint32_t uart_intr_status = UART[uart_num]->int_st.val; int rx_fifo_len = 0; uart_event_t uart_event; - - static uint8_t * tx_ptr; - static uart_event_t* tx_head; - static int tx_len_tot = 0; - static int brk_flg = 0; - static int tx_brk_len = 0; - static int wait_brk = 0; - - portBASE_TYPE HPTaskAwoken = 0; + while(uart_intr_status != 0x0) { buf_idx = 0; uart_event.type = UART_EVENT_MAX; @@ -456,85 +455,92 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.txfifo_empty = 0; uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - if(wait_brk) { + if(p_uart->tx_waiting_brk) { return; } - if(p_uart->tx_waiting == true) { - p_uart->tx_waiting = false; + if(p_uart->tx_waiting_fifo == true) { + p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } else { + //We don't use TX ring buffer, because the size if zero. + if(p_uart->tx_buf_size == 0) { + return; + } int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; bool en_tx_flg = false; - if(tx_len_tot == 0) { - size_t size; -// ets_printf("dbg1,tot=0,get 1st head\n"); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - if(tx_head) { //enable empty intr -// tx_ptr = (uint8_t*)tx_head + sizeof(uart_event_t); - tx_ptr = NULL; -// en_tx_flg = true; - tx_len_tot = tx_head->data.size; - if(tx_head->type == UART_DATA_BREAK) { - tx_len_tot = tx_head->data.size; - brk_flg = 1; - tx_brk_len = tx_head->data.brk_len; + //We need to put a loop here, in case all the buffer items are very short. + //That would cause a watch_dog reset because empty interrupt happens so often. + //Although this is a loop in ISR, this loop will execute at most 128 turns. + while(tx_fifo_rem) { + if(p_uart->tx_len_tot == 0) { + size_t size; + //The first item is the data description + //Get the first item to get the data information + p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + if(p_uart->tx_head) { + p_uart->tx_ptr = NULL; + p_uart->tx_len_tot = p_uart->tx_head->data.size; + if(p_uart->tx_head->type == UART_DATA_BREAK) { + p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_brk_flg = 1; + p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + } + //We have saved the data description from the 1st item, return buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + } + else { + //Can not get data from ring buffer, return; + return; } -// ets_printf("ret1,tot: %d\n", tx_len_tot); - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); } - else { - return; - } - } - if(tx_ptr == NULL) { - size_t size; -// ets_printf("dbg2, tx ptr null, get 2nd tx ptr\n"); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); - -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - if(tx_ptr) { - tx_head = (void*) tx_ptr; -// ets_printf("get size: %d ; h size: %d\n", size, tx_len_tot); - en_tx_flg = true; - } else { - return; - } - } -// else - if(tx_len_tot > 0 && tx_ptr) { //tx - int send_len = tx_len_tot > tx_fifo_rem ? tx_fifo_rem : tx_len_tot; - for(buf_idx = 0; buf_idx < send_len; buf_idx++) { - WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(tx_ptr++) & 0xff); - } - tx_len_tot -= send_len; -// ets_printf("tot: %d\n", tx_len_tot); - if(tx_len_tot == 0) { - if(brk_flg == 1) { - UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); - uart_reg->int_ena.tx_brk_done = 0; - uart_reg->idle_conf.tx_brk_num = tx_brk_len; - uart_reg->conf0.txd_brk = 1; - uart_reg->int_clr.tx_brk_done = 1; - uart_reg->int_ena.tx_brk_done = 1; - UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - wait_brk = 1; + if(p_uart->tx_ptr == NULL) { + size_t size; + //2nd item is the data we need to send through UART + //Get 2nd item from ring buffer + p_uart->tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + if(p_uart->tx_ptr) { + //Update the TX item head, we will need this to return item to buffer. + p_uart->tx_head = (void*) p_uart->tx_ptr; + en_tx_flg = true; } else { + //Can not get data from ring buffer, return; + return; + } + } + if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr) { + //To fill the TX FIFO. + int send_len = p_uart->tx_len_tot > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_tot; + for(buf_idx = 0; buf_idx < send_len; buf_idx++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(p_uart->tx_ptr++) & 0xff); + } + p_uart->tx_len_tot -= send_len; + tx_fifo_rem -= send_len; + if(p_uart->tx_len_tot == 0) { + //Sending item done, now we need to send break if there is a record. + //Return item to ring buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + p_uart->tx_head = NULL; + p_uart->tx_ptr = NULL; + //Set TX break signal after FIFO is empty + if(p_uart->tx_brk_flg == 1) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->idle_conf.tx_brk_num = p_uart->tx_brk_len; + uart_reg->conf0.txd_brk = 1; + uart_reg->int_clr.tx_brk_done = 1; + uart_reg->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + p_uart->tx_waiting_brk = 1; + return; + } else { + //enable TX empty interrupt + en_tx_flg = true; + } + } else { + //enable TX empty interrupt en_tx_flg = true; } -// ets_printf("ret2\n"); - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_head = NULL; - tx_ptr = NULL; - } else { - en_tx_flg = true; } } if(en_tx_flg) { @@ -546,14 +552,13 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } } else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { - if(p_uart->buffer_full_flg == false) { + if(p_uart->rx_buffer_full_flg == false) { //Get the buffer from the FIFO -// ESP_LOGE(UART_TAG, "FULL\n"); rx_fifo_len = uart_reg->status.rxfifo_cnt; - p_uart->data_len = rx_fifo_len; - memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); + p_uart->rx_stash_len = rx_fifo_len; + //We have to read out all data in RX FIFO to clear the interrupt signal while(buf_idx < rx_fifo_len) { - p_uart->data_buf[buf_idx++] = uart_reg->fifo.rw_byte; + p_uart->rx_data_buf[buf_idx++] = uart_reg->fifo.rw_byte; } //After Copying the Data From FIFO ,Clear intr_status UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); @@ -562,12 +567,14 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; uart_event.data.size = rx_fifo_len; - if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + //If we fail to push data to ring buffer, we will have to stash the data, and send next time. + //Mainly for applications that uses flow control or small ring buffer. + if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &HPTaskAwoken)) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; uart_reg->int_ena.rxfifo_tout = 0; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - p_uart->buffer_full_flg = true; + p_uart->rx_buffer_full_flg = true; uart_event.type = UART_BUFFER_FULL; } else { uart_event.type = UART_DATA; @@ -597,19 +604,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { -// ESP_LOGE(UART_TAG, "UART TX BRK DONE\n"); - ets_printf("tx brk done\n"); UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->conf0.txd_brk = 0; uart_reg->int_ena.tx_brk_done = 0; uart_reg->int_clr.tx_brk_done = 1; - if(brk_flg == 1) { + if(p_uart->tx_brk_flg == 1) { uart_reg->int_ena.txfifo_empty = 1; } UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - if(brk_flg == 1) { - brk_flg = 0; - wait_brk = 0; + if(p_uart->tx_brk_flg == 1) { + p_uart->tx_brk_flg = 0; + p_uart->tx_waiting_brk = 0; } else { xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); } @@ -644,8 +649,8 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); BaseType_t res; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - //Take tx_mutex - res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)ticks_to_wait); + //Take tx_mux + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)ticks_to_wait); if(res == pdFALSE) { return ESP_ERR_TIMEOUT; } @@ -653,7 +658,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0); ticks_to_wait = ticks_end - xTaskGetTickCount(); if(UART[uart_num]->status.txfifo_cnt == 0) { - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_OK; } uart_enable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); @@ -661,10 +666,10 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); if(res == pdFALSE) { uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_ERR_TIMEOUT; } - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_OK; } @@ -701,9 +706,9 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) if(len == 0) { return 0; } - xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); int tx_len = uart_fill_fifo(uart_num, buffer, len); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return tx_len; } @@ -716,14 +721,14 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return 0; } //lock for uart_tx - xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); size_t original_size = size; while(size) { //semaphore for tx_fifo available if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { size_t sent = uart_fill_fifo(uart_num, (char*) src, size); if(sent < size) { - p_uart_obj[uart_num]->tx_waiting = true; + p_uart_obj[uart_num]->tx_waiting_fifo = true; uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); } size -= sent; @@ -735,49 +740,25 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); } xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return original_size; } -//static void uart_tx_task(void* arg) -//{ -// uart_obj_t* p_uart = (uart_obj_t*) arg; -// size_t size; -// uart_event_t evt; -// for(;;) { -// char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); -// if(data == NULL) { -// continue; -// } -// memcpy(&evt, data, sizeof(evt)); -// if(evt.type == UART_DATA) { -// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); -// } else if(evt.type == UART_DATA_BREAK) { -// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); -// } -// vRingbufferReturnItem(p_uart->tx_ring_buf, data); -// } -// vTaskDelete(NULL); -//} - int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); UART_CHECK(src, "buffer null"); + //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); evt.type = UART_DATA; evt.data.size = size; - ets_printf("-----1st send-----\n"); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); - ets_printf("====2nd send====\n"); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { @@ -785,6 +766,7 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) return uart_tx_all(uart_num, src, size, 0, 0); } } else { + //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 0, 0); } } @@ -796,16 +778,17 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((size > 0), "uart size error"); UART_CHECK((src), "uart data null"); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); evt.type = UART_DATA_BREAK; evt.data.size = size; evt.data.brk_len = brk_len; xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { @@ -813,6 +796,7 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s return uart_tx_all(uart_num, src, size, 1, brk_len); } } else { + //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 1, brk_len); } } @@ -828,29 +812,29 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } - if(p_uart_obj[uart_num]->cur_remain == 0) { + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { ticks_to_wait = ticks_end - xTaskGetTickCount(); data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { - p_uart_obj[uart_num]->head_ptr = data; - p_uart_obj[uart_num]->rd_ptr = data; - p_uart_obj[uart_num]->cur_remain = size; + p_uart_obj[uart_num]->rx_head_ptr = data; + p_uart_obj[uart_num]->rx_ptr = data; + p_uart_obj[uart_num]->rx_cur_remain = size; } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return -1; } } - val = *(p_uart_obj[uart_num]->rd_ptr); - p_uart_obj[uart_num]->rd_ptr++; - p_uart_obj[uart_num]->cur_remain--; - if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rd_ptr = NULL; - if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + val = *(p_uart_obj[uart_num]->rx_ptr); + p_uart_obj[uart_num]->rx_ptr++; + p_uart_obj[uart_num]->rx_cur_remain--; + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ptr = NULL; + if(p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); if(res == pdTRUE) { - p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } @@ -872,46 +856,40 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp return -1; } while(length) { - if(p_uart_obj[uart_num]->cur_remain == 0) { + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { - p_uart_obj[uart_num]->head_ptr = data; - p_uart_obj[uart_num]->rd_ptr = data; - p_uart_obj[uart_num]->cur_remain = size; -// ets_printf("dbg0\n"); + p_uart_obj[uart_num]->rx_head_ptr = data; + p_uart_obj[uart_num]->rx_ptr = data; + p_uart_obj[uart_num]->rx_cur_remain = size; } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); -// ets_printf("dbg1\n"); return copy_len; } } - if(p_uart_obj[uart_num]->cur_remain > length) { + if(p_uart_obj[uart_num]->rx_cur_remain > length) { len_tmp = length; } else { - len_tmp = p_uart_obj[uart_num]->cur_remain; + len_tmp = p_uart_obj[uart_num]->rx_cur_remain; } -// ets_printf("dbga\n"); - memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); - p_uart_obj[uart_num]->rd_ptr += len_tmp; - p_uart_obj[uart_num]->cur_remain -= len_tmp; + memcpy(buf + copy_len, p_uart_obj[uart_num]->rx_ptr, len_tmp); + p_uart_obj[uart_num]->rx_ptr += len_tmp; + p_uart_obj[uart_num]->rx_cur_remain -= len_tmp; copy_len += len_tmp; length -= len_tmp; -// ets_printf("dbgb\n"); - if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rd_ptr = NULL; - if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); -// ets_printf("dbg2\n"); + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ptr = NULL; + if(p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); if(res == pdTRUE) { - p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } } } -// ets_printf("dbg3\n"); xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -926,11 +904,11 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); while(true) { - if(p_uart->head_ptr) { - vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->head_ptr); - p_uart->rd_ptr = NULL; - p_uart->cur_remain = 0; - p_uart->head_ptr = NULL; + if(p_uart->rx_head_ptr) { + vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; } data = (uint8_t*) xRingbufferReceive(p_uart->rx_ring_buf, &size, (portTickType) 0); if(data == NULL) { @@ -938,19 +916,24 @@ esp_err_t uart_flush(uart_port_t uart_num) } vRingbufferReturnItem(p_uart->rx_ring_buf, data); } - p_uart->rd_ptr = NULL; - p_uart->cur_remain = 0; - p_uart->head_ptr = NULL; + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; xSemaphoreGive(p_uart->rx_mux); - xSemaphoreTake(p_uart->tx_mutex, (portTickType)portMAX_DELAY); - do { - data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); - if(data == NULL) { - break; - } - vRingbufferReturnItem(p_uart->rx_ring_buf, data); - } while(1); - xSemaphoreGive(p_uart->tx_mutex); + + xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); + if(p_uart->tx_buf_size > 0) { + xSemaphoreTake(p_uart->tx_buffer_mux, (portTickType)portMAX_DELAY); + do { + data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->rx_ring_buf, data); + } while(1); + xSemaphoreGive(p_uart->tx_buffer_mux); + } + xSemaphoreGive(p_uart->tx_mux); uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; @@ -1009,7 +992,7 @@ int uart_get_print_port() return s_uart_print_nport; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); @@ -1025,11 +1008,16 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); - p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->tx_buffer_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->intr_num = uart_intr_num; p_uart_obj[uart_num]->queue_size = queue_size; + p_uart_obj[uart_num]->tx_ptr = NULL; + p_uart_obj[uart_num]->tx_head = NULL; + p_uart_obj[uart_num]->tx_len_tot = 0; + p_uart_obj[uart_num]->tx_brk_flg = 0; + p_uart_obj[uart_num]->tx_brk_len = 0; + p_uart_obj[uart_num]->tx_waiting_brk = 0; if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); @@ -1038,19 +1026,20 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b } else { p_uart_obj[uart_num]->xQueueUart = NULL; } - p_uart_obj[uart_num]->buffer_full_flg = false; - p_uart_obj[uart_num]->tx_waiting = false; - p_uart_obj[uart_num]->rd_ptr = NULL; - p_uart_obj[uart_num]->cur_remain = 0; - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; - p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting_fifo = false; + p_uart_obj[uart_num]->rx_ptr = NULL; + p_uart_obj[uart_num]->rx_cur_remain = 0; + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, RINGBUF_TYPE_BYTEBUF); if(tx_buffer_size > 0) { p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; + p_uart_obj[uart_num]->tx_buffer_mux = xSemaphoreCreateMutex(); } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; + p_uart_obj[uart_num]->tx_buffer_mux = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -1097,13 +1086,13 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); p_uart_obj[uart_num]->tx_brk_sem = NULL; } - if(p_uart_obj[uart_num]->tx_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); - p_uart_obj[uart_num]->tx_mutex = NULL; + if(p_uart_obj[uart_num]->tx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mux); + p_uart_obj[uart_num]->tx_mux = NULL; } - if(p_uart_obj[uart_num]->tx_buffer_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mutex); - p_uart_obj[uart_num]->tx_buffer_mutex = NULL; + if(p_uart_obj[uart_num]->tx_buffer_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mux); + p_uart_obj[uart_num]->tx_buffer_mux = NULL; } if(p_uart_obj[uart_num]->rx_mux) { vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 560eb5fdd9..a4205d88dc 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -77,13 +77,9 @@ static int ringbufferFreeMem(ringbuf_t *rb) { int free_size = rb->free_ptr-rb->write_ptr; if (free_size <= 0) free_size += rb->size; - //If we free the last dummy item in the buffer, free_ptr will point to rb->data - //In this case, after we write the last some bytes, the buffer might wrap around if we don't have room for a header anymore. -// if (free_size == 0 && rb->read_ptr == rb->write_ptr) free_size += rb->size; //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation - //where write_ptr == free_ptr, messing up the next calculation. -// return free_size == 0 ? 0 : free_size - 1; - return free_size - 1; + //where read_ptr == free_ptr, messing up the next calculation. + return free_size-1; } @@ -338,10 +334,6 @@ static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wan //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { - ets_printf("in returnItemToRingbufDefault\n"); - xRingbufferPrintInfo(rb); - - uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -358,16 +350,9 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { hdr=(buf_entry_hdr_t *)rb->free_ptr; //basically forward free_ptr until we run into either a block that is still in use or the write pointer. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) { - if (hdr->flags & iflag_dummydata) { - ets_printf("hrd len: %d; flg: 0x%02x\n",hdr->len,hdr->flags); //Rest is dummy data. Reset to start of ringbuffer. rb->free_ptr=rb->data; - //If the read_ptr is pointing to this dummy item, - //we should also move the read pointer to data, in case we overwrite the read hdr. -// if(rb->read_ptr == (uint8_t*)hdr) { -// rb->read_ptr = rb->data; -// } } else { //Skip past item size_t len=(hdr->len+3)&~3; @@ -378,10 +363,11 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) { rb->free_ptr=rb->data; } + //The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr. + //Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data. if(rb->free_ptr == rb->read_ptr) break; //Next header hdr=(buf_entry_hdr_t *)rb->free_ptr; - } } @@ -403,12 +389,6 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) configASSERT(rb); ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n", rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data); - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr; - if(rb->write_ptr == rb->read_ptr) { - ets_printf("write que read\n"); - } else { - ets_printf("hdr len: %d; flg: 0x%08x\n", hdr->len, hdr->flags); - } } @@ -516,7 +496,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ticks_to_wait = ticks_end - xTaskGetTickCount(); } } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry From a6b3be6734b8f8acd13a8f401d046f3531b7a893 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 4 Nov 2016 02:48:25 +0800 Subject: [PATCH 019/131] Update UART driver 1. fix bug in ringbuffer.c: When return an dummy item, free_ptr might exceed rd_ptr, so the write_ptr would overwrite rd_ptr in this case. 2. Delete UART tx task in buffer mode. UART ISR will copy the data from tx buffer to fifo. --- components/driver/include/driver/uart.h | 110 +++++++-- components/driver/uart.c | 285 ++++++++++++------------ 2 files changed, 237 insertions(+), 158 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 0d9ab3c563..200d1148cb 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -87,8 +87,8 @@ typedef struct { uart_word_length_t data_bits; /*!< UART byte size*/ uart_parity_t parity; /*!< UART parity mode*/ uart_stop_bits_t stop_bits; /*!< UART stop bits*/ - uart_hw_flowcontrol_t flow_ctrl; /*!< UART hw flow control mode(cts/rts)*/ - uint8_t rx_flow_ctrl_thresh ; /*!< UART hw RTS threshold*/ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode(cts/rts)*/ + uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/ } uart_config_t; typedef struct { @@ -124,6 +124,7 @@ typedef struct { * @brief Set UART data bits. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param data_bit UART data bits * * @return @@ -147,6 +148,7 @@ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bi * @brief Set UART stop bits. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param bit_num UART stop bits * * @return @@ -170,6 +172,7 @@ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); * @brief Set UART parity. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param parity_mode the enum of uart parity configuration * * @return @@ -194,6 +197,7 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); * @brief Set UART baud rate. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param baud_rate UART baud-rate. * * @return @@ -216,7 +220,9 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param inverse_mask Choose the wires that need to be inversed. * * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) @@ -232,7 +238,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; * @brief Set hardware flow control. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param flow_ctrl Hardware flow control mode + * * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * * @return @@ -243,6 +251,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ /** * @brief Get hardware flow control mode + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return @@ -255,6 +264,7 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo * @brief Clear UART interrupt status * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param clr_mask Bit mask of the status that to be cleared. * * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) @@ -269,6 +279,7 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); * @brief Set UART interrupt enable * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param enable_mask Bit mask of the enable bits. * * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) @@ -283,6 +294,7 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); * @brief Clear UART interrupt enable bits * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param disable_mask Bit mask of the disable bits. * * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) @@ -331,7 +343,9 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param enable 1: enable; 0: disable + * * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return @@ -342,13 +356,16 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** * @brief register UART interrupt handler(ISR). +* @note * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * * @param fn Interrupt handler function. * @attention * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. @@ -364,9 +381,13 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* * @brief Set UART pin number * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param tx_io_num UART TX pin GPIO number + * * @param rx_io_num UART RX pin GPIO number + * * @param rts_io_num UART RTS pin GPIO number + * * @param cts_io_num UART CTS pin GPIO number * * @return @@ -380,6 +401,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * UART rx hardware flow control should not be set. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return @@ -392,6 +414,7 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); * @brief UART set DTR level (before inverse) * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param level 1: DTR output low; 0: DTR output high * * @return @@ -404,6 +427,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * @brief UART parameter configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param uart_config UART parameter settings * * @return @@ -416,6 +440,7 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); * @brief UART interrupt configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param p_intr_conf UART interrupt settings * * @return @@ -431,12 +456,19 @@ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param rx_buffer_size UART RX ring buffer size - * @param tx_buffer_size UART TX ring buffer size, if set to zero, driver will not use TX buffer and TX task. - * @param queue_size UART event queue size/depth. + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param rx_buffer_size UART RX ring buffer size + * + * @param tx_buffer_size UART TX ring buffer size. + * + * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. + * + * @param queue_size UART event queue size/depth. + * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return * - ESP_OK Success @@ -459,6 +491,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @brief Wait UART TX FIFO empty * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -473,7 +506,9 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param buffer data buffer address + * * @param len data length to send * * @return @@ -484,10 +519,17 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, + * + * If parameter tx_buffer_size is set to zero: * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param src data buffer address + * * @param size data length to send * * @return @@ -498,23 +540,37 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, + * + * If parameter tx_buffer_size is set to zero: * This function will not return until all the data and the break signal have been sent out. + * After all data send out, send a break signal. + * + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * After all data send out, send a break signal. + * + * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param src data buffer address + * * @param size data length to send + * * @param brk_len break signal length (unit: one bit's time@current_baudrate) * * @return * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ + int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read one char * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -527,10 +583,14 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); * @brief UART read bytes from UART buffer * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param buf pointer to the buffer. + * * @param length data length + * * @param ticks_to_wait sTimeout, count in RTOS ticks * + * * @return * - (-1) Error * - Others return a char data from uart fifo. @@ -588,14 +648,17 @@ int uart_get_print_port(void); * uart_param_config(uart_num, &uart_config); * //b1. Setup UART driver(with UART queue) * QueueHandle_t uart_queue; - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //parameters here are just an example, tx buffer size is 2048 + * uart_driver_install(uart_num, 1024 * 2, 1024 * 2, 10, UART_INTR_NUM, &uart_queue); * //b2. Setup UART driver(without UART queue) - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + * //parameters here are just an example, tx buffer size is 0 + * uart_driver_install(uart_num, 1024 * 2, 0, 10, UART_INTR_NUM, NULL); *@endcode *-----------------------------------------------------------------------------* * @code{c} * //2. Set UART pin - * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * //set UART pin, not needed if use default pins. + * uart_set_pin(uart_num, -1, -1, 15, 13); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -629,15 +692,20 @@ int uart_get_print_port(void); * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); //Config UART1 parameters - * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * //Configure UART1 parameters + * uart_param_config(uart_num, &uart_config); + * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * uart_set_pin(uart_num, 16, 17, 18, 19); + * //Set UART log level + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); * //Install UART driver( We don't need an event queue here) * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); * uint8_t data[1000]; * while(1) { - * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART - * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART + * //Read data from UART + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); + * //Write data back to UART + * uart_tx_all_chars(uart_num, (const char*)data, len); * } * } * @endcode @@ -704,12 +772,16 @@ int uart_get_print_port(void); * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); //Set UART parameters - * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * //Set UART parameters + * uart_param_config(uart_num, &uart_config); + * //Set UART pins,(-1: default pin, no change.) + * uart_set_pin(uart_num, -1, -1, 15, 13); + * //Set UART log level + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * //Create a task to handler UART event from ISR + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); * } * @endcode * diff --git a/components/driver/uart.c b/components/driver/uart.c index 9eaf783438..cab05dd0be 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -30,9 +30,9 @@ #include "soc/uart_struct.h" const char* UART_TAG = "UART"; -#define UART_CHECK(a, str) if (!(a)) { \ +#define UART_CHECK(a, str, ret) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ - return ESP_FAIL; \ + return (ret); \ } #define UART_EMPTY_THRESH_DEFAULT (10) #define UART_FULL_THRESH_DEFAULT (120) @@ -42,6 +42,7 @@ const char* UART_TAG = "UART"; #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + typedef struct { uart_port_t uart_num; /*!< UART port number*/ int queue_size; /*!< UART event queue size*/ @@ -60,7 +61,6 @@ typedef struct { //tx parameters SemaphoreHandle_t tx_fifo_sem; /*!< UART TX FIFO semaphore*/ SemaphoreHandle_t tx_mux; /*!< UART TX mutex*/ - SemaphoreHandle_t tx_buffer_mux; /*!< UART TX buffer semaphore*/ SemaphoreHandle_t tx_done_sem; /*!< UART TX done semaphore*/ SemaphoreHandle_t tx_brk_sem; /*!< UART TX send break done semaphore*/ int tx_buf_size; /*!< TX ring buffer size */ @@ -69,6 +69,7 @@ typedef struct { uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ + uint32_t tx_len_cur; uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ uint8_t tx_brk_len; /*!< TX break signal cycle length/number */ uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ @@ -80,8 +81,8 @@ static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.bit_num = data_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -90,15 +91,15 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); *(data_bit) = UART[uart_num]->conf0.bit_num; return ESP_OK; } esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.stop_bit_num = stop_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -107,14 +108,14 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); (*stop_bit) = UART[uart_num]->conf0.stop_bit_num; return ESP_OK; } esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.parity = parity_mode & 0x1; UART[uart_num]->conf0.parity_en = (parity_mode >> 1) & 0x1; @@ -124,7 +125,7 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); int val = UART[uart_num]->conf0.val; if(val & UART_PARITY_EN_M) { if(val & UART_PARITY_M) { @@ -140,8 +141,8 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error", ESP_FAIL); uint32_t clk_div = (((UART_CLK_FREQ) << 4) / baud_rate); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->clk_div.div_int = clk_div >> 4; @@ -152,7 +153,7 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -162,8 +163,8 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_CONF0_REG(uart_num), UART_LINE_INV_MASK); SET_PERI_REG_MASK(UART_CONF0_REG(uart_num), inverse_mask); @@ -174,9 +175,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) //only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error"); - UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error", ESP_FAIL); + UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); if(flow_ctrl & UART_HW_FLOWCTRL_RTS) { UART[uart_num]->conf1.rx_flow_thrhd = rx_thresh; @@ -195,7 +196,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; if(UART[uart_num]->conf1.rx_flow_en) { val |= UART_HW_FLOWCTRL_RTS; @@ -209,7 +210,7 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo static esp_err_t uart_reset_fifo(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.rxfifo_rst = 1; UART[uart_num]->conf0.rxfifo_rst = 0; @@ -221,7 +222,7 @@ static esp_err_t uart_reset_fifo(uart_port_t uart_num) esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); //intr_clr register is write-only UART[uart_num]->int_clr.val = clr_mask; return ESP_OK; @@ -229,7 +230,7 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); SET_PERI_REG_MASK(UART_INT_CLR_REG(uart_num), enable_mask); SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), enable_mask); @@ -239,7 +240,7 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), disable_mask); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -248,7 +249,7 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) esp_err_t uart_enable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -257,7 +258,7 @@ esp_err_t uart_enable_rx_intr(uart_port_t uart_num) esp_err_t uart_disable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -266,7 +267,7 @@ esp_err_t uart_disable_rx_intr(uart_port_t uart_num) esp_err_t uart_disable_tx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_ena.txfifo_empty = 0; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -275,8 +276,8 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num) esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.txfifo_empty = 1; UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; @@ -288,7 +289,7 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); ESP_INTR_DISABLE(uart_intr_num); switch(uart_num) { @@ -313,11 +314,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error", ESP_FAIL); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error", ESP_FAIL); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error", ESP_FAIL); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error", ESP_FAIL); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { @@ -373,8 +374,8 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_rts = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -383,7 +384,7 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level) esp_err_t uart_set_dtr(uart_port_t uart_num, int level) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_dtr = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -392,8 +393,8 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((uart_config), "param null\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((uart_config), "param null\n", ESP_FAIL); if(uart_num == UART_NUM_0) { periph_module_enable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { @@ -414,8 +415,8 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_intr_conf), "param null\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_intr_conf), "param null\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { @@ -458,7 +459,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_waiting_brk) { return; } - if(p_uart->tx_waiting_fifo == true) { + //TX semaphore used in none tx ringbuffer mode. + if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size > 0) { p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } @@ -473,57 +475,51 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) //That would cause a watch_dog reset because empty interrupt happens so often. //Although this is a loop in ISR, this loop will execute at most 128 turns. while(tx_fifo_rem) { - if(p_uart->tx_len_tot == 0) { + if(p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) { size_t size; - //The first item is the data description - //Get the first item to get the data information p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); if(p_uart->tx_head) { - p_uart->tx_ptr = NULL; - p_uart->tx_len_tot = p_uart->tx_head->data.size; - if(p_uart->tx_head->type == UART_DATA_BREAK) { + //The first item is the data description + //Get the first item to get the data information + if(p_uart->tx_len_tot == 0) { + p_uart->tx_ptr = NULL; p_uart->tx_len_tot = p_uart->tx_head->data.size; - p_uart->tx_brk_flg = 1; - p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + if(p_uart->tx_head->type == UART_DATA_BREAK) { + p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_brk_flg = 1; + p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + } + //We have saved the data description from the 1st item, return buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + }else if(p_uart->tx_ptr == NULL) { + //Update the TX item pointer, we will need this to return item to buffer. + p_uart->tx_ptr = (uint8_t*) p_uart->tx_head; + en_tx_flg = true; + p_uart->tx_len_cur = size; } - //We have saved the data description from the 1st item, return buffer. - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); } else { //Can not get data from ring buffer, return; return; } } - if(p_uart->tx_ptr == NULL) { - size_t size; - //2nd item is the data we need to send through UART - //Get 2nd item from ring buffer - p_uart->tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); - if(p_uart->tx_ptr) { - //Update the TX item head, we will need this to return item to buffer. - p_uart->tx_head = (void*) p_uart->tx_ptr; - en_tx_flg = true; - } else { - //Can not get data from ring buffer, return; - return; - } - } - if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr) { + if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) { //To fill the TX FIFO. - int send_len = p_uart->tx_len_tot > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_tot; + int send_len = p_uart->tx_len_cur > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_cur; for(buf_idx = 0; buf_idx < send_len; buf_idx++) { WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(p_uart->tx_ptr++) & 0xff); } p_uart->tx_len_tot -= send_len; + p_uart->tx_len_cur -= send_len; tx_fifo_rem -= send_len; - if(p_uart->tx_len_tot == 0) { - //Sending item done, now we need to send break if there is a record. + if(p_uart->tx_len_cur == 0) { //Return item to ring buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); p_uart->tx_head = NULL; p_uart->tx_ptr = NULL; + //Sending item done, now we need to send break if there is a record. //Set TX break signal after FIFO is empty - if(p_uart->tx_brk_flg == 1) { + if(p_uart->tx_brk_flg == 1 && p_uart->tx_len_tot == 0) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.tx_brk_done = 0; uart_reg->idle_conf.tx_brk_num = p_uart->tx_brk_len; @@ -645,8 +641,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) /**************************************************************/ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_FAIL); BaseType_t res; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; //Take tx_mux @@ -700,9 +696,9 @@ static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK(buffer, "buffer null"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK(buffer, "buffer null", (-1)); if(len == 0) { return 0; } @@ -714,9 +710,6 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool brk_en, int brk_len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK(src, "buffer null"); if(size == 0) { return 0; } @@ -746,25 +739,28 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); - UART_CHECK(src, "buffer null"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); + UART_CHECK(src, "buffer null", (-1)); //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { - uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); - evt.type = UART_DATA; - evt.data.size = size; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return size; - } else { - ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); - return uart_tx_all(uart_num, src, size, 0, 0); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); + int ori_size = size; + int offset = 0; + uart_event_t evt; + evt.type = UART_DATA; + evt.data.size = size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + while(size > 0) { + int send_size = size > max_size / 2 ? max_size / 2 : size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); + size -= send_size; + offset += send_size; } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + return ori_size; } else { //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 0, 0); @@ -773,28 +769,31 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK((size > 0), "uart size error"); - UART_CHECK((src), "uart data null"); - UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK((size > 0), "uart size error", (-1)); + UART_CHECK((src), "uart data null", (-1)); + UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error", (-1)); //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { - uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); - evt.type = UART_DATA_BREAK; - evt.data.size = size; - evt.data.brk_len = brk_len; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return size; - } else { - ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); - return uart_tx_all(uart_num, src, size, 1, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); + int ori_size = size; + int offset = 0; + uart_event_t evt; + evt.type = UART_DATA_BREAK; + evt.data.size = size; + evt.data.brk_len = brk_len; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + while(size > 0) { + int send_size = size > max_size / 2 ? max_size / 2 : size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); + size -= send_size; + offset += send_size; } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + return ori_size; } else { //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 1, brk_len); @@ -803,8 +802,8 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); uint8_t* data; size_t size; int val; @@ -845,9 +844,9 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((buf), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((buf), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); uint8_t* data = NULL; size_t size; size_t copy_len = 0; @@ -896,13 +895,15 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp esp_err_t uart_flush(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_FAIL); uart_obj_t* p_uart = p_uart_obj[uart_num]; uint8_t* data; size_t size; + //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); + ESP_INTR_DISABLE(p_uart->intr_num); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); @@ -919,11 +920,16 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; + ESP_INTR_ENABLE(p_uart->intr_num); xSemaphoreGive(p_uart->rx_mux); - xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); if(p_uart->tx_buf_size > 0) { - xSemaphoreTake(p_uart->tx_buffer_mux, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); + ESP_INTR_DISABLE(p_uart->intr_num); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_ena.txfifo_empty = 0; + UART[uart_num]->int_clr.txfifo_empty = 1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); do { data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); if(data == NULL) { @@ -931,10 +937,17 @@ esp_err_t uart_flush(uart_port_t uart_num) } vRingbufferReturnItem(p_uart->rx_ring_buf, data); } while(1); - xSemaphoreGive(p_uart->tx_buffer_mux); + p_uart->tx_brk_flg = 0; + p_uart->tx_brk_len = 0; + p_uart->tx_head = NULL; + p_uart->tx_len_cur = 0; + p_uart->tx_len_tot = 0; + p_uart->tx_ptr = NULL; + p_uart->tx_waiting_brk = 0; + p_uart->tx_waiting_fifo = false; + ESP_INTR_ENABLE(p_uart->intr_num); + xSemaphoreGive(p_uart->tx_mux); } - xSemaphoreGive(p_uart->tx_mux); - uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; } @@ -966,8 +979,8 @@ static void uart_ignore_char(char chr) //Only effective to ets_printf function, not ESP_LOGX macro. esp_err_t uart_set_print_port(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "UART driver error", ESP_FAIL); s_uart_print_nport = uart_num; switch(s_uart_print_nport) { case UART_NUM_0: @@ -994,8 +1007,8 @@ int uart_get_print_port() esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); @@ -1033,13 +1046,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->rx_head_ptr = NULL; p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, RINGBUF_TYPE_BYTEBUF); if(tx_buffer_size > 0) { - p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; - p_uart_obj[uart_num]->tx_buffer_mux = xSemaphoreCreateMutex(); } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; - p_uart_obj[uart_num]->tx_buffer_mux = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -1064,7 +1075,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b //Make sure no other tasks are still using UART before you call this function esp_err_t uart_driver_delete(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_LOGI(UART_TAG, "ALREADY NULL\n"); return ESP_OK; @@ -1090,10 +1101,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) vSemaphoreDelete(p_uart_obj[uart_num]->tx_mux); p_uart_obj[uart_num]->tx_mux = NULL; } - if(p_uart_obj[uart_num]->tx_buffer_mux) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mux); - p_uart_obj[uart_num]->tx_buffer_mux = NULL; - } if(p_uart_obj[uart_num]->rx_mux) { vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); p_uart_obj[uart_num]->rx_mux = NULL; From 3ec23f1b837959548da8e7ddec4dfd7db7929a6c Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 4 Nov 2016 12:52:34 +0800 Subject: [PATCH 020/131] Modify as Angus's suggestion: 1. Set XXX_TAG static, remove extern XXX_TAG in uart.h/ledc.h/gpio.h 2. I removed uart_set/get_print_port() functions, these functions are not well tested, I removed them for now. 3. Modify some function names for uart_read/write_bytes 4. Modify uart_write_bytes and uart_write_bytes_with_break. --- components/driver/gpio.c | 2 +- components/driver/include/driver/gpio.h | 2 +- components/driver/include/driver/ledc.h | 1 - components/driver/include/driver/uart.h | 98 ++++------ components/driver/ledc.c | 2 +- components/driver/uart.c | 238 ++++++------------------ 6 files changed, 96 insertions(+), 247 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index da0fedb895..62a0e7faa7 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -20,7 +20,7 @@ #include "soc/soc.h" #include "esp_log.h" -const char* GPIO_TAG = "GPIO"; +static const char* GPIO_TAG = "GPIO"; #define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index a31c2f64b7..903621f619 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -27,7 +27,7 @@ #ifdef __cplusplus extern "C" { #endif -extern const char* GPIO_TAG; + #define GPIO_SEL_0 (BIT(0)) /*!< Pin 0 selected */ #define GPIO_SEL_1 (BIT(1)) /*!< Pin 1 selected */ #define GPIO_SEL_2 (BIT(2)) /*!< Pin 2 selected */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index ac29eaf56a..3ab0ebff1e 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -26,7 +26,6 @@ extern "C" { #endif -extern const char* LEDC_TAG; #define LEDC_APB_CLK_HZ (APB_CLK_FREQ) #define LEDC_REF_CLK_HZ (1*1000000) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 200d1148cb..ba487d57d5 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -32,11 +32,11 @@ extern "C" { #include "freertos/ringbuf.h" #include -extern const char* UART_TAG; -#define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 +#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */ #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) #define UART_BITRATE_MAX 5000000 +#define UART_PIN_NO_CHANGE (-1) typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ @@ -243,6 +243,8 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; * * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * + * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. + * * @return * - ESP_OK Success * - ESP_FAIL Parameter error @@ -380,15 +382,19 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* /** * @brief Set UART pin number * + * @note + * Internal signal can be output to multiple GPIO pads + * Only one GPIO pad can connect with input signal + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param tx_io_num UART TX pin GPIO number + * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rx_io_num UART RX pin GPIO number + * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rts_io_num UART RTS pin GPIO number + * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param cts_io_num UART CTS pin GPIO number + * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return * - ESP_OK Success @@ -434,20 +440,20 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); +esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config); /** * @brief UART interrupt configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param p_intr_conf UART interrupt settings + * @param intr_conf UART interrupt settings * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); +esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf); /** * @brief Install UART driver. @@ -504,6 +510,9 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * @note + * This function should only be used when UART TX buffer is not enabled. + * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @@ -515,7 +524,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); +int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, @@ -536,7 +545,7 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); +int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, @@ -564,20 +573,7 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); - -/** -* @brief UART read one char - * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @param ticks_to_wait Timeout, count in RTOS ticks - * - * @return - * - (-1) Error - * - Others return a char data from UART. - */ -int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); +int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read bytes from UART buffer @@ -608,25 +604,6 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp */ esp_err_t uart_flush(uart_port_t uart_num); -/** - * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. - * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @return - * - ESP_OK Success - * - ESP_FAIL Parameter error, or UART driver not installed. - */ -esp_err_t uart_set_print_port(uart_port_t uart_no); - -/** - * @brief Get the current serial port for ets_printf function - * - * - * @return current print port(0: UART0; 1: UART1; 2: UART2) - */ -int uart_get_print_port(void); - /***************************EXAMPLE********************************** * * @@ -658,7 +635,7 @@ int uart_get_print_port(void); * @code{c} * //2. Set UART pin * //set UART pin, not needed if use default pins. - * uart_set_pin(uart_num, -1, -1, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -671,12 +648,12 @@ int uart_get_print_port(void); * @code{c} * //4. Write data to UART. * char* test_str = "This is a test string.\n" - * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str)); * @endcode *-----------------------------------------------------------------------------* * @code{c} * //5. Write data to UART, end with a break signal. - * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * uart_write_bytes_with_break(0, "test break\n",strlen("test break\n"), 100); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -696,8 +673,6 @@ int uart_get_print_port(void); * uart_param_config(uart_num, &uart_config); * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) * uart_set_pin(uart_num, 16, 17, 18, 19); - * //Set UART log level - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); * //Install UART driver( We don't need an event queue here) * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); * uint8_t data[1000]; @@ -705,7 +680,7 @@ int uart_get_print_port(void); * //Read data from UART * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); * //Write data back to UART - * uart_tx_all_chars(uart_num, (const char*)data, len); + * uart_write_bytes(uart_num, (const char*)data, len); * } * } * @endcode @@ -715,6 +690,7 @@ int uart_get_print_port(void); * #include "freertos/queue.h" * //A queue to handle UART event. * QueueHandle_t uart0_queue; + * static const char *TAG = "uart_example"; * void uart_task(void *pvParameters) * { * int uart_num = (int)pvParameters; @@ -723,37 +699,37 @@ int uart_get_print_port(void); * for(;;) { * //Waiting for UART event. * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { - * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { * //Event of UART receving data * case UART_DATA: - * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * ESP_LOGI(TAG,"data, len: %d\n", event.data.size); * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * ESP_LOGI(TAG, "uart read: %d\n", len); * break; * //Event of HW FIFO overflow detected * case UART_FIFO_OVF: - * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * ESP_LOGI(TAG, "hw fifo overflow\n"); * break; * //Event of UART ring buffer full * case UART_BUFFER_FULL: - * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * ESP_LOGI(TAG, "ring buffer full\n"); * break; * //Event of UART RX break detected * case UART_BREAK: - * ESP_LOGI(UART_TAG, "uart rx break\n"); + * ESP_LOGI(TAG, "uart rx break\n"); * break; * //Event of UART parity check error * case UART_PARITY_ERR: - * ESP_LOGI(UART_TAG, "uart parity error\n"); + * ESP_LOGI(TAG, "uart parity error\n"); * break; * //Event of UART frame error * case UART_FRAME_ERR: - * ESP_LOGI(UART_TAG, "uart frame error\n"); + * ESP_LOGI(TAG, "uart frame error\n"); * break; * //Others * default: - * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * ESP_LOGI(TAG, "uart event type: %d\n", event.type); * break; * } * } @@ -775,9 +751,9 @@ int uart_get_print_port(void); * //Set UART parameters * uart_param_config(uart_num, &uart_config); * //Set UART pins,(-1: default pin, no change.) - * uart_set_pin(uart_num, -1, -1, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); * //Set UART log level - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); + * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); * //Create a task to handler UART event from ISR diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 771c4a17df..b9039cf626 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -20,7 +20,7 @@ #include "driver/ledc.h" #include "esp_log.h" -const char* LEDC_TAG = "LEDC"; +static const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; #define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ diff --git a/components/driver/uart.c b/components/driver/uart.c index cab05dd0be..a3e0b92b2b 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -29,7 +29,7 @@ #include "driver/gpio.h" #include "soc/uart_struct.h" -const char* UART_TAG = "UART"; +static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ @@ -249,28 +249,19 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) esp_err_t uart_enable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_enable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); return ESP_OK; } esp_err_t uart_disable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); return ESP_OK; } esp_err_t uart_disable_tx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - UART[uart_num]->int_ena.txfifo_empty = 0; - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA); return ESP_OK; } @@ -391,7 +382,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) return ESP_OK; } -esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) +esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((uart_config), "param null\n", ESP_FAIL); @@ -413,25 +404,25 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) return ESP_OK; } -esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) +esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((p_intr_conf), "param null\n", ESP_FAIL); + UART_CHECK((intr_conf), "param null\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; - if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { - UART[uart_num]->conf1.rx_tout_thrhd = ((p_intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); + if(intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { + UART[uart_num]->conf1.rx_tout_thrhd = ((intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); UART[uart_num]->conf1.rx_tout_en = 1; } else { UART[uart_num]->conf1.rx_tout_en = 0; } - if(p_intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { - UART[uart_num]->conf1.rxfifo_full_thrhd = p_intr_conf->rxfifo_full_thresh; + if(intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { + UART[uart_num]->conf1.rxfifo_full_thrhd = intr_conf->rxfifo_full_thresh; } - if(p_intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { - UART[uart_num]->conf1.txfifo_empty_thrhd = p_intr_conf->txfifo_empty_intr_thresh; + if(intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { + UART[uart_num]->conf1.txfifo_empty_thrhd = intr_conf->txfifo_empty_intr_thresh; } - UART[uart_num]->int_ena.val = p_intr_conf->intr_enable_mask; + UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); return ESP_FAIL; } @@ -459,8 +450,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_waiting_brk) { return; } - //TX semaphore used in none tx ringbuffer mode. - if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size > 0) { + //TX semaphore will only be used when tx_buf_size is zero. + if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) { p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } @@ -682,7 +673,7 @@ static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) //Fill UART tx_fifo and return a number, //This function by itself is not thread-safe, always call from within a muxed section. -static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +static int uart_fill_fifo(uart_port_t uart_num, const char* buffer, uint32_t len) { uint8_t i = 0; uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; @@ -694,7 +685,7 @@ static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) return copy_cnt; } -int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) +int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); @@ -703,7 +694,7 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) return 0; } xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - int tx_len = uart_fill_fifo(uart_num, buffer, len); + int tx_len = uart_fill_fifo(uart_num, (const char*) buffer, len); xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return tx_len; } @@ -713,44 +704,21 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool if(size == 0) { return 0; } + size_t original_size = size; + //lock for uart_tx xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - size_t original_size = size; - while(size) { - //semaphore for tx_fifo available - if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { - size_t sent = uart_fill_fifo(uart_num, (char*) src, size); - if(sent < size) { - p_uart_obj[uart_num]->tx_waiting_fifo = true; - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - } - size -= sent; - src += sent; - } - } - if(brk_en) { - uart_set_break(uart_num, brk_len); - xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); - } - xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - return original_size; -} - -int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); - UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); - UART_CHECK(src, "buffer null", (-1)); - //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); - int ori_size = size; int offset = 0; uart_event_t evt; - evt.type = UART_DATA; evt.data.size = size; + evt.data.brk_len = brk_len; + if(brk_en) { + evt.type = UART_DATA_BREAK; + } else { + evt.type = UART_DATA; + } xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); while(size > 0) { int send_size = size > max_size / 2 ? max_size / 2 : size; @@ -760,86 +728,45 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) } xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return ori_size; } else { - //Send data without TX ring buffer, the task will block until all data have been sent out - return uart_tx_all(uart_num, src, size, 0, 0); + while(size) { + //semaphore for tx_fifo available + if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { + size_t sent = uart_fill_fifo(uart_num, (char*) src, size); + if(sent < size) { + p_uart_obj[uart_num]->tx_waiting_fifo = true; + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + } + size -= sent; + src += sent; + } + } + if(brk_en) { + uart_set_break(uart_num, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + return original_size; } -int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) +int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); + UART_CHECK(src, "buffer null", (-1)); + return uart_tx_all(uart_num, src, size, 0, 0); +} + +int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); UART_CHECK((size > 0), "uart size error", (-1)); UART_CHECK((src), "uart data null", (-1)); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error", (-1)); - //Push data to TX ring buffer and return, ISR will send the data. - if(p_uart_obj[uart_num]->tx_buf_size > 0) { - xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); - int ori_size = size; - int offset = 0; - uart_event_t evt; - evt.type = UART_DATA_BREAK; - evt.data.size = size; - evt.data.brk_len = brk_len; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - while(size > 0) { - int send_size = size > max_size / 2 ? max_size / 2 : size; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); - size -= send_size; - offset += send_size; - } - xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return ori_size; - } else { - //Send data without TX ring buffer, the task will block until all data have been sent out - return uart_tx_all(uart_num, src, size, 1, brk_len); - } -} - -int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); - uint8_t* data; - size_t size; - int val; - portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { - return -1; - } - if(p_uart_obj[uart_num]->rx_cur_remain == 0) { - ticks_to_wait = ticks_end - xTaskGetTickCount(); - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); - if(data) { - p_uart_obj[uart_num]->rx_head_ptr = data; - p_uart_obj[uart_num]->rx_ptr = data; - p_uart_obj[uart_num]->rx_cur_remain = size; - } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); - return -1; - } - } - val = *(p_uart_obj[uart_num]->rx_ptr); - p_uart_obj[uart_num]->rx_ptr++; - p_uart_obj[uart_num]->rx_cur_remain--; - if(p_uart_obj[uart_num]->rx_cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); - p_uart_obj[uart_num]->rx_head_ptr = NULL; - p_uart_obj[uart_num]->rx_ptr = NULL; - if(p_uart_obj[uart_num]->rx_buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); - if(res == pdTRUE) { - p_uart_obj[uart_num]->rx_buffer_full_flg = false; - uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); - } - } - } - xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); - return val; + return uart_tx_all(uart_num, src, size, 1, brk_len); } int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) @@ -952,59 +879,6 @@ esp_err_t uart_flush(uart_port_t uart_num) return ESP_OK; } -//----------------------------------- -//Should not enable hw flow control the debug print port. -//Use uart_tx_all_chars() as a thread-safe function to send data. -static int s_uart_print_nport = UART_NUM_0; -static void uart2_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_2, (const char*)&chr, 1); -} - -static void uart1_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_1, (const char*)&chr, 1); -} - -static void uart0_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_0, (const char*)&chr, 1); -} - -static void uart_ignore_char(char chr) -{ - -} - -//Only effective to ets_printf function, not ESP_LOGX macro. -esp_err_t uart_set_print_port(uart_port_t uart_num) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((p_uart_obj[uart_num]), "UART driver error", ESP_FAIL); - s_uart_print_nport = uart_num; - switch(s_uart_print_nport) { - case UART_NUM_0: - ets_install_putc1(uart0_write_char); - break; - case UART_NUM_1: - ets_install_putc1(uart1_write_char); - break; - case UART_NUM_2: - ets_install_putc1(uart2_write_char); - break; - case UART_NUM_MAX: - default: - ets_install_putc1(uart_ignore_char); - break; - } - return ESP_OK; -} - -int uart_get_print_port() -{ - return s_uart_print_nport; -} - esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); From b981b195bebce18a197a79d559b1cdad43b21bfb Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:04:35 +0100 Subject: [PATCH 021/131] Fixed broken links --- CONTRIBUTING.rst | 3 ++- docs/contributor-agreement.rst | 2 +- examples/README.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3bf43f6dbe..968826790a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -39,6 +39,7 @@ If this process passes, it will be merged onto the public github repository. Legal Part ---------- -Before a contribution can be accepted, you will need to sign our `Contributor Agreement `_. You will be prompted for this automatically as part of the Pull Request process. +Before a contribution can be accepted, you will need to sign our :doc:`contributor-agreement`. You will be prompted for this automatically as part of the Pull Request process. + diff --git a/docs/contributor-agreement.rst b/docs/contributor-agreement.rst index de294740c4..a7919da8f8 100644 --- a/docs/contributor-agreement.rst +++ b/docs/contributor-agreement.rst @@ -13,7 +13,7 @@ Framework (esp-idf) ("We" or "Us"). The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us. To make this document effective, please follow the instructions at -https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.md. +https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.rst. 1. DEFINITIONS ~~~~~~~~~~~~~~ diff --git a/examples/README.md b/examples/README.md index 2cf66b9d07..e4422e91bf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -26,7 +26,7 @@ If you're looking for a more bare-bones project to start from, try [esp-idf-temp If you have a new example you think we'd like, please consider sending it to us as a Pull Request. -Please read the esp-idf CONTRIBUTING.md file which lays out general contribution rules. +Please read the esp-idf CONTRIBUTING.rst file which lays out general contribution rules. In addition, here are some tips for creating good examples: From 9c2ab4559dea66f3df1eff976cf28d06ba75b602 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:06:55 +0100 Subject: [PATCH 022/131] Fixed section header style Please follow https://docs.python.org/devguide/documenting.html#sections --- docs/deep-sleep-stub.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/deep-sleep-stub.rst b/docs/deep-sleep-stub.rst index 983f8bbf26..7711b1ce23 100644 --- a/docs/deep-sleep-stub.rst +++ b/docs/deep-sleep-stub.rst @@ -1,12 +1,12 @@ Deep Sleep Wake Stubs ---------------------- +===================== ESP32 supports running a "deep sleep wake stub" when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally. Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep. Rules for Wake Stubs -==================== +-------------------- Wake stub code must be carefully written: @@ -23,9 +23,9 @@ Wake stub code must be carefully written: * Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app. Implementing A Stub -=================== +------------------- -The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()` then this will override the default. +The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()`` then this will override the default. If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``. @@ -36,7 +36,7 @@ If you want to swap between different deep sleep stubs at runtime, it is also po All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32. Loading Code Into RTC Memory -============================ +---------------------------- Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways. @@ -53,7 +53,7 @@ The first way is simpler for very short and simple code, or for source files whe Loading Data Into RTC Memory -============================ +---------------------------- Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP. From adca98348ea3ffb110764e5fc1801ace643ce700 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:11:40 +0100 Subject: [PATCH 023/131] Resolved issue with version / release on Read the Docs Read the Docs is building documentation referencing to specific releases on GitHub. Changing version / release in this script is breaking menu in bottom left corner Now version / release should change only for local builds and not for builds on Read the Docs --- docs/conf.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8a4a275c8a..494ab3cf7f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -75,13 +75,23 @@ copyright = u'2016, Espressif' # |version| and |release|, also used in various other places throughout the # built documents. # -# This is supposed to be "the short X.Y version", but it's the only version -# visible when you open index.html. -# Display full version to make things less confusing. -# If needed, nearest tag is returned by 'git describe --abbrev=0'. -version = run_cmd_get_output('git describe') -# The full version, including alpha/beta/rc tags. -release = run_cmd_get_output('git describe') + +# Different setup depending if script is running on ReadTheDocs or elsewhere +on_rtd = os.environ.get('READTHEDOCS') == 'True' +if on_rtd: + # The short X.Y version. + # Apparently ReadTheDocs is getting confused by other version / release + # ReadTheDocs is building specific or the latest release from GitHub. + version = '1.0' + release = '1.0' +else: + # This is supposed to be "the short X.Y version", but it's the only version + # visible when you open index.html. + # Display full version to make things less confusing. + # If needed, nearest tag is returned by 'git describe --abbrev=0'. + version = run_cmd_get_output('git describe') + # The full version, including alpha/beta/rc tags. + release = run_cmd_get_output('git describe') # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 7bcd68fb30c4b9d73b66aafcea005eebb618e133 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:13:44 +0100 Subject: [PATCH 024/131] deep-sleep-stub added + TOC outline TOC outline to show overal planned structure --- docs/index.rst | 66 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c973950615..42be69ee0c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,6 @@ ESP32 Programming Guide Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! - Contents: .. toctree:: @@ -16,8 +15,6 @@ Contents: Linux Mac OS -.. Configure - TBA - .. Connect - TBA .. toctree:: @@ -35,16 +32,74 @@ Contents: build_system openocd +.. API Reference + .. + Table of Contents Outline + .. + 1. System - TBA + 1.1. Fundamentals of multiprocessor programming with FreeRTOS - TBA + 1.2. Application startup flow - TBA + 1.3. Flash encryption and secure boot: how they work and APIs - TBA + 1.4. Lower Power Coprocessor - TBA + 1.5. Watchdogs + 1.6. ... + 2. Memeory - TBA + 2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA + 2.2. Flash layout and partitions - TBA + 2.3. Flash access APIs - TBA + 2.4. Partition APIs - TBA + 2.5. OTA mechanism (app partitions, OTA partition) and APIs - TBA + 2.6. ... + 3. Wi-Fi + 4. Bluetooth + 4.1. BT Classic - TBA + 4.2. BLE + 5. Ethernet - TBA + 6. Interfaces + 6.1. GPIO + 6.2. ADC - TBA + 6.3. DAC - TBA + 6.4. UART - TBA + 6.5. I2C - TBA + 6.6. I2S - TBA + 6.7. SPI - TBA + 6.8. CAN - TBA + 6.9. SD Controller - TBA + 6.10. Infrared - TBA + 6.11. Pulse Counter - TBA + 6.12. PWM - TBA + 6.13. LED PWM + 6.14. ... + 7. Sensors - TBA + 7.1. Hall Sensor - TBA + 7.2. Temperature Sensor - TBA + 7.3. Touch Sensor - TBA + 8. Protocols - TBA + 9. Components + 9.1. Logging + 9.2 Non-Volatile Storage + 9.3 Virtual Filesystem + 9.3. Http sever - TBA + 10. Applications - TBA + .. + API Dcoumentation Teamplate + .. + .. toctree:: :caption: API Reference :maxdepth: 1 Wi-Fi Bluetooth + GPIO + LED Control + Logging - Non-volatile storage - Virtual filesystem + Non-Volatile Storage + Virtual Filesystem + deep-sleep-stub + Template .. toctree:: @@ -76,3 +131,4 @@ Indices * :ref:`genindex` * :ref:`search` + From 3eaf1ec907ff407a098586bca80270b9dd938b02 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:18:25 +0100 Subject: [PATCH 025/131] All API Reference docs updated to match header files 1. Wi-Fi 2. Bluetooth 2. GPIO 4. LED Control 5. Logging 6. Non-Volatile Storage 7. Virtual Filesystem --- docs/Doxyfile | 2 +- docs/api/bt.rst | 16 +++-- docs/api/esp_wifi.rst | 33 +++++---- docs/api/gpio.rst | 159 ++++++++++++++++++++++++++--------------- docs/api/ledc.rst | 61 ++++++++++++++++ docs/api/log.rst | 35 +++++++++ docs/api/nvs_flash.rst | 67 +++++++++++++++++ docs/api/vfs.rst | 30 +++++--- 8 files changed, 312 insertions(+), 91 deletions(-) create mode 100644 docs/api/ledc.rst create mode 100644 docs/api/nvs_flash.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index 9c53aff1f3..6ff4c45860 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,6 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include WARN_NO_PARAMDOC = YES diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 72ab9fbd12..7cbbb9158b 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -1,20 +1,22 @@ -Bluetooth API -============= +Bluetooth +========= Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst index e4ec59fc82..e417e18ca0 100644 --- a/docs/api/esp_wifi.rst +++ b/docs/api/esp_wifi.rst @@ -1,20 +1,22 @@ -Wi-Fi API -========= +Wi-Fi +===== Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html Macros ------ @@ -22,14 +24,13 @@ Macros .. doxygendefine:: WIFI_INIT_CONFIG_DEFAULT -Typedefs --------- +Type Definitions +---------------- .. doxygentypedef:: wifi_promiscuous_cb_t .. doxygentypedef:: wifi_rxcb_t .. doxygentypedef:: esp_vendor_ie_cb_t - Functions --------- @@ -42,11 +43,12 @@ Functions .. doxygenfunction:: esp_wifi_connect .. doxygenfunction:: esp_wifi_disconnect .. doxygenfunction:: esp_wifi_clear_fast_connect -.. doxygenfunction:: esp_wifi_kick_station +.. doxygenfunction:: esp_wifi_deauth_sta .. doxygenfunction:: esp_wifi_scan_start .. doxygenfunction:: esp_wifi_scan_stop -.. doxygenfunction:: esp_wifi_get_ap_num -.. doxygenfunction:: esp_wifi_get_ap_list +.. doxygenfunction:: esp_wifi_scan_get_ap_num +.. doxygenfunction:: esp_wifi_scan_get_ap_records +.. doxygenfunction:: esp_wifi_sta_get_ap_info .. doxygenfunction:: esp_wifi_set_ps .. doxygenfunction:: esp_wifi_get_ps .. doxygenfunction:: esp_wifi_set_protocol @@ -64,11 +66,12 @@ Functions .. doxygenfunction:: esp_wifi_get_promiscuous .. doxygenfunction:: esp_wifi_set_config .. doxygenfunction:: esp_wifi_get_config -.. doxygenfunction:: esp_wifi_get_station_list -.. doxygenfunction:: esp_wifi_free_station_list +.. doxygenfunction:: esp_wifi_ap_get_sta_list .. doxygenfunction:: esp_wifi_set_storage .. doxygenfunction:: esp_wifi_reg_rxcb .. doxygenfunction:: esp_wifi_set_auto_connect .. doxygenfunction:: esp_wifi_get_auto_connect .. doxygenfunction:: esp_wifi_set_vendor_ie .. doxygenfunction:: esp_wifi_set_vendor_ie_cb + + diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 3c5c122921..72ba3e82fb 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -1,26 +1,120 @@ -GPIO API -======== +GPIO +==== Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html + +Macros +------ + +.. doxygendefine:: GPIO_SEL_0 +.. doxygendefine:: GPIO_SEL_1 +.. doxygendefine:: GPIO_SEL_2 +.. doxygendefine:: GPIO_SEL_3 +.. doxygendefine:: GPIO_SEL_4 +.. doxygendefine:: GPIO_SEL_5 +.. doxygendefine:: GPIO_SEL_6 +.. doxygendefine:: GPIO_SEL_7 +.. doxygendefine:: GPIO_SEL_8 +.. doxygendefine:: GPIO_SEL_9 +.. doxygendefine:: GPIO_SEL_10 +.. doxygendefine:: GPIO_SEL_11 +.. doxygendefine:: GPIO_SEL_12 +.. doxygendefine:: GPIO_SEL_13 +.. doxygendefine:: GPIO_SEL_14 +.. doxygendefine:: GPIO_SEL_15 +.. doxygendefine:: GPIO_SEL_16 +.. doxygendefine:: GPIO_SEL_17 +.. doxygendefine:: GPIO_SEL_18 +.. doxygendefine:: GPIO_SEL_19 +.. doxygendefine:: GPIO_SEL_21 +.. doxygendefine:: GPIO_SEL_22 +.. doxygendefine:: GPIO_SEL_23 +.. doxygendefine:: GPIO_SEL_25 +.. doxygendefine:: GPIO_SEL_26 +.. doxygendefine:: GPIO_SEL_27 +.. doxygendefine:: GPIO_SEL_32 +.. doxygendefine:: GPIO_SEL_33 +.. doxygendefine:: GPIO_SEL_34 +.. doxygendefine:: GPIO_SEL_35 +.. doxygendefine:: GPIO_SEL_36 +.. doxygendefine:: GPIO_SEL_37 +.. doxygendefine:: GPIO_SEL_38 +.. doxygendefine:: GPIO_SEL_39 +.. doxygendefine:: GPIO_PIN_REG_0 +.. doxygendefine:: GPIO_PIN_REG_1 +.. doxygendefine:: GPIO_PIN_REG_2 +.. doxygendefine:: GPIO_PIN_REG_3 +.. doxygendefine:: GPIO_PIN_REG_4 +.. doxygendefine:: GPIO_PIN_REG_5 +.. doxygendefine:: GPIO_PIN_REG_6 +.. doxygendefine:: GPIO_PIN_REG_7 +.. doxygendefine:: GPIO_PIN_REG_8 +.. doxygendefine:: GPIO_PIN_REG_9 +.. doxygendefine:: GPIO_PIN_REG_10 +.. doxygendefine:: GPIO_PIN_REG_11 +.. doxygendefine:: GPIO_PIN_REG_12 +.. doxygendefine:: GPIO_PIN_REG_13 +.. doxygendefine:: GPIO_PIN_REG_14 +.. doxygendefine:: GPIO_PIN_REG_15 +.. doxygendefine:: GPIO_PIN_REG_16 +.. doxygendefine:: GPIO_PIN_REG_17 +.. doxygendefine:: GPIO_PIN_REG_18 +.. doxygendefine:: GPIO_PIN_REG_19 +.. doxygendefine:: GPIO_PIN_REG_20 +.. doxygendefine:: GPIO_PIN_REG_21 +.. doxygendefine:: GPIO_PIN_REG_22 +.. doxygendefine:: GPIO_PIN_REG_23 +.. doxygendefine:: GPIO_PIN_REG_25 +.. doxygendefine:: GPIO_PIN_REG_26 +.. doxygendefine:: GPIO_PIN_REG_27 +.. doxygendefine:: GPIO_PIN_REG_32 +.. doxygendefine:: GPIO_PIN_REG_33 +.. doxygendefine:: GPIO_PIN_REG_34 +.. doxygendefine:: GPIO_PIN_REG_35 +.. doxygendefine:: GPIO_PIN_REG_36 +.. doxygendefine:: GPIO_PIN_REG_37 +.. doxygendefine:: GPIO_PIN_REG_38 +.. doxygendefine:: GPIO_PIN_REG_39 +.. doxygendefine:: GPIO_APP_CPU_INTR_ENA +.. doxygendefine:: GPIO_APP_CPU_NMI_INTR_ENA +.. doxygendefine:: GPIO_PRO_CPU_INTR_ENA +.. doxygendefine:: GPIO_PRO_CPU_NMI_INTR_ENA +.. doxygendefine:: GPIO_SDIO_EXT_INTR_ENA +.. doxygendefine:: GPIO_MODE_DEF_INPUT +.. doxygendefine:: GPIO_MODE_DEF_OUTPUT +.. doxygendefine:: GPIO_MODE_DEF_OD +.. doxygendefine:: GPIO_PIN_COUNT +.. doxygendefine:: GPIO_IS_VALID_GPIO +.. doxygendefine:: GPIO_IS_VALID_OUTPUT_GPIO + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: gpio_event_callback Enumerations ^^^^^^^^^^^^ +.. doxygenenum:: gpio_num_t .. doxygenenum:: gpio_int_type_t .. doxygenenum:: gpio_mode_t +.. doxygenenum:: gpio_pullup_t +.. doxygenenum:: gpio_pulldown_t .. doxygenenum:: gpio_pull_mode_t Functions @@ -37,54 +131,3 @@ Functions .. doxygenfunction:: gpio_wakeup_enable .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register - -*Example code:* Configuration of GPIO as an output - -.. code-block:: c - - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode - io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 - io_conf.pull_down_en = 0; //disable pull-down mode - io_conf.pull_up_en = 0; //disable pull-up mode - gpio_config(&io_conf); //configure GPIO with the given settings - -*Example code:* Configuration of GPIO as an input - -.. code-block:: c - - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt - io_conf.mode = GPIO_MODE_INPUT; //set as input - io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 - io_conf.pull_down_en = 0; //disable pull-down mode - io_conf.pull_up_en = 1; //enable pull-up mode - gpio_config(&io_conf); //configure GPIO with the given settings - - -ROM GPIO functions -^^^^^^^^^^^^^^^^^^ - -.. doxygenfunction:: gpio_init -.. doxygenfunction:: gpio_output_set -.. doxygenfunction:: gpio_output_set_high -.. doxygenfunction:: gpio_input_get -.. doxygenfunction:: gpio_input_get_high -.. doxygenfunction:: gpio_intr_handler_register -.. doxygenfunction:: gpio_intr_pending -.. doxygenfunction:: gpio_intr_pending_high -.. doxygenfunction:: gpio_intr_ack -.. doxygenfunction:: gpio_intr_ack_high -.. doxygenfunction:: gpio_pin_wakeup_enable -.. doxygenfunction:: gpio_pin_wakeup_disable -.. doxygenfunction:: gpio_matrix_in -.. doxygenfunction:: gpio_matrix_out -.. doxygenfunction:: gpio_pad_select_gpio -.. doxygenfunction:: gpio_pad_set_drv -.. doxygenfunction:: gpio_pad_pullup -.. doxygenfunction:: gpio_pad_pulldown -.. doxygenfunction:: gpio_pad_unhold -.. doxygenfunction:: gpio_pad_hold - - diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst new file mode 100644 index 0000000000..32b639f3f9 --- /dev/null +++ b/docs/api/ledc.rst @@ -0,0 +1,61 @@ +LED Control +=========== + +Overview +-------- + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +API Reference +------------- + +`Instructions`_ + +.. _Instructions: template.html + +Data Structures +^^^^^^^^^^^^^^^ + +.. doxygenstruct:: ledc_channel_config_t +.. doxygenstruct:: ledc_timer_config_t + +Macros +^^^^^^ + +.. doxygendefine:: LEDC_APB_CLK_HZ +.. doxygendefine:: LEDC_REF_CLK_HZ + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: ledc_mode_t +.. doxygenenum:: ledc_intr_type_t +.. doxygenenum:: ledc_duty_direction_t +.. doxygenenum:: ledc_clk_src_t +.. doxygenenum:: ledc_timer_t +.. doxygenenum:: ledc_channel_t +.. doxygenenum:: ledc_timer_bit_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: ledc_channel_config +.. doxygenfunction:: ledc_timer_config +.. doxygenfunction:: ledc_update_duty +.. doxygenfunction:: ledc_stop +.. doxygenfunction:: ledc_set_freq +.. doxygenfunction:: ledc_get_freq +.. doxygenfunction:: ledc_set_duty +.. doxygenfunction:: ledc_get_duty +.. doxygenfunction:: ledc_set_fade +.. doxygenfunction:: ledc_isr_register +.. doxygenfunction:: ledc_timer_set +.. doxygenfunction:: ledc_timer_rst +.. doxygenfunction:: ledc_timer_pause +.. doxygenfunction:: ledc_timer_resume +.. doxygenfunction:: ledc_bind_channel_timer diff --git a/docs/api/log.rst b/docs/api/log.rst index dc76a30470..49f97108aa 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -3,6 +3,33 @@ API Reference ------------- +Macros +^^^^^^ + +.. doxygendefine:: LOG_COLOR_E +.. doxygendefine:: LOG_COLOR_W +.. doxygendefine:: LOG_COLOR_I +.. doxygendefine:: LOG_COLOR_D +.. doxygendefine:: LOG_COLOR_V +.. doxygendefine:: LOG_RESET_COLOR +.. doxygendefine:: LOG_FORMAT +.. doxygendefine:: LOG_LOCAL_LEVEL +.. doxygendefine:: ESP_EARLY_LOGE +.. doxygendefine:: ESP_EARLY_LOGW +.. doxygendefine:: ESP_EARLY_LOGI +.. doxygendefine:: ESP_EARLY_LOGD +.. doxygendefine:: ESP_EARLY_LOGV +.. doxygendefine:: ESP_LOGE +.. doxygendefine:: ESP_LOGW +.. doxygendefine:: ESP_LOGI +.. doxygendefine:: ESP_LOGD +.. doxygendefine:: ESP_LOGV + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: vprintf_like_t + Enumerations ^^^^^^^^^^^^ @@ -17,3 +44,11 @@ Functions .. doxygenfunction:: esp_log_write + + + + + + + + diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst new file mode 100644 index 0000000000..16c74fa530 --- /dev/null +++ b/docs/api/nvs_flash.rst @@ -0,0 +1,67 @@ +.. include:: ../../components/nvs_flash/README.rst + +Application Example +------------------- + +`Instructions `_ + +API Reference +------------- + +Macros +^^^^^^ + +.. doxygendefine:: ESP_ERR_NVS_BASE +.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED +.. doxygendefine:: ESP_ERR_NVS_NOT_FOUND +.. doxygendefine:: ESP_ERR_NVS_TYPE_MISMATCH +.. doxygendefine:: ESP_ERR_NVS_READ_ONLY +.. doxygendefine:: ESP_ERR_NVS_NOT_ENOUGH_SPACE +.. doxygendefine:: ESP_ERR_NVS_INVALID_NAME +.. doxygendefine:: ESP_ERR_NVS_INVALID_HANDLE +.. doxygendefine:: ESP_ERR_NVS_REMOVE_FAILED +.. doxygendefine:: ESP_ERR_NVS_KEY_TOO_LONG +.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL +.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE +.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: nvs_handle + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: nvs_open_mode + +Functions +^^^^^^^^^ +.. doxygenfunction:: nvs_open +.. doxygenfunction:: nvs_set_i8 +.. doxygenfunction:: nvs_set_u8 +.. doxygenfunction:: nvs_set_i16 +.. doxygenfunction:: nvs_set_u16 +.. doxygenfunction:: nvs_set_i32 +.. doxygenfunction:: nvs_set_u32 +.. doxygenfunction:: nvs_set_i64 +.. doxygenfunction:: nvs_set_u64 +.. doxygenfunction:: nvs_set_str +.. doxygenfunction:: nvs_set_blob +.. doxygenfunction:: nvs_get_i8 +.. doxygenfunction:: nvs_get_u8 +.. doxygenfunction:: nvs_get_i16 +.. doxygenfunction:: nvs_get_u16 +.. doxygenfunction:: nvs_get_i32 +.. doxygenfunction:: nvs_get_u32 +.. doxygenfunction:: nvs_get_i64 +.. doxygenfunction:: nvs_get_u64 +.. doxygenfunction:: nvs_get_str +.. doxygenfunction:: nvs_get_blob +.. doxygenfunction:: nvs_erase_key +.. doxygenfunction:: nvs_erase_all +.. doxygenfunction:: nvs_commit +.. doxygenfunction:: nvs_close +.. doxygenfunction:: nvs_flash_init +.. doxygenfunction:: nvs_flash_init_custom + diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 97ea1a5848..122a8671ea 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -1,15 +1,19 @@ .. include:: ../../components/vfs/README.rst +Application Example +------------------- + +`Instructions `_ + API Reference ------------- -Defines -^^^^^^^ +Macros +^^^^^^ -.. doxygendefine:: ESP_VFS_PATH_MAX +.. doxygendefine:: ESP_VFS_PATH_MAX .. doxygendefine:: ESP_VFS_FLAG_DEFAULT -.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR - +.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR Structures ^^^^^^^^^^ @@ -19,9 +23,15 @@ Structures Functions ^^^^^^^^^ -.. doxygenfunction:: esp_vfs_dev_uart_register .. doxygenfunction:: esp_vfs_register - - - - +.. doxygenfunction:: esp_vfs_write +.. doxygenfunction:: esp_vfs_lseek +.. doxygenfunction:: esp_vfs_read +.. doxygenfunction:: esp_vfs_open +.. doxygenfunction:: esp_vfs_close +.. doxygenfunction:: esp_vfs_fstat +.. doxygenfunction:: esp_vfs_stat +.. doxygenfunction:: esp_vfs_link +.. doxygenfunction:: esp_vfs_unlink +.. doxygenfunction:: esp_vfs_rename +.. doxygenfunction:: esp_vfs_dev_uart_register From c3f7d15246506cac2f948565e9c9d830e6478a9a Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:19:31 +0100 Subject: [PATCH 026/131] Instructions to prepare documentation --- docs/api/template.rst | 87 ++++++++++++++++++------------ docs/documenting-code.rst | 111 ++++++++++++++++++++++++-------------- 2 files changed, 126 insertions(+), 72 deletions(-) diff --git a/docs/api/template.rst b/docs/api/template.rst index 0f2623c47f..3c8bcdb62c 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -1,70 +1,91 @@ -Template API -============= +Template +======== + +.. note:: + + *INSTRUCTIONS* + + 1. Use this file as a template to document API. + 2. Change the file name to the name of the header file that represents documented API. + 3. Include respective files with descriptions from the API folder using ``..include::`` + + * README.rst + * example.rst + + 4. Optionally provide description right in this file. + 5. Once done, remove all instructions like this one and any superfluous headers. Overview -------- -INSTRUCTIONS: Provide overview where and how this API may be used. For large number of functions, break down description into groups. +.. note:: -Use the folowing heading levels: + *INSTRUCTIONS* -* # with overline, for parts -* \* with overline, for chapters -* =, for sections -* -, for subsections -* ^, for subsubsections -* ", for paragraphs + 1. Provide overview where and how this API may be used. + 2. Where applicable include code snippets to illustrate functionality of particular functions. + 3. To distinguish between sections, use the following `heading levels `_: + * ``#`` with overline, for parts + * ``*`` with overline, for chapters + * ``=``, for sections + * ``-``, for subsections + * ``^``, for subsubsections + * ``"``, for paragraphs Application Example ------------------- -INSTRUCTIONS: Provide one or more pratical examples to demonstrate functionality of this API. +.. note:: + *INSTRUCTIONS* -Reference ---------- + 1. Provide one or more practical examples to demonstrate functionality of this API. + 2. Break down the code into parts and describe functionality of each part. + 3. Provide screenshots if applicable. -INSTRUCTIONS: Provide list of API memebers divided into sections. Use coresponding **.. doxygen** directices, so member documentation is auto updated. +API Reference +------------- -* Data Structures **.. doxygenstruct** -* Macros **.. doxygendefine** -* Type Definitions **.. doxygentypedef** -* Enumerations **.. doxygenenum** -* Functions **.. doxygenfunction** -* Variables **.. doxygenvariable** +.. note:: -Include code snippotes to ilustrate functionality of particular functions where applicable. Skip section hearder if empty. + *INSTRUCTIONS* + + 1. Provide list of API members divided into sections. + 2. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. + * Data Structures -``.. doxygenstruct::`` + * Macros - ``.. doxygendefine::`` + * Type Definitions - ``.. doxygentypedef::`` + * Enumerations - ``.. doxygenenum::`` + * Functions - ``.. doxygenfunction::`` + + See `Breathe documentation `_ for additional information. + + 3. Once done remove superfluous headers. + 4. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. Data Structures ^^^^^^^^^^^^^^^ -.. Data Structures .. doxygenstruct +``.. doxygenstruct:: name_of_structure`` Macros ^^^^^^ -.. Macros .. doxygendefine +``.. doxygendefine:: name_of_macro`` Type Definitions ^^^^^^^^^^^^^^^^ -.. Type Definitions .. doxygentypedef +``.. doxygentypedef:: name_of_type`` Enumerations ^^^^^^^^^^^^ -.. Enumerations .. doxygenenum +``.. doxygenenum:: name_of_enumeration`` Functions ^^^^^^^^^ -.. Functions .. doxygenfunction - -Variables -^^^^^^^^^ - -.. Variables .. doxygenvariable - - +``.. doxygenfunction:: name_of_function`` diff --git a/docs/documenting-code.rst b/docs/documenting-code.rst index 51a0dbf7d3..72e3cea147 100644 --- a/docs/documenting-code.rst +++ b/docs/documenting-code.rst @@ -1,10 +1,18 @@ Documenting Code ================ +The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf`_ repository and how to add new documentation. + Introduction ------------ -When documenting code for this repository, please follow `Doxygen style `_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``. +When documenting code for this repository, please follow `Doxygen style `_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks, for example: + +:: + + /** + * @param ratio this is oxygen to air ratio + */ Doxygen is phrasing the code, extracting the commands together with subsequent text, and building documentation out of it. @@ -14,20 +22,19 @@ Typical comment block, that contains documentation of a function, looks like bel :align: center :alt: Sample inline code documentation -Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual `_. +Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get familiar with available features, please check data reach and very well organized `Doxygen Manual `_. Why we need it? --------------- -The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf `_ repository. +The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx `_ and `Breathe `_ to aid preparation and automatic updates of API documentation when the code changes. -The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx `_ and `Breathe `_ to aid preparation and automatic updates of API documentation when the code changes. The above piece of code renders in Sphinx like below: +With these tools the above piece of code renders like below: .. image:: _static/doc-code-documentation-rendered.png :align: center :alt: Sample inline code after rendering - Go for it! ---------- @@ -57,7 +64,7 @@ When writing code for this repository, please follow guidelines below. 6. To provide well formatted lists, break the line after command (like ``@return`` in example below). - :: + .. code-block:: c ... * @@ -70,53 +77,76 @@ When writing code for this repository, please follow guidelines below. * ... - - 7. Overview of functionality of documented header file, or group of files that make a library, should be placed in separate ``README.rst`` file. + 7. Overview of functionality of documented header file, or group of files that make a library, should be placed in the same directory in a separate ``README.rst`` file. If directory contains header files for different APIs, then the file name should be ``apiname-readme.rst``. Go one extra mile ----------------- -There are couple of tips how you can make your documentation even better and more useful to the reader. +There is couple of tips, how you can make your documentation even better and more useful to the reader. -Add code snippets to illustrate implementation. To do so, enclose the snippet using ``@code{c}`` and ``@endcode`` commands. + 1. Add code snippets to illustrate implementation. To do so, enclose snippet using ``@code{c}`` and ``@endcode`` commands. -:: + .. code-block:: c - ... - * - * @code{c} - * // Example of using nvs_get_i32: - * int32_t max_buffer_size = 4096; // default value - * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); - * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); - * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still - * // have its default value. - * @endcode - * - ... + ... + * + * @code{c} + * // Example of using nvs_get_i32: + * int32_t max_buffer_size = 4096; // default value + * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); + * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); + * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still + * // have its default value. + * @endcode + * + ... -To highlight some information use command ``@attention`` or ``@note``. Example below also shows how to use a numbered list. + The code snippet should be enclosed in a comment block of the function that it illustrates. -:: + 2. To highlight some important information use command ``@attention`` or ``@note``. - ... - * - * @attention - * 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode - * 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. - * - ... + .. code-block:: c + ... + * + * @attention + * 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode + * 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. + * + ... -Use markdown to make your documentation even more readable. With markdown you can add headers, links, tables and more. + Above example also shows how to use a numbered list. -:: + 3. Use markdown to make your documentation even more readable. You will add headers, links, tables and more. - ... - * - * [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) - * - ... + .. code-block:: c + + ... + * + * [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) + * + ... + + .. note:: + + Code snippets, notes, links, etc. will not make it to the documentation, if not enclosed in a comment block associated with one of documented objects. + + 5. Prepare one or more complete code examples together with description. Place them in a separate file ``example.rst`` in the same directory as the API header files. If directory contains header files for different APIs, then the file name should be ``apiname-example.rst``. + +Put it all together +------------------- + +Once all the above steps are complete, follow instruction in :doc:`api/template` and create a single file, that will merge all individual pieces of prepared documentation. Finally add a link to this file to respective ``.. toctree::`` in ``index.rst`` file located in ``/docs`` folder. + +OK, but I am new to Sphinx! +--------------------------- + +1. No worries. All the software you need is well documented. It is also open source and free. Start by checking `Sphinx `_ documentation. If you are not clear how to write using rst markup language, see `reStructuredText Primer `_. +2. Check the source files of this documentation to understand what is behind of what you see now on the screen. Sources are maintained on GitHub in `espressif/esp-idf`_ repository in `/docs `_ folder. You can go directly to the source file of this page by scrolling up and clicking the link in the top right corner. When on GitHub, see what's really inside, open source files by clicking ``Raw`` button. +3. You will likely want to see how documentation builds and looks like before posting it on the GitHub. There are two options to do so: + + * Install `Sphinx `_, `Breathe `_ and `Doxygen `_ to build it locally. You would need a Linux machine for that. + * Set up an account on `Read the Docs `_ and build documentation in the cloud. Read the Docs provides document building and hosting for free and their service works really quick and great. Wrap up ------- @@ -124,3 +154,6 @@ Wrap up We love good code that is doing cool things. We love it even better, if it is well documented, so we can quickly make it run and also do the cool things. +Go ahead, contribute your code and documentation! + +.. _espressif/esp-idf: https://github.com/espressif/esp-idf/ From 025bb473020da4b1c2f8b53987efeb4e7ab2fea0 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 6 Nov 2016 17:41:08 +0100 Subject: [PATCH 027/131] Non-Volatile Storage (NVS) example Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. Example also shows how to use basic diagnostics if read / write operation was successful. --- examples/07_nvs_read_write/Makefile | 9 +++ examples/07_nvs_read_write/README.md | 7 +++ examples/07_nvs_read_write/main/component.mk | 10 +++ .../07_nvs_read_write/main/nvs_read_write.c | 62 +++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 examples/07_nvs_read_write/Makefile create mode 100644 examples/07_nvs_read_write/README.md create mode 100644 examples/07_nvs_read_write/main/component.mk create mode 100644 examples/07_nvs_read_write/main/nvs_read_write.c diff --git a/examples/07_nvs_read_write/Makefile b/examples/07_nvs_read_write/Makefile new file mode 100644 index 0000000000..3d6adb4d02 --- /dev/null +++ b/examples/07_nvs_read_write/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := nvs-read-write + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/07_nvs_read_write/README.md b/examples/07_nvs_read_write/README.md new file mode 100644 index 0000000000..bac8ee8d54 --- /dev/null +++ b/examples/07_nvs_read_write/README.md @@ -0,0 +1,7 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. + +Example also shows how to use basic diagnostics if read / write operation was successful. + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/component.mk b/examples/07_nvs_read_write/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/07_nvs_read_write/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/07_nvs_read_write/main/nvs_read_write.c b/examples/07_nvs_read_write/main/nvs_read_write.c new file mode 100644 index 0000000000..40d330f62f --- /dev/null +++ b/examples/07_nvs_read_write/main/nvs_read_write.c @@ -0,0 +1,62 @@ +/* Non-Volatile Storage (NVS) Read and Write Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" + +void app_main() +{ + nvs_flash_init(); + system_init(); + + nvs_handle handle_to_settings; + esp_err_t err; + int32_t restart_counter = 0; + + // Open the NVS + printf("Opening Non-Volatile Storage (NVS) ... "); + err = nvs_open("settings", NVS_READWRITE, &handle_to_settings); + printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); + + // Read from the NVS + printf("Reading restart counter from NVS ... "); + err = nvs_get_i32(handle_to_settings, "restart_conter", &restart_counter); + switch (err) { + case ESP_OK: + printf("OK\n"); + printf("Restart counter = %d\n", restart_counter); + break; + case ESP_ERR_NVS_NOT_FOUND: + printf("The counter is not initialized yet!\n"); + break; + default : + printf("Error (%d) reading!\n", err); + } + + // Write to the NVS + printf("Updating restart counter in NVS ... "); + restart_counter++; + err = nvs_set_i32(handle_to_settings, "restart_conter", restart_counter); + printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); + + // Close the NVS + nvs_close(handle_to_settings); + + // Restart module + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + system_restart(); +} From e452278194ff16696519444fb9132b3ffa143dc0 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 7 Nov 2016 14:16:52 +0800 Subject: [PATCH 028/131] Minor changes for driver 1. remove "\n" when calling ESP_LOGX APIs. 2. modify uart_event_t for uart rx data. 3. use MICRO for uart inverse value 4. add uart_tx_data_t for internal tx function. --- components/driver/gpio.c | 59 ++++++++++--------- components/driver/include/driver/uart.h | 39 +++++-------- components/driver/ledc.c | 78 ++++++++++++------------- components/driver/uart.c | 48 +++++++++------ 4 files changed, 114 insertions(+), 110 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 62a0e7faa7..b445d3df03 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -22,7 +22,7 @@ static const char* GPIO_TAG = "GPIO"; #define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ } @@ -71,15 +71,15 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); - GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_type = intr_type; return ESP_OK; } esp_err_t gpio_intr_enable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(xPortGetCoreID() == 0) { GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr } else { @@ -90,14 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num) esp_err_t gpio_intr_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr return ESP_OK; } static esp_err_t gpio_output_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1tc = (0x1 << gpio_num); } else { @@ -108,7 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num) static esp_err_t gpio_output_enable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1ts = (0x1 << gpio_num); } else { @@ -119,7 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(level) { if(gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); @@ -147,8 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num) esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); - GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: @@ -168,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); break; default: - ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); + ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull); ret = ESP_ERR_INVALID_ARG; break; } @@ -177,9 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) { - ESP_LOGE(GPIO_TAG, "io_num=%d can only be input\n",gpio_num); + ESP_LOGE(GPIO_TAG, "io_num=%d can only be input",gpio_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -214,53 +214,56 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) uint64_t gpio_pin_mask = (pGPIOConfig->pin_bit_mask); uint32_t io_reg = 0; uint32_t io_num = 0; - uint64_t bit_valid = 0; + uint8_t input_en = 0; + uint8_t output_en = 0; + uint8_t od_en = 0; + uint8_t pu_en = 0; + uint8_t pd_en = 0; if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) { - ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error \n"); + ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error "); return ESP_ERR_INVALID_ARG; } if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) { //GPIO 34/35/36/37/38/39 can only be used as input mode; if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) { - ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode\n"); + ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode"); return ESP_ERR_INVALID_ARG; } } do { io_reg = GPIO_PIN_MUX_REG[io_num]; if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) { - ESP_LOGI(GPIO_TAG, "Gpio%02d |Mode:",io_num); if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) { - ESP_LOGI(GPIO_TAG, "INPUT "); + input_en = 1; PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]); } else { PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]); } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) { - ESP_LOGI(GPIO_TAG, "OD "); + od_en = 1; GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */ } else { GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */ } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) { - ESP_LOGI(GPIO_TAG, "OUTPUT "); + output_en = 1; gpio_output_enable(io_num); } else { gpio_output_disable(io_num); } if(pGPIOConfig->pull_up_en) { - ESP_LOGI(GPIO_TAG, "PU "); + pu_en = 1; PIN_PULLUP_EN(io_reg); } else { PIN_PULLUP_DIS(io_reg); } if(pGPIOConfig->pull_down_en) { - ESP_LOGI(GPIO_TAG, "PD "); + pd_en = 1; PIN_PULLDWN_EN(io_reg); } else { PIN_PULLDWN_DIS(io_reg); } - ESP_LOGI(GPIO_TAG, "Intr:%d |\n",pGPIOConfig->intr_type); + ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); if(pGPIOConfig->intr_type) { gpio_intr_enable(io_num); @@ -268,8 +271,6 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) gpio_intr_disable(io_num); } PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */ - } else if(bit_valid && (io_reg == 0)) { - ESP_LOGW(GPIO_TAG, "io_num=%d does not exist\n",io_num); } io_num++; } while(io_num < GPIO_PIN_COUNT); @@ -278,7 +279,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) { - GPIO_CHECK(fn, "GPIO ISR null\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); ESP_INTR_DISABLE(gpio_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); xt_set_interrupt_handler(gpio_intr_num, fn, arg); @@ -289,13 +290,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar /*only level interrupt can be used for wake-up function*/ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { - ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); + ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u",gpio_num); ret = ESP_ERR_INVALID_ARG; } return ret; @@ -303,7 +304,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].wakeup_enable = 0; return ESP_OK; } diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index ba487d57d5..7dccf1666c 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -38,12 +38,18 @@ extern "C" { #define UART_BITRATE_MAX 5000000 #define UART_PIN_NO_CHANGE (-1) +#define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/ +#define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/ +#define UART_INVERSE_CTS (UART_CTS_INV_M) /*!< UART CTS input inverse*/ +#define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/ +#define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/ + typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/ UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/ - UART_DATA_MAX_BITS = 0X4, + UART_DATA_BITS_MAX = 0X4, } uart_word_length_t; typedef enum { @@ -74,14 +80,6 @@ typedef enum { UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; -typedef enum { - UART_INVERSE_DISABLE = 0x0, /*!< Disable UART wire output inverse*/ - UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, /*!< UART RXD input inverse*/ - UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, /*!< UART CTS input inverse*/ - UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, /*!< UART TXD output inverse*/ - UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, /*!< UART RTS output inverse*/ -} uart_inverse_t; - typedef struct { int baud_rate; /*!< UART baudrate*/ uart_word_length_t data_bits; /*!< UART byte size*/ @@ -110,14 +108,8 @@ typedef enum { } uart_event_type_t; typedef struct { - uart_event_type_t type; - union { - struct { - int brk_len; - size_t size; - uint8_t data[]; - } data; - }; + uart_event_type_t type; /*!< UART event type */ + size_t size; /*!< UART data size for UART_DATA event*/ } uart_event_t; /** @@ -225,14 +217,13 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * * @param inverse_mask Choose the wires that need to be inversed. * - * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) + * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; - +esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); /** * @brief Set hardware flow control. @@ -701,11 +692,13 @@ esp_err_t uart_flush(uart_port_t uart_num); * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { + * memset(dtmp, 0, sizeof(dtmp)); * //Event of UART receving data * case UART_DATA: - * ESP_LOGI(TAG,"data, len: %d\n", event.data.size); - * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(TAG, "uart read: %d\n", len); + * ESP_LOGI(TAG,"data, len: %d", event.size); + * int len = uart_read_bytes(uart_num, dtmp, event.size, 10); + * ESP_LOGI(TAG, "uart read: %d", len); + uart_write_bytes(uart_num, (const char*)dtmp, len); * break; * //Event of HW FIFO overflow detected * case UART_FIFO_OVF: diff --git a/components/driver/ledc.c b/components/driver/ledc.c index b9039cf626..41eb82cbdd 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -23,14 +23,14 @@ static const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; #define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ } esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; @@ -58,8 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -68,8 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -79,8 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -89,8 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -99,7 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); @@ -115,7 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) { - LEDC_CHECK(fn, "ledc isr null\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); ESP_INTR_DISABLE(ledc_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); @@ -131,13 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { - ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u\n", freq_hz, bit_num); + ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } if(timer_num > LEDC_TIMER_3) { - ESP_LOGE(LEDC_TAG, "Time Select %u\n", timer_num); + ESP_LOGE(LEDC_TAG, "Time Select %u", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -149,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) /*Selet the reference tick*/ div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", (uint32_t)div_param); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", (uint32_t)div_param); ret = ESP_FAIL; } timer_clk_src = LEDC_REF_TICK; @@ -166,9 +166,9 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) { - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); if(speed_mode == LEDC_HIGH_SPEED_MODE) { @@ -187,10 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; /*set channel parameters*/ /* channel parameters decide how the waveform looks like in one period*/ @@ -202,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", + ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -214,8 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -225,8 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -237,11 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG); if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { - ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); + ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } ledc_duty_config(speed_mode, @@ -258,8 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -274,14 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (-1)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; @@ -294,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; } if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { - ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", div_num); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num); ret = ESP_FAIL; } LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num; @@ -304,7 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (0)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0)); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; diff --git a/components/driver/uart.c b/components/driver/uart.c index a3e0b92b2b..b961fbed71 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -31,7 +31,7 @@ static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ - ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(UART_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ } #define UART_EMPTY_THRESH_DEFAULT (10) @@ -42,6 +42,14 @@ static const char* UART_TAG = "UART"; #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) +typedef struct { + uart_event_type_t type; /*!< UART TX data type */ + struct { + int brk_len; + size_t size; + uint8_t data[0]; + } tx_data; +} uart_tx_data_t; typedef struct { uart_port_t uart_num; /*!< UART port number*/ @@ -67,7 +75,7 @@ typedef struct { RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler*/ bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/ uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ - uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ + uart_tx_data_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ uint32_t tx_len_cur; uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ @@ -75,6 +83,8 @@ typedef struct { uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ } uart_obj_t; + + static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; @@ -82,7 +92,7 @@ static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error", ESP_FAIL); + UART_CHECK((data_bit < UART_DATA_BITS_MAX), "data bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.bit_num = data_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -366,7 +376,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n", ESP_FAIL); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_rts = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -385,7 +395,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((uart_config), "param null\n", ESP_FAIL); + UART_CHECK((uart_config), "param null", ESP_FAIL); if(uart_num == UART_NUM_0) { periph_module_enable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { @@ -407,7 +417,7 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((intr_conf), "param null\n", ESP_FAIL); + UART_CHECK((intr_conf), "param null", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; if(intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { @@ -468,17 +478,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) while(tx_fifo_rem) { if(p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) { size_t size; - p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + p_uart->tx_head = (uart_tx_data_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); if(p_uart->tx_head) { //The first item is the data description //Get the first item to get the data information if(p_uart->tx_len_tot == 0) { p_uart->tx_ptr = NULL; - p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_len_tot = p_uart->tx_head->tx_data.size; if(p_uart->tx_head->type == UART_DATA_BREAK) { - p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_len_tot = p_uart->tx_head->tx_data.size; p_uart->tx_brk_flg = 1; - p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + p_uart->tx_brk_len = p_uart->tx_head->tx_data.brk_len; } //We have saved the data description from the 1st item, return buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); @@ -553,7 +563,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.rxfifo_full = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; - uart_event.data.size = rx_fifo_len; + uart_event.size = rx_fifo_len; //If we fail to push data to ring buffer, we will have to stash the data, and send next time. //Mainly for applications that uses flow control or small ring buffer. if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &HPTaskAwoken)) { @@ -711,9 +721,9 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool if(p_uart_obj[uart_num]->tx_buf_size > 0) { int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); int offset = 0; - uart_event_t evt; - evt.data.size = size; - evt.data.brk_len = brk_len; + uart_tx_data_t evt; + evt.tx_data.size = size; + evt.tx_data.brk_len = brk_len; if(brk_en) { evt.type = UART_DATA_BREAK; } else { @@ -882,12 +892,12 @@ esp_err_t uart_flush(uart_port_t uart_num) esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n", ESP_FAIL); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { - ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + ESP_LOGE(UART_TAG, "UART driver malloc error"); return ESP_FAIL; } p_uart_obj[uart_num]->uart_num = uart_num; @@ -909,7 +919,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; - ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + ESP_LOGI(UART_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); } else { p_uart_obj[uart_num]->xQueueUart = NULL; } @@ -927,7 +937,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_buf_size = 0; } } else { - ESP_LOGE(UART_TAG, "UART driver already installed\n"); + ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); @@ -951,7 +961,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { - ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + ESP_LOGI(UART_TAG, "ALREADY NULL"); return ESP_OK; } ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); From 98a0387854918b73d10e00820e40a36786ee8eab Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 17:54:47 +1100 Subject: [PATCH 029/131] bootloader_support: Move secure boot code to bootloader_support --- .../bootloader/src/main/bootloader_config.h | 1 - .../bootloader/src/main/bootloader_start.c | 7 +- .../{esp_secureboot.h => esp_secure_boot.h} | 24 +++- .../src}/secure_boot.c | 128 +++++++++--------- .../bootloader_support/src/secureboot.c | 7 - 5 files changed, 85 insertions(+), 82 deletions(-) rename components/bootloader_support/include/{esp_secureboot.h => esp_secure_boot.h} (64%) rename components/{bootloader/src/main => bootloader_support/src}/secure_boot.c (71%) delete mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 0823581824..4559d5f816 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -60,7 +60,6 @@ typedef struct { } bootloader_state_t; bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3bc2696a4b..59b180fd84 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -34,6 +34,7 @@ #include "sdkconfig.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -214,6 +215,7 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -308,8 +310,9 @@ void bootloader_main() if(fhdr.secure_boot_flag == 0x01) { /* Generate secure digest from this bootloader to protect future modifications */ - if (secure_boot_generate_bootloader_digest() == false){ - ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK){ + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably due to user-configured EFUSEs for testing... */ diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secure_boot.h similarity index 64% rename from components/bootloader_support/include/esp_secureboot.h rename to components/bootloader_support/include/esp_secure_boot.h index b0097df8a6..4bf2dc8b23 100644 --- a/components/bootloader_support/include/esp_secureboot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -16,6 +16,7 @@ #include #include +#include "soc/efuse_reg.h" /* Support functions for secure boot features. @@ -30,21 +31,34 @@ * * @return true if secure boot is enabled. */ -bool esp_secure_boot_enabled(void); +static inline bool esp_secure_boot_enabled(void) { + return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); +} -/** @brief Enable secure boot if it isw not already enabled. +/** @brief Enable secure boot if it is not already enabled. * - * @important If this function succeeds, secure boot is permanentl + * @important If this function succeeds, secure boot is permanently * enabled on the chip via efuse. * - * This function is intended to be called from bootloader code. + * @important This function is intended to be called from bootloader code only. + * + * If secure boot is not yet enabled for bootloader, this will + * generate the secure boot digest and enable secure boot by blowing + * the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * Will fail if efuses have been part-burned in a way that indicates + * secure boot should not or could not be correctly enabled. + * * * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow * secure boot to be enabled cleanly. ESP_OK if secure boot * is enabled on this chip from now on. */ -esp_err_t esp_secure_boot_enable(void); +esp_err_t esp_secure_boot_permanently_enable(void); diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader_support/src/secure_boot.c similarity index 71% rename from components/bootloader/src/main/secure_boot.c rename to components/bootloader_support/src/secure_boot.c index 2b1b8573fc..c17aebfbea 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -30,72 +30,81 @@ #include "sdkconfig.h" -#include "bootloader_config.h" #include "bootloader_flash.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" static const char* TAG = "secure_boot"; +#define HASH_BLOCK_SIZE 128 +#define IV_LEN HASH_BLOCK_SIZE +#define DIGEST_LEN 64 + /** * @function : secure_boot_generate - * @description: generate boot abstract & iv + * @description: generate boot digest (aka "abstract") & iv * - * @inputs: bool + * @inputs: image_len - length of image to calculate digest for */ static bool secure_boot_generate(uint32_t image_len){ - SpiFlashOpResult spiRet; - uint32_t buf[32]; + SpiFlashOpResult spiRet; + /* buffer is uint32_t not uint8_t to meet ROM SPI API signature */ + uint32_t buf[IV_LEN / sizeof(uint32_t)]; const void *image; - if (image_len % 128 != 0) { - image_len = (image_len / 128 + 1) * 128; - } - ets_secure_boot_start(); - ets_secure_boot_rd_iv(buf); - ets_secure_boot_hash(NULL); - Cache_Read_Disable(0); - /* iv stored in sec 0 */ - spiRet = SPIEraseSector(0); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - Cache_Read_Enable(0); + /* hardware secure boot engine only takes full blocks, so round up the + image length. The additional data should all be 0xFF. + */ + if (image_len % HASH_BLOCK_SIZE != 0) { + image_len = (image_len / HASH_BLOCK_SIZE + 1) * HASH_BLOCK_SIZE; + } + ets_secure_boot_start(); + ets_secure_boot_rd_iv(buf); + ets_secure_boot_hash(NULL); + Cache_Read_Disable(0); + /* iv stored in sec 0 */ + spiRet = SPIEraseSector(0); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI erase failed %d", spiRet); + return false; + } + Cache_Read_Enable(0); - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ ESP_LOGD(TAG, "write iv to flash."); - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } + spiRet = SPIWrite(0, buf, IV_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + bzero(buf, sizeof(buf)); - /* generate digest from image contents */ + /* generate digest from image contents */ image = bootloader_mmap(0x1000, image_len); if (!image) { ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); return false; } - for (int i = 0; i < image_len; i+=128) { - ets_secure_boot_hash(image + i/sizeof(void *)); - } + for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } bootloader_unmap(image); - ets_secure_boot_obtain(); - ets_secure_boot_rd_abstract(buf); - ets_secure_boot_finish(); + ets_secure_boot_obtain(); + ets_secure_boot_rd_abstract(buf); + ets_secure_boot_finish(); - ESP_LOGD(TAG, "write abstract to flash."); - spiRet = SPIWrite(0x80, buf, 64); - if (spiRet != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - ESP_LOGD(TAG, "write abstract to flash."); - Cache_Read_Enable(0); - return true; + ESP_LOGD(TAG, "write digest to flash."); + spiRet = SPIWrite(0x80, buf, DIGEST_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + ESP_LOGD(TAG, "write digest to flash."); + Cache_Read_Enable(0); + return true; } /* Burn values written to the efuse write registers */ @@ -109,34 +118,19 @@ static inline void burn_efuses() while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ } -/** - * @brief Enable secure boot if it is not already enabled. - * - * Called if the secure boot flag is set on the - * bootloader image in flash. If secure boot is not yet enabled for - * bootloader, this will generate the secure boot digest and enable - * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. - * - * This function does not verify secure boot of the bootloader (the - * ROM bootloader does this.) - * - * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) false - * implies an error occured (possibly secure boot is part-enabled.) - */ -bool secure_boot_generate_bootloader_digest(void) { +esp_err_t esp_secure_boot_permanently_enable(void) { esp_err_t err; uint32_t image_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); - return true; + return ESP_OK; } err = esp_image_basic_verify(0x1000, &image_len); if (err != ESP_OK) { ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); - return false; + return err; } uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); @@ -178,17 +172,17 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGI(TAG, "Generating secure boot digest..."); if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); - return false; + return ESP_FAIL; } ESP_LOGI(TAG, "Digest generation complete."); if (!efuse_key_read_protected) { ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } if (!efuse_key_write_protected) { ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); @@ -200,9 +194,9 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); if (after & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); - return true; + return ESP_OK; } else { ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); - return false; + return ESP_ERR_INVALID_STATE; } } diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c deleted file mode 100644 index e55c1f33fa..0000000000 --- a/components/bootloader_support/src/secureboot.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -#include "esp_log.h" - - - From fce359b240cc3869f609256701a202c788ba4634 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 12:51:47 +1100 Subject: [PATCH 030/131] build system: Add support for embedded arbitrary binary or text files in .rodata Simplifies examples of embedding a certificate file or a root cert. This is a much cruder mechanism than the full flash filesystem we want eventually, but still sometimes useful. --- docs/build_system.rst | 22 +++++++ examples/04_https_request/main/cert.c | 44 -------------- examples/04_https_request/main/component.mk | 9 ++- .../main/https_request_main.c | 18 +++++- .../main/server_root_cert.pem | 27 +++++++++ make/component_common.mk | 57 ++++++++++++++++--- 6 files changed, 117 insertions(+), 60 deletions(-) delete mode 100644 examples/04_https_request/main/cert.c create mode 100644 examples/04_https_request/main/server_root_cert.pem diff --git a/docs/build_system.rst b/docs/build_system.rst index 34db487e0a..85ebfe46d5 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -280,6 +280,28 @@ component and resides under the component path. Because logo.h is a generated file, it needs to be cleaned when make clean is called which why it is added to the COMPONENT_EXTRA_CLEAN variable. +Embedding Binary Data +===================== + +Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source. + +You can set a variable COMPONENT_EMBED_FILES in component.mk, giving the names of the files to embed in this way:: + + COMPONENT_EMBED_FILES := server_root_cert.der + +Or if the file is a string, you can use the variable COMPONENT_EMBED_TXTFILES. This will embed the contents of the text file as a null-terminated string:: + + COMPONENT_EMBED_TXTFILES := server_root_cert.pem + +The file's contents will be added to the .rodata section in flash, and are available via symbol names as follows:: + + extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start"); + extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end"); + +The names are generated from the full name of the file, as given in COMPONENT_EMBED_FILES. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files. + +For an example of using this technique, see examples/04_https_request - the certificate file contents are loaded from the text .pem file at compile time. + Cosmetic Improvements ===================== diff --git a/examples/04_https_request/main/cert.c b/examples/04_https_request/main/cert.c deleted file mode 100644 index 7acc438ef3..0000000000 --- a/examples/04_https_request/main/cert.c +++ /dev/null @@ -1,44 +0,0 @@ -/* This is the CA certificate for the CA trust chain of - www.howsmyssl.com in PEM format, as dumped via: - - openssl s_client -showcerts -connect www.howsmyssl.com:443 -#include -#include - -/* - 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 - i:/O=Digital Signature Trust Co./CN=DST Root CA X3 - */ -const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n" -"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n" -"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n" -"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n" -"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n" -"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n" -"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n" -"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n" -"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n" -"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n" -"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n" -"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n" -"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n" -"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n" -"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n" -"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n" -"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n" -"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n" -"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n" -"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n" -"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n" -"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n" -"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n" -"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n" -"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n" -"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n" -"-----END CERTIFICATE-----\r\n"; - - diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index 24356f23ed..7fbfcc55d2 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,10 +1,9 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := server_root_cert.pem include $(IDF_PATH)/make/component_common.mk diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 0c8b2463f8..7f302409d8 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -74,8 +74,18 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.1\n" "User-Agent: esp-idf/1.0 esp32\n" "\n"; -/* Root cert for howsmyssl.com, found in cert.c */ -extern const char *server_root_cert; +/* Root cert for howsmyssl.com, taken from server_root_cert.pem + + The PEM file was extracted from the output of this command: + openssl s_client -showcerts -connect www.howsmyssl.com:443 > $$(notdir $$<) ) + $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ + $$(Q) rm $$(notdir $$<) +endef + +# generate targets to embed binary & text files +$(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin))) + +$(foreach txtfile,$(COMPONENT_EMBED_TXTFILES), $(eval $(call GenerateEmbedTarget,$(txtfile),txt))) + +# generate targets to create binary embed directories +$(foreach bindir,$(sort $(dir $(COMPONENT_EMBED_FILES))), $(eval $(call GenerateBuildDirTarget,$(bindir)))) From bdd7fb7a137c4b6e2bacc95976f6c901f590e9ed Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Mon, 7 Nov 2016 14:59:35 +0800 Subject: [PATCH 031/131] Add wps API --- components/esp32/component.mk | 2 +- components/esp32/include/esp_wps.h | 131 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 components/esp32/include/esp_wps.h diff --git a/components/esp32/component.mk b/components/esp32/component.mk index c658787d87..e483b58ee7 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -10,7 +10,7 @@ COMPONENT_SRCDIRS := . hwcrypto -LIBS := core net80211 phy rtc pp wpa smartconfig coexist +LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h new file mode 100644 index 0000000000..1588d36191 --- /dev/null +++ b/components/esp32/include/esp_wps.h @@ -0,0 +1,131 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_WPS_H__ +#define __ESP_WPS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup WiFi_APIs WiFi Related APIs + * @brief WiFi APIs + */ + +/** @addtogroup WiFi_APIs + * @{ + */ + +/** \defgroup WPS_APIs WPS APIs + * @brief ESP32 WPS APIs + * + * WPS can only be used when ESP32 station is enabled. + * + */ + +/** @addtogroup WPS_APIs + * @{ + */ + +typedef enum wps_type { + WPS_TYPE_DISABLE = 0, + WPS_TYPE_PBC, + WPS_TYPE_PIN, + WPS_TYPE_DISPLAY, + WPS_TYPE_MAX, +} WPS_TYPE_t; + +enum wps_cb_status { + WPS_CB_ST_SUCCESS = 0, /**< WPS succeed */ + WPS_CB_ST_FAILED, /**< WPS fail */ + WPS_CB_ST_TIMEOUT, /**< WPS timeout, fail */ + WPS_CB_ST_WEP, /**< WPS failed because that WEP is not supported */ + WPS_CB_ST_SCAN_ERR, /**< can not find the target WPS AP */ +}; + +/** + * @brief Enable Wi-Fi WPS function. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param WPS_TYPE_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported + * + * @return true : succeed + * @return false : fail + */ +bool esp_wifi_wps_enable(WPS_TYPE_t wps_type); + +/** + * @brief Disable Wi-Fi WPS function and release resource it taken. + * + * @param null + * + * @return true : succeed + * @return false : fail + */ +bool esp_wifi_wps_disable(void); + +/** + * @brief WPS starts to work. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param null + * + * @return true : WPS starts to work successfully, but does not mean WPS succeed. + * @return false : fail + */ +bool esp_wifi_wps_start(void); + +/** + * @brief WPS callback. + * + * @param int status : status of WPS, enum wps_cb_status. + * - If parameter status == WPS_CB_ST_SUCCESS in WPS callback, it means WPS got AP's + * information, user can call wifi_wps_disable to disable WPS and release resource, + * then call wifi_station_connect to connect to target AP. + * - Otherwise, it means that WPS fail, user can create a timer to retry WPS by + * wifi_wps_start after a while, or call wifi_wps_disable to disable WPS and release resource. + * + * @return null + */ +typedef void (*wps_st_cb_t)(int status); + +/** + * @brief Set WPS callback. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param wps_st_cb_t cb : callback. + * + * @return true : WPS starts to work successfully, but does not mean WPS succeed. + * @return false : fail + */ +bool esp_wifi_set_wps_cb(wps_st_cb_t cb); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_WPS_H__ */ From 830e5caf4de6c211ff0321d6d74010bafca35a0b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 12:51:55 +1100 Subject: [PATCH 032/131] build system: Replace get_variable target w/ component_project_vars.mk generated makefiles Reduces number of make invocations, allows variables exported in project to be seen in all component make processes, not just the main ones. Also makes a no-op build about 3x faster than it was. --- components/bootloader/src/main/component.mk | 2 +- components/bt/component.mk | 4 +- components/esp32/component.mk | 6 +- components/newlib/component.mk | 2 +- make/common.mk | 6 -- make/component_common.mk | 23 ++++-- make/project.mk | 86 ++++++++++----------- make/project_config.mk | 2 +- 8 files changed, 66 insertions(+), 65 deletions(-) diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index 8c8ea4cb63..bd1dcf4d90 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -7,6 +7,6 @@ # please read the esp-idf build system document if you need to do this. # -COMPONENT_ADD_LDFLAGS := -L $(abspath .) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld include $(IDF_PATH)/make/component_common.mk diff --git a/components/bt/component.mk b/components/bt/component.mk index e88651aa13..5addee2ceb 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -2,15 +2,13 @@ # Component Makefile # -#COMPONENT_ADD_INCLUDEDIRS := - COMPONENT_ADD_INCLUDEDIRS := include CFLAGS += -Wno-error=unused-label -Wno-error=return-type -Wno-error=missing-braces -Wno-error=pointer-sign -Wno-error=parentheses LIBS := btdm_app -COMPONENT_ADD_LDFLAGS := -lbt -L$(abspath lib) \ +COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ $(LINKER_SCRIPTS) diff --git a/components/esp32/component.mk b/components/esp32/component.mk index c658787d87..7e22b65183 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -15,10 +15,10 @@ LIBS := core net80211 phy rtc pp wpa smartconfig coexist LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld COMPONENT_ADD_LDFLAGS := -lesp32 \ - $(abspath libhal.a) \ - -L$(abspath lib) \ + $(COMPONENT_PATH)/libhal.a \ + -L$(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ - -L $(abspath ld) \ + -L $(COMPONENT_PATH)/ld \ $(LINKER_SCRIPTS) include $(IDF_PATH)/make/component_common.mk diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 3731b5ef0e..0648946c20 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -1,4 +1,4 @@ -COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) -lnewlib +COMPONENT_ADD_LDFLAGS := $(COMPONENT_PATH)/lib/libc.a $(COMPONENT_PATH)/lib/libm.a -lnewlib COMPONENT_ADD_INCLUDEDIRS := include platform_include diff --git a/make/common.mk b/make/common.mk index 2b7376a4db..6183cfbc46 100644 --- a/make/common.mk +++ b/make/common.mk @@ -2,12 +2,6 @@ # and component makefiles # -# Include project config file, if it exists. -# -# (Note that we only rebuild auto.conf automatically for some targets, -# see project_config.mk for details.) --include $(BUILD_DIR_BASE)/include/config/auto.conf - #Handling of V=1/VERBOSE=1 flag # # if V=1, $(summary) does nothing and $(details) will echo extra details diff --git a/make/component_common.mk b/make/component_common.mk index bf2eace019..3177ca6e30 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -58,10 +58,19 @@ COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) -#This target is used to collect variable values from inside project.mk -# see project.mk GetVariable macro for details. -get_variable: - @echo "$(GET_VARIABLE)=$(call $(GET_VARIABLE)) " +# This target is used to take component.mk variables COMPONENT_ADD_INCLUDEDIRS, +# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject them into the project +# makefile level. +# +# The target here has no dependencies, as the parent target in +# project.mk evaluates dependencies before calling down to here. See +# GenerateProjectVarsTarget in project.mk. +component_project_vars.mk:: + $(details) "Rebuilding component project variables list $(abspath $@)" + @echo "# Automatically generated build file. Do not edit." > $@ + @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ + @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ + @echo "$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))" >> $@ #Targets for build/clean. Use builtin recipe if component Makefile #hasn't defined its own. @@ -77,10 +86,12 @@ $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(Q) $(AR) cru $@ $(COMPONENT_OBJS) endif +CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk + ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") clean: - $(summary) RM $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) - $(Q) rm -f $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) + $(summary) RM $(CLEAN_FILES) + $(Q) rm -f $(CLEAN_FILES) endif #Include all dependency files already generated diff --git a/make/project.mk b/make/project.mk index a60a3958fe..ac02261869 100644 --- a/make/project.mk +++ b/make/project.mk @@ -48,11 +48,22 @@ PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif +COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_common.mk) +export COMMON_MAKEFILES + #The directory where we put all objects/libraries/binaries. The project Makefile can #configure this if needed. BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE +# Include project config file, if it exists. +# +# (Note that we only rebuild auto.conf automatically for some targets, +# see project_config.mk for details.) +SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf +-include $(SDKCONFIG_MAKEFILE) +export $(filter CONFIG_%,$(.VARIABLES)) + #Component directories. These directories are searched for components. #The project Makefile can override these component dirs, or define extra component directories. COMPONENT_DIRS ?= $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components @@ -87,48 +98,33 @@ COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(c # LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. COMPONENT_INCLUDES := COMPONENT_LDFLAGS := -# -# Also add any inter-component dependencies for each component. -# Extract a variable from a child make process +# include paths for generated "component project variables" targets with +# COMPONENT_INCLUDES, COMPONENT_LDFLAGS & dependency targets # -# $(1) - path to directory to invoke make in -# $(2) - name of variable to print via the get_variable target (passed in GET_VARIABLE) +# See component_project_vars.mk target in component_common.mk +COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) +COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) +include $(COMPONENT_PROJECT_VARS) + + +# Generate a target to rebuild component_project_vars.mk for a component +# $(1) - component directory +# $(2) - component name only # -# needs 'sed' processing of stdout because make sometimes echoes other stuff on stdout, -# even if asked not to. -# -# Debugging this? Replace $(shell with $(error and you'll see the full command as-run. -define GetVariable -$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" ) +# Rebuilds if component.mk, makefiles or sdkconfig changes. +define GenerateProjectVarsTarget +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(if $(MAKE_RESTARTS),,$(SDKCONFIG_MAKEFILE)) $(BUILD_DIR_BASE)/$(2) + $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) endef +$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) -COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \ - $(call GetVariable,$(comp),COMPONENT_ADD_INCLUDEDIRS)))) - -#Also add project include path, for sdk includes +#Also add project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) + export COMPONENT_INCLUDES - -#COMPONENT_LDFLAGS has a list of all flags that are needed to link the components together. It's collected -#in the same way as COMPONENT_INCLUDES is. -COMPONENT_LDFLAGS := $(foreach comp,$(COMPONENT_PATHS_BUILDABLE), \ - $(call GetVariable,$(comp),COMPONENT_ADD_LDFLAGS)) export COMPONENT_LDFLAGS -# Generate component dependency targets from dependencies lists -# each component gains a target of its own -build with dependencies -# of the names of any other components (-build) that need building first -# -# the actual targets (that invoke submakes) are generated below by -# GenerateComponentTarget macro. -define GenerateComponentDependencies -# $(1) = component path -.PHONY: $$(notdir $(1)) -$$(notdir $(1))-build: $(addsuffix -build,$(call GetVariable,$(1),COMPONENT_DEPENDS)) -endef -$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateComponentDependencies,$(comp)))) - #Make sure submakes can also use this. export PROJECT_PATH @@ -242,7 +238,7 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) -# once we know component paths, we can include the config +# once we know component paths, we can include the config generation targets include $(IDF_PATH)/make/project_config.mk # A "component" library is any library in the LDFLAGS where @@ -269,29 +265,31 @@ $(BUILD_DIR_BASE): define GenerateComponentPhonyTarget # $(1) - path to component dir -# $(2) - target to generate (build, clean) -.PHONY: $(notdir $(1))-$(2) -$(notdir $(1))-$(2): | $(BUILD_DIR_BASE)/$(notdir $(1)) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(notdir $(1)) -f $(1)/component.mk COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(notdir $(1)) $(2) +# $(2) - name of component +# $(3) - target to generate (build, clean) +.PHONY: $(2)-$(3) +$(2)-$(3): | $(BUILD_DIR_BASE)/$(2) + $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) $(3) endef define GenerateComponentTargets # $(1) - path to component dir -$(BUILD_DIR_BASE)/$(notdir $(1)): - @mkdir -p $(BUILD_DIR_BASE)/$(notdir $(1)) +# $(2) - name of component +$(BUILD_DIR_BASE)/$(2): + @mkdir -p $(BUILD_DIR_BASE)/$(2) # tell make it can build any component's library by invoking the recursive -build target # (this target exists for all components even ones which don't build libraries, but it's # only invoked for the targets whose libraries appear in COMPONENT_LIBRARIES and hence the # APP_ELF dependencies.) -$(BUILD_DIR_BASE)/$(notdir $(1))/lib$(notdir $(1)).a: $(notdir $(1))-build +$(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file endef -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component)))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component),$(notdir $(component))))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),build))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),clean))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),build))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),clean))) app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) diff --git a/make/project_config.mk b/make/project_config.mk index 7ca83ce5af..4d0f6c2059 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -37,7 +37,7 @@ defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it -NON_CONFIG_TARGETS := clean %-clean get_variable help menuconfig defconfig +NON_CONFIG_TARGETS := clean %-clean help menuconfig defconfig AUTO_CONF_REGEN_TARGET := $(BUILD_DIR_BASE)/include/config/auto.conf # disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets From 155f912433d93882f55f6f813ce72b3623cd073a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 14:26:50 +1100 Subject: [PATCH 033/131] build system: Don't build an sdkconfig for bootloader, share the top-level one This works because all CONFIG variables are exported into child make processes. --- components/bootloader/Makefile.projbuild | 16 ++++------------ components/bootloader/src/Makefile | 7 +++++-- make/component_common.mk | 2 +- make/project.mk | 10 +++++++++- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 0b460059e8..5bcd77b36f 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -13,21 +13,18 @@ ifndef IS_BOOTLOADER_BUILD BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin -BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ - V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) -$(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig +$(BOOTLOADER_BIN): $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean config-clean - $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old + $(Q) $(BOOTLOADER_MAKE) app-clean clean: bootloader-clean @@ -43,15 +40,10 @@ ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) bootloader-flash: $(BOOTLOADER_BIN) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ -# synchronise the project level config to the bootloader's -# config -$(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) - $(Q) cp $< $@ - $(BOOTLOADER_BUILD_DIR): $(Q) mkdir -p $@ else -CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include +CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include endif diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index add9c15d61..3a939a8857 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,6 +4,9 @@ # PROJECT_NAME := bootloader + +#We cannot include the esp32 component directly but we need its includes. +#This is fixed by adding CFLAGS from Makefile.projbuild COMPONENTS := esptool_py bootloader log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. @@ -12,7 +15,7 @@ COMPONENTS := esptool_py bootloader log spi_flash IS_BOOTLOADER_BUILD := 1 export IS_BOOTLOADER_BUILD -#We cannot include the esp32 component directly but we need its includes. -#This is fixed by adding CFLAGS from Makefile.projbuild +# include the top-level "project" include directory, for sdkconfig.h +CFLAGS += -I$(BUILD_DIR_BASE)/../include include $(IDF_PATH)/make/project.mk diff --git a/make/component_common.mk b/make/component_common.mk index 3177ca6e30..9db57bf948 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -66,7 +66,7 @@ COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_I # project.mk evaluates dependencies before calling down to here. See # GenerateProjectVarsTarget in project.mk. component_project_vars.mk:: - $(details) "Rebuilding component project variables list $(abspath $@)" + $(details) "Building component project variables list $(abspath $@)" @echo "# Automatically generated build file. Do not edit." > $@ @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ diff --git a/make/project.mk b/make/project.mk index ac02261869..deaf987709 100644 --- a/make/project.mk +++ b/make/project.mk @@ -56,13 +56,17 @@ export COMMON_MAKEFILES BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE +ifndef IS_BOOTLOADER_BUILD # Include project config file, if it exists. +# (bootloader build doesn't need this, config is exported from top-level) # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) +# SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf -include $(SDKCONFIG_MAKEFILE) export $(filter CONFIG_%,$(.VARIABLES)) +endif #Component directories. These directories are searched for components. #The project Makefile can override these component dirs, or define extra component directories. @@ -114,7 +118,7 @@ include $(COMPONENT_PROJECT_VARS) # # Rebuilds if component.mk, makefiles or sdkconfig changes. define GenerateProjectVarsTarget -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(if $(MAKE_RESTARTS),,$(SDKCONFIG_MAKEFILE)) $(BUILD_DIR_BASE)/$(2) +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) endef $(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) @@ -238,8 +242,12 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) +ifndef IS_BOOTLOADER_BUILD # once we know component paths, we can include the config generation targets +# +# (bootloader build doesn't need this, config is exported from top-level) include $(IDF_PATH)/make/project_config.mk +endif # A "component" library is any library in the LDFLAGS where # the name of the library is also a name of the component From d7e57eb66830ce0b6b8f9a168b7af67efc34ec38 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 17:25:57 +1100 Subject: [PATCH 034/131] build system: Refactor the three component-target-related macros into one --- make/project.mk | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/make/project.mk b/make/project.mk index deaf987709..17d9e99c0c 100644 --- a/make/project.mk +++ b/make/project.mk @@ -111,18 +111,6 @@ COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMP COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) include $(COMPONENT_PROJECT_VARS) - -# Generate a target to rebuild component_project_vars.mk for a component -# $(1) - component directory -# $(2) - component name only -# -# Rebuilds if component.mk, makefiles or sdkconfig changes. -define GenerateProjectVarsTarget -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) -endef -$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) - #Also add project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) @@ -271,34 +259,45 @@ all_binaries: $(APP_BIN) $(BUILD_DIR_BASE): mkdir -p $(BUILD_DIR_BASE) -define GenerateComponentPhonyTarget -# $(1) - path to component dir -# $(2) - name of component -# $(3) - target to generate (build, clean) -.PHONY: $(2)-$(3) -$(2)-$(3): | $(BUILD_DIR_BASE)/$(2) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) $(3) +# Macro for the recursive sub-make for each component +# $(1) - component directory +# $(2) - component name only +# +# Is recursively expanded by the GenerateComponentTargets macro +define ComponentMake +$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) endef define GenerateComponentTargets # $(1) - path to component dir # $(2) - name of component +.PHONY: $(2)-build $(2)-clean + +$(2)-build: + $(call ComponentMake,$(1),$(2)) build + +$(2)-clean: + $(call ComponentMake,$(1),$(2)) clean + $(BUILD_DIR_BASE)/$(2): @mkdir -p $(BUILD_DIR_BASE)/$(2) -# tell make it can build any component's library by invoking the recursive -build target +# tell make it can build any component's library by invoking the -build target # (this target exists for all components even ones which don't build libraries, but it's # only invoked for the targets whose libraries appear in COMPONENT_LIBRARIES and hence the # APP_ELF dependencies.) $(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file + +# add a target to generate the component_project_vars.mk files +# that are used to inject variables into project make pass (see +# component_project_vars.mk target in component_common.mk). +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) + $(call ComponentMake,$(1),$(2)) component_project_vars.mk endef $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component),$(notdir $(component))))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),build))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),clean))) - app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) From 3b96f68afd4e998f987fcc1a22810b5902b25b7e Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Wed, 9 Nov 2016 17:27:12 +0800 Subject: [PATCH 035/131] 1. Add wps event processing. 2. Change wps API return type and value. --- components/esp32/event_default_handlers.c | 20 +++++++ components/esp32/include/esp_event.h | 9 +++ components/esp32/include/esp_wps.h | 71 ++++++++--------------- 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index a2bb3ccea3..497c437163 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -67,6 +67,10 @@ static system_event_handle_t g_system_event_handle_table[] = { {SYSTEM_EVENT_STA_DISCONNECTED, system_event_sta_disconnected_handle_default}, {SYSTEM_EVENT_STA_AUTHMODE_CHANGE, NULL}, {SYSTEM_EVENT_STA_GOT_IP, system_event_sta_got_ip_default}, + {SYSTEM_EVENT_STA_WPS_ER_SUCCESS, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_FAILED, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_PIN, NULL}, {SYSTEM_EVENT_AP_START, system_event_ap_start_handle_default}, {SYSTEM_EVENT_AP_STOP, system_event_ap_stop_handle_default}, {SYSTEM_EVENT_AP_STACONNECTED, NULL}, @@ -221,6 +225,22 @@ static esp_err_t esp_system_event_debug(system_event_t *event) IP2STR(&got_ip->ip_info.gw)); break; } + case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_SUCCESS"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_FAILED: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_FAILED"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_TIMEOUT"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_PIN: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_PIN"); + break; + } case SYSTEM_EVENT_AP_START: { ESP_LOGD(TAG, "SYSTEM_EVENT_AP_START"); break; diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 0ca4ca90ba..481c5effd5 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -35,6 +35,10 @@ typedef enum { SYSTEM_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */ SYSTEM_EVENT_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */ SYSTEM_EVENT_STA_GOT_IP, /**< ESP32 station got IP from connected AP */ + SYSTEM_EVENT_STA_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */ SYSTEM_EVENT_AP_START, /**< ESP32 soft-AP start */ SYSTEM_EVENT_AP_STOP, /**< ESP32 soft-AP stop */ SYSTEM_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */ @@ -73,6 +77,10 @@ typedef struct { tcpip_adapter_ip_info_t ip_info; } system_event_sta_got_ip_t; +typedef struct { + uint8_t pin_code; /**< PIN code of station in enrollee mode */ +}system_event_sta_wps_er_pin_t; + typedef struct { uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */ uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */ @@ -94,6 +102,7 @@ typedef union { system_event_sta_scan_done_t scan_done; /**< ESP32 station scan (APs) done */ system_event_sta_authmode_change_t auth_change; /**< the auth mode of AP ESP32 station connected to changed */ system_event_sta_got_ip_t got_ip; /**< ESP32 station got IP */ + system_event_sta_wps_er_pin_t sta_er_pin; system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 soft-AP receive probe request packet */ diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 1588d36191..2ea7e194bb 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -16,6 +16,7 @@ #define __ESP_WPS_H__ #include +#include "esp_err.h" #ifdef __cplusplus extern "C" { @@ -40,43 +41,43 @@ extern "C" { * @{ */ +#define ESP_ERR_WIFI_REGISTRAR (ESP_ERR_WIFI_BASE + 51) /*!< WPS registrar is not supported */ +#define ESP_ERR_WIFI_WPS_TYPE (ESP_ERR_WIFI_BASE + 52) /*!< WPS type error */ +#define ESP_ERR_WIFI_WPS_SM (ESP_ERR_WIFI_BASE + 53) /*!< WPS state machine is not initialized */ + typedef enum wps_type { WPS_TYPE_DISABLE = 0, WPS_TYPE_PBC, WPS_TYPE_PIN, - WPS_TYPE_DISPLAY, WPS_TYPE_MAX, -} WPS_TYPE_t; - -enum wps_cb_status { - WPS_CB_ST_SUCCESS = 0, /**< WPS succeed */ - WPS_CB_ST_FAILED, /**< WPS fail */ - WPS_CB_ST_TIMEOUT, /**< WPS timeout, fail */ - WPS_CB_ST_WEP, /**< WPS failed because that WEP is not supported */ - WPS_CB_ST_SCAN_ERR, /**< can not find the target WPS AP */ -}; +} wps_type_t; /** * @brief Enable Wi-Fi WPS function. * * @attention WPS can only be used when ESP32 station is enabled. * - * @param WPS_TYPE_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported + * @param wps_type_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported * - * @return true : succeed - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized + * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -bool esp_wifi_wps_enable(WPS_TYPE_t wps_type); +esp_err_t esp_wifi_wps_enable(wps_type_t wps_type); /** * @brief Disable Wi-Fi WPS function and release resource it taken. * * @param null * - * @return true : succeed - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on */ -bool esp_wifi_wps_disable(void); +esp_err_t esp_wifi_wps_disable(void); /** * @brief WPS starts to work. @@ -85,36 +86,14 @@ bool esp_wifi_wps_disable(void); * * @param null * - * @return true : WPS starts to work successfully, but does not mean WPS succeed. - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized + * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -bool esp_wifi_wps_start(void); - -/** - * @brief WPS callback. - * - * @param int status : status of WPS, enum wps_cb_status. - * - If parameter status == WPS_CB_ST_SUCCESS in WPS callback, it means WPS got AP's - * information, user can call wifi_wps_disable to disable WPS and release resource, - * then call wifi_station_connect to connect to target AP. - * - Otherwise, it means that WPS fail, user can create a timer to retry WPS by - * wifi_wps_start after a while, or call wifi_wps_disable to disable WPS and release resource. - * - * @return null - */ -typedef void (*wps_st_cb_t)(int status); - -/** - * @brief Set WPS callback. - * - * @attention WPS can only be used when ESP32 station is enabled. - * - * @param wps_st_cb_t cb : callback. - * - * @return true : WPS starts to work successfully, but does not mean WPS succeed. - * @return false : fail - */ -bool esp_wifi_set_wps_cb(wps_st_cb_t cb); +esp_err_t esp_wifi_wps_start(void); /** * @} From 2f0efe151084f9c77e4c1f6dce9e5711b02ba7a7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 20:38:16 +1100 Subject: [PATCH 036/131] build system: Fix defconfig vs menuconfig regression in 155f9124 --- make/build_examples.sh | 2 +- make/project_config.mk | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index d264862874..53cba23b1e 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -22,7 +22,7 @@ for example in ${IDF_PATH}/examples/*; do pushd ${EXAMPLE_NUM}/`basename ${example}` # can't do "make defconfig all" as this will trip menuconfig # sometimes - make defconfig && make || RESULT=$? + make defconfig V=1 && make V=1 || RESULT=$? popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done diff --git a/make/project_config.mk b/make/project_config.mk index 4d0f6c2059..555086fb8d 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -21,13 +21,17 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(summary) MENUCONFIG $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") -#No sdkconfig found. Need to run menuconfig to make this if we need it. +ifeq ("$(filter defconfig,$(MAKECMDGOALS))","") +# if not configuration is present and defconfig is not a target, run makeconfig $(SDKCONFIG): menuconfig +else +$(SDKCONFIG): defconfig +endif endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) From c441d3630c267e047327a7afd29c56cf683cbc56 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Wed, 9 Nov 2016 18:15:10 +0800 Subject: [PATCH 037/131] change pin code value type and wps API comments. --- components/esp32/include/esp_event.h | 2 +- components/esp32/include/esp_wps.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 481c5effd5..3b2b54acca 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -78,7 +78,7 @@ typedef struct { } system_event_sta_got_ip_t; typedef struct { - uint8_t pin_code; /**< PIN code of station in enrollee mode */ + uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */ }system_event_sta_wps_er_pin_t; typedef struct { diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 2ea7e194bb..0a413a1978 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -63,7 +63,6 @@ typedef enum wps_type { * - ESP_OK : succeed * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on - * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized * - ESP_ERR_WIFI_FAIL : wps initialization fails */ esp_err_t esp_wifi_wps_enable(wps_type_t wps_type); From f1938a909ae8fd94be5a4f97a6485a0206327aec Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 10:29:42 +1100 Subject: [PATCH 038/131] build system: Generated makefiles should contain environment-variable-relative paths where possible Means that moving directories around then partial building should succeed when possible. --- make/component_common.mk | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/make/component_common.mk b/make/component_common.mk index 9db57bf948..a9598a23ba 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -58,19 +58,33 @@ COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) -# This target is used to take component.mk variables COMPONENT_ADD_INCLUDEDIRS, -# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject them into the project -# makefile level. +# macro to generate relative paths inside component_project_vars.mk, whenever possible +# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated +# makefiles where possible. +# +# This means if directories move (breaking absolute paths), don't need to 'make clean' +define MakeRelativePath +$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) +endef + +# This target generates component_project_vars.mk for the +# component. This is used to take component.mk variables +# COMPONENT_ADD_INCLUDEDIRS, COMPONENT_ADD_LDFLAGS and +# COMPONENT_DEPENDS and inject those into the project makefile. # # The target here has no dependencies, as the parent target in # project.mk evaluates dependencies before calling down to here. See -# GenerateProjectVarsTarget in project.mk. +# GenerateComponentTargets macro in project.mk. +# +# If you are thinking of editing the output of this makefile for a +# component-specific feature, please don't! What you want is a +# Makefile.projbuild for your component (see docs/build-system.rst for more.) component_project_vars.mk:: $(details) "Building component project variables list $(abspath $@)" - @echo "# Automatically generated build file. Do not edit." > $@ - @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ - @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ - @echo "$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))" >> $@ + @echo '# Automatically generated build file. Do not edit.' > $@ + @echo 'COMPONENT_INCLUDES += $(call MakeRelativePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ + @echo 'COMPONENT_LDFLAGS += $(call MakeRelativePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ #Targets for build/clean. Use builtin recipe if component Makefile #hasn't defined its own. From 208e83def7839c0b317a37f54d9c46cfe91421ef Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 13:20:55 +1100 Subject: [PATCH 039/131] build system: Refactor component.mk to not need component_common.mk New makefile component_wrapper.mk allows some variables to be set before component.mk is evaluated. This properly fixes problems with sdkconfig being hard to access in all phases of the build. Including component_common.mk is no longer necessary and will print a deprecation warning for components which use it. --- components/bootloader/Makefile.projbuild | 2 +- components/bootloader/src/main/component.mk | 9 +- components/bt/component.mk | 2 - components/driver/component.mk | 6 - components/esp32/component.mk | 10 -- components/expat/component.mk | 6 - components/freertos/component.mk | 1 - components/json/component.mk | 6 - components/log/component.mk | 6 +- components/lwip/component.mk | 1 - components/mbedtls/component.mk | 1 - components/newlib/component.mk | 1 - components/nghttp/component.mk | 2 - components/nvs_flash/component.mk | 1 - components/openssl/component.mk | 1 - components/spi_flash/component.mk | 1 - components/tcpip_adapter/component.mk | 2 +- components/vfs/component.mk | 6 +- components/wpa_supplicant/component.mk | 2 - components/xtensa-debug-module/component.mk | 3 +- examples/01_hello_world/main/component.mk | 7 +- examples/02_blink/main/component.mk | 8 +- examples/03_http_request/main/component.mk | 8 +- examples/04_https_request/main/component.mk | 7 +- examples/05_ble_adv/main/component.mk | 8 +- examples/06_sntp/main/component.mk | 8 +- make/common.mk | 12 +- make/component_common.mk | 136 +--------------- make/component_wrapper.mk | 169 ++++++++++++++++++++ make/project.mk | 110 +++++++------ make/project_config.mk | 4 +- 31 files changed, 261 insertions(+), 285 deletions(-) create mode 100644 make/component_wrapper.mk diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 5bcd77b36f..c03288d107 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) -$(BOOTLOADER_BIN): +$(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE) $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index bd1dcf4d90..01b07b9498 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -1,12 +1,9 @@ # -# Main Makefile. This is basically the same as a component makefile. +# Main bootloader Makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the esp-idf build system document if you need to do this. +# This is basically the same as a component makefile, but in the case of the bootloader +# we pull in bootloader-specific linker arguments. # COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld -include $(IDF_PATH)/make/component_common.mk diff --git a/components/bt/component.mk b/components/bt/component.mk index 5addee2ceb..91620ddc14 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -12,8 +12,6 @@ COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ $(LINKER_SCRIPTS) -include $(IDF_PATH)/make/component_common.mk - ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) diff --git a/components/driver/component.mk b/components/driver/component.mk index a19b131ef5..a208f6ae20 100644 --- a/components/driver/component.mk +++ b/components/driver/component.mk @@ -1,14 +1,8 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include/driver -include $(IDF_PATH)/make/component_common.mk diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 7e22b65183..3866d3e4b9 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -1,12 +1,6 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the esp-idf build system document if you need to do this. -# --include include/config/auto.conf COMPONENT_SRCDIRS := . hwcrypto @@ -21,8 +15,6 @@ COMPONENT_ADD_LDFLAGS := -lesp32 \ -L $(COMPONENT_PATH)/ld \ $(LINKER_SCRIPTS) -include $(IDF_PATH)/make/component_common.mk - ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) # automatically trigger a git submodule update @@ -44,8 +36,6 @@ $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) # saves us from having to add the target to a Makefile.projbuild $(COMPONENT_LIBRARY): esp32_out.ld -# .. is BUILD_DIR_BASE here, as component makefiles -# are evaluated with CWD=component build dir esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h $(CC) -I ../include -C -P -x c -E $< -o $@ diff --git a/components/expat/component.mk b/components/expat/component.mk index 907b358a87..0ee1199d1b 100644 --- a/components/expat/component.mk +++ b/components/expat/component.mk @@ -1,15 +1,9 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := port/include include/expat COMPONENT_SRCDIRS := library port CFLAGS += -Wno-unused-function -DHAVE_EXPAT_CONFIG_H -include $(IDF_PATH)/make/component_common.mk diff --git a/components/freertos/component.mk b/components/freertos/component.mk index 6702d1b95c..0240ea235f 100644 --- a/components/freertos/component.mk +++ b/components/freertos/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) -Wl,--undefined=uxTopUsedPriority COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include/freertos -include $(IDF_PATH)/make/component_common.mk diff --git a/components/json/component.mk b/components/json/component.mk index 311a902f99..2dd6ea8b87 100644 --- a/components/json/component.mk +++ b/components/json/component.mk @@ -1,13 +1,7 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := include port/include COMPONENT_SRCDIRS := library port -include $(IDF_PATH)/make/component_common.mk diff --git a/components/log/component.mk b/components/log/component.mk index ef497a7ecb..c2c4c03a1a 100755 --- a/components/log/component.mk +++ b/components/log/component.mk @@ -1,3 +1,5 @@ -COMPONENT_ADD_INCLUDEDIRS := include +# +# Component Makefile +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/components/lwip/component.mk b/components/lwip/component.mk index 5d15020047..49fc644ae4 100644 --- a/components/lwip/component.mk +++ b/components/lwip/component.mk @@ -8,4 +8,3 @@ COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/free CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable -include $(IDF_PATH)/make/component_common.mk diff --git a/components/mbedtls/component.mk b/components/mbedtls/component.mk index 98838d4d7b..bd7209a926 100644 --- a/components/mbedtls/component.mk +++ b/components/mbedtls/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := port/include include COMPONENT_SRCDIRS := library port -include $(IDF_PATH)/make/component_common.mk diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 0648946c20..4f567242b6 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -2,4 +2,3 @@ COMPONENT_ADD_LDFLAGS := $(COMPONENT_PATH)/lib/libc.a $(COMPONENT_PATH)/lib/libm COMPONENT_ADD_INCLUDEDIRS := include platform_include -include $(IDF_PATH)/make/component_common.mk diff --git a/components/nghttp/component.mk b/components/nghttp/component.mk index a4dca5cf70..d2cd0455f5 100644 --- a/components/nghttp/component.mk +++ b/components/nghttp/component.mk @@ -5,5 +5,3 @@ COMPONENT_ADD_INCLUDEDIRS := port/include include COMPONENT_SRCDIRS := library port - -include $(IDF_PATH)/make/component_common.mk \ No newline at end of file diff --git a/components/nvs_flash/component.mk b/components/nvs_flash/component.mk index 02ff8cf038..a905ca689e 100755 --- a/components/nvs_flash/component.mk +++ b/components/nvs_flash/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_SRCDIRS := src -include $(IDF_PATH)/make/component_common.mk diff --git a/components/openssl/component.mk b/components/openssl/component.mk index 2dfcc6b38d..be40549d20 100644 --- a/components/openssl/component.mk +++ b/components/openssl/component.mk @@ -7,4 +7,3 @@ COMPONENT_PRIV_INCLUDEDIRS := include/internal include/platform include/openssl COMPONENT_SRCDIRS := library platform -include $(IDF_PATH)/make/component_common.mk diff --git a/components/spi_flash/component.mk b/components/spi_flash/component.mk index 459da06419..d511eedb8e 100755 --- a/components/spi_flash/component.mk +++ b/components/spi_flash/component.mk @@ -5,4 +5,3 @@ ifdef IS_BOOTLOADER_BUILD COMPONENT_OBJS := spi_flash_rom_patch.o endif -include $(IDF_PATH)/make/component_common.mk diff --git a/components/tcpip_adapter/component.mk b/components/tcpip_adapter/component.mk index a57ae0b12b..c2c4c03a1a 100755 --- a/components/tcpip_adapter/component.mk +++ b/components/tcpip_adapter/component.mk @@ -1,5 +1,5 @@ # # Component Makefile # +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/components/vfs/component.mk b/components/vfs/component.mk index fccf88db8a..c2c4c03a1a 100755 --- a/components/vfs/component.mk +++ b/components/vfs/component.mk @@ -1 +1,5 @@ -include $(IDF_PATH)/make/component_common.mk +# +# Component Makefile +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index cb6b06652f..b01eb83be9 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -2,5 +2,3 @@ COMPONENT_ADD_INCLUDEDIRS := include port/include COMPONENT_SRCDIRS := src/crypto CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing - -include $(IDF_PATH)/make/component_common.mk diff --git a/components/xtensa-debug-module/component.mk b/components/xtensa-debug-module/component.mk index a57ae0b12b..308f64f0ea 100755 --- a/components/xtensa-debug-module/component.mk +++ b/components/xtensa-debug-module/component.mk @@ -1,5 +1,4 @@ # # Component Makefile # - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/01_hello_world/main/component.mk b/examples/01_hello_world/main/component.mk index 24356f23ed..4d3b30caf3 100644 --- a/examples/01_hello_world/main/component.mk +++ b/examples/01_hello_world/main/component.mk @@ -1,10 +1,5 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/02_blink/main/component.mk b/examples/02_blink/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/02_blink/main/component.mk +++ b/examples/02_blink/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/03_http_request/main/component.mk b/examples/03_http_request/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/03_http_request/main/component.mk +++ b/examples/03_http_request/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index 24356f23ed..4d3b30caf3 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,10 +1,5 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/05_ble_adv/main/component.mk b/examples/05_ble_adv/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/05_ble_adv/main/component.mk +++ b/examples/05_ble_adv/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/06_sntp/main/component.mk b/examples/06_sntp/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/06_sntp/main/component.mk +++ b/examples/06_sntp/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/make/common.mk b/make/common.mk index 6183cfbc46..779811f1ad 100644 --- a/make/common.mk +++ b/make/common.mk @@ -1,7 +1,15 @@ -# Functionality common to both top-level project makefile -# and component makefiles +# Functionality common to both top-level project makefile (project.mk) +# and component makefiles (component_wrapper.mk) # +# Include project config makefile, if it exists. +# +# (Note that we only rebuild this makefile automatically for some +# targets, see project_config.mk for details.) +SDKCONFIG_MAKEFILE ?= $(abspath $(BUILD_DIR_BASE)/include/config/auto.conf) +-include $(SDKCONFIG_MAKEFILE) +export SDKCONFIG_MAKEFILE # sub-makes (like bootloader) will reuse this path + #Handling of V=1/VERBOSE=1 flag # # if V=1, $(summary) does nothing and $(details) will echo extra details diff --git a/make/component_common.mk b/make/component_common.mk index a9598a23ba..32af3c7c06 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -1,135 +1,3 @@ -# Component common makefile -# -# This Makefile gets included in the Makefile of all the components to set the correct include paths etc. -# PWD is the build directory of the component and the top Makefile is the one in the -# component source dir. -# -# The way the Makefile differentiates between those two is by looking at the environment -# variable PROJECT_PATH. If this is set (to the basepath of the project), we're building a -# component and its Makefile has included this makefile. If not, we're building the entire project. -# +$(warning Deprecated feature: It is no longer necessary to include component_common.mk.) +$(warning The line "include $$(IDF_PATH)/make/component_common.mk" can be removed from component.mk files.) -# -# This Makefile requires the environment variable IDF_PATH to be set -# to the top-level directory where ESP-IDF is located (the directory -# containing this 'make' directory). -# - -ifeq ("$(PROJECT_PATH)","") -$(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) -endif - -# Find the path to the component -COMPONENT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) -export COMPONENT_PATH - -include $(IDF_PATH)/make/common.mk - -#Some of these options are overridable by the component's component.mk Makefile - -#Name of the component -COMPONENT_NAME ?= $(lastword $(subst /, ,$(realpath $(COMPONENT_PATH)))) - -#Absolute path of the .a file -COMPONENT_LIBRARY := lib$(COMPONENT_NAME).a - -#Source dirs a component has. Default to root directory of component. -COMPONENT_SRCDIRS ?= . - -#Object files which need to be linked into the library -#By default we take all .c/.S files in the component directory. -ifeq ("$(COMPONENT_OBJS)", "") -#Find all source files in all COMPONENT_SRCDIRS -COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) -COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) -COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) -#Make relative by removing COMPONENT_PATH from all found object paths -COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) -endif - -#By default, include only the include/ dir. -COMPONENT_ADD_INCLUDEDIRS ?= include -COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) - -#If we're called to compile something, we'll get passed the COMPONENT_INCLUDES -#variable with all the include dirs from all the components in random order. This -#means we can accidentally grab a header from another component before grabbing our own. -#To make sure that does not happen, re-order the includes so ours come first. -OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) -COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) - -# macro to generate relative paths inside component_project_vars.mk, whenever possible -# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated -# makefiles where possible. -# -# This means if directories move (breaking absolute paths), don't need to 'make clean' -define MakeRelativePath -$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) -endef - -# This target generates component_project_vars.mk for the -# component. This is used to take component.mk variables -# COMPONENT_ADD_INCLUDEDIRS, COMPONENT_ADD_LDFLAGS and -# COMPONENT_DEPENDS and inject those into the project makefile. -# -# The target here has no dependencies, as the parent target in -# project.mk evaluates dependencies before calling down to here. See -# GenerateComponentTargets macro in project.mk. -# -# If you are thinking of editing the output of this makefile for a -# component-specific feature, please don't! What you want is a -# Makefile.projbuild for your component (see docs/build-system.rst for more.) -component_project_vars.mk:: - $(details) "Building component project variables list $(abspath $@)" - @echo '# Automatically generated build file. Do not edit.' > $@ - @echo 'COMPONENT_INCLUDES += $(call MakeRelativePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ - @echo 'COMPONENT_LDFLAGS += $(call MakeRelativePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ - @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ - -#Targets for build/clean. Use builtin recipe if component Makefile -#hasn't defined its own. -ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") -build: $(COMPONENT_LIBRARY) - @mkdir -p $(COMPONENT_SRCDIRS) - -#Build the archive. We remove the archive first, otherwise ar will get confused if we update -#an archive when multiple filenames have the same name (src1/test.o and src2/test.o) -$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) - $(summary) AR $@ - $(Q) rm -f $@ - $(Q) $(AR) cru $@ $(COMPONENT_OBJS) -endif - -CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk - -ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") -clean: - $(summary) RM $(CLEAN_FILES) - $(Q) rm -f $(CLEAN_FILES) -endif - -#Include all dependency files already generated --include $(COMPONENT_OBJS:.o=.d) - -#This pattern is generated for each COMPONENT_SRCDIR to compile the files in it. -define GenerateCompileTargets -# $(1) - directory containing source files, relative to $(COMPONENT_PATH) -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) - $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) - $$(summary) CXX $$@ - $$(Q) $$(CXX) $$(CXXFLAGS) $(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) - $$(summary) AS $$@ - $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -# CWD is build dir, create the build subdirectory if it doesn't exist -$(1): - @mkdir -p $(1) -endef - -#Generate all the compile target recipes -$(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(srcdir)))) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk new file mode 100644 index 0000000000..68efe0d211 --- /dev/null +++ b/make/component_wrapper.mk @@ -0,0 +1,169 @@ +# Component wrapper makefile +# +# This makefile gets called recursively from the project make, once for each component. +# COMPONENT_MAKEFILE is set to point at the component.mk file for the component itself, +# which is included as part of this process (after default variables are defined). +# +# This makefile comprises multiple stages, marked in blocked comments below. +# +# CWD is the build directory of the component. + +ifeq ("$(PROJECT_PATH)","") +$(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) +endif + + +################################################################################ +# 1) Set default variables for the component build (including configuration +# loaded from sdkconfig.) +################################################################################ + +# Find the path to the component +COMPONENT_PATH := $(abspath $(dir $(COMPONENT_MAKEFILE))) +export COMPONENT_PATH + +# COMPONENT_BUILD_DIR is otherwise known as CWD for the build +COMPONENT_BUILD_DIR := $(abspath .) + +# include elements common to both project & component makefiles +# (includes project configuration set via menuconfig) +include $(IDF_PATH)/make/common.mk + +# Some of the following defaults may be overriden by the component's component.mk makefile, +# during the next step: + +# Name of the component +COMPONENT_NAME := $(lastword $(subst /, ,$(realpath $(COMPONENT_PATH)))) + +# Absolute path of the .a file +COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a + +# Source dirs a component has. Default to root directory of component. +COMPONENT_SRCDIRS = . + +# By default, include only the include/ dir. +COMPONENT_ADD_INCLUDEDIRS = include +COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) + + +################################################################################ +# 2) Include the component.mk for the specific component (COMPONENT_MAKEFILE) to +# override variables & optionally define custom targets. +################################################################################ + +include $(COMPONENT_MAKEFILE) + + +################################################################################ +# 3) Set variables that depend on values that may changed by component.mk +################################################################################ + +# Object files which need to be linked into the library +# By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. +ifeq ("$(COMPONENT_OBJS)", "") +# Find all source files in all COMPONENT_SRCDIRS +COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) +COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) +COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) +# Make relative by removing COMPONENT_PATH from all found object paths +COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) +endif + +# If we're called to compile something, we'll get passed the COMPONENT_INCLUDES +# variable with all the include dirs from all the components in random order. This +# means we can accidentally grab a header from another component before grabbing our own. +# To make sure that does not happen, re-order the includes so ours come first. +OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) +COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) + + +################################################################################ +# 4) Define a target to generate component_project_vars.mk Makefile which +# contains common per-component settings which are included directly in the +# top-level project make +################################################################################ + +# macro to generate variable-relative paths inside component_project_vars.mk, whenever possible +# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated +# makefiles where possible. +# +# This means if directories move (breaking absolute paths), don't need to 'make clean' +define MakeVariablePath +$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) +endef + +# component_project_vars.mk target for the component. This is used to +# take component.mk variables COMPONENT_ADD_INCLUDEDIRS, +# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject those into +# the project make pass. +# +# The target here has no dependencies, as the parent target in +# project.mk evaluates dependencies before calling down to here. See +# GenerateComponentTargets macro in project.mk. +# +# If you are thinking of editing the output of this target for a +# component-specific feature, please don't! What you want is a +# Makefile.projbuild for your component (see docs/build-system.rst for +# more.) +component_project_vars.mk:: + $(details) "Building component project variables list $(abspath $@)" + @echo '# Automatically generated build file. Do not edit.' > $@ + @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ + @echo 'COMPONENT_LDFLAGS += $(call MakeVariablePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ + + +################################################################################ +# 5) If COMPONENT_OWNBUILDTARGET / COMPONENT_OWNCLEANTARGET is not set by component.mk, +# define default build, clean, etc. targets +################################################################################ + +# If COMPONENT_OWNBUILDTARGET is not set, define a phony build target and +# a COMPONENT_LIBRARY link target. +ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") +.PHONY: build +build: $(COMPONENT_LIBRARY) + @mkdir -p $(COMPONENT_SRCDIRS) + +# Build the archive. We remove the archive first, otherwise ar will get confused if we update +# an archive when multiple filenames have the same name (src1/test.o and src2/test.o) +$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) + $(summary) AR $@ + $(Q) rm -f $@ + $(Q) $(AR) cru $@ $(COMPONENT_OBJS) +endif + +# If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target +ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") +CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk +.PHONY: clean +clean: + $(summary) RM $(CLEAN_FILES) + $(Q) rm -f $(CLEAN_FILES) +endif + +# Include all dependency files already generated +-include $(COMPONENT_OBJS:.o=.d) + +# This pattern is generated for each COMPONENT_SRCDIR to compile the files in it. +define GenerateCompileTargets +# $(1) - directory containing source files, relative to $(COMPONENT_PATH) +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) + $$(summary) CC $$@ + $$(Q) $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) + $$(summary) CXX $$@ + $$(Q) $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) + $$(summary) AS $$@ + $$(Q) $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +# CWD is build dir, create the build subdirectory if it doesn't exist +$(1): + @mkdir -p $(1) +endef + +# Generate all the compile target patterns +$(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(srcdir)))) diff --git a/make/project.mk b/make/project.mk index 17d9e99c0c..f9cf20a329 100644 --- a/make/project.mk +++ b/make/project.mk @@ -48,80 +48,73 @@ PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif -COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_common.mk) +# A list of the "common" makefiles, to use as a target dependency +COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_wrapper.mk) export COMMON_MAKEFILES -#The directory where we put all objects/libraries/binaries. The project Makefile can -#configure this if needed. +# The directory where we put all objects/libraries/binaries. The project Makefile can +# configure this if needed. BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE -ifndef IS_BOOTLOADER_BUILD -# Include project config file, if it exists. -# (bootloader build doesn't need this, config is exported from top-level) -# -# (Note that we only rebuild auto.conf automatically for some targets, -# see project_config.mk for details.) -# -SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf --include $(SDKCONFIG_MAKEFILE) -export $(filter CONFIG_%,$(.VARIABLES)) -endif - -#Component directories. These directories are searched for components. -#The project Makefile can override these component dirs, or define extra component directories. +# Component directories. These directories are searched for components. +# The project Makefile can override these component dirs, or define extra component directories. COMPONENT_DIRS ?= $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components export COMPONENT_DIRS -#The project Makefile can define a list of components, but if it does not do this we just take -#all available components in the component dirs. +# Source directories of the project itself (a special, project-specific component.) Defaults to only "main". +SRCDIRS ?= main + +# The project Makefile can define a list of components, but if it does not do this we just take +# all available components in the component dirs. ifeq ("$(COMPONENTS)","") -#Find all component names. The component names are the same as the -#directories they're in, so /bla/components/mycomponent/ -> mycomponent. We later use -#the COMPONENT_DIRS bit to find back the component path. +# Find all component names. The component names are the same as the +# directories they're in, so /bla/components/mycomponent/ -> mycomponent. We then use +# COMPONENT_DIRS to build COMPONENT_PATHS with the full path to each component. COMPONENTS := $(foreach dir,$(COMPONENT_DIRS),$(wildcard $(dir)/*)) COMPONENTS := $(sort $(foreach comp,$(COMPONENTS),$(lastword $(subst /, ,$(comp))))) endif export COMPONENTS -#Sources default to only "main" -SRCDIRS ?= main - -#Here, we resolve and add all the components and source paths into absolute paths. -#If a component exists in multiple COMPONENT_DIRS, we take the first match. -#WARNING: These directories paths must be generated WITHOUT a trailing / so we -#can use $(notdir x) to get the component name. +# Resolve all of COMPONENTS into absolute paths in COMPONENT_PATHS. +# +# If a component name exists in multiple COMPONENT_DIRS, we take the first match. +# +# NOTE: These paths must be generated WITHOUT a trailing / so we +# can use $(notdir x) to get the component name. COMPONENT_PATHS := $(foreach comp,$(COMPONENTS),$(firstword $(foreach dir,$(COMPONENT_DIRS),$(wildcard $(dir)/$(comp))))) COMPONENT_PATHS += $(abspath $(SRCDIRS)) -#A component is buildable if it has a component.mk makefile; we assume that a -# 'make -C $(component dir) -f component.mk build' results in a lib$(componentname).a +# A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) -# Assemble global list of include dirs (COMPONENT_INCLUDES), and -# LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# Initialise a project-wide list of include dirs (COMPONENT_INCLUDES), +# and LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# +# These variables are built up via the component_project_vars.mk +# generated makefiles (one per component). COMPONENT_INCLUDES := COMPONENT_LDFLAGS := -# include paths for generated "component project variables" targets with -# COMPONENT_INCLUDES, COMPONENT_LDFLAGS & dependency targets +# COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles +# for each component. # -# See component_project_vars.mk target in component_common.mk +# Including $(COMPONENT_PROJECT_VARS) builds the COMPONENT_INCLUDES, +# COMPONENT_LDFLAGS variables and also targets for any inter-component +# dependencies. +# +# See the component_project_vars.mk target in component_wrapper.mk COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) include $(COMPONENT_PROJECT_VARS) -#Also add project include path, for top-level includes +# Also add top-level project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) export COMPONENT_INCLUDES -export COMPONENT_LDFLAGS -#Make sure submakes can also use this. -export PROJECT_PATH - -#Include functionality common to both project & component --include $(IDF_PATH)/make/common.mk +# Set variables common to both project & component +include $(IDF_PATH)/make/common.mk # Set default LDFLAGS @@ -198,15 +191,15 @@ CXXFLAGS := $(strip \ export CFLAGS CPPFLAGS CXXFLAGS -#Set host compiler and binutils +# Set host compiler and binutils HOSTCC := $(CC) HOSTLD := $(LD) HOSTAR := $(AR) HOSTOBJCOPY := $(OBJCOPY) export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY -#Set target compiler. Defaults to whatever the user has -#configured as prefix + yer olde gcc commands +# Set target compiler. Defaults to whatever the user has +# configured as prefix + ye olde gcc commands CC := $(call dequote,$(CONFIG_TOOLPREFIX))gcc CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++ LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld @@ -230,10 +223,10 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) -ifndef IS_BOOTLOADER_BUILD # once we know component paths, we can include the config generation targets # # (bootloader build doesn't need this, config is exported from top-level) +ifndef IS_BOOTLOADER_BUILD include $(IDF_PATH)/make/project_config.mk endif @@ -265,12 +258,14 @@ $(BUILD_DIR_BASE): # # Is recursively expanded by the GenerateComponentTargets macro define ComponentMake -$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) +$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk endef -define GenerateComponentTargets +# Generate top-level component-specific targets for each component # $(1) - path to component dir # $(2) - name of component +# +define GenerateComponentTargets .PHONY: $(2)-build $(2)-clean $(2)-build: @@ -289,10 +284,19 @@ $(BUILD_DIR_BASE)/$(2): $(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file -# add a target to generate the component_project_vars.mk files -# that are used to inject variables into project make pass (see -# component_project_vars.mk target in component_common.mk). -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) +# add a target to generate the component_project_vars.mk files that +# are used to inject variables into project make pass (see matching +# component_project_vars.mk target in component_wrapper.mk). +# +# If any component_project_vars.mk file is out of date, the make +# process will call this target to rebuild it and then restart. +# +# Note: $(SDKCONFIG) is a normal prereq as we need to rebuild these +# files whenever the config changes. $(SDKCONFIG_MAKEFILE) is an +# order-only prereq because if it hasn't been rebuilt, we need to +# build it first - but including it as a normal prereq can lead to +# infinite restarts as the conf process will keep updating it. +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) $(SDKCONFIG_MAKEFILE) $(call ComponentMake,$(1),$(2)) component_project_vars.mk endef diff --git a/make/project_config.mk b/make/project_config.mk index 555086fb8d..09cec2e0be 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -42,7 +42,7 @@ defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it NON_CONFIG_TARGETS := clean %-clean help menuconfig defconfig -AUTO_CONF_REGEN_TARGET := $(BUILD_DIR_BASE)/include/config/auto.conf +AUTO_CONF_REGEN_TARGET := $(SDKCONFIG_MAKEFILE) # disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets # (and not building default target) @@ -50,7 +50,7 @@ ifneq ("$(MAKECMDGOALS)","") ifeq ($(filter $(NON_CONFIG_TARGETS), $(MAKECMDGOALS)),$(MAKECMDGOALS)) AUTO_CONF_REGEN_TARGET := # dummy target -$(BUILD_DIR_BASE)/include/config/auto.conf: +$(SDKCONFIG_MAKEFILE): endif endif From f7cb6fc9690074674ad4a0e4d302131787b77253 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 9 Nov 2016 20:26:28 +0100 Subject: [PATCH 040/131] NVS Examples update Updated previusly submitted example, saved it as "07_nvs_rw_value" and added a new one. Overview of examples: * 07_nvs_rw_value - simple read and write a single value * 08_nvs_rw_blob - read and write a blob that is extened with new data on each restart of ESP32 Removed LICENSE files --- examples/07_nvs_read_write/README.md | 7 - .../07_nvs_read_write/main/nvs_read_write.c | 62 ------ .../Makefile | 2 +- examples/07_nvs_rw_value/README.md | 13 ++ .../main/component.mk | 0 examples/07_nvs_rw_value/main/nvs_rw_value.c | 77 ++++++++ examples/08_nvs_rw_blob/Makefile | 9 + examples/08_nvs_rw_blob/README.md | 14 ++ examples/08_nvs_rw_blob/main/component.mk | 10 + examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 183 ++++++++++++++++++ 10 files changed, 307 insertions(+), 70 deletions(-) delete mode 100644 examples/07_nvs_read_write/README.md delete mode 100644 examples/07_nvs_read_write/main/nvs_read_write.c rename examples/{07_nvs_read_write => 07_nvs_rw_value}/Makefile (83%) create mode 100644 examples/07_nvs_rw_value/README.md rename examples/{07_nvs_read_write => 07_nvs_rw_value}/main/component.mk (100%) create mode 100644 examples/07_nvs_rw_value/main/nvs_rw_value.c create mode 100644 examples/08_nvs_rw_blob/Makefile create mode 100644 examples/08_nvs_rw_blob/README.md create mode 100644 examples/08_nvs_rw_blob/main/component.mk create mode 100644 examples/08_nvs_rw_blob/main/nvs_rw_blob.c diff --git a/examples/07_nvs_read_write/README.md b/examples/07_nvs_read_write/README.md deleted file mode 100644 index bac8ee8d54..0000000000 --- a/examples/07_nvs_read_write/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Non-Volatile Storage (NVS) Read and Write Example - -Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. - -Example also shows how to use basic diagnostics if read / write operation was successful. - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/nvs_read_write.c b/examples/07_nvs_read_write/main/nvs_read_write.c deleted file mode 100644 index 40d330f62f..0000000000 --- a/examples/07_nvs_read_write/main/nvs_read_write.c +++ /dev/null @@ -1,62 +0,0 @@ -/* Non-Volatile Storage (NVS) Read and Write Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "nvs_flash.h" -#include "nvs.h" - -void app_main() -{ - nvs_flash_init(); - system_init(); - - nvs_handle handle_to_settings; - esp_err_t err; - int32_t restart_counter = 0; - - // Open the NVS - printf("Opening Non-Volatile Storage (NVS) ... "); - err = nvs_open("settings", NVS_READWRITE, &handle_to_settings); - printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); - - // Read from the NVS - printf("Reading restart counter from NVS ... "); - err = nvs_get_i32(handle_to_settings, "restart_conter", &restart_counter); - switch (err) { - case ESP_OK: - printf("OK\n"); - printf("Restart counter = %d\n", restart_counter); - break; - case ESP_ERR_NVS_NOT_FOUND: - printf("The counter is not initialized yet!\n"); - break; - default : - printf("Error (%d) reading!\n", err); - } - - // Write to the NVS - printf("Updating restart counter in NVS ... "); - restart_counter++; - err = nvs_set_i32(handle_to_settings, "restart_conter", restart_counter); - printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); - - // Close the NVS - nvs_close(handle_to_settings); - - // Restart module - for (int i = 10; i >= 0; i--) { - printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); - } - printf("Restarting now.\n"); - fflush(stdout); - system_restart(); -} diff --git a/examples/07_nvs_read_write/Makefile b/examples/07_nvs_rw_value/Makefile similarity index 83% rename from examples/07_nvs_read_write/Makefile rename to examples/07_nvs_rw_value/Makefile index 3d6adb4d02..fdfc09c574 100644 --- a/examples/07_nvs_read_write/Makefile +++ b/examples/07_nvs_rw_value/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := nvs-read-write +PROJECT_NAME := nvs-rw-value include $(IDF_PATH)/make/project.mk diff --git a/examples/07_nvs_rw_value/README.md b/examples/07_nvs_rw_value/README.md new file mode 100644 index 0000000000..83dc29fd18 --- /dev/null +++ b/examples/07_nvs_rw_value/README.md @@ -0,0 +1,13 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a single integer value using NVS. + +The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. + +Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVR. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. + +Check another example *08_nvs_rw_blob*, that shows how to read and write a blob (binary large object). + +Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/component.mk b/examples/07_nvs_rw_value/main/component.mk similarity index 100% rename from examples/07_nvs_read_write/main/component.mk rename to examples/07_nvs_rw_value/main/component.mk diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c new file mode 100644 index 0000000000..df53d33b4a --- /dev/null +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -0,0 +1,77 @@ +/* Non-Volatile Storage (NVS) Read and Write a Value - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" + +void app_main() +{ + nvs_flash_init(); + + nvs_handle my_handle; + esp_err_t err; + + printf("\n"); + + // Open + printf("Opening Non-Volatile Storage (NVS) ... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + printf("Error (%d) opening NVS!\n", err); + } else { + printf("Done\n"); + + // Read + printf("Reading restart counter from NVS ... "); + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + switch (err) { + case ESP_OK: + printf("Done\n"); + printf("Restart counter = %d\n", restart_counter); + break; + case ESP_ERR_NVS_NOT_FOUND: + printf("The value is not initialized yet!\n"); + break; + default : + printf("Error (%d) reading!\n", err); + } + + // Write + printf("Updating restart counter in NVS ... "); + restart_counter++; + err = nvs_set_i32(my_handle, "restart_conter", restart_counter); + printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); + + // Commit + printf("Committing updates in NVS ... "); + err = nvs_commit(my_handle); + printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); + + // Close + nvs_close(my_handle); + } + + printf("\n"); + + // Restart module + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + system_restart(); +} diff --git a/examples/08_nvs_rw_blob/Makefile b/examples/08_nvs_rw_blob/Makefile new file mode 100644 index 0000000000..717744bbe8 --- /dev/null +++ b/examples/08_nvs_rw_blob/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := nvs-rw-blob + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/08_nvs_rw_blob/README.md b/examples/08_nvs_rw_blob/README.md new file mode 100644 index 0000000000..81a0e36c71 --- /dev/null +++ b/examples/08_nvs_rw_blob/README.md @@ -0,0 +1,14 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts. + + * value - tracks number of ESP32 module soft and hard restarts. + * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0. + +Example also shows how to implement diagnostics if read / write operation was successful. + +If not done already, consider checking simpler example *07_nvs_rw_value*, that has been used as a starting point for preparing this one. + +Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c new file mode 100644 index 0000000000..b4ff7e98ac --- /dev/null +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -0,0 +1,183 @@ +/* Non-Volatile Storage (NVS) Read and Write a Blob - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "driver/gpio.h" + +#define STORAGE_NAMESPACE "storage" + +/* Save the number of module restarts in NVS + by first reading and then incrementing + the number that has been saved previously. + Return an error if anything goes wrong + during this process. + */ +esp_err_t save_restart_counter(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + + // Write + restart_counter++; + err = nvs_set_i32(my_handle, "restart_conter", restart_counter); + if (err != ESP_OK) return err; + + // Commit + err = nvs_commit(my_handle); + if (err != ESP_OK) return err; + + // Close + nvs_close(my_handle); + return ESP_OK; +} + +/* Save new run time value in NVS + by first reading a table of previously saved values + and then adding the new value at the end of the table. + Return an error if anything goes wrong + during this process. + */ +esp_err_t save_run_time(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read the size of memory space required for blob + size_t required_size = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_blob(my_handle, "run_time", NULL, &required_size); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + + // Read previously saved blob if available + uint32_t* run_time; + if (required_size > 0) { + run_time = malloc(required_size); + // read previously saved blob + err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); + if (err != ESP_OK) return err; + // add extra space for the new value + required_size += sizeof(uint32_t); + run_time = realloc(run_time, required_size); + } else { + // nothing saved jet - just allocate space for the first value to save + required_size = sizeof(uint32_t); + run_time = malloc(required_size); + } + + // Write value including previously saved blob if available + run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS; + err = nvs_set_blob(my_handle, "run_time", run_time, required_size); + if (err != ESP_OK) return err; + + free(run_time); + + // Commit + err = nvs_commit(my_handle); + if (err != ESP_OK) return err; + + // Close + nvs_close(my_handle); + return ESP_OK; +} + +/* Read from NVS and print restart counter + and the table with run times. + Return an error if anything goes wrong + during this process. + */ +esp_err_t print_what_saved(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read restart counter + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + printf("Restart counter = %d\n", restart_counter); + + // Read run time blob + size_t required_size = 0; // value will default to 0, if not set yet in NVS + // obtain required memory space to store blob being read from NVS + err = nvs_get_blob(my_handle, "run_time", NULL, &required_size); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + printf("Run time:\n"); + if (required_size == 0) { + printf("Nothing saved yet!\n"); + } else { + uint32_t* run_time = malloc(required_size); + err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); + if (err != ESP_OK) return err; + for (int i = 0; i < required_size / sizeof(uint32_t); i++) { + printf("%d: %d\n", i + 1, run_time[i]); + } + free(run_time); + } + + // Close + nvs_close(my_handle); + return ESP_OK; +} + + +void app_main() +{ + nvs_flash_init(); + + esp_err_t err; + + err = print_what_saved(); + if (err != ESP_OK) printf("Error (%d) reading data from NVS!\n", err); + + err = save_restart_counter(); + if (err != ESP_OK) printf("Error (%d) saving restart counter to NVS!\n", err); + + gpio_pad_select_gpio(GPIO_NUM_0); + gpio_set_direction(GPIO_NUM_0, GPIO_MODE_DEF_INPUT); + + /* Read the status of GPIO0. If GPIO0 is LOW for longer than 1000 ms, + then save module's run time and restart it + */ + while (1) { + if (gpio_get_level(GPIO_NUM_0) == 0) { + vTaskDelay(1000 / portTICK_RATE_MS); + if(gpio_get_level(GPIO_NUM_0) == 0) { + err = save_run_time(); + if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err); + printf("Restarting...\n"); + fflush(stdout); + system_restart(); + } + } + vTaskDelay(200 / portTICK_RATE_MS); + } +} From 3aca537157d7e8fc0d88be780260e4e1c134a897 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 07:37:16 +0100 Subject: [PATCH 041/131] Optimsed code Following note in review by @igrr :+1: --- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index b4ff7e98ac..38066bf62f 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -74,22 +74,14 @@ esp_err_t save_run_time(void) if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; // Read previously saved blob if available - uint32_t* run_time; + uint32_t* run_time = malloc(required_size + sizeof(uint32_t)); if (required_size > 0) { - run_time = malloc(required_size); - // read previously saved blob err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); if (err != ESP_OK) return err; - // add extra space for the new value - required_size += sizeof(uint32_t); - run_time = realloc(run_time, required_size); - } else { - // nothing saved jet - just allocate space for the first value to save - required_size = sizeof(uint32_t); - run_time = malloc(required_size); } // Write value including previously saved blob if available + required_size += sizeof(uint32_t); run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS; err = nvs_set_blob(my_handle, "run_time", run_time, required_size); if (err != ESP_OK) return err; From 25db1effd6f7673bac7f4eef290f6cd7a0556485 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 15:52:47 +1100 Subject: [PATCH 042/131] docs: Rewrite build system docs to address new system, plus general editing Fixes TW#8017 --- docs/build_system.rst | 491 ++++++++++++++++++++++++++---------------- 1 file changed, 304 insertions(+), 187 deletions(-) diff --git a/docs/build_system.rst b/docs/build_system.rst index 34db487e0a..a9ebcfe004 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -8,262 +8,368 @@ Read this document if you want to know how to organise a new ESP-IDF project. We recommend using the esp-idf-template_ project as a starting point for your project. +Using the Build System +====================== + +The esp-idf README file contains a description of how to use the build system to build your project. + Overview ======== -An ESP-IDF project can be seen as an almagation of a number of components. -For example, for a webserver that shows the current humidity, we would -have: +An ESP-IDF project can be seen as an amalgamation of a number of components. +For example, for a webserver that shows the current humidity, there could be: - The ESP32 base libraries (libc, rom bindings etc) - The WiFi drivers - A TCP/IP stack - The FreeRTOS operating system - A webserver -- A driver for an humidity sensor +- A driver for the humidity sensor - Main code tying it all together -ESP-IDF makes these components explicit and configurable. To do that, when a project -is compiled, the build environment will look up all the components in the -ESP-IDF directories, the project directories and optionally custom other component -directories. It then allows the user to configure compile-time options using -a friendly text-based menu system to customize the ESP-IDF as well as other components -to the requirements of the project. After the components are customized, the -build process will compile everything into an output file, which can then be uploaded -into a board in a way that can also be defined by components. +ESP-IDF makes these components explicit and configurable. To do that, +when a project is compiled, the build environment will look up all the +components in the ESP-IDF directories, the project directories and +(optionally) in additional custom component directories. It then +allows the user to configure the ESP-IDF project using a a text-based +menu system to customize each component. After the components in the +project are configured, the build process will compile the project. -A project in this sense is defined as a directory under which all the files required -to build it live, excluding the ESP-IDF files and the toolchain. A simple project -tree might look like this:: +Concepts +-------- - - myProject/ - build/ +- A "project" is a directory that contains all the files and configuration to build a single "app" (executable), as well as additional supporting output such as a partition table, data/filesystem partitions, and a bootloader. + +- "Project configuration" is held in a single file called sdkconfig in the root directory of the project. This configuration file is modified via ``make menuconfig`` to customise the configuration of the project. A single project contains exactly one project configuration. + +- An "app" is an executable which is built by esp-idf. A single project will usually build two apps - a "project app" (the main executable, ie your custom firmware) and a "bootloader app" (the initial bootloader program which launches the project app). + +- "components" are modular pieces of standalone code which are compiled into static libraries (.a files) and linked into an app. Some are provided by esp-idf itself, others may be sourced from other places. + +Some things are not part of the project: + +- "ESP-IDF" is not part of the project. Instead it is standalone, and linked to the project via the ``IDF_PATH`` environment variable which holds the path of the ``esp-idf`` directory. This allows the IDF framework to be decoupled from your project. + +- The toolchain for compilation is not part of the project. The toolchain should be installed in the system command line PATH, or the path to the toolchain can be set as part of the compiler prefix in the project configuration. + + +Example Project +--------------- + +An example project directory tree might look like this:: + - myProject/ + - Makefile + - sdkconfig - components/ - component1/ - component.mk - Kconfig - src1.c - component2/ - component.mk - Kconfig - src1.c + - include/ + - component2.h - main/ - src1.c - src2.c - - Makefile + - component.mk + - build/ -As we can see, a project consists of a components/ subdirectory containing its -components as well as one or more directories containing the project-specific -sources; by default a single directory called 'main' is assumed. The project -directory will also have a Makefile where the projects name as well as optionally -other options are defined. After compilation, the project directory will contain -a 'build'-directory containing all of the objects, libraries and other generated -files as well as the final binary. +This example "myProject" contains the following elements: -Components also have a custom makefile - ``component.mk``. This contains various definititions -influencing the build process of the component as well as the project it's used -in. Components may also include a Kconfig file defining the compile-time options that are -settable by means of the menu system. +- A top-level project Makefile. This Makefile set the ``PROJECT_NAME`` variable and (optionally) defines + other project-wide make variables. It includes the core ``$(IDF_PATH)/make/project.mk`` makefile which + implements the rest of the ESP-IDF build system. -Project Makefile variables that can be set by the programmer:: +- "sdkconfig" project configuration file. This file is created/updated when "make menuconfig" runs, and holds configuration for all of the components in the project (including esp-idf itself). The "sdkconfig" file may or may not be added to the source control system of the project. - PROJECT_NAME: Mandatory. Name for the project - BUILD_DIR_BASE: Set the directory where all objects/libraries/binaries end up in. - Defaults to $(PROJECT_PATH)/build - COMPONENT_DIRS: Search path for components. Defaults to the component/ directories - in the ESP-IDF path and the project path. - COMPONENTS: A list of component names. Defaults to all the component found in the - COMPONENT_DIRS directory - EXTRA_COMPONENT_DIRS: Defaults to unset. Use this to add directories to the default - COMPONENT_DIRS. - SRCDIRS: Directories under the project dir containing project-specific sources. - Defaults to 'main'. These are treated as 'lite' components: they do not have - include directories that are passed to the compilation pass of all components and - they do not have a Kconfig option. +- Optional "components" directory contains components that are part of the project. A project does not have to contain custom components of this kind, but it can be useful for structuring reusable code or including third party components that aren't part of ESP-IDF. -Component-specific component.mk variables that can be set by the programmer:: +- "main" directory is a special "pseudo-component" that contains source code for the project itself. "main" is a default name, the Makefile variable ``SRCDIRS`` defaults to this but can be set to look for pseudo-components in other directories. - COMPONENT_ADD_INCLUDEDIRS: Relative path to include directories to be added to - the entire project. If an include directory is only needed to compile this - specific component, don't add it here. - COMPONENT_PRIV_INCLUDEDIRS: Relative path to include directories that are only used - when compiling this specific component. - COMPONENT_DEPENDS: Names of any components that need to be compiled before this component. - COMPONENT_ADD_LDFLAGS: LD flags to add for the entire project. Defaults to -l$(COMPONENT_NAME). - Add libraries etc in the current directory as $(abspath libwhatever.a) - COMPONENT_EXTRA_INCLUDES: Any extra include paths used when compiling the component's - source files. These will be prefixed with '-I' and passed to the compiler. - Similar to COMPONENT_PRIV_INCLUDEDIRS, but these paths are passed as-is instead of - expanded relative to the component directory. - COMPONENT_SRCDIRS: Relative directories to look in for sources. Defaults to '.', the current - directory (the root of the component) only. Use this to specify any subdirectories. Note - that specifying this overwrites the default action of compiling everything in the - components root dir; to keep this behaviour please also add '.' as a directory in this - list. - COMPONENT_OBJS: Object files to compile. Defaults to the .o variants of all .c and .S files - that are found in COMPONENT_SRCDIRS. - COMPONENT_EXTRA_CLEAN: Files that are generated using rules in the components Makefile - that also need to be cleaned - COMPONENT_BUILDRECIPE: Recipe to build the component. Optional. Defaults to building all - COMPONENT_OBJS and linking them into lib(componentname).a - COMPONENT_CLEANRECIPE: Recipe to clean the component. Optional. Defaults to removing - all built objects and libraries. - COMPONENT_BUILD_DIR: Equals the cwd of the component build, which is the build dir - of the component (where all the .o etc files should be created). +- "build" directory is where build output is created. After the make process is run, this directory will contain interim object files and libraries as well as final binary output files. This directory is usually not added to source control or distributed with the project source code. -These variables are already set early on in the Makefile and the values in it will -be usable in component or project Makefiles:: +Component directories contain a component makefile - ``component.mk``. This may contain variable definitions +to control the build process of the component, and its integration into the overall project. See `Component Makefiles` for more details. - CC, LD, AR, OBJCOPY: Xtensa gcc tools - HOSTCC, HOSTLD etc: Host gcc tools - LDFLAGS, CFLAGS: Set to usable values as defined in ESP-IDF Makefile - PROJECT_NAME: Name of the project, as set in project makefile - PROJECT_PATH: Path to the root of the project folder - COMPONENTS: Name of the components to be included - CONFIG_*: All values set by 'make menuconfig' have corresponding Makefile variables. +Each component may also include a ``Kconfig`` file defining the `component configuration` options that can be set via the project configuration. Some components may also include ``Kconfig.projbuild`` and ``Makefile.projbuild`` files, which are special files for `overriding parts of the project`. -Inside your component's component.mk makefile, you can override or add to these variables -as necessary. The changes are isolated from other components (see Makefile.projbuild below -if you want to share these changes with all other components.) +Project Makefiles +----------------- -For components, there also are these defines:: +Each project has a single Makefile that contains build settings for the entire project. By default, the project Makefile can be quite minimal. - COMPONENT_PATH: Absolute path to the root of the source tree of the component we're - compiling - COMPONENT_LIBRARY: The full path to the static library the components compilation pass - is supposed to generate +Minimal Example Makefile +^^^^^^^^^^^^^^^^^^^^^^^^ -Make Process ------------- +:: + PROJECT_NAME := myProject + + include $(IDF_PATH)/make/project.mk -The Make process is always invoked from the project directory by the -user; invoking it anywhere else gives an error. This is what happens if -we build a binary: -The Makefile first determines how it was included. It figures out -various paths as well as the components available to it. It will also -collect the ldflags and includes that the components specify they need. -It does this by running a dummy make on the components with a "get_variable" -target that will output these values. +Mandatory Project Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Makefile will then create targets to build the lib*.a libraries of -all components and make the elf target depend on this. The main Makefile -invokes Make on the componen.mk of each components inside a sub-mke: this way -the components have full freedom to do whatever is necessary to build -the library without influencing other components. By default, the -component.mk includes the utility makefile $(IDF_PATH)/make/component_common.mk. -This provides default targets and configurations that will work -out-of-the-box for most projects. +- ``PROJECT_NAME``: Name of the project. Binary output files will use this name - ie myProject.bin, myProject.elf. -KConfig -------- +Optional Project Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^ -Each component can also have a Kconfig file, alongside the component.mk, that contains -details to add to "menuconfig" for this component. +These variables all have default values that can be overridden for custom behaviour. Look in ``make/project.mk`` for all of the implementation details. + +- ``PROJECT_PATH``: Top-level project directory. Defaults to the directory containing the Makefile. Many other project variables are based on this variable. The project path cannot contain spaces. +- ``BUILD_DIR_BASE``: The build directory for all objects/libraries/binaries. Defaults to ``$(PROJECT_PATH)/build``. +- ``COMPONENT_DIRS``: Directories to search for components. Defaults to `$(IDF_PATH)/components`, `$(PROJECT_PATH)/components` and ``EXTRA_COMPONENT_DIRS``. Override this variable if you don't want to search for components in the esp-idf & project ``components`` directories. +- ``EXTRA_COMPONENT_DIRS``: Optional list of additional directories to search for components. Components themselves are in sub-directories of these directories, this is a top-level directory containing the component directories. +- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the COMPONENT_DIRS directories. +- ``SRCDIRS``: Directories under the main project directory which contain project-specific "pseudo-components". Defaults to 'main'. The difference between specifying a directory here and specifying it under ``EXTRA_COMPONENT_DIRS`` is that a directory in ``SRCDIRS`` is a component itself (contains a file "component.mk"), whereas a directory in ``EXTRA_COMPONENT_DIRS`` contains component directories which contain a file "component.mk". See the `Example Project` for a concrete case of this. + + +Component Makefiles +------------------- + +Each project contains one or more components, which can either be part of esp-idf or added from other component directories. + +A component is any sub-directory that contains a `component.mk` file.[#f1]_. + +Minimal Component Makefile +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set: +- All source files in the same directory as the makefile (*.c, *.cpp, *.S) will be compiled into the component library +- A sub-directory "include" will be added to the global include search path for all other components. +- The component library will be linked into the project app. + +See `example component makefiles` for more complete component makefile examples. + +Note that there is a different between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process. + +.. component variables: + +Preset Component Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following component-specific variables are available for use inside ``component.mk``, but should not be modified: + +- ``COMPONENT_PATH``: The component directory. Evaluates to the absolute path of the directory containing ``component.mk``. The component path cannot contain spaces. +- ``COMPONENT_NAME``: Name of the component. Defaults to the name of the component directory. +- ``COMPONENT_BUILD_DIR``: The component build directory. Evaluates to the absolute path of a directory inside `$(BUILD_DIR_BASE)` where this component's source files are to be built. This is also the Current Working Directory any time the component is being built, so relative paths in make targets, etc. will be relative to this directory. +- ``COMPONENT_LIBRARY``: Name of the static library file (relative to the component build directory) that will be built for this component. Defaults to ``$(COMPONENT_NAME).a``. + +The following variables are set at the project level, but exported for use in the component build: + +- ``PROJECT_NAME``: Name of the project, as set in project Makefile +- ``PROJECT_PATH``: Absolute path of the project directory containing the project Makefile. +- ``COMPONENTS``: Name of all components that are included in this build. +- ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. +- ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. +- ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. + +If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug. + +Optional Project-Wide Component Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following variables can be set inside ``component.mk`` to control build settings across the entire project: + +- ``COMPONENT_ADD_INCLUDEDIRS``: Paths, relative to the component + directory, which will be added to the include search path for + all components in the project. Defaults to ``include`` if not overridden. If an include directory is only needed to compile + this specific component, add it to ``COMPONENT_PRIV_INCLUDEDIRS`` instead. +- ``COMPONENT_ADD_LDFLAGS``: Add linker arguments to the LDFLAGS for + the app executable. Defaults to ``-l$(COMPONENT_NAME)``. If + adding pre-compiled libraries to this directory, add them as + absolute paths - ie $(COMPONENT_PATH)/libwhatever.a +- ``COMPONENT_DEPENDS``: Optional list of component names that should + be compiled before this component. This is not necessary for + link-time dependencies, because all component include directories + are available at all times. It is necessary if one component + generates an include file which you then want to include in another + component. Most components do not need to set this variable. + + +Optional Component-Specific Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following variables can be set inside ``component.mk`` to control the build of that component: + +- ``COMPONENT_PRIV_INCLUDEDIRS``: Directory paths, must be relative to + the component directory, which will be added to the include search + path for this component's source files only. +- ``COMPONENT_EXTRA_INCLUDES``: Any extra include paths used when + compiling the component's source files. These will be prefixed with + '-I' and passed as-is to the compiler. Similar to the + ``COMPONENT_PRIV_INCLUDEDIRS`` variable, except these paths are not + expanded relative to the component directory. +- ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the + component directory, which will be searched for source files (*.cpp, + *.c, *.S). Defaults to '.', ie the component directory + itself. Override this to specify a different list of directories + which contain source files. +- ``COMPONENT_OBJS``: Object files to compile. Default value is a .o + file for each source file that is found in ``COMPONENT_SRCDIRS``. + Overriding this list allows you to exclude source files in + ``COMPONENT_SRCDIRS`` that would otherwise be compiled. See + `Specifying source files` +- ``COMPONENT_EXTRA_CLEAN``: Paths, relative to the component build + directory, of any files that are generated using custom make rules + in the component.mk file and which need to be removed as part of + ``make clean``. See `Source Code Generation` for an example. +- ``COMPONENT_OWNBUILDTARGET`` & `COMPONENT_OWNCLEANTARGET`: These + targets allow you to fully override the default build behaviour for + the component. See `Fully Overriding The Component Makefile` for + more details. +- ``CFLAGS``: Flags passed to the C compiler. A default set of + ``CFLAGS`` is defined based on project settings. Component-specific + additions can be made via ``CFLAGS +=``. It is also possible + (although not recommended) to override this variable completely for + a component. +- ``CPPFLAGS``: Flags passed to the C preprocessor (used for .c, .cpp + and .S files). A default set of ``CPPFLAGS`` is defined based on + project settings. Component-specific additions can be made via + ``CPPFLAGS +=``. It is also possible (although not recommended) to + override this variable completely for a component. +- ``CXXFLAGS``: Flags passed to the C++ compiler. A default set of + ``CXXFLAGS`` is defined based on project + settings. Component-specific additions can be made via ``CXXFLAGS + +=``. It is also possible (although not recommended) to override + this variable completely for a component. + +Component Configuration +----------------------- + +Each component can also have a Kconfig file, alongside ``component.mk``. This contains contains +configuration settings to add to the "make menuconfig" for this component. + +These settings are found under the "Component Settings" menu when menuconfig is run. + +To create a component KConfig file, it is easiest to start with one of the KConfig files distributed with esp-idf. + +For an example, see `Adding conditional configuration`. + +Build Process Internals +----------------------- + +Top Level: Project Makefile +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- "make" is always run from the project directory and the project makefile, typically named Makefile. +- The project makefile sets ``PROJECT_NAME`` and optionally customises other `optional project variables` +- The project makefile includes ``$(IDF_PATH)/make/project.mk`` which contains the project-level Make logic. +- ``project.mk`` fills in default project-level make variables and includes make variables from the project configuration. If the generated makefile containing project configuration is out of date, then it is regenerated (via targets in ``project_config.mk``) and then the make process restarts from the top. +- ``project.mk`` builds a list of components to build, based on the default component directories or a custom list of components set in `optional project variables`. +- Each component can set some `optional project-wide component variables`. These are included via generated makefiles named ``component_project_vars.mk`` - there is one per component. These generated makefiles are included into ``project.mk``. If any are missing or out of date, they are regenerated (via a recursive make call to the component makefile) and then the make process restarts from the top. +- `Makefile.projbuild` files from components are included into the make process, to add extra targets or configuration. +- By default, the project makefile also generates top-level build & clean targets for each component and sets up `app` and `clean` targets to invoke all of these sub-targets. +- In order to compile each component, a recursive make is performed for the component makefile. + +To better understand the project make process, have a read through the ``project.mk`` file itself. + +Second Level: Component Makefiles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile. +- The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``. +- ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these. +- If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file. +- The ``component_project_vars.mk`` file has its own target in ``component_wrapper.mk``, which is evaluated from ``project.mk`` if this file needs to be rebuilt due to changes in the component makefile or the project configuration. + +To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf. + +Overriding Parts of the Project +------------------------------- Makefile.projbuild ------------------- +^^^^^^^^^^^^^^^^^^ -For components that have parts that need to be evaluated in the top-level -project context, you can create a file called Makefile.projbuild in the -component root directory. These files is included into the project's -top-level Makefile. +For components that have build requirements that must be evaluated in the top-level +project make pass, you can create a file called ``Makefile.projbuild`` in the +component directory. This makefile is included when ``project.mk`` is evaluated. For example, if your component needs to add to CFLAGS for the entire project (not just for its own source files) then you can set -``CFLAGS +=`` in Makefile.projbuild. Note that this isn't necessary for -adding include directories to the project, you can set -``COMPONENT_ADD_INCLUDEDIRS`` (see above) in the component.mk. +``CFLAGS +=`` in Makefile.projbuild. +``Makefile.projbuild`` files are used heavily inside esp-idf, for defining project-wide build features such as ``esptool.py`` command line arguments and the ``bootloader`` "special app". + +Note that ``Makefile.projbuild`` isn't necessary for the most common component uses - such as adding include directories to the project, or LDFLAGS to the final linking step. These values can be customised via the ``component.mk`` file itself. See `Optional Project-Wide Component Variables` for details. + +Take care when setting variables or targets in this file. As the values are included into the top-level project makefile pass, they can influence or break functionality across all components! KConfig.projbuild ------------------ +^^^^^^^^^^^^^^^^^ -There's an equivalent to Makefile.projbuild for KConfig: if you want to include -options at the top-level, not inside the 'components' submenu then create a Kconfig.projbuild and -it will be included in the main menu of menuconfig. +This is an equivalent to `Makefile.projbuild` for `component configuration` KConfig files. If you want to include +configuration options at the top-level of menuconfig, rather than inside the "Component Configuration" sub-menu, then these can be defined in the KConfig.projbuild file alongside the ``component.mk`` file. -Take good care when (re)defining stuff here: because it's included with all the other -.projbuild files, it's possible to overwrite variables or re-declare targets defined in -the ESP-IDF makefile/Kconfig and other .projbuild files. It's generally better to just -create a KConfig file, if you can. +Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`. -Writing Component Makefiles +Example Component Makefiles --------------------------- -A component consists of a directory which doubles as the name for the -component: a component named 'httpd' lives in a directory called 'httpd' -Because components usually live under the project directory (although -they can also reside in an other folder), the path to this may be -something like /home/myuser/projects/myprojects/components/httpd . +Because the build environment tries to set reasonable defaults that will work most +of the time, component.mk can be very small or even empty (see `Minimal Component Makefile`). However, overriding `component variables` is usually required for some functionality. -Components can have any name (unique to the project) but the name -cannot contain spaces (esp-idf does not support spaces in paths). - -One of the things that most components will have is a component.mk makefile, -containing instructions on how to build the component. Because the -build environment tries to set reasonable defaults that will work most -of the time, component.mk can be very small. - -Simplest component.mk -===================== - -At the minimum, component.mk will just include the ESP-IDF component "common" makefile, -which adds common component functionality:: - - include $(IDF_PATH)/make/component_common.mk - -This will take all the .c and .S files in the component root and compile -them into object files, finally linking them into a library. +Here are some more advanced examples of ``component.mk`` makefiles: Adding source directories -========================= +^^^^^^^^^^^^^^^^^^^^^^^^^ -By default, subdirectories are ignored. If your project has sources in subdirectories +By default, sub-directories are ignored. If your project has sources in sub-directories instead of in the root of the component then you can tell that to the build -system by setting COMPONENT_SRCDIRS:: +system by setting ``COMPONENT_SRCDIRS``:: COMPONENT_SRCDIRS := src1 src2 - include $(IDF_PATH)/make/component_common.mk -This will compile all source files in the src1/ and src2/ subdirectories +This will compile all source files in the src1/ and src2/ sub-directories instead. Specifying source files -======================= +^^^^^^^^^^^^^^^^^^^^^^^ The standard component.mk logic adds all .S and .c files in the source directories as sources to be compiled unconditionally. It is possible -to circumvent that logic and hardcode the objects to be compiled by -manually setting the COMPONENT_OBJS variable to the name of the +to circumvent that logic and hard-code the objects to be compiled by +manually setting the ``COMPONENT_OBJS`` variable to the name of the objects that need to be generated:: COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o - include $(IDF_PATH)/make/component_common.mk + COMPONENT_SRCDIRS := . thing anotherthing +Note that ``COMPONENT_SRCDIRS`` must be set as well. Adding conditional configuration -================================ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The configuration system can be used to conditionally compile some files -dependending on the options selected in ``make menuconfig``: +depending on the options selected in ``make menuconfig``: -Kconfig:: +``Kconfig``:: config FOO_ENABLE_BAR - bool "Enable the BAR feature." - help - This enables the BAR feature of the FOO component. + bool "Enable the BAR feature." + help + This enables the BAR feature of the FOO component. -Makefile:: - COMPONENT_OBJS := foo_a.o foo_b.o $(if $(CONFIG_FOO_ENABLE_BAR),foo_bar.o foo_bar_interface.o) - include $(IDF_PATH)/make/component_common.mk +``component.mk``:: + COMPONENT_OBJS := foo_a.o foo_b.o + + ifdef CONFIG_FOO_BAR + COMPONENT_OBJS += foo_bar.o foo_bar_interface.o + endif + +See the `GNU Make Manual` for conditional syntax that can be used use in makefiles. Source Code Generation -====================== +^^^^^^^^^^^^^^^^^^^^^^ -Some components will have a situation where a source file isn't supplied -with the component itself but has to be generated from another file. Say -our component has a header file that consists of the converted binary -data of a BMP file, converted using a hypothetical tool called bmp2h. The -header file is then included in as C source file called graphics_lib.c:: +Some components will have a situation where a source file isn't +supplied with the component itself but has to be generated from +another file. Say our component has a header file that consists of the +converted binary data of a BMP file, converted using a hypothetical +tool called bmp2h. The header file is then included in as C source +file called graphics_lib.c:: COMPONENT_EXTRA_CLEAN := logo.h @@ -272,16 +378,25 @@ header file is then included in as C source file called graphics_lib.c:: logo.h: $(COMPONENT_PATH)/logo.bmp bmp2h -i $^ -o $@ - include $(IDF_PATH)/make/component_common.mk In this example, graphics_lib.o and logo.h will be generated in the -current directory (the build directory) while logo.bmp comes with the -component and resides under the component path. Because logo.h is a -generated file, it needs to be cleaned when make clean is called which -why it is added to the COMPONENT_EXTRA_CLEAN variable. +component build directory, whereas logo.bmp resides in the component +source directory. + +Because logo.h is a generated file, it needs to be cleaned when make +clean is called which why it is added to the COMPONENT_EXTRA_CLEAN +variable. + +Adding logo.h to the ``graphics_lib.o`` dependencies causes it to be +generated before ``graphics_lib.c`` is compiled. + +If a a source file in another component included ``logo.h``, then this +component's name would have to be added to the other component's +``COMPONENT_DEPENDS`` list to ensure that the components were built +in-order. Cosmetic Improvements -===================== +^^^^^^^^^^^^^^^^^^^^^ The above example will work just fine, but there's one last cosmetic improvement that can be done. The make system tries to make the make @@ -295,10 +410,9 @@ make process:: graphics_lib.o: logo.h logo.h: $(COMPONENT_PATH)/logo.bmp - $(summary) BMP2H $@ - $(Q) bmp2h -i $^ -o $@ + $(summary) BMP2H $@ + $(Q) bmp2h -i $^ -o $@ - include $(IDF_PATH)/make/component_common.mk Fully Overriding The Component Makefile --------------------------------------- @@ -307,12 +421,15 @@ Obviously, there are cases where all these recipes are insufficient for a certain component, for example when the component is basically a wrapper around another third-party component not originally intended to be compiled under this build system. In that case, it's possible to forego -the build system entirely by setting COMPONENT_OWNBUILDTARGET and -possibly COMPONENT_OWNCLEANTARGET and defining your own build- and clean +the esp-idf build system entirely by setting COMPONENT_OWNBUILDTARGET and +possibly COMPONENT_OWNCLEANTARGET and defining your own targets named ``build`` and ``clean`` in ``component.mk`` target. The build target can do anything as long as it creates -$(COMPONENT_LIBRARY) for the main file to link into the project binary, -and even that is not necessary: if the COMPONENT_ADD_LDFLAGS variable -is set, the component can instruct the linker to do anything else as well. +$(COMPONENT_LIBRARY) for the project make process to link into the app binary. + +(Actually, even this is not strictly necessary - if the COMPONENT_ADD_LDFLAGS variable +is set then the component can instruct the linker to link other binaries instead.) .. _esp-idf-template: https://github.com/espressif/esp-idf-template +.. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html +.. _[_f1]: Actually, some components in esp-idf are "pure configuration" components that don't have a component.mk file, only a Makefile.projbuild and/or Kconfig.projbuild file. However, these components are unusual and most components have a component.mk file. From 07f36a9437fa2fa3f8de7bd2dba0759504f9ad86 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 16:19:59 +1100 Subject: [PATCH 043/131] Build system: Use ifndef X in makefiles instead of ifeq("$(X)","") --- make/component_wrapper.mk | 8 ++++---- make/project.mk | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 68efe0d211..fe081e32fd 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -8,7 +8,7 @@ # # CWD is the build directory of the component. -ifeq ("$(PROJECT_PATH)","") +ifndef PROJECT_PATH $(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) endif @@ -60,7 +60,7 @@ include $(COMPONENT_MAKEFILE) # Object files which need to be linked into the library # By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. -ifeq ("$(COMPONENT_OBJS)", "") +ifndef COMPONENT_OBJS # Find all source files in all COMPONENT_SRCDIRS COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) @@ -120,7 +120,7 @@ component_project_vars.mk:: # If COMPONENT_OWNBUILDTARGET is not set, define a phony build target and # a COMPONENT_LIBRARY link target. -ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") +ifndef COMPONENT_OWNBUILDTARGET .PHONY: build build: $(COMPONENT_LIBRARY) @mkdir -p $(COMPONENT_SRCDIRS) @@ -134,7 +134,7 @@ $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target -ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") +ifndef COMPONENT_OWNCLEANTARGET CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk .PHONY: clean clean: diff --git a/make/project.mk b/make/project.mk index f9cf20a329..4554d1d329 100644 --- a/make/project.mk +++ b/make/project.mk @@ -40,10 +40,9 @@ help: MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR -# Figure out PROJECT_PATH if not set -ifeq ("$(PROJECT_PATH)","") -#The path to the project: we assume the Makefile including this file resides -#in the root of that directory. +# Default path to the project: we assume the Makefile including this file +# is in the project directory +ifndef PROJECT_PATH PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif @@ -67,7 +66,7 @@ SRCDIRS ?= main # The project Makefile can define a list of components, but if it does not do this we just take # all available components in the component dirs. -ifeq ("$(COMPONENTS)","") +ifndef COMPONENTS # Find all component names. The component names are the same as the # directories they're in, so /bla/components/mycomponent/ -> mycomponent. We then use # COMPONENT_DIRS to build COMPONENT_PATHS with the full path to each component. From 4f4c9030fd284e79eed46aa403fb9ec5efba84a8 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 10 Nov 2016 17:01:39 +0800 Subject: [PATCH 044/131] Fix uart tx bug, data pass(with flow control) through test ok. --- components/driver/uart.c | 2 +- components/esp32/include/esp_err.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index b961fbed71..d9e3fd64ca 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -729,7 +729,7 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool } else { evt.type = UART_DATA; } - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_tx_data_t), portMAX_DELAY); while(size > 0) { int send_size = size > max_size / 2 ? max_size / 2 : size; xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index f8271ba259..a1f4b8f359 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -34,6 +34,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_INVALID_SIZE 0x104 #define ESP_ERR_NOT_FOUND 0x105 #define ESP_ERR_NOT_SUPPORTED 0x106 +#define ESP_ERR_TIMEOUT 0x107 + #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ From 86d8f63005d42bb9c46330c67b35ba8efc3a04ec Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 10 Nov 2016 17:59:46 +0800 Subject: [PATCH 045/131] Per-CPU interrupt handlers and args --- components/esp32/crosscore_int.c | 7 +---- .../include/freertos/FreeRTOSConfig.h | 6 ----- components/freertos/readme_smp.txt | 5 ---- components/freertos/xtensa_intr.c | 11 ++++++-- components/freertos/xtensa_intr_asm.S | 23 ++++++++++++++-- components/freertos/xtensa_vectors.S | 26 +++++++++++++++++++ 6 files changed, 57 insertions(+), 21 deletions(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 60a45da402..60f972a2a2 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -45,14 +45,9 @@ the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably */ static void esp_crosscore_isr(void *arg) { uint32_t myReasonVal; -#if 0 //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; -#else - //The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... this is a valid but slightly less optimal replacement. - volatile uint32_t *myReason=&reason[xPortGetCoreID()]; -#endif + //Clear the interrupt first. if (xPortGetCoreID()==0) { WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 224857c86c..47566ab3b3 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -265,12 +265,6 @@ #define INCLUDE_eTaskGetState 1 #define configUSE_QUEUE_SETS 1 -#if (!defined XT_INTEXC_HOOKS) -#define configXT_INTEXC_HOOKS 1 /* Exception hooks used by certain tests */ -#if configUSE_TRACE_FACILITY_2 -#define configASSERT_2 1 /* Specific to Xtensa port */ -#endif -#endif #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 diff --git a/components/freertos/readme_smp.txt b/components/freertos/readme_smp.txt index 38f332416a..fdd9b146b1 100644 --- a/components/freertos/readme_smp.txt +++ b/components/freertos/readme_smp.txt @@ -19,11 +19,6 @@ it would on a single-core system: the other core still will keep on executing all it's own. Use a mux, queue or semaphore to protect your structures instead. -- While each core has individual interrupts, the handlers are shared. This -means that when you set a handler for an interrupt, it will get triggered if -the interrupt is triggered on both CPU0 as well as on CPU1. This is something -we may change in future FreeRTOS-esp32 releases. - - This FreeRTOS version has the task local storage backported from the 8.2.x versions. It, however, has an addition: you can also set a callback when you set the pointer. This callback will be called by the idle task, with the diff --git a/components/freertos/xtensa_intr.c b/components/freertos/xtensa_intr.c index f5ca7d151f..e9c0b79b96 100644 --- a/components/freertos/xtensa_intr.c +++ b/components/freertos/xtensa_intr.c @@ -30,7 +30,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" +#include "freertos/portable.h" #include "rom/ets_sys.h" @@ -39,7 +41,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Handler table is in xtensa_intr_asm.S */ // Todo: Make multicore - JD -extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM]; +extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS]; /* @@ -66,6 +68,8 @@ xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f) if( n < 0 || n >= XCHAL_EXCCAUSE_NUM ) return 0; /* invalid exception number */ + /* Convert exception number to _xt_exception_table name */ + n = n * portNUM_PROCESSORS + xPortGetCoreID(); old = _xt_exception_table[n]; if (f) { @@ -89,7 +93,7 @@ typedef struct xt_handler_table_entry { void * arg; } xt_handler_table_entry; -extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS]; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; /* @@ -118,6 +122,9 @@ xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg) if( Xthal_intlevel[n] > XCHAL_EXCM_LEVEL ) return 0; /* priority level too high to safely handle in C */ + /* Convert exception number to _xt_exception_table name */ + n = n * portNUM_PROCESSORS + xPortGetCoreID(); + entry = _xt_interrupt_table + n; old = entry->handler; diff --git a/components/freertos/xtensa_intr_asm.S b/components/freertos/xtensa_intr_asm.S index 5f9890dfe4..8c7ae63fdb 100644 --- a/components/freertos/xtensa_intr_asm.S +++ b/components/freertos/xtensa_intr_asm.S @@ -30,6 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include "xtensa_context.h" +#include "FreeRTOSConfig.h" #if XCHAL_HAVE_INTERRUPTS @@ -59,6 +60,15 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ Table of C-callable interrupt handlers for each interrupt. Note that not all slots can be filled, because interrupts at level > EXCM_LEVEL will not be dispatched to a C handler by default. + + Stored as: + int 0 cpu 0 + int 0 cpu 1 + ... + int 0 cpu n + int 1 cpu 0 + int 1 cpu 1 + etc ------------------------------------------------------------------------------- */ @@ -69,7 +79,7 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ _xt_interrupt_table: .set i, 0 - .rept XCHAL_NUM_INTERRUPTS + .rept XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS .word xt_unhandled_interrupt /* handler address */ .word i /* handler arg (default: intnum) */ .set i, i+1 @@ -85,6 +95,15 @@ _xt_interrupt_table: Table of C-callable exception handlers for each exception. Note that not all slots will be active, because some exceptions (e.g. coprocessor exceptions) are always handled by the OS and cannot be hooked by user handlers. + + Stored as: + exc 0 cpu 0 + exc 0 cpu 1 + ... + exc 0 cpu n + exc 1 cpu 0 + exc 1 cpu 1 + etc ------------------------------------------------------------------------------- */ @@ -93,7 +112,7 @@ _xt_interrupt_table: .align 4 _xt_exception_table: - .rept XCHAL_EXCCAUSE_NUM + .rept XCHAL_EXCCAUSE_NUM * portNUM_PROCESSORS .word xt_unhandled_exception /* handler address */ .endr diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 7cf70f0032..2a373810fd 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -113,6 +113,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define XIE_ARG 4 #define XIE_SIZE 8 + +/* + Macro get_percpu_entry_for - convert a per-core ID into a multicore entry. + Basically does reg=reg*portNUM_PROCESSORS+current_core_id + Multiple versions here for multiple +*/ + .macro get_percpu_entry_for reg scratch +#if (portNUM_PROCESSORS == 1) + /* No need to do anything */ +#elif (portNUM_PROCESSORS == 2) + /* Optimized 2-core code. */ + getcoreid \scratch + addx2 \reg,\reg,\scratch +#else + /* Generalized n-core code. Untested! */ + movi \scratch,portNUM_PROCESSORS + mull \scratch,\reg,\scratch + getcoreid \reg + add \reg,\scratch,\reg +#endif + .endm /* -------------------------------------------------------------------------------- Macro extract_msb - return the input with only the highest bit set. @@ -229,6 +250,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ + get_percpu_entry_for a3, a12 movi a4, _xt_interrupt_table addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ l32i a4, a3, XIE_HANDLER /* a4 = handler address */ @@ -395,6 +417,9 @@ panic_print_hex_ok: with index 0 containing the entry for user exceptions. Initialized with all 0s, meaning no handler is installed at each level. See comment in xtensa_rtos.h for more details. + + *WARNING* This array is for all CPUs, that is, installing a hook for + one CPU will install it for all others as well! -------------------------------------------------------------------------------- */ @@ -688,6 +713,7 @@ _xt_user_exc: rsr a2, EXCCAUSE /* recover exc cause */ movi a3, _xt_exception_table + get_percpu_entry_for a3, a4 addx4 a4, a2, a3 /* a4 = address of exception table entry */ l32i a4, a4, 0 /* a4 = handler address */ #ifdef __XTENSA_CALL0_ABI__ From 07c30f22b58f4511912a2e809bba3474769632bd Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 10 Nov 2016 18:04:23 +0800 Subject: [PATCH 046/131] Add core info to xt interrupt api --- components/freertos/include/freertos/xtensa_api.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/freertos/include/freertos/xtensa_api.h b/components/freertos/include/freertos/xtensa_api.h index 04ad7d62d8..87922691dd 100644 --- a/components/freertos/include/freertos/xtensa_api.h +++ b/components/freertos/include/freertos/xtensa_api.h @@ -42,7 +42,8 @@ typedef void (*xt_exc_handler)(XtExcFrame *); /* ------------------------------------------------------------------------------- - Call this function to set a handler for the specified exception. + Call this function to set a handler for the specified exception. The handler + will be installed on the core that calls this function. n - Exception number (type) f - Handler function address, NULL to uninstall handler. @@ -61,7 +62,8 @@ extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f); /* ------------------------------------------------------------------------------- - Call this function to set a handler for the specified interrupt. + Call this function to set a handler for the specified interrupt. The handler + will be installed on the core that calls this function. n - Interrupt number. f - Handler function address, NULL to uninstall handler. @@ -73,7 +75,8 @@ extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg); /* ------------------------------------------------------------------------------- - Call this function to enable the specified interrupts. + Call this function to enable the specified interrupts on the core that runs + this code. mask - Bit mask of interrupts to be enabled. ------------------------------------------------------------------------------- @@ -83,7 +86,8 @@ extern void xt_ints_on(unsigned int mask); /* ------------------------------------------------------------------------------- - Call this function to disable the specified interrupts. + Call this function to disable the specified interrupts on the core that runs + this code. mask - Bit mask of interrupts to be disabled. ------------------------------------------------------------------------------- From 2f6d71ff8ce28fda15898c5fe1b5669ee8dba505 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 10 Nov 2016 21:12:22 +0800 Subject: [PATCH 047/131] esp32: merge amsdu esp32 now can support AMSDU --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 76f9109806..84af0ed366 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 76f91098061b0052fe1bb67e85001014f39b84a0 +Subproject commit 84af0ed366e1ba38984f7df517a77f8ec4fa27ed From c6073cb5de044d9fe5662dd3eee50ec4f37181ce Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:44:09 +0100 Subject: [PATCH 048/131] Conform Style Guide --- docs/conf.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 494ab3cf7f..551cd86dd0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,9 +22,6 @@ import os # -- Run DoxyGen to prepare XML for Sphinx--------------------------------- # ref. https://github.com/rtfd/readthedocs.org/issues/388 -# -# added by krzychb, 24-Oct-2016 -# from subprocess import call, Popen, PIPE import shlex @@ -298,8 +295,6 @@ texinfo_documents = [ # -- Use sphinx_rtd_theme for local builds -------------------------------- # ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs # -# added by krzychb, 24-Oct-2016 -# # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' From 48a976ddbf3706c67eb15729777fba96c831a526 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:44:59 +0100 Subject: [PATCH 049/131] Fixed link so it can render by Sphinx --- docs/style-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4fa8321762..9bf00f1f7d 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -172,7 +172,7 @@ To re-format a file, run:: Documenting code ---------------- -Please see the guide here: `Documenting Code `_. +Please see the guide here: :doc:`documenting-code`. Structure and naming -------------------- From 9858fde4a23ffd56454641741185979555bb1372 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:47:29 +0100 Subject: [PATCH 050/131] New items to documentation TOC - Style Guide - UART API --- docs/index.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 42be69ee0c..3f32806e5c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -92,9 +92,10 @@ Contents: Wi-Fi Bluetooth - GPIO - LED Control - + api/gpio + api/uart + api/ledc + Logging Non-Volatile Storage Virtual Filesystem @@ -114,6 +115,7 @@ Contents: :maxdepth: 1 contributing + Style Guide documenting-code contributor-agreement From ce0382f0c03f7ffbe5d75d4a2b6a3ede30406cef Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:50:55 +0100 Subject: [PATCH 051/131] Revised api files - Included UART API - Addedd "Header Files" - Improved template - deleted redundat nvs.rst --- docs/api/bt.rst | 5 +++ docs/api/esp_wifi.rst | 7 ++- docs/api/gpio.rst | 7 ++- docs/api/ledc.rst | 5 +++ docs/api/log.rst | 14 ++++++ docs/api/nvs.rst | 68 ----------------------------- docs/api/nvs_flash.rst | 6 +++ docs/api/template.rst | 39 ++++++++++++----- docs/api/uart.rst | 98 ++++++++++++++++++++++++++++++++++++++++++ docs/api/vfs.rst | 6 +++ 10 files changed, 174 insertions(+), 81 deletions(-) delete mode 100644 docs/api/nvs.rst create mode 100644 docs/api/uart.rst diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 7cbbb9158b..0ab17b2aa5 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `bt/include/bt.h `_ + Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst index e417e18ca0..c13d3d751a 100644 --- a/docs/api/esp_wifi.rst +++ b/docs/api/esp_wifi.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `esp32/include/esp_wifi.h `_ + Macros ------ @@ -28,7 +33,6 @@ Type Definitions ---------------- .. doxygentypedef:: wifi_promiscuous_cb_t -.. doxygentypedef:: wifi_rxcb_t .. doxygentypedef:: esp_vendor_ie_cb_t Functions @@ -68,7 +72,6 @@ Functions .. doxygenfunction:: esp_wifi_get_config .. doxygenfunction:: esp_wifi_ap_get_sta_list .. doxygenfunction:: esp_wifi_set_storage -.. doxygenfunction:: esp_wifi_reg_rxcb .. doxygenfunction:: esp_wifi_set_auto_connect .. doxygenfunction:: esp_wifi_get_auto_connect .. doxygenfunction:: esp_wifi_set_vendor_ie diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 72ba3e82fb..0cd4eca365 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -18,8 +18,13 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/driver/gpio.h `_ + Macros ------- +^^^^^^ .. doxygendefine:: GPIO_SEL_0 .. doxygendefine:: GPIO_SEL_1 diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 32b639f3f9..f379e9d008 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/ledc.h `_ + Data Structures ^^^^^^^^^^^^^^^ diff --git a/docs/api/log.rst b/docs/api/log.rst index 49f97108aa..d2f2fcd073 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -1,8 +1,22 @@ .. include:: ../../components/log/README.rst +Application Example +------------------- + +`Instructions`_ + API Reference ------------- +`Instructions`_ + +.. _Instructions: template.html + +Header Files +^^^^^^^^^^^^ + + * `log/include/esp_log.h `_ + Macros ^^^^^^ diff --git a/docs/api/nvs.rst b/docs/api/nvs.rst deleted file mode 100644 index fc2bba5a16..0000000000 --- a/docs/api/nvs.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. include:: ../../components/nvs_flash/README.rst - -API Reference -------------- - -Enumerations -^^^^^^^^^^^^ - -.. doxygenenum:: nvs_open_mode - -Functions -^^^^^^^^^ - -.. doxygenfunction:: nvs_flash_init -.. doxygenfunction:: nvs_flash_init_custom - -.. doxygenfunction:: nvs_open - -*Note: the following nvs_set_X function are "the same" except the data type accepted* - -.. doxygenfunction:: nvs_set_i8 -.. doxygenfunction:: nvs_set_u8 -.. doxygenfunction:: nvs_set_i16 -.. doxygenfunction:: nvs_set_u16 -.. doxygenfunction:: nvs_set_i32 -.. doxygenfunction:: nvs_set_u32 -.. doxygenfunction:: nvs_set_i64 -.. doxygenfunction:: nvs_set_u64 -.. doxygenfunction:: nvs_set_str -.. doxygenfunction:: nvs_set_blob - -*Note: the following nvs_get_X functions are "the same" except the data type returned* - -.. doxygenfunction:: nvs_get_i8 -.. doxygenfunction:: nvs_get_u8 -.. doxygenfunction:: nvs_get_i16 -.. doxygenfunction:: nvs_get_u16 -.. doxygenfunction:: nvs_get_i32 -.. doxygenfunction:: nvs_get_u32 -.. doxygenfunction:: nvs_get_i64 -.. doxygenfunction:: nvs_get_u64 -.. doxygenfunction:: nvs_get_str -.. doxygenfunction:: nvs_get_blob - -.. doxygenfunction:: nvs_erase_key -.. doxygenfunction:: nvs_erase_all -.. doxygenfunction:: nvs_commit -.. doxygenfunction:: nvs_close - -Error codes -^^^^^^^^^^^ - -.. doxygendefine:: ESP_ERR_NVS_BASE -.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED -.. doxygendefine:: ESP_ERR_NVS_NOT_FOUND -.. doxygendefine:: ESP_ERR_NVS_TYPE_MISMATCH -.. doxygendefine:: ESP_ERR_NVS_READ_ONLY -.. doxygendefine:: ESP_ERR_NVS_NOT_ENOUGH_SPACE -.. doxygendefine:: ESP_ERR_NVS_INVALID_NAME -.. doxygendefine:: ESP_ERR_NVS_INVALID_HANDLE -.. doxygendefine:: ESP_ERR_NVS_REMOVE_FAILED -.. doxygendefine:: ESP_ERR_NVS_KEY_TOO_LONG -.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL -.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE -.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH - - - diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst index 16c74fa530..0768fa5597 100644 --- a/docs/api/nvs_flash.rst +++ b/docs/api/nvs_flash.rst @@ -8,6 +8,12 @@ Application Example API Reference ------------- +Header Files +^^^^^^^^^^^^ + + * `nvs_flash/include/nvs_flash.h `_ + * `nvs_flash/include/nvs.h `_ + Macros ^^^^^^ diff --git a/docs/api/template.rst b/docs/api/template.rst index 3c8bcdb62c..6feb7ba271 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -51,10 +51,11 @@ API Reference *INSTRUCTIONS* - 1. Provide list of API members divided into sections. - 2. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. + 1. Specify the names of header files used to generate this reference. Each name should be linked to the source on `espressif/esp-idf `_ repository. + 2. Provide list of API members divided into sections. + 3. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. - * Data Structures -``.. doxygenstruct::`` + * Data Structures -``.. doxygenstruct::`` together with ``:members:`` * Macros - ``.. doxygendefine::`` * Type Definitions - ``.. doxygentypedef::`` * Enumerations - ``.. doxygenenum::`` @@ -62,30 +63,48 @@ API Reference See `Breathe documentation `_ for additional information. - 3. Once done remove superfluous headers. - 4. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. + 4. Once done remove superfluous headers. + 5. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. + +Header Files +^^^^^^^^^^^^ + + * `path/header-file.h` Data Structures ^^^^^^^^^^^^^^^ -``.. doxygenstruct:: name_of_structure`` +:: + + .. doxygenstruct:: name_of_structure + :members: Macros ^^^^^^ -``.. doxygendefine:: name_of_macro`` +:: + + .. doxygendefine:: name_of_macro Type Definitions ^^^^^^^^^^^^^^^^ -``.. doxygentypedef:: name_of_type`` +:: + + .. doxygentypedef:: name_of_type Enumerations ^^^^^^^^^^^^ -``.. doxygenenum:: name_of_enumeration`` +:: + + .. doxygenenum:: name_of_enumeration Functions ^^^^^^^^^ -``.. doxygenfunction:: name_of_function`` +:: + + .. doxygenfunction:: name_of_function + + diff --git a/docs/api/uart.rst b/docs/api/uart.rst new file mode 100644 index 0000000000..609816fd46 --- /dev/null +++ b/docs/api/uart.rst @@ -0,0 +1,98 @@ +UART +==== + +Overview +-------- + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +API Reference +------------- + +`Instructions`_ + +.. _Instructions: template.html + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/uart.h `_ + +Data Structures +^^^^^^^^^^^^^^^ + +.. doxygenstruct:: uart_config_t + :members: + +.. doxygenstruct:: uart_intr_config_t + :members: + +.. doxygenstruct:: uart_event_t + :members: + +Macros +^^^^^^ + +.. doxygendefine:: UART_FIFO_LEN +.. doxygendefine:: UART_INTR_MASK +.. doxygendefine:: UART_LINE_INV_MASK +.. doxygendefine:: UART_BITRATE_MAX +.. doxygendefine:: UART_PIN_NO_CHANGE +.. doxygendefine:: UART_INVERSE_DISABLE +.. doxygendefine:: UART_INVERSE_RXD +.. doxygendefine:: UART_INVERSE_CTS +.. doxygendefine:: UART_INVERSE_TXD +.. doxygendefine:: UART_INVERSE_RTS + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: uart_word_length_t +.. doxygenenum:: uart_stop_bits_t +.. doxygenenum:: uart_port_t +.. doxygenenum:: uart_parity_t +.. doxygenenum:: uart_hw_flowcontrol_t +.. doxygenenum:: uart_event_type_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: uart_set_word_length +.. doxygenfunction:: uart_get_word_length +.. doxygenfunction:: uart_set_stop_bits +.. doxygenfunction:: uart_get_stop_bits +.. doxygenfunction:: uart_set_parity +.. doxygenfunction:: uart_get_parity +.. doxygenfunction:: uart_set_baudrate +.. doxygenfunction:: uart_get_baudrate +.. doxygenfunction:: uart_set_line_inverse +.. doxygenfunction:: uart_set_hw_flow_ctrl +.. doxygenfunction:: uart_get_hw_flow_ctrl +.. doxygenfunction:: uart_clear_intr_status +.. doxygenfunction:: uart_enable_intr_mask +.. doxygenfunction:: uart_disable_intr_mask +.. doxygenfunction:: uart_enable_rx_intr +.. doxygenfunction:: uart_disable_rx_intr +.. doxygenfunction:: uart_disable_tx_intr +.. doxygenfunction:: uart_enable_tx_intr +.. doxygenfunction:: uart_isr_register +.. doxygenfunction:: uart_set_pin +.. doxygenfunction:: uart_set_rts +.. doxygenfunction:: uart_set_dtr +.. doxygenfunction:: uart_param_config +.. doxygenfunction:: uart_intr_config +.. doxygenfunction:: uart_driver_install +.. doxygenfunction:: uart_driver_delete +.. doxygenfunction:: uart_wait_tx_done +.. doxygenfunction:: uart_tx_chars +.. doxygenfunction:: uart_write_bytes +.. doxygenfunction:: uart_write_bytes_with_break +.. doxygenfunction:: uart_read_bytes +.. doxygenfunction:: uart_flush + + diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 122a8671ea..df6cd03f67 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -8,6 +8,12 @@ Application Example API Reference ------------- +Header Files +^^^^^^^^^^^^ + + * `vfs/include/esp_vfs.h `_ + * `vfs/include/esp_vfs_dev.h `_ + Macros ^^^^^^ From 341593f7d204898fbc9686a3edc05972765386ee Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 12:29:38 +1100 Subject: [PATCH 052/131] build system: Remove need for $(Q) macro in recipes, use --silent in MAKEFLAGS instead --- components/bootloader/Makefile.projbuild | 6 ++-- components/esptool_py/Makefile.projbuild | 6 ++-- components/partition_table/Makefile.projbuild | 8 ++--- docs/build_system.rst | 30 ++++++++----------- make/common.mk | 9 +++--- make/component_wrapper.mk | 12 ++++---- make/project.mk | 6 ++-- make/project_config.mk | 14 ++++----- tools/kconfig/Makefile | 26 ++++++++-------- 9 files changed, 56 insertions(+), 61 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index c03288d107..90d9784427 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -21,10 +21,10 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE) - $(Q) $(BOOTLOADER_MAKE) $@ + $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean + $(BOOTLOADER_MAKE) app-clean clean: bootloader-clean @@ -41,7 +41,7 @@ bootloader-flash: $(BOOTLOADER_BIN) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ $(BOOTLOADER_BUILD_DIR): - $(Q) mkdir -p $@ + mkdir -p $@ else CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 69c01e1e7f..27a2132845 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -24,14 +24,14 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< + $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." - $(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." - $(Q) $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) + $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) $(eval $(call SubmoduleCheck,$(ESPTOOLPY_SRC),$(COMPONENT_PATH)/esptool)) diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 98631cc854..0799c91d4f 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -20,7 +20,7 @@ PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.cs $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." - $(Q) $(GEN_ESP32PART) $< $@ + $(GEN_ESP32PART) $< $@ all_binaries: $(PARTITION_TABLE_BIN) @@ -30,16 +30,16 @@ ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN) partition_table: $(PARTITION_TABLE_BIN) @echo "Partition table binary generated. Contents:" @echo $(SEPARATOR) - $(Q) $(GEN_ESP32PART) $< + $(GEN_ESP32PART) $< @echo $(SEPARATOR) @echo "Partition flashing command:" @echo "$(PARTITION_TABLE_FLASH_CMD)" partition_table-flash: $(PARTITION_TABLE_BIN) @echo "Flashing partition table..." - $(Q) $(PARTITION_TABLE_FLASH_CMD) + $(PARTITION_TABLE_FLASH_CMD) partition_table-clean: - $(Q) rm -f $(PARTITION_TABLE_BIN) + rm -f $(PARTITION_TABLE_BIN) clean: partition_table-clean diff --git a/docs/build_system.rst b/docs/build_system.rst index a9ebcfe004..50514083ef 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -275,6 +275,18 @@ Second Level: Component Makefiles To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf. +Debugging The Make Process +-------------------------- + +Some tips for debugging the esp-idf build system: + +- Appending ``V=1`` to the make arguments (or setting it as an environment variable) will cause make to echo all commands executed, and also each directory as it is entered for a sub-make. +- Running ``make -w`` will cause make to echo each directory as it is entered for a sub-make - same as ``V=1`` but without also echoing all commands. +- Running ``make --trace`` (possibly in addition to one of the above arguments) will print out every target as it is built, and the dependency which caused it to be built. +- Running ``make -p`` prints a (very verbose) summary of every generated target in each makefile. + +For more debugging tips and general make information, see the `GNU Make Manual`. + Overriding Parts of the Project ------------------------------- @@ -395,24 +407,6 @@ component's name would have to be added to the other component's ``COMPONENT_DEPENDS`` list to ensure that the components were built in-order. -Cosmetic Improvements -^^^^^^^^^^^^^^^^^^^^^ - -The above example will work just fine, but there's one last cosmetic -improvement that can be done. The make system tries to make the make -process somewhat easier on the eyes by hiding the commands (unless you -run make with the V=1 switch) and this does not do that yet. Here's an -improved version that will output in the same style as the rest of the -make process:: - - COMPONENT_EXTRA_CLEAN := test_tjpgd_logo.h - - graphics_lib.o: logo.h - - logo.h: $(COMPONENT_PATH)/logo.bmp - $(summary) BMP2H $@ - $(Q) bmp2h -i $^ -o $@ - Fully Overriding The Component Makefile --------------------------------------- diff --git a/make/common.mk b/make/common.mk index 779811f1ad..c465fd4b22 100644 --- a/make/common.mk +++ b/make/common.mk @@ -16,13 +16,14 @@ export SDKCONFIG_MAKEFILE # sub-makes (like bootloader) will reuse this path # if V is unset or not 1, $(summary) echoes a summary and $(details) does nothing V ?= $(VERBOSE) ifeq ("$(V)","1") -Q := summary := @true details := @echo else -Q := @ summary := @echo details := @true + +# disable echoing of commands, directory names +MAKEFLAGS += --silent endif # Pseudo-target to check a git submodule has been properly initialised @@ -36,8 +37,8 @@ endif define SubmoduleCheck $(1): @echo "WARNING: Missing submodule $(2) for $$@..." - $(Q) [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) - $(Q) [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) + [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) + [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) @echo "Attempting 'git submodule update --init' in esp-idf root directory..." cd ${IDF_PATH} && git submodule update --init $(2) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index fe081e32fd..84bd52da5a 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -129,8 +129,8 @@ build: $(COMPONENT_LIBRARY) # an archive when multiple filenames have the same name (src1/test.o and src2/test.o) $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(summary) AR $@ - $(Q) rm -f $@ - $(Q) $(AR) cru $@ $(COMPONENT_OBJS) + rm -f $@ + $(AR) cru $@ $(COMPONENT_OBJS) endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target @@ -139,7 +139,7 @@ CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(C .PHONY: clean clean: $(summary) RM $(CLEAN_FILES) - $(Q) rm -f $(CLEAN_FILES) + rm -f $(CLEAN_FILES) endif # Include all dependency files already generated @@ -150,15 +150,15 @@ define GenerateCompileTargets # $(1) - directory containing source files, relative to $(COMPONENT_PATH) $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) $$(summary) CXX $$@ - $$(Q) $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) $$(summary) AS $$@ - $$(Q) $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ # CWD is build dir, create the build subdirectory if it doesn't exist $(1): diff --git a/make/project.mk b/make/project.mk index 4554d1d329..a273da1c25 100644 --- a/make/project.mk +++ b/make/project.mk @@ -238,7 +238,7 @@ COMPONENT_LIBRARIES = $(filter $(notdir $(COMPONENT_PATHS_BUILDABLE)),$(APP_LIBR # the rules to build these are emitted as part of GenerateComponentTarget below $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp)/lib$(libcomp).a) $(summary) LD $(notdir $@) - $(Q) $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) + $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) # Generation of $(APP_BIN) from $(APP_ELF) is added by the esptool # component's Makefile.projbuild @@ -257,7 +257,7 @@ $(BUILD_DIR_BASE): # # Is recursively expanded by the GenerateComponentTargets macro define ComponentMake -$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk ++$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk endef # Generate top-level component-specific targets for each component @@ -303,7 +303,7 @@ $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponent app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) - $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) + rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) clean: app-clean diff --git a/make/project_config.mk b/make/project_config.mk index 09cec2e0be..aac231f99f 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -23,7 +23,7 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(summary) MENUCONFIG - $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig + $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") ifeq ("$(filter defconfig,$(MAKECMDGOALS))","") @@ -36,8 +36,8 @@ endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG - $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config - $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig + mkdir -p $(BUILD_DIR_BASE)/include/config + $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it @@ -56,9 +56,9 @@ endif $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) $(summary) GENCONFIG - $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config - $(Q) cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig - $(Q) touch $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h + mkdir -p $(BUILD_DIR_BASE)/include/config + cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig + touch $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h # touch to ensure both output files are newer - as 'conf' can also update sdkconfig (a dependency). Without this, # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) @@ -68,4 +68,4 @@ clean: config-clean config-clean: $(summary RM CONFIG) $(MAKE) -C $(KCONFIG_TOOL_DIR) clean - $(Q) rm -rf $(BUILD_DIR_BASE)/include/config $(BUILD_DIR_BASE)/include/sdkconfig.h + rm -rf $(BUILD_DIR_BASE)/include/config $(BUILD_DIR_BASE)/include/sdkconfig.h diff --git a/tools/kconfig/Makefile b/tools/kconfig/Makefile index fee5a69316..9680b74109 100644 --- a/tools/kconfig/Makefile +++ b/tools/kconfig/Makefile @@ -41,13 +41,13 @@ nconfig: nconf $< $(silent) $(Kconfig) silentoldconfig: conf - $(Q)mkdir -p include/config include/generated + mkdir -p include/config include/generated $< $(silent) --$@ $(Kconfig) localyesconfig localmodconfig: streamline_config.pl conf - $(Q)mkdir -p include/config include/generated - $(Q)perl $< --$@ . $(Kconfig) > .tmp.config - $(Q)if [ -f .config ]; then \ + mkdir -p include/config include/generated + perl $< --$@ . $(Kconfig) > .tmp.config + if [ -f .config ]; then \ cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ mv -f .tmp.config .config; \ @@ -57,7 +57,7 @@ localyesconfig localmodconfig: streamline_config.pl conf mv -f .tmp.config .config; \ conf $(silent) --silentoldconfig $(Kconfig); \ fi - $(Q)rm -f .tmp.config + rm -f .tmp.config # These targets map 1:1 to the commandline options of 'conf' @@ -84,22 +84,22 @@ ifeq ($(KBUILD_DEFCONFIG),) else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) @$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) + $< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) else @$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'" - $(Q) $(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) + $(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) endif endif %_defconfig: conf - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + $< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) %.config: conf $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) - $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) - +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig + $(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) + +yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig PHONY += kvmconfig kvmconfig: kvm_guest.config @@ -111,7 +111,7 @@ xenconfig: xen.config PHONY += tinyconfig tinyconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config + $(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config # Help text used by make help help: @@ -181,7 +181,7 @@ clean-files += $(conf-objs) $(mconf-objs) conf mconf $(lxdialog) PHONY += dochecklxdialog $(addprefix ,$(lxdialog)): dochecklxdialog dochecklxdialog: - $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(CC) $(CFLAGS) $(LOADLIBES_mconf) + $(CONFIG_SHELL) $(check-lxdialog) -check $(CC) $(CFLAGS) $(LOADLIBES_mconf) always := dochecklxdialog @@ -285,7 +285,7 @@ quiet_cmd_moc = MOC $@ # Extract gconf menu items for i18n support gconf.glade.h: gconf.glade - $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \ + intltool-extract --type=gettext/glade --srcdir=$(srctree) \ gconf.glade From 7b77daaea41274a221930f01d8bc1226ef46a336 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Fri, 11 Nov 2016 10:51:33 +0800 Subject: [PATCH 053/131] wps: add blocking param for API esp_wifi_wps_start() --- components/esp32/include/esp_wps.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 0a413a1978..f95eaa5e2a 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -83,7 +83,9 @@ esp_err_t esp_wifi_wps_disable(void); * * @attention WPS can only be used when ESP32 station is enabled. * - * @param null + * @param timeout_ms : maximum blocking time before API return. + * - 0 : non-blocking + * - 1~120000 : blocking time (not supported in IDF v1.0) * * @return * - ESP_OK : succeed @@ -92,7 +94,7 @@ esp_err_t esp_wifi_wps_disable(void); * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -esp_err_t esp_wifi_wps_start(void); +esp_err_t esp_wifi_wps_start(int timeout_ms); /** * @} From 9aa9086c8b72852a58ded9bb1769640c5eaffee1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 11 Nov 2016 12:16:54 +0800 Subject: [PATCH 054/131] examples: minor tweaks to comments --- examples/07_nvs_rw_value/README.md | 4 ++-- examples/07_nvs_rw_value/main/component.mk | 8 -------- examples/07_nvs_rw_value/main/nvs_rw_value.c | 5 ++++- examples/08_nvs_rw_blob/main/component.mk | 8 -------- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 5 ++++- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/examples/07_nvs_rw_value/README.md b/examples/07_nvs_rw_value/README.md index 83dc29fd18..09cd364e8d 100644 --- a/examples/07_nvs_rw_value/README.md +++ b/examples/07_nvs_rw_value/README.md @@ -4,9 +4,9 @@ Demonstrates how to read and write a single integer value using NVS. The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. -Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVR. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. +Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. -Check another example *08_nvs_rw_blob*, that shows how to read and write a blob (binary large object). +Check another example *08_nvs_rw_blob*, that shows how to read and write variable length binary data (blob). Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). diff --git a/examples/07_nvs_rw_value/main/component.mk b/examples/07_nvs_rw_value/main/component.mk index 24356f23ed..d33485c26c 100644 --- a/examples/07_nvs_rw_value/main/component.mk +++ b/examples/07_nvs_rw_value/main/component.mk @@ -1,10 +1,2 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# include $(IDF_PATH)/make/component_common.mk diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c index df53d33b4a..978c48edb8 100644 --- a/examples/07_nvs_rw_value/main/nvs_rw_value.c +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -55,7 +55,10 @@ void app_main() err = nvs_set_i32(my_handle, "restart_conter", restart_counter); printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); - // Commit + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. printf("Committing updates in NVS ... "); err = nvs_commit(my_handle); printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk index 24356f23ed..d33485c26c 100644 --- a/examples/08_nvs_rw_blob/main/component.mk +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -1,10 +1,2 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index 38066bf62f..3fbdfcacd6 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -44,7 +44,10 @@ esp_err_t save_restart_counter(void) err = nvs_set_i32(my_handle, "restart_conter", restart_counter); if (err != ESP_OK) return err; - // Commit + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. err = nvs_commit(my_handle); if (err != ESP_OK) return err; From fdf3db19590cd722c8d0be11b3649cc2b1ff8c29 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 11 Nov 2016 12:26:42 +0800 Subject: [PATCH 055/131] Comment fix --- components/freertos/xtensa_vectors.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 2a373810fd..f180705e70 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -117,7 +117,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Macro get_percpu_entry_for - convert a per-core ID into a multicore entry. Basically does reg=reg*portNUM_PROCESSORS+current_core_id - Multiple versions here for multiple + Multiple versions here to optimize for specific portNUM_PROCESSORS values. */ .macro get_percpu_entry_for reg scratch #if (portNUM_PROCESSORS == 1) From 57009aaa7f9138958678d08bc9ecc2f037f4e9da Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 11 Nov 2016 19:20:54 +0800 Subject: [PATCH 056/131] Add a more scalable mechanism for the FreeRTOS tick- and idle hooks; idle handler now uses WAITI to reduce power --- components/esp32/freertos_hooks.c | 81 +++++++++++++++++++ components/esp32/include/esp_freertos_hooks.h | 58 +++++++++++++ components/esp32/int_wdt.c | 64 ++++++++------- components/esp32/task_wdt.c | 24 ++++-- components/freertos/Kconfig | 29 +++++++ .../include/freertos/FreeRTOSConfig.h | 4 +- components/freertos/tasks.c | 20 +++-- 7 files changed, 234 insertions(+), 46 deletions(-) create mode 100644 components/esp32/freertos_hooks.c create mode 100644 components/esp32/include/esp_freertos_hooks.h diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c new file mode 100644 index 0000000000..50ebd3d054 --- /dev/null +++ b/components/esp32/freertos_hooks.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include "esp_attr.h" +#include "esp_freertos_hooks.h" + +//We use just a static array here because it's not expected many components will need +//an idle or tick hook. +#define MAX_HOOKS 8 + +static esp_freertos_idle_cb_t idle_cb[MAX_HOOKS]={0}; +static esp_freertos_tick_cb_t tick_cb[MAX_HOOKS]={0}; + +void IRAM_ATTR esp_vApplicationTickHook() +{ + int n; + for (n=0; n +#include "esp_err.h" + + +/* + Definitions for the tickhook and idlehook callbacks +*/ +typedef bool (*esp_freertos_idle_cb_t)(); +typedef void (*esp_freertos_tick_cb_t)(); + +/** + * @brief Register a callback to be called on the freertos idle hook + * The callback should return true if it's okay for the core to + * sleep until an interrupt (or FreeRTOS tick) happens and false + * if it should be called again as fast as possible. + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be called + * + * @return ESP_OK : Callback registered + * @return ESP_ERR_NO_MEM : No more space to register hook + */ +esp_err_t esp_register_freertos_idle_hook(esp_freertos_idle_cb_t new_idle_cb); + +/** + * @brief Register a callback to be called on the freertos tick hook + * + * @param esp_freertos_tick_cb_t new_tick_cb : Callback to be called + * + * @return ESP_OK : Callback registered + * @return ESP_ERR_NO_MEM : No more space to register hook + */ +esp_err_t esp_register_freertos_tick_hook(esp_freertos_tick_cb_t tick_cb); + + +/** + * @brief Unregister an idle callback registered earlier + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered + * + * @return void + */ +void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb); + + +/** + * @brief Unregister a tick callback registered earlier + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered + * + * @return void + */ +void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb); + + +#endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 11de8f20d2..fe3ddab370 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -25,6 +25,7 @@ #include "esp_err.h" #include "esp_intr.h" #include "esp_attr.h" +#include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" @@ -36,6 +37,38 @@ #define WDT_INT_NUM 24 +//Take care: the tick hook can also be called before esp_int_wdt_init() is called. +#if CONFIG_INT_WDT_CHECK_CPU1 +//Not static; the ISR assembly checks this. +bool int_wdt_app_cpu_ticked=false; + +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } +} +#else +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; +} +#endif + + void esp_int_wdt_init() { TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS @@ -53,6 +86,7 @@ void esp_int_wdt_init() { TIMERG1.wdt_wprotect=0; TIMERG1.int_clr_timers.wdt=1; TIMERG1.int_ena.wdt=1; + esp_register_freertos_tick_hook(tick_hook); ESP_INTR_DISABLE(WDT_INT_NUM); intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); //We do not register a handler for the interrupt because it is interrupt level 4 which @@ -62,35 +96,5 @@ void esp_int_wdt_init() { } -//Take care: the tick hook can also be called before esp_int_wdt_init() is called. -#if CONFIG_INT_WDT_CHECK_CPU1 -//Not static; the ISR assembly checks this. -bool int_wdt_app_cpu_ticked=false; - -void IRAM_ATTR vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) { - int_wdt_app_cpu_ticked=true; - } else { - //Only feed wdt if app cpu also ticked. - if (int_wdt_app_cpu_ticked) { - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; - int_wdt_app_cpu_ticked=false; - } - } -} -#else -void IRAM_ATTR vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) return; - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; -} -#endif #endif \ No newline at end of file diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index bec1cadaa7..f8cfdef26e 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -26,6 +26,7 @@ #include "esp_err.h" #include "esp_intr.h" #include "esp_attr.h" +#include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" #include "esp_log.h" @@ -140,6 +141,18 @@ void esp_task_wdt_delete() { } } + +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK +static bool idle_hook(void) { +#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 + if (xPortGetCoreID()!=0) return; +#endif + esp_task_wdt_feed(); + return true; +} +#endif + + void esp_task_wdt_init() { TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS @@ -153,6 +166,9 @@ void esp_task_wdt_init() { TIMERG0.wdt_config0.en=1; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK + esp_register_freertos_idle_hook(idle_hook); +#endif ESP_INTR_DISABLE(ETS_T0_WDT_INUM); intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); @@ -161,13 +177,5 @@ void esp_task_wdt_init() { ESP_INTR_ENABLE(ETS_T0_WDT_INUM); } -#if CONFIG_TASK_WDT_CHECK_IDLE_TASK -void vApplicationIdleHook(void) { -#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; -#endif - esp_task_wdt_feed(); -} -#endif #endif \ No newline at end of file diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 91e824b2dc..b9db00e50b 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -141,6 +141,35 @@ config FREERTOS_ISR_STACKSIZE The interrupt handlers have their own stack. The size of the stack can be defined here. Each processor has its own stack, so the total size occupied will be twice this. +config FREERTOS_LEGACY_HOOKS + bool "Use FreeRTOS legacy hooks" + default n + help + FreeRTOS offers a number of hooks/callback functions that are called when a timer + tick happens, the idle thread runs etc. esp-idf replaces these by runtime registerable + hooks using the esp_register_freertos_xxx_hook system, but for legacy reasons the old + hooks can also still be enabled. Please enable this only if you have code that for some + reason can't be migrated to the esp_register_freertos_xxx_hook system. + +if FREERTOS_LEGACY_HOOKS + +config FREERTOS_LEGACY_IDLE_HOOK + bool "Enable legacy idle hook" + default n + help + If enabled, FreeRTOS will call a function called vApplicationIdleHook when the idle thread + on a CPU is running. Please make sure your code defines such a function. + +config FREERTOS_LEGACY_TICK_HOOK + bool "Enable legacy tick hook" + default n + help + If enabled, FreeRTOS will call a function called vApplicationTickHook when a FreeRTOS + tick is executed. Please make sure your code defines such a function. + +endif #FREERTOS_LEGACY_HOOKS + + menuconfig FREERTOS_DEBUG_INTERNALS bool "Debug FreeRTOS internals" default n diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 47566ab3b3..13ce73e0a8 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -152,9 +152,9 @@ *----------------------------------------------------------*/ #define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK ) +#define configUSE_IDLE_HOOK ( CONFIG_FREERTOS_LEGACY_IDLE_HOOK ) -#define configUSE_TICK_HOOK ( CONFIG_INT_WDT ) +#define configUSE_TICK_HOOK ( CONFIG_FREERTOS_LEGACY_TICK_HOOK ) #define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ ) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index b79d3a98ba..88aa8d3ef5 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -476,6 +476,7 @@ to its original value when it is released. */ #if configUSE_TICK_HOOK > 0 extern void vApplicationTickHook( void ); #endif +extern void esp_vApplicationTickHook( void ); #if portFIRST_TASK_HOOK extern void vPortFirstTaskHook(TaskFunction_t taskfn); @@ -2360,22 +2361,21 @@ BaseType_t xSwitchRequired = pdFALSE; We can't really calculate what we need, that's done on core 0... just assume we need a switch. ToDo: Make this more intelligent? -- JD */ - //We do need the tick hook to satisfy the int watchdog. - #if ( configUSE_TICK_HOOK == 1 ) { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ if( ( uxSchedulerSuspended[ xPortGetCoreID() ] != ( UBaseType_t ) pdFALSE ) || uxPendedTicks == ( UBaseType_t ) 0U ) { + #if ( configUSE_TICK_HOOK == 1 ) vApplicationTickHook(); + #endif /* configUSE_TICK_HOOK */ + esp_vApplicationTickHook(); } else { mtCOVERAGE_TEST_MARKER(); } } - #endif /* configUSE_TICK_HOOK */ - return pdTRUE; } @@ -2506,20 +2506,21 @@ BaseType_t xSwitchRequired = pdFALSE; } #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ - #if ( configUSE_TICK_HOOK == 1 ) { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ if( uxPendedTicks == ( UBaseType_t ) 0U ) { + #if ( configUSE_TICK_HOOK == 1 ) vApplicationTickHook(); + #endif /* configUSE_TICK_HOOK */ + esp_vApplicationTickHook(); } else { mtCOVERAGE_TEST_MARKER(); } } - #endif /* configUSE_TICK_HOOK */ taskEXIT_CRITICAL_ISR(&xTaskQueueMutex); } else @@ -2533,6 +2534,7 @@ BaseType_t xSwitchRequired = pdFALSE; vApplicationTickHook(); } #endif + esp_vApplicationTickHook(); } #if ( configUSE_PREEMPTION == 1 ) @@ -3270,6 +3272,12 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) vApplicationIdleHook(); } #endif /* configUSE_IDLE_HOOK */ + { + /* Call the esp-idf hook system */ + extern void esp_vApplicationIdleHook( void ); + esp_vApplicationIdleHook(); + } + /* This conditional compilation should use inequality to 0, not equality to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when From cc510c1d95086b6a94ea92c260eceb40f75e3577 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 10:51:53 +1100 Subject: [PATCH 057/131] build system: Remove make 3.81 "component_project_vars.mk: No such file or directory" messages Also add an explicit make version check & warning. --- make/project.mk | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/make/project.mk b/make/project.mk index a273da1c25..a10f6cd24a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -36,6 +36,13 @@ help: @echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', " @echo "'make partition_table', etc, etc." +# dependency checks +ifndef MAKE_RESTARTS +ifeq ("$(filter 4.% 3.81 3.82,$(MAKE_VERSION))","") +$(warning "esp-idf build system only supports GNU Make versions 3.81 or newer. You may see unexpected results with other Makes.") +endif +endif + # disable built-in make rules, makes debugging saner MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR @@ -105,7 +112,8 @@ COMPONENT_LDFLAGS := # See the component_project_vars.mk target in component_wrapper.mk COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) -include $(COMPONENT_PROJECT_VARS) +# this line is -include instead of include to prevent a spurious error message on make 3.81 +-include $(COMPONENT_PROJECT_VARS) # Also add top-level project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) From f9e9e6b9389e009d45a9c3271180666007eb27f4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 10:57:45 +1100 Subject: [PATCH 058/131] Build system: Change deprecation message to include component path, easier to fix --- make/component_common.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/make/component_common.mk b/make/component_common.mk index 32af3c7c06..187e1ed63a 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -1,3 +1 @@ -$(warning Deprecated feature: It is no longer necessary to include component_common.mk.) -$(warning The line "include $$(IDF_PATH)/make/component_common.mk" can be removed from component.mk files.) - +$(warning Deprecated feature: No longer necessary to include component_common.mk from $(COMPONENT_PATH)/component.mk) From b5de58139919e1143f289efc426569bb2d111106 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 3 Nov 2016 17:33:30 +1100 Subject: [PATCH 059/131] Secure boot: initial image signature support --- .gitmodules | 3 + components/bootloader/Kconfig.projbuild | 16 +++ components/bootloader/Makefile.projbuild | 24 +++-- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_start.c | 25 +++++ .../bootloader_support/Makefile.projbuild | 3 + components/bootloader_support/component.mk | 25 +++++ .../include/esp_secure_boot.h | 10 +- .../src/secure_boot_signatures.c | 99 +++++++++++++++++++ components/esptool_py/Makefile.projbuild | 6 ++ components/esptool_py/esptool | 2 +- components/micro-ecc/component.mk | 8 ++ components/micro-ecc/micro-ecc | 1 + components/partition_table/Makefile.projbuild | 3 + docs/security/secure-boot.rst | 87 +++++++++------- make/common.mk | 20 +++- make/component_common.mk | 6 +- make/project.mk | 1 + 18 files changed, 291 insertions(+), 50 deletions(-) create mode 100644 components/bootloader_support/Makefile.projbuild create mode 100644 components/bootloader_support/src/secure_boot_signatures.c create mode 100644 components/micro-ecc/component.mk create mode 160000 components/micro-ecc/micro-ecc diff --git a/.gitmodules b/.gitmodules index df40848261..c26f92e80c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "components/bt/lib"] path = components/bt/lib url = https://github.com/espressif/esp32-bt-lib.git +[submodule "components/micro-ecc/micro-ecc"] + path = components/micro-ecc/micro-ecc + url = https://github.com/kmackay/micro-ecc.git diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 55c2eebd14..b568f61a04 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -77,6 +77,22 @@ config SECURE_BOOTLOADER_KEY_FILE See docs/security/secure-boot.rst for details. +config SECURE_BOOT_SIGNING_KEY + string "Secure boot signing key" + depends on SECURE_BOOTLOADER_ENABLED + default secure_boot_signing_key.pem + help + Path to the key file used to sign partition tables and app images for secure boot. + + Key file is an ECDSA private key (NIST256p curve) in PEM format. + + Path is evaluated relative to the project directory. + + You can generate a new signing key by running the following command: + espsecure.py generate_signing_key secure_boot_signing_key.pem + + See docs/security/secure-boot.rst for details. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3799e913c8..831d988584 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,13 +15,17 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig -SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +# both signing key paths are resolved relative to the project directory +SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) +export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 + .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig @@ -59,16 +63,15 @@ bootloader: $(BOOTLOADER_BIN) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE -# Reflashable secure bootloader (recommended for testing only) -# generates a digest binary (bootloader + digest) and prints -# instructions for reflashing. User must run commands manually. +# Reflashable secure bootloader +# generates a digest binary (bootloader + digest) BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @echo "Bootloader built and secure digest generated. First time flash command is:" - @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOT_KEYFILE)" + @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOTLOADER_KEY)" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "To reflash the bootloader after initial flash:" @@ -77,15 +80,16 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." -$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) - @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" - $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOT_KEYFILE) -o $@ $< +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) + @echo "DIGEST $(notdir $@)" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOT_KEYFILE): +$(SECURE_BOOTLOADER_KEY): @echo $(SEPARATOR) - @echo "Need to generate secure boot digest key first. Run following command:" + @echo "Need to generate secure boot signing key. Run following command:" @echo "$(ESPSECUREPY) generate_key $@" @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for caveats & alternatives.)") @exit 1 else diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index 278e0e9afb..1cfc80c5fe 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader bootloader_support log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash micro-ecc # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 59b180fd84..3ebb8cf520 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -110,6 +110,7 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { + esp_err_t err; const esp_partition_info_t *partitions; const int PARTITION_TABLE_SIZE = 0x1000; const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); @@ -118,6 +119,14 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + if(esp_secure_boot_enabled()) { + err = esp_secure_boot_verify_signature(addr, 0x1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table signature."); + return false; + } + } + partitions = bootloader_mmap(addr, 0x1000); if (!partitions) { ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); @@ -334,7 +343,23 @@ void bootloader_main() static void unpack_load_app(const esp_partition_pos_t* partition) { + esp_err_t err; esp_image_header_t image_header; + uint32_t image_length; + + if (esp_secure_boot_enabled()) { + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + err = esp_secure_boot_verify_signature(partition->offset, image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); + return; + } + } if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild new file mode 100644 index 0000000000..f93967a719 --- /dev/null +++ b/components/bootloader_support/Makefile.projbuild @@ -0,0 +1,3 @@ +# projbuild file for bootloader support +# (included in bootloader & main app) + diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 2988fe287e..7f546dcba9 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -1,11 +1,36 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include_priv +# include configuration macros early +include $(IDF_PATH)/make/common.mk + ifdef IS_BOOTLOADER_BUILD # share "private" headers with the bootloader component +# eventual goal: all functionality that needs this lives in bootloader_support COMPONENT_ADD_INCLUDEDIRS += include_priv endif COMPONENT_SRCDIRS := src +# +# Secure boot signing key support +# +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + +SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + +$(SECURE_BOOT_SIGNING_KEY): + @echo "Need to generate secure boot signing key." + @echo "One way is to run this command:" + @echo "$(ESPSECUREPY) generate_signing_key $@" + @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for risks & alternatives.)" + @exit 1 + +$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(ESPSECUREPY) extract_public_key --keyfile $< $@ +endif + include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index 4bf2dc8b23..c1c0171512 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,6 +60,14 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); - +/** @brief Verify the signature appended to some binary data in flash. + * + * @param src_addr Starting offset of the data in flash. + * @param length Length of data in bytes. Signature is appended -after- length bytes. + * + * @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if + * signature fails, ESP_FAIL for other failures (ie can't read flash). + */ +esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length); #endif diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c new file mode 100644 index 0000000000..5f02de38b0 --- /dev/null +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -0,0 +1,99 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "sdkconfig.h" + +#include "bootloader_flash.h" +#include "esp_log.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" + +#include "uECC.h" + +#ifdef BOOTLOADER_BUILD +#include "rom/sha.h" +typedef SHA_CTX sha_context; +#else +#include "hwcrypto/sha.h" +typedef esp_sha_context sha_context; +#endif + +typedef struct { + uint32_t version; + uint8_t signature[64]; +} signature_block_t; + +static const char* TAG = "secure_boot"; + +extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start"); +extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end"); + +#define SIGNATURE_VERIFICATION_KEYLEN 64 + +esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) +{ + sha_context sha; + uint8_t digest[64]; + ptrdiff_t keylen; + const uint8_t *data; + const signature_block_t *sigblock; + bool is_valid; + + ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); + + data = bootloader_mmap(src_addr, length + sizeof(signature_block_t)); + if(data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t)); + return ESP_FAIL; + } + + sigblock = (const signature_block_t *)(data + length); + + if (sigblock->version != 0) { + ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version); + goto unmap_and_fail; + } + +#ifdef BOOTLOADER_BUILD + /* Use ROM SHA functions directly */ + ets_sha_enable(); + ets_sha_init(&sha); + ets_sha_update(&sha, SHA2_512, data, length); + ets_sha_finish(&sha, SHA2_512, digest); + ets_sha_disable(); +#else + /* Use thread-safe esp-idf SHA layer */ + esp_sha512_init(&sha); + esp_sha512_start(&sha, false); + esp_sha512_update(&sha, data, length); + esp_sha512_finish(&sha, digest); + esp_sha512_free(&sha); +#endif + + keylen = signature_verification_key_end - signature_verification_key_start; + if(keylen != SIGNATURE_VERIFICATION_KEYLEN) { + ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen); + goto unmap_and_fail; + } + + is_valid = uECC_verify(signature_verification_key_start, + digest, sizeof(digest), sigblock->signature, + uECC_secp256r1()); + + bootloader_unmap(data); + return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; + + unmap_and_fail: + bootloader_unmap(data); + return ESP_FAIL; +} diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index dfb9dfc4c2..5807512b86 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -18,6 +18,7 @@ ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) # Supporting esptool command line tools ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py +export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) @@ -33,6 +34,11 @@ ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifndef IS_BOOTLOADER_BUILD + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif +endif flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 95dae1651e..68ed7c7a4e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 +Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 diff --git a/components/micro-ecc/component.mk b/components/micro-ecc/component.mk new file mode 100644 index 0000000000..df29f400d4 --- /dev/null +++ b/components/micro-ecc/component.mk @@ -0,0 +1,8 @@ +# only compile the micro-ecc/uECC.c source file +# (SRCDIRS is needed so build system can find the source file) +COMPONENT_SRCDIRS := micro-ecc +COMPONENT_OBJS := micro-ecc/uECC.o + +COMPONENT_ADD_INCLUDEDIRS := micro-ecc + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/micro-ecc/micro-ecc b/components/micro-ecc/micro-ecc new file mode 160000 index 0000000000..14222e062d --- /dev/null +++ b/components/micro-ecc/micro-ecc @@ -0,0 +1 @@ +Subproject commit 14222e062d77f45321676e813d9525f32a88e8fa diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 98631cc854..e6f3bce8f9 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -21,6 +21,9 @@ PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.cs $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 5e33367b49..169f2d56bc 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -8,9 +8,9 @@ Secure Boot is separate from the Encrypted Flash feature, and you can use secure Background ---------- -- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. -- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. @@ -21,57 +21,62 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". -2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. +2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: - - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - Bootloader also disables JTAG via efuse. -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. +- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. + + - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. -- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) -2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. +2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet. 3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration -4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. +4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``. - **IMPORTANT** The key pair is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + **IMPORTANT** A signing key genereated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + + **IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See `Generating Secure Boot Signing Key` for more details. 5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. -6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not performed by make) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. -7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. +7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the signing key you generated in step 4. *NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled. 8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occured due to the build configuration. -**IMPORTANT** Secure boot won't ever be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. +*NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). Re-Flashable Software Bootloader -------------------------------- @@ -80,19 +85,34 @@ The "Secure Boot: One-Time Flash" is the recommended software bootloader configu However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. + +To enable a reflashable bootloader: 1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". -2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. +2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). - **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. +4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. -4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). +Generating Secure Boot Signing Key +---------------------------------- -5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. +The build system will prompt you with a command to generate a new signing key via ``espsecure.py generate_signing_key``. This uses the python-ecdsa library, which in turn uses Python's os.urandom() as a random number source. + +The strength of the signing key is proportional to (a) the random number source of the system, and (b) the correctness of the algorithm used. For production devices, we recommend generating signing keys from a system with a quality entropy source, and using the best available EC key generation utilities. + +For example, to generate a signing key using the openssl command line: + +``` +openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem +``` + +Remember that the strength of the secure boot system depends on keeping the signing key private. Technical Details @@ -107,9 +127,9 @@ The Secure Boot support hardware can perform three basic operations: 1. Generate a random sequence of bytes from a hardware random number generator. -2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -122,9 +142,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) -3. For each 16 byte block of the input image: - - Reverse the byte order of the block (^) - - Use the AES256 algorithm in ECB mode to encrypt the block. +3. For each 16 byte plaintext block of the input image: + - Reverse the byte order of the plaintext block (^) + - Apply AES256 in ECB mode to the plaintext block. - Reverse the byte order of the 16 bytes of ciphertext output. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) @@ -137,9 +157,10 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. -Curve is TBD. -Key format is TBD. -Output format is TBD. +- Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Key format used for storage is PEM. + - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. +- Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. .. _esp-idf boot process: ../boot-process.rst diff --git a/make/common.mk b/make/common.mk index 2b7376a4db..0c523c8775 100644 --- a/make/common.mk +++ b/make/common.mk @@ -53,8 +53,26 @@ endef # convenience variable for printing an 80 asterisk wide separator line SEPARATOR:="*******************************************************************************" -# macro to remove quotes from an argument, ie $(call dequote (CONFIG_BLAH)) +# macro to remove quotes from an argument, ie $(call dequote,$(CONFIG_BLAH)) define dequote $(subst ",,$(1)) endef # " comment kept here to keep syntax highlighting happy + + +# macro to keep an absolute path as-is, but resolve a relative path +# against a particular parent directory +# +# $(1) path to resolve +# $(2) directory to resolve non-absolute path against +# +# Path and directory don't have to exist (definition of a "relative +# path" is one that doesn't start with /) +# +# $(2) can contain a trailing forward slash or not, result will not +# double any path slashes. +# +# example $(call resolvepath,$(CONFIG_PATH),$(CONFIG_DIR)) +define resolvepath +$(if $(filter /%,$(1)),$(1),$(subst //,/,$(2)/$(1))) +endef diff --git a/make/component_common.mk b/make/component_common.mk index 58711b7c6e..70a6f60f07 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -134,10 +134,10 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # because objcopy generates the symbol name from the full command line # path to the input file. define GenerateEmbedTarget -$(1).$(2).o: $$(COMPONENT_PATH)/$(1) | $$(dir $(1)) +$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) $$(summary) EMBED $$@ - $$(Q) cp $$< $$(notdir $$<) - $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) + $$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ $$(Q) rm $$(notdir $$<) endef diff --git a/make/project.mk b/make/project.mk index 7e8dc46d54..5a12732a4e 100644 --- a/make/project.mk +++ b/make/project.mk @@ -151,6 +151,7 @@ LDFLAGS ?= -nostdlib \ -L$(IDF_PATH)/ld \ $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(SRCDIRS)) \ -u call_user_start_cpu0 \ + $(EXTRA_LDFLAGS) \ -Wl,--gc-sections \ -Wl,-static \ -Wl,--start-group \ From 64f3893cb9c7a4c792f4ecbb7c4375f25bf768f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 4 Nov 2016 16:05:00 +1100 Subject: [PATCH 060/131] secure boot: Derive secure bootloader key from private key Means only one key needs to be managed. --- components/bootloader/Kconfig.projbuild | 23 +++-------------- components/bootloader/Makefile.projbuild | 25 ++++++++----------- .../bootloader_support/Makefile.projbuild | 3 --- components/bootloader_support/component.mk | 8 ++++-- components/esptool_py/esptool | 2 +- make/project.mk | 5 +++- make/project_config.mk | 1 - 7 files changed, 25 insertions(+), 42 deletions(-) delete mode 100644 components/bootloader_support/Makefile.projbuild diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index b568f61a04..536b971268 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -54,29 +54,14 @@ config SECURE_BOOTLOADER_ONE_TIME_FLASH config SECURE_BOOTLOADER_REFLASHABLE bool "Reflashable" help - Generate the bootloader digest key on the computer instead of inside - the chip. Allows the secure bootloader to be re-flashed by using the - same key. + Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key. - This option is less secure than one-time flash, because a leak of the digest key allows reflashing of any device that uses it. + This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. + + This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it. endchoice -config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader key file" - depends on SECURE_BOOTLOADER_REFLASHABLE - default secure_boot_key.bin - help - Path to the key file for a reflashable secure bootloader digest. - File must contain 32 randomly generated bytes. - - Path is evaluated relative to the project directory. - - You can generate a new key by running the following command: - espsecure.py generate_key secure_boot_key.bin - - See docs/security/secure-boot.rst for details. - config SECURE_BOOT_SIGNING_KEY string "Secure boot signing key" depends on SECURE_BOOTLOADER_ENABLED diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 831d988584..1b1c07bea3 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,8 +15,7 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig -# both signing key paths are resolved relative to the project directory -SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +# signing key path is resolved relative to the project directory SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component @@ -31,10 +30,6 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig $(Q) $(BOOTLOADER_MAKE) $@ -bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean config-clean - $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old - clean: bootloader-clean ifdef CONFIG_SECURE_BOOTLOADER_DISABLED @@ -66,7 +61,11 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) -BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin + +$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(Q) $(ESPSECUREPY) digest_private_key -k $< $@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @@ -84,20 +83,16 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOTLOADER_KEY): - @echo $(SEPARATOR) - @echo "Need to generate secure boot signing key. Run following command:" - @echo "$(ESPSECUREPY) generate_key $@" - @echo "Keep key file safe after generating." - @echo "(See secure boot documentation for caveats & alternatives.)") - @exit 1 - else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" @exit 1 endif +bootloader-clean: + $(Q) $(BOOTLOADER_MAKE) app-clean config-clean + $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old $(SECURE_BOOTLOADER_KEY) $(BOOTLOADER_DIGEST_BIN) + all_binaries: $(BOOTLOADER_BIN) # synchronise the project level config to the bootloader's diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild deleted file mode 100644 index f93967a719..0000000000 --- a/components/bootloader_support/Makefile.projbuild +++ /dev/null @@ -1,3 +0,0 @@ -# projbuild file for bootloader support -# (included in bootloader & main app) - diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 7f546dcba9..e68949d82a 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -17,10 +17,9 @@ COMPONENT_SRCDIRS := src # ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +# this path is created relative to the component build directory SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) -COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) - $(SECURE_BOOT_SIGNING_KEY): @echo "Need to generate secure boot signing key." @echo "One way is to run this command:" @@ -31,6 +30,11 @@ $(SECURE_BOOT_SIGNING_KEY): $(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(ESPSECUREPY) extract_public_key --keyfile $< $@ + +COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + endif include $(IDF_PATH)/make/component_common.mk diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 68ed7c7a4e..98e5dbfa78 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 +Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 diff --git a/make/project.mk b/make/project.mk index 5a12732a4e..e0d578c104 100644 --- a/make/project.mk +++ b/make/project.mk @@ -306,6 +306,9 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) -clean: app-clean +# NB: this ordering is deliberate (app-clean before config-clean), +# so config remains valid during all component clean targets +config-clean: app-clean +clean: config-clean diff --git a/make/project_config.mk b/make/project_config.mk index 7ca83ce5af..56a05090bc 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -59,7 +59,6 @@ $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $( # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) -clean: config-clean .PHONY: config-clean config-clean: $(summary RM CONFIG) From 7402a1b9738a7542e6e37de8768c9580a196aee0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 14:35:23 +1100 Subject: [PATCH 061/131] partition_table: Move from 0x4000 to 0x8000 Also fix a bug with correctly padding bootloader image when length is already a multiple of 16. --- components/bootloader_support/src/bootloader_flash.c | 4 ++-- components/bootloader_support/src/esp_image_format.c | 8 +++++++- components/esp32/include/esp_flash_data_types.h | 2 +- components/partition_table/Makefile.projbuild | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index a50cd157e9..fcaf24ad2c 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -94,11 +94,11 @@ void bootloader_unmap(const void *mapping) esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) { if(src_addr & 3) { - ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr); return ESP_FAIL; } if((intptr_t)dest & 3) { - ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest); return ESP_FAIL; } diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index ad3cd33f13..3e7dcafa9e 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -62,6 +62,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const } for(int i = 0; i <= index && err == ESP_OK; i++) { + ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr); err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); if (err == ESP_OK) { if ((segment_header->data_len & 3) != 0 @@ -69,6 +70,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } + ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); *segment_data_offset = next_addr; next_addr += segment_header->data_len; @@ -140,7 +142,11 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) } /* image padded to next full 16 byte block, with checksum byte at very end */ - length += 15 - (length % 16); + ESP_LOGV(TAG, "unpadded image length 0x%x", length); + length += 16; /* always pad by at least 1 byte */ + length = length - (length % 16); + ESP_LOGV(TAG, "padded image length 0x%x", length); + ESP_LOGD(TAG, "reading checksum block at 0x%x", src_addr + length - 16); bootloader_flash_read(src_addr + length - 16, buf, 16); if (checksum != buf[15]) { ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index ce4acb7bc9..783f2c59bb 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -21,7 +21,7 @@ extern "C" { #endif -#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_TABLE_ADDR 0x8000 #define ESP_PARTITION_MAGIC 0x50AA /* OTA selection structure (two copies in the OTA data partition.) diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index e6f3bce8f9..4273f53c3a 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -11,6 +11,8 @@ # NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q +PARTITION_TABLE_OFFSET := 0x8000 + # Path to partition CSV file is relative to project path for custom # partition CSV files, but relative to component dir otherwise.$ PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(COMPONENT_PATH))) @@ -27,8 +29,8 @@ endif all_binaries: $(PARTITION_TABLE_BIN) -PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash 0x4000 $(PARTITION_TABLE_BIN) -ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN) +PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) partition_table: $(PARTITION_TABLE_BIN) @echo "Partition table binary generated. Contents:" From ff1b2c603911f58f3602fe8d9f7cf6c1cddff63b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:32:21 +1100 Subject: [PATCH 062/131] partition_table: Pad generated table to 0xC00 length, for easier signing --- components/partition_table/gen_esp32part.py | 8 +++++++- .../partition_table/tests/gen_esp32part_tests.py | 15 ++++++++++----- docs/partition-tables.rst | 5 +++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index 8b5df2b3b5..b399f31b78 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -9,6 +9,8 @@ import struct import argparse import sys +MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature + __version__ = '1.0' quiet = False @@ -92,7 +94,11 @@ class PartitionTable(list): return result def to_binary(self): - return "".join(e.to_binary() for e in self) + result = "".join(e.to_binary() for e in self) + if len(result )>= MAX_PARTITION_LENGTH: + raise InputError("Binary partition table length (%d) longer than max" % len(result)) + result += "\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing + return result def to_csv(self, simple_formatting=False): rows = [ "# Espressif ESP32 Partition Table", diff --git a/components/partition_table/tests/gen_esp32part_tests.py b/components/partition_table/tests/gen_esp32part_tests.py index 413f1aac91..d12539ea87 100755 --- a/components/partition_table/tests/gen_esp32part_tests.py +++ b/components/partition_table/tests/gen_esp32part_tests.py @@ -37,6 +37,10 @@ LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \ "second" + ("\0"*10) + \ "\x00\x00\x00\x00" +def _strip_trailing_ffs(binary_table): + while binary_table.endswith("\xFF"): + binary_table = binary_table[0:len(binary_table)-1] + return binary_table class CSVParserTests(unittest.TestCase): @@ -156,7 +160,7 @@ class BinaryOutputTests(unittest.TestCase): first, 0x30, 0xEE, 0x100400, 0x300000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 32) self.assertEqual('\xAA\x50', tb[0:2]) # magic self.assertEqual('\x30\xee', tb[2:4]) # type, subtype @@ -170,7 +174,7 @@ first, 0x30, 0xEE, 0x100400, 0x300000 second,0x31, 0xEF, , 0x100000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 64) self.assertEqual('\xAA\x50', tb[0:2]) self.assertEqual('\xAA\x50', tb[32:34]) @@ -215,7 +219,7 @@ class BinaryParserTests(unittest.TestCase): self.assertEqual(t[2].type, 0x10) self.assertEqual(t[2].name, "second") - round_trip = t.to_binary() + round_trip = _strip_trailing_ffs(t.to_binary()) self.assertEqual(round_trip, LONGER_BINARY_TABLE) def test_bad_magic(self): @@ -267,7 +271,7 @@ class CSVOutputTests(unittest.TestCase): self.assertEqual(row[0], "factory") self.assertEqual(row[1], "app") self.assertEqual(row[2], "2") - self.assertEqual(row[3], "64K") + self.assertEqual(row[3], "0x10000") self.assertEqual(row[4], "1M") # round trip back to a PartitionTable and check is identical @@ -291,7 +295,7 @@ class CommandLineTests(unittest.TestCase): # reopen the CSV and check the generated binary is identical with open(csvpath, 'r') as f: from_csv = PartitionTable.from_csv(f.read()) - self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE) + self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE) # run gen_esp32part.py to conver the CSV to binary again subprocess.check_call([sys.executable, "../gen_esp32part.py", @@ -299,6 +303,7 @@ class CommandLineTests(unittest.TestCase): # assert that file reads back as identical with open(binpath, 'rb') as f: binary_readback = f.read() + binary_readback = _strip_trailing_ffs(binary_readback) self.assertEqual(binary_readback, LONGER_BINARY_TABLE) finally: diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 88597532d2..5f5911bd52 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -6,6 +6,8 @@ Overview A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. +Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data. + Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded. The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables: @@ -130,3 +132,6 @@ Flashing the partition table * ``make flash``: Will flash everything including the partition table. A manual flashing command is also printed as part of ``make partition_table``. + + +.. _secure boot: security/secure-boot.rst From fe66dd85f09881c4ab8c48e37cda715f065fccfe Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:26 +1100 Subject: [PATCH 063/131] secure boot: Enable based on sdkconfig, remove "secure boot flag" from binary image --- components/bootloader/Kconfig.projbuild | 35 ++++++++++++++++++- .../bootloader/src/main/bootloader_start.c | 24 +++++++------ .../include/esp_image_format.h | 3 +- .../include/esp_secure_boot.h | 2 +- .../bootloader_support/src/secure_boot.c | 26 ++++++++++++-- 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 536b971268..949638594d 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -28,6 +28,12 @@ config LOG_BOOTLOADER_LEVEL default 4 if LOG_BOOTLOADER_LEVEL_DEBUG default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE +endmenu + + + +menu "Secure boot configuration" + choice SECURE_BOOTLOADER bool "Secure bootloader" default SECURE_BOOTLOADER_DISABLED @@ -78,8 +84,35 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. +config SECURE_BOOT_DISABLE_JTAG + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_DISABLE_UART_BOOTLOADER + bool "First boot: Permanently disable UART bootloader" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_TEST_MODE + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. + + This option is for testing purposes only - it effectively completely disables secure boot protection. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE -endmenu +endmenu \ No newline at end of file diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3ebb8cf520..6c23257be5 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -316,17 +316,17 @@ void bootloader_main() ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secure_boot_flag == 0x01) { - /* Generate secure digest from this bootloader to protect future - modifications */ - err = esp_secure_boot_permanently_enable(); - if (err != ESP_OK){ - ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); - /* Allow booting to continue, as the failure is probably - due to user-configured EFUSEs for testing... - */ - } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + /* Generate secure digest from this bootloader to protect future + modifications */ + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } +#endif if(fhdr.encrypt_flag == 0x01) { /* encrypt flash */ @@ -354,12 +354,16 @@ static void unpack_load_app(const esp_partition_pos_t* partition) ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); return; } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); return; } + ESP_LOGD(TAG, "App signature is valid"); } +#endif if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index a8b1739d73..326ff130d5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -64,8 +64,7 @@ typedef struct { uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ uint32_t entry_addr; uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ + uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */ } esp_image_header_t; /* Header of binary image segment */ diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index c1c0171512..eaa1048628 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -32,7 +32,7 @@ * @return true if secure boot is enabled. */ static inline bool esp_secure_boot_enabled(void) { - return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); + return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0; } diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index c17aebfbea..2cfe505492 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -110,12 +110,16 @@ static bool secure_boot_generate(uint32_t image_len){ /* Burn values written to the efuse write registers */ static inline void burn_efuses() { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!"); +#else REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +#endif } esp_err_t esp_secure_boot_permanently_enable(void) { @@ -185,10 +189,22 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_ERR_INVALID_STATE; } - ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGI(TAG, "blowing secure boot efuse..."); ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - REG_WRITE(EFUSE_BLK0_WDATA6_REG, - EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + + uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0; + + #ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG + ESP_LOGI(TAG, "disabling JTAG..."); + new_wdata6 |= EFUSE_RD_DISABLE_JTAG; + #endif + + #ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER + ESP_LOGI(TAG, "disabling UART bootloader..."); + new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S; + #endif + + REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6); burn_efuses(); uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG); ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); @@ -196,7 +212,11 @@ esp_err_t esp_secure_boot_permanently_enable(void) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); return ESP_OK; } else { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "secure boot not enabled due to test mode"); +#else ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); +#endif return ESP_ERR_INVALID_STATE; } } From e459f803da9cc3bd6640c9d56215ebeb63c27946 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:57 +1100 Subject: [PATCH 064/131] secure boot: Functional partition table & app signature verification --- .../bootloader/src/main/bootloader_start.c | 54 ++++++++++--------- .../include/esp_image_format.h | 2 +- .../include/esp_secure_boot.h | 4 +- .../include_priv/bootloader_flash.h | 8 +-- .../bootloader_support/src/bootloader_flash.c | 6 +-- .../bootloader_support/src/esp_image_format.c | 4 +- .../bootloader_support/src/secure_boot.c | 2 +- .../src/secure_boot_signatures.c | 30 +++++++---- components/esptool_py/Makefile.projbuild | 17 +++--- components/esptool_py/esptool | 2 +- components/partition_table/Makefile.projbuild | 15 ++++-- components/partition_table/gen_esp32part.py | 9 ++-- docs/security/secure-boot.rst | 39 ++++++++------ make/common.mk | 3 +- 14 files changed, 116 insertions(+), 79 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 6c23257be5..36d3ff0d29 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -104,35 +104,38 @@ void IRAM_ATTR call_start_cpu0() * OTA info sector, factory app sector, and test app sector. * * @inputs: bs bootloader state structure used to save the data - * addr address of partition table in flash * @return: return true, if the partition table is loaded (and MD5 checksum is valid) * */ -bool load_partition_table(bootloader_state_t* bs, uint32_t addr) +bool load_partition_table(bootloader_state_t* bs) { esp_err_t err; const esp_partition_info_t *partitions; - const int PARTITION_TABLE_SIZE = 0x1000; - const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); + const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ + const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { - err = esp_secure_boot_verify_signature(addr, 0x1000); + ESP_LOGI(TAG, "Verifying partition table signature..."); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; } + ESP_LOGD(TAG, "Partition table signature verified"); } +#endif - partitions = bootloader_mmap(addr, 0x1000); + partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (!partitions) { - ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); return false; } - ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); for(int i = 0; i < MAX_PARTITIONS; i++) { const esp_partition_info_t *partition = &partitions[i]; @@ -197,7 +200,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) partition->pos.offset, partition->pos.size); } - bootloader_unmap(partitions); + bootloader_munmap(partitions); ESP_LOGI(TAG,"End of partition table"); return true; @@ -248,7 +251,7 @@ void bootloader_main() update_flash_config(&fhdr); - if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { + if (!load_partition_table(&bs)) { ESP_LOGE(TAG, "load partition table error!"); return; } @@ -268,7 +271,7 @@ void bootloader_main() } sa = ota_select_map[0]; sb = ota_select_map[1]; - bootloader_unmap(ota_select_map); + bootloader_munmap(ota_select_map); if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash @@ -336,7 +339,7 @@ void bootloader_main() } } - // copy sections to RAM, set up caches, and start application + // copy loaded segments to RAM, set up caches for mapped segments, and start application unpack_load_app(&load_part_pos); } @@ -347,14 +350,15 @@ static void unpack_load_app(const esp_partition_pos_t* partition) esp_image_header_t image_header; uint32_t image_length; - if (esp_secure_boot_enabled()) { - /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ - err = esp_image_basic_verify(partition->offset, &image_length); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); - return; - } + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { @@ -377,7 +381,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) uint32_t irom_load_addr = 0; uint32_t irom_size = 0; - /* Reload the RTC memory sections whenever a non-deepsleep reset + /* Reload the RTC memory segments whenever a non-deepsleep reset is occurring */ bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; @@ -409,7 +413,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found drom segment, map from %08x to %08x", data_offs, segment_header.load_addr); drom_addr = data_offs; drom_load_addr = segment_header.load_addr; @@ -419,7 +423,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found irom segment, map from %08x to %08x", data_offs, segment_header.load_addr); irom_addr = data_offs; irom_load_addr = segment_header.load_addr; @@ -429,12 +433,12 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC code segment at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC data segment at %08x\n", data_offs); load = false; } @@ -449,7 +453,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) return; } memcpy((void *)segment_header.load_addr, data, segment_header.data_len); - bootloader_unmap(data); + bootloader_munmap(data); } } diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 326ff130d5..a32a50a4a5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -107,7 +107,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const * * Image validation checks: * - Magic byte - * - No single section longer than 16MB + * - No single segment longer than 16MB * - Total image no longer than 16MB * - 8 bit image checksum is valid * diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index eaa1048628..f9fc57708d 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,7 +60,9 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); -/** @brief Verify the signature appended to some binary data in flash. +/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash. + * + * Public key is compiled into the calling program. See docs/security/secure-boot.rst for details. * * @param src_addr Starting offset of the data in flash. * @param length Length of data in bytes. Signature is appended -after- length bytes. diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h index d70ec22d56..769c47b904 100644 --- a/components/bootloader_support/include_priv/bootloader_flash.h +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -29,11 +29,11 @@ /** * @brief Map a region of flash to data memory * - * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_munmapped before another region is mapped. * * @important In app code, these functions are not thread safe. * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. * * In esp-idf app, this function maps directly to spi_flash_mmap. * @@ -49,9 +49,9 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size); /** * @brief Unmap a previously mapped region of flash * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. */ -void bootloader_unmap(const void *mapping); +void bootloader_munmap(const void *mapping); /** * @brief Read data from Flash. diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index fcaf24ad2c..3fd107dea8 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -38,7 +38,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return result; } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if(mapping && map) { spi_flash_munmap(map); @@ -68,7 +68,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) } uint32_t src_addr_aligned = src_addr & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; + uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000; Cache_Read_Disable(0); Cache_Flush(0); ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); @@ -80,7 +80,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if (mapped) { /* Full MMU reset */ diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 3e7dcafa9e..42a3e4ffe2 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -70,8 +70,8 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } - ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); + ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", segment_header->data_len, next_addr); *segment_data_offset = next_addr; next_addr += segment_header->data_len; } @@ -124,7 +124,7 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) for(int i = 0; i < segment_header.data_len; i++) { checksum ^= segment_data[i]; } - bootloader_unmap(segment_data); + bootloader_munmap(segment_data); } /* End of image, verify checksum */ diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index 2cfe505492..d908d39c38 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -90,7 +90,7 @@ static bool secure_boot_generate(uint32_t image_len){ for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { ets_secure_boot_hash(image + i/sizeof(void *)); } - bootloader_unmap(image); + bootloader_munmap(image); ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5f02de38b0..5106eb396f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -43,9 +43,10 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) { sha_context sha; - uint8_t digest[64]; + uint8_t digest[32]; ptrdiff_t keylen; - const uint8_t *data; + const uint8_t *data, *digest_data; + uint32_t digest_len, chunk_len; const signature_block_t *sigblock; bool is_valid; @@ -68,16 +69,23 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) /* Use ROM SHA functions directly */ ets_sha_enable(); ets_sha_init(&sha); - ets_sha_update(&sha, SHA2_512, data, length); - ets_sha_finish(&sha, SHA2_512, digest); + digest_len = length * 8; + digest_data = data; + while (digest_len > 0) { + uint32_t chunk_len = (digest_len > 64) ? 64 : digest_len; + ets_sha_update(&sha, SHA2_256, digest_data, chunk_len); + digest_len -= chunk_len; + digest_data += chunk_len / 8; + } + ets_sha_finish(&sha, SHA2_256, digest); ets_sha_disable(); #else /* Use thread-safe esp-idf SHA layer */ - esp_sha512_init(&sha); - esp_sha512_start(&sha, false); - esp_sha512_update(&sha, data, length); - esp_sha512_finish(&sha, digest); - esp_sha512_free(&sha); + esp_sha256_init(&sha); + esp_sha256_start(&sha, false); + esp_sha256_update(&sha, data, length); + esp_sha256_finish(&sha, digest); + esp_sha256_free(&sha); #endif keylen = signature_verification_key_end - signature_verification_key_start; @@ -90,10 +98,10 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) digest, sizeof(digest), sigblock->signature, uECC_secp256r1()); - bootloader_unmap(data); + bootloader_munmap(data); return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; unmap_and_fail: - bootloader_unmap(data); + bootloader_munmap(data); return ESP_FAIL; } diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 5807512b86..bfbece45e6 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -24,21 +24,24 @@ ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFRE ESPTOOL_ELF2IMAGE_OPTIONS := -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED -ESPTOOL_ELF2IMAGE_OPTIONS += "--set-secure-boot-flag" -endif - ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) -$(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< ifdef CONFIG_SECURE_BOOTLOADER_ENABLED ifndef IS_BOOTLOADER_BUILD - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +# for secure boot, add a signing step to get from unsiged app to signed app +APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin) + +$(APP_BIN): $(APP_BIN_UNSIGNED) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place endif endif +# non-secure boot (or bootloader), both these files are the same +APP_BIN_UNSIGNED ?= $(APP_BIN) + +$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 98e5dbfa78..d8b1ffdd50 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 +Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 4273f53c3a..f8b822ef2c 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -20,12 +20,19 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) -$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin) +# add an extra signing step for secure partition table +$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< +else +# secure bootloader disabled, both files are the same +PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN) +endif + +$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place -endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index b399f31b78..cb6a5f24a1 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -86,11 +86,14 @@ class PartitionTable(list): @classmethod def from_binary(cls, b): - if len(b) % 32 != 0: - raise InputError("Partition table length must be a multiple of 32 bytes. Got %d bytes." % len(b)) result = cls() for o in range(0,len(b),32): - result.append(PartitionDefinition.from_binary(b[o:o+32])) + data = b[o:o+32] + if len(data) != 32: + raise InputError("Ran out of partition table data before reaching end marker") + if data == '\xFF'*32: + break # end of partition table + result.append(PartitionDefinition.from_binary(data)) return result def to_binary(self): diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 169f2d56bc..9a22aeca97 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -19,28 +19,28 @@ Secure Boot Process Overview This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: -1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". 2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. -3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: +3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Bootloader also disables JTAG via efuse. + - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) 4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. - "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. @@ -52,15 +52,15 @@ The following keys are used by the secure boot process: How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Secure Boot Configuration" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) 2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet. -3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration +3. Set other menuconfig options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration 4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``. - **IMPORTANT** A signing key genereated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + **IMPORTANT** A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. **IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See `Generating Secure Boot Signing Key` for more details. @@ -76,16 +76,16 @@ How To Enable Secure Boot *NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- -The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. +Configuration "Secure Boot: One-Time Flash" is the recommended configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the 256-bit secure bootloader key. This is a convenience so you only need to generate/protect a single private key. *NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. @@ -95,7 +95,7 @@ To enable a reflashable bootloader: 2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process). 4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. @@ -114,6 +114,13 @@ openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key. Remember that the strength of the secure boot system depends on keeping the signing key private. +Secure Boot Best Practices +-------------------------- + +* Generate the signing key on a system with a quality source of entropy. +* Keep the signing key private at all times. A leak of this key will compromise the secure boot system. +* Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks. +* Enable all secure boot options. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. Technical Details ----------------- @@ -143,9 +150,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - - Reverse the byte order of the plaintext block (^) + - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. - - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Reverse the byte order of the ciphertext output block. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) 5. Calculate SHA-512 of the ciphertext. @@ -158,10 +165,12 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. - Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Hash function is SHA256. - Key format used for storage is PEM. - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. + .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 diff --git a/make/common.mk b/make/common.mk index 0c523c8775..bec8151b55 100644 --- a/make/common.mk +++ b/make/common.mk @@ -6,7 +6,8 @@ # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) --include $(BUILD_DIR_BASE)/include/config/auto.conf +SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf +-include $(SDKCONFIG_MAKEFILE) #Handling of V=1/VERBOSE=1 flag # From 5a5e19cd8b5b1ea09adce8ae73e62e400b09b65a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 11:55:09 +1100 Subject: [PATCH 065/131] Bump esptool revision (espefuse.py fixes) --- components/esptool_py/esptool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index d8b1ffdd50..b1e00025fa 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 +Subproject commit b1e00025fa6cbc63062b205259ee70d91bfe4989 From bcdebda8e47961751d35031272350f6a33a004b4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 14:44:10 +1100 Subject: [PATCH 066/131] Build system: Don't shell-quote SEPARATOR variable or it evaluates as a bunch of wildcards! --- components/bootloader/Makefile.projbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 1b1c07bea3..3ed5e18783 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -37,7 +37,7 @@ ifdef CONFIG_SECURE_BOOTLOADER_DISABLED # with 'make flash' and no warnings are printed. bootloader: $(BOOTLOADER_BIN) - @echo "$(SEPARATOR)" + @echo $(SEPARATOR) @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" From 8691b54758102275b7e635bdaa52c2fafe8b4334 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:14:13 +1100 Subject: [PATCH 067/131] secure boot: Rename efuse option for UART bootloader to option for ROM interpreter --- components/bootloader/Kconfig.projbuild | 38 +++++++++---------- .../src/secure_boot_signatures.c | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 949638594d..50165d0e58 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -85,31 +85,31 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. config SECURE_BOOT_DISABLE_JTAG - bool "First boot: Permanently disable JTAG" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. - It is recommended this option remains set for production environments. + It is recommended this option remains set for production environments. -config SECURE_BOOT_DISABLE_UART_BOOTLOADER - bool "First boot: Permanently disable UART bootloader" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. +config SECURE_BOOT_DISABLE_ROM_BASIC + bool "First boot: Permanently disable ROM BASIC fallback" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot. - It is recommended this option remains set for production environments. + It is recommended this option remains set in production environments. config SECURE_BOOT_TEST_MODE - bool "Test mode: don't actually enable secure boot" - depends on SECURE_BOOTLOADER_ENABLED - default N - help - If this option is set, all permanent secure boot changes (via Efuse) are disabled. + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. - This option is for testing purposes only - it effectively completely disables secure boot protection. + This option is for testing purposes only - it effectively completely disables secure boot protection. config SECURE_BOOTLOADER_ENABLED bool diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5106eb396f..6d47651b2f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -46,7 +46,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) uint8_t digest[32]; ptrdiff_t keylen; const uint8_t *data, *digest_data; - uint32_t digest_len, chunk_len; + uint32_t digest_len; const signature_block_t *sigblock; bool is_valid; From 734c1dd954a37cc859962f567b2d4b9c148b7657 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 14 Nov 2016 09:40:12 +0800 Subject: [PATCH 068/131] components/openssl: sync the code form esp8266 sdk --- components/openssl/include/internal/ssl_dbg.h | 7 ++-- components/openssl/platform/ssl_pm.c | 35 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 887fe2e82b..b4c0754637 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -55,16 +55,17 @@ #else #ifdef SSL_PRINT_LOG #undef SSL_PRINT_LOG - #define SSL_PRINT_LOG(...) #endif + #define SSL_PRINT_LOG(...) + #ifdef SSL_ERROR_LOG #undef SSL_ERROR_LOG - #define SSL_ERROR_LOG(...) #endif + #define SSL_ERROR_LOG(...) #ifdef SSL_LOCAL_LOG #undef SSL_LOCAL_LOG - #define SSL_LOCAL_LOG(...) #endif + #define SSL_LOCAL_LOG(...) #endif #if SSL_DEBUG_LOCATION_ENABLE diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 92e72bfdb8..091402cda4 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -215,6 +215,31 @@ static int ssl_pm_reload_crt(SSL *ssl) return 0; } +/* + * Perform the mbedtls SSL handshake instead of mbedtls_ssl_handshake. + * We can add debug here. + */ +LOCAL int mbedtls_handshake( mbedtls_ssl_context *ssl ) +{ + int ret = 0; + + if (ssl == NULL || ssl->conf == NULL) + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + + while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) + { + ret = mbedtls_ssl_handshake_step(ssl); + + SSL_DEBUG(1, "ssl ret %d state %d heap %d\n", + ret, ssl->state, system_get_free_heap_size()); + + if (ret != 0) + break; + } + + return ret; +} + int ssl_pm_handshake(SSL *ssl) { int ret, mbed_ret; @@ -224,13 +249,19 @@ int ssl_pm_handshake(SSL *ssl) if (mbed_ret) return 0; + SSL_DEBUG(1, "ssl_speed_up_enter "); ssl_speed_up_enter(); - while((mbed_ret = mbedtls_ssl_handshake(&ssl_pm->ssl)) != 0) { + SSL_DEBUG(1, "OK\n"); + + while((mbed_ret = mbedtls_handshake(&ssl_pm->ssl)) != 0) { if (mbed_ret != MBEDTLS_ERR_SSL_WANT_READ && mbed_ret != MBEDTLS_ERR_SSL_WANT_WRITE) { break; } } + + SSL_DEBUG(1, "ssl_speed_up_exit "); ssl_speed_up_exit(); + SSL_DEBUG(1, "OK\n"); if (!mbed_ret) { struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; @@ -492,6 +523,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) return 0; failed2: + mbedtls_x509_crt_free(x509_pm->x509_crt); ssl_mem_free(x509_pm->x509_crt); x509_pm->x509_crt = NULL; failed1: @@ -567,6 +599,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) return 0; failed2: + mbedtls_pk_free(pkey_pm->pkey); ssl_mem_free(pkey_pm->pkey); pkey_pm->pkey = NULL; failed1: From 045a53a7e5aeb1263ee24671473e3a705d9df6e3 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 14 Nov 2016 10:20:49 +0800 Subject: [PATCH 069/131] Fix wdt idle hook --- components/esp32/task_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index f8cfdef26e..549b7f58b2 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -145,7 +145,7 @@ void esp_task_wdt_delete() { #if CONFIG_TASK_WDT_CHECK_IDLE_TASK static bool idle_hook(void) { #if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; + if (xPortGetCoreID()!=0) return true; #endif esp_task_wdt_feed(); return true; From 572f62928b9dd6a5036b98893a6a2059c34d9e87 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:29 +1100 Subject: [PATCH 070/131] Secure boot: Documentation tweaks --- docs/index.rst | 1 + docs/security/secure-boot.rst | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c973950615..30cfa177c3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Contents: partition-tables build_system openocd + Secure Boot .. toctree:: :caption: API Reference diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 9a22aeca97..c08b17cb47 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -3,17 +3,20 @@ Secure Boot Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. -Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. +Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. + Background ---------- -- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored (non-software-accessible) in Efuses internal to the chip. - Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. +- Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship. + Secure Boot Process Overview ---------------------------- @@ -21,19 +24,19 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". -2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. +2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file. 2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. + - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted. Keys ---- @@ -42,11 +45,11 @@ The following keys are used by the secure boot process: - "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. +- "secure boot signing key" is a standard ECDSA public/private key pair (see `Image Signing Algorithm`) in PEM format. - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. - - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to any bootloader that is configured with secure boot and the matching public key. How To Enable Secure Boot @@ -76,7 +79,7 @@ How To Enable Secure Boot *NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed (using the secure bootloader key) and then the software bootloader will verify the signed partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- @@ -120,7 +123,7 @@ Secure Boot Best Practices * Generate the signing key on a system with a quality source of entropy. * Keep the signing key private at all times. A leak of this key will compromise the secure boot system. * Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks. -* Enable all secure boot options. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. +* Enable all secure boot options in the Secure Boot Configuration. These include flash encryption, disabling of JTAG, disabling BASIC ROM interpeter, and disabling the UART bootloader encrypted flash access. Technical Details ----------------- @@ -136,19 +139,19 @@ The Secure Boot support hardware can perform three basic operations: 2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. +3. Generate a digest from data (usually the bootloader image from flash) using the same algorithm as step 2 and compare it to a pre-calculated digest supplied in a buffer (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Starting with an "image" of binary data as input, this algorithm generates a digest as output. +Starting with an "image" of binary data as input, this algorithm generates a digest as output. The digest is sometimes referred to as an "abstract" in hardware documentation. For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. 1. Prefix the image with a 128 byte randomly generated IV. -2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +2. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. @@ -171,6 +174,5 @@ Deterministic ECDSA as specified by `RFC6979`. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. - .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 From 0b4fe9dd6dc62571abcc66c0235582f00d94d2e5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:58 +1100 Subject: [PATCH 071/131] secure boot: Add warnings this feature is not finished yet --- components/bootloader/Makefile.projbuild | 21 +++++++++++++++++++++ docs/security/secure-boot.rst | 1 + 2 files changed, 22 insertions(+) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3ed5e18783..551bceb981 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -47,6 +47,16 @@ bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH + +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device" + @echo "If you flash this bootloader, you will be left with an non-updateable bootloader that is missing features." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. @@ -57,10 +67,20 @@ bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" +endif # IDF_INSECURE_SECURE_BOOT else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device." + @echo "If using this feature, expect to reflash the bootloader at least one more time." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin @@ -83,6 +103,7 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< +endif # IDF_INSECURE_SECURE_BOOT else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index c08b17cb47..bdc1b71699 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -5,6 +5,7 @@ Secure Boot is a feature for ensuring only your code can run on the chip. Data l Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. +**IMPORTANT: As Encrypted Flash feature and related security features are not yet released, Secure Boot should not be considered sufficient for a secure device and we strongly recommend not enabling the one-time secure bootloader feature until it is mature.** Background ---------- From 09c7ccfa2cd2c21f14c0c599e25e3bb473dd64eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:47:38 +1100 Subject: [PATCH 072/131] bootloader: Fix unused variable errors when secure boot is disabled --- components/bootloader/src/main/bootloader_start.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 36d3ff0d29..a811294a8f 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -109,7 +109,6 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs) { - esp_err_t err; const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); @@ -121,7 +120,7 @@ bool load_partition_table(bootloader_state_t* bs) #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -227,7 +226,6 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -322,7 +320,7 @@ void bootloader_main() #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED /* Generate secure digest from this bootloader to protect future modifications */ - err = esp_secure_boot_permanently_enable(); + esp_err_t err = esp_secure_boot_permanently_enable(); if (err != ESP_OK) { ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably From c04f05ee84754a378d0d25eddf2d00d8a2e52226 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:29:27 +1100 Subject: [PATCH 073/131] build examples: Only build verbose on failure, save on log output --- make/build_examples.sh | 19 +++++++++---------- make/component_wrapper.mk | 20 +++++++++++++------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index 53cba23b1e..a522666a98 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -15,16 +15,15 @@ RESULT=0 set -e for example in ${IDF_PATH}/examples/*; do - [ -f ${example}/Makefile ] || continue - echo "Building ${example} as ${EXAMPLE_NUM}..." - mkdir ${EXAMPLE_NUM} - cp -r ${example} ${EXAMPLE_NUM} - pushd ${EXAMPLE_NUM}/`basename ${example}` - # can't do "make defconfig all" as this will trip menuconfig - # sometimes - make defconfig V=1 && make V=1 || RESULT=$? - popd - EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) + [ -f ${example}/Makefile ] || continue + echo "Building ${example} as ${EXAMPLE_NUM}..." + mkdir ${EXAMPLE_NUM} + cp -r ${example} ${EXAMPLE_NUM} + pushd ${EXAMPLE_NUM}/`basename ${example}` + # build non-verbose first, only build verbose if there's an error + make defconfig all || (RESULT=$?; make V=1) + popd + EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done exit $RESULT diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index e1283bda8f..55a135158a 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -41,8 +41,9 @@ COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a # Source dirs a component has. Default to root directory of component. COMPONENT_SRCDIRS = . -#Names of binary files to embed as symbols in the component library +#Names of binary & text files to embed as raw content in the component library COMPONENT_EMBED_FILES ?= +COMPONENT_EMBED_TXTFILES ?= # By default, include only the include/ dir. COMPONENT_ADD_INCLUDEDIRS = include @@ -72,6 +73,11 @@ COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$ COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) endif +# Object files with embedded binaries to add to the component library +# Correspond to the files named in COMPONENT_EMBED_FILES & COMPONENT_EMBED_TXTFILES +COMPONENT_EMBED_OBJS ?= $(addsuffix .bin.o,$(COMPONENT_EMBED_FILES)) $(addsuffix .txt.o,$(COMPONENT_EMBED_TXTFILES)) + + # If we're called to compile something, we'll get passed the COMPONENT_INCLUDES # variable with all the include dirs from all the components in random order. This # means we can accidentally grab a header from another component before grabbing our own. @@ -133,7 +139,7 @@ build: $(COMPONENT_LIBRARY) $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS) $(summary) AR $@ rm -f $@ - $(AR) cru $@ $(COMPONENT_OBJS) + $(AR) cru $@ $^ endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target @@ -187,11 +193,11 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # path to the input file. define GenerateEmbedTarget $(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) - $$(summary) EMBED $$@ - $$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir - $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output - $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ - $$(Q) rm $$(notdir $$<) + $(summary) EMBED $$@ + $$(if $$(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output + $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ + rm $$(notdir $$<) endef # generate targets to embed binary & text files From f7af4ddc89d553c04bddb5e070dc0c26e5b1d0c2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:54:33 +1100 Subject: [PATCH 074/131] Examples: Remove deprecated system_init() call --- examples/01_hello_world/main/hello_world_main.c | 1 - examples/02_blink/main/blink.c | 1 - examples/03_http_request/main/http_request_main.c | 1 - examples/04_https_request/main/https_request_main.c | 1 - examples/06_sntp/main/sntp_main.c | 1 - 5 files changed, 5 deletions(-) diff --git a/examples/01_hello_world/main/hello_world_main.c b/examples/01_hello_world/main/hello_world_main.c index ad2c0acbbd..1ff190acef 100644 --- a/examples/01_hello_world/main/hello_world_main.c +++ b/examples/01_hello_world/main/hello_world_main.c @@ -27,6 +27,5 @@ void hello_task(void *pvParameter) void app_main() { nvs_flash_init(); - system_init(); xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL); } diff --git a/examples/02_blink/main/blink.c b/examples/02_blink/main/blink.c index 6de0e1fa36..1e49e51b2f 100644 --- a/examples/02_blink/main/blink.c +++ b/examples/02_blink/main/blink.c @@ -43,6 +43,5 @@ void blink_task(void *pvParameter) void app_main() { nvs_flash_init(); - system_init(); xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL); } diff --git a/examples/03_http_request/main/http_request_main.c b/examples/03_http_request/main/http_request_main.c index 32f75c0da2..2c203f8394 100644 --- a/examples/03_http_request/main/http_request_main.c +++ b/examples/03_http_request/main/http_request_main.c @@ -175,7 +175,6 @@ static void http_get_task(void *pvParameters) void app_main() { nvs_flash_init(); - system_init(); initialise_wifi(); xTaskCreate(&http_get_task, "http_get_task", 2048, NULL, 5, NULL); } diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 7f302409d8..2ad56681d0 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -369,7 +369,6 @@ static void https_get_task(void *pvParameters) void app_main() { nvs_flash_init(); - system_init(); initialise_wifi(); xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL); } diff --git a/examples/06_sntp/main/sntp_main.c b/examples/06_sntp/main/sntp_main.c index 7f516625e3..83f33b9656 100644 --- a/examples/06_sntp/main/sntp_main.c +++ b/examples/06_sntp/main/sntp_main.c @@ -94,7 +94,6 @@ void app_main() static void obtain_time(void) { nvs_flash_init(); - system_init(); initialise_wifi(); xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); From 751736f902f9790f69e8f1cb43348ef35d8f077b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:55:26 +1100 Subject: [PATCH 075/131] ble_adv example: Remove int return value from app_main() --- examples/05_ble_adv/main/app_bt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/05_ble_adv/main/app_bt.c b/examples/05_ble_adv/main/app_bt.c index 7f5dda5ec5..bfdd0b8c68 100755 --- a/examples/05_ble_adv/main/app_bt.c +++ b/examples/05_ble_adv/main/app_bt.c @@ -197,10 +197,9 @@ void bleAdvtTask(void *pvParameters) } } -int app_main() +void app_main() { bt_controller_init(); xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0); - return 0; } From 9a7db9ca85f9471de4d8019aead68a1176cf62d3 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Mon, 14 Nov 2016 14:34:09 +0800 Subject: [PATCH 076/131] add comments for system_event_sta_wps_er_pin_t --- components/esp32/include/esp_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 3b2b54acca..55575b13a8 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -102,7 +102,7 @@ typedef union { system_event_sta_scan_done_t scan_done; /**< ESP32 station scan (APs) done */ system_event_sta_authmode_change_t auth_change; /**< the auth mode of AP ESP32 station connected to changed */ system_event_sta_got_ip_t got_ip; /**< ESP32 station got IP */ - system_event_sta_wps_er_pin_t sta_er_pin; + system_event_sta_wps_er_pin_t sta_er_pin; /**< ESP32 station WPS enrollee mode PIN code received */ system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 soft-AP receive probe request packet */ From 794b4ff578c6c79416d616659250299d6eab087b Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 14 Nov 2016 15:11:22 +0800 Subject: [PATCH 077/131] components/openssl: add client and server demo --- examples/09_openssl/Makefile | 9 + examples/09_openssl/README.md | 21 + examples/09_openssl/main/Kconfig.projbuild | 27 ++ examples/09_openssl/main/component.mk | 10 + examples/09_openssl/main/openssl_demo.c | 430 +++++++++++++++++++++ examples/09_openssl/main/openssl_demo.h | 66 ++++ 6 files changed, 563 insertions(+) create mode 100644 examples/09_openssl/Makefile create mode 100644 examples/09_openssl/README.md create mode 100644 examples/09_openssl/main/Kconfig.projbuild create mode 100644 examples/09_openssl/main/component.mk create mode 100644 examples/09_openssl/main/openssl_demo.c create mode 100644 examples/09_openssl/main/openssl_demo.h diff --git a/examples/09_openssl/Makefile b/examples/09_openssl/Makefile new file mode 100644 index 0000000000..8987be554c --- /dev/null +++ b/examples/09_openssl/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := openssl + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/09_openssl/README.md b/examples/09_openssl/README.md new file mode 100644 index 0000000000..54b0133420 --- /dev/null +++ b/examples/09_openssl/README.md @@ -0,0 +1,21 @@ +# Openssl Example + +The Example contains of OpenSSL client and server demo. + +First you should config the project by "menuconfig": + Example Configuration -> + 1. Openssl demo: select your demo (client or server) + 2. WiFi SSID: you own wifi, which you pc is connected to alse. + 3. WiFi Password: wifi password + +If you want to test the OpenSSL client demo: + 1. compile the code and load the firmware + 2. you can see it will download the "www.baidu.com" main page and print the context + +IF you want to test the openSSL client demo: + 1. compile the code and load the firmware + 2. You should input the context of "https://192.168.17.128", the IP of your module may not be 192.168.17.128, you should input your module's IP + 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" + 4. You should wait for a moment until your see the "OpenSSL server demo!" in your IE page + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl/main/Kconfig.projbuild b/examples/09_openssl/main/Kconfig.projbuild new file mode 100644 index 0000000000..b42529ca19 --- /dev/null +++ b/examples/09_openssl/main/Kconfig.projbuild @@ -0,0 +1,27 @@ +menu "Example Configuration" + +choice OPENSSL_DEMO + prompt "Openssl demo" + default OPENSSL_CLIENT_DEMO + help + Openssl test demo mode, client or server. + +config OPENSSL_CLIENT_DEMO + bool "client demo" +config OPENSSL_SERVER_DEMO + bool "server demon" +endchoice + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/component.mk b/examples/09_openssl/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/09_openssl/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/09_openssl/main/openssl_demo.c b/examples/09_openssl/main/openssl_demo.c new file mode 100644 index 0000000000..d541b26253 --- /dev/null +++ b/examples/09_openssl/main/openssl_demo.c @@ -0,0 +1,430 @@ +#include +#include + +#include "openssl_demo.h" +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "lwip/sockets.h" +#include "lwip/api.h" + +#include "nvs_flash.h" + + +#define os_printf(fmt, ...) ESP_LOGI("openssl_demo", fmt, ##__VA_ARGS__) + +#define IP_ADDR(ip) ip.u_addr.ip4.addr + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define OPENSSL_DEMO_THREAD_STACK_WORDS 8192 +#define OPENSSL_DEMO_THREAD_PRORIOTY 6 + +#define OPENSSL_DEMO_FRAGMENT_SIZE 8192 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#ifdef CONFIG_OPENSSL_CLIENT_DEMO + +#define OPENSSL_DEMO_THREAD_NAME "ssl_client" + +#define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" +#define OPENSSL_DEMO_TARGET_TCP_PORT 443 + +#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +LOCAL void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket; + struct sockaddr_in sock_addr; + + ip_addr_t target_ip; + + int recv_bytes = 0; + + LOCAL char send_data[] = OPENSSL_DEMO_REQUEST; + LOCAL int send_bytes = sizeof(send_data); + + LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + os_printf("OpenSSL demo thread start\n"); + + do { + ret = netconn_gethostbyname(OPENSSL_DEMO_TARGET_NAME, &target_ip); + } while(ret); + os_printf("get target IP is %d.%d.%d.%d\n", + (unsigned char)((IP_ADDR(target_ip) & 0x000000ff) >> 0), + (unsigned char)((IP_ADDR(target_ip) & 0x0000ff00) >> 8), + (unsigned char)((IP_ADDR(target_ip) & 0x00ff0000) >> 16), + (unsigned char)((IP_ADDR(target_ip) & 0xff000000) >> 24)); + + os_printf("create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + os_printf("failed\n"); + goto failed1; + } + os_printf("OK\n"); + + /** + * The openssl does not support "SSL_CTX_set_default_read_buffer_len" + * at the platform of ESP32 esp_idf now. + * + * So you should not care it now. And We my let it work later. + */ + os_printf("set SSL context read buffer size ......"); + SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); + ret = 0; + if (ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed4; + } + os_printf("OK\n"); + + os_printf("socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = IP_ADDR(target_ip); + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed5; + } + os_printf("OK\n"); + + os_printf("create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + os_printf("failed\n"); + goto failed6; + } + os_printf("OK\n"); + + SSL_set_fd(ssl, socket); + + os_printf("SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + os_printf("failed, return [-0x%x]\n", -ret); + goto failed7; + } + os_printf("OK\n"); + + os_printf("send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + os_printf("failed\n"); + goto failed8; + } + os_printf("OK\n\n"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + os_printf("%s", recv_buf); + } while (1); + + os_printf("\r\ntotaly read %d bytes data from %s ......\n", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed8: + SSL_shutdown(ssl); +failed7: + SSL_free(ssl); + ssl = NULL; +failed6: +failed5: +failed4: + close(socket); + socket = -1; +failed3: +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + + os_printf("task exit\n"); + + return ; +} + +#elif defined(CONFIG_OPENSSL_SERVER_DEMO) + +#define OPENSSL_DEMO_THREAD_NAME "openssl_server" + +#define OPENSSL_DEMO_CLIENT_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "openSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +LOCAL void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + LOCAL char send_data[] = OPENSSL_DEMO_SERVER_ACK; + LOCAL int send_bytes = sizeof(send_data); + + LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + os_printf("server SSL context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + os_printf("failed\n"); + goto failed1; + } + os_printf("OK\n"); + + /** + * The openssl does not support "SSL_CTX_set_default_read_buffer_len" + * at the platform of ESP32 esp_idf now. + * + * So you should not care it now. And We my let it work later. + */ + os_printf("server SSL context set fragment ......"); + SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); + ret = 0; + if (ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server SSL context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cert_bytes, cert_ctx); + if (!ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server SSL context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, key_ctx, key_bytes); + if (!ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + os_printf("failed\n"); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + +reconnect: + os_printf("server SSL create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + os_printf("failed, return [-0x%x]\n", -new_socket); + goto failed4; + } + os_printf("OK\n"); + + SSL_set_fd(ssl, new_socket); + + os_printf("server SSL accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + os_printf("failed\n"); + goto failed5; + } + os_printf("OK\n"); + + os_printf("server SSL read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + os_printf("result %d\n", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + + return ; +} + +#else + #error "you must choose the right demo type" +#endif + +LOCAL void demo_init(void) +{ + int ret = pdFALSE; + +#if defined(CONFIG_OPENSSL_CLIENT_DEMO) || defined(CONFIG_OPENSSL_SERVER_DEMO) + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); +#endif + + if (ret != pdPASS) { + os_printf("create thread %s failed\n", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +LOCAL esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + demo_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + break; + default: + break; + } + return ESP_OK; +} + +LOCAL void wifi_conn_init(void) +{ + tcpip_adapter_init(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + os_printf("start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} + diff --git a/examples/09_openssl/main/openssl_demo.h b/examples/09_openssl/main/openssl_demo.h new file mode 100644 index 0000000000..89cd264b46 --- /dev/null +++ b/examples/09_openssl/main/openssl_demo.h @@ -0,0 +1,66 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +const static unsigned char key_ctx[] = + "-----BEGIN PRIVATE KEY-----\r\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQyyF0WBb2XbkL\r\n" + "wYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijBR0Rr\r\n" + "0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe6eFG\r\n" + "AGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wHzywA\r\n" + "DvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwbrdcD\r\n" + "HNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gmpqfh\r\n" + "CwEPoKCNAgMBAAECggEBAIVr+LHXBL1kQLZhbiKC5t1FrMY8FLKYGM3JCevciMlk\r\n" + "lhIXwR7p29vsRYZfHBv7zWWyI9/C04JG31MiGOMaCbBxfnA2HBrVEqFwwxqnKVi8\r\n" + "CxzwGQkfwu3Y9j7TEj0PipfQYo+aKzmDrN/FrXnHjyEJryxAQbAZPVLW+Z7OR41R\r\n" + "ZOwtZLeVqmbeARGpu2Xd9SKAhbjdLSz96IdUcNrwbP/lzUgrKaiUioBMVFfIG5ce\r\n" + "4Mm2seCwaWxI8k24q0keSjsjV+5IxatVUNtJ9vYv6Tzo+3oqGvPeUBO7w9xhbLKf\r\n" + "jw1uEykcs0wcftWb1iB7r78bMPZ/KYhnSFsjT+vnIOECgYEA9LM5p63sn6OWrULd\r\n" + "doBBLaIw1ZK9rDUFLLRwv0e6+7tuuB08ok6D7PSfOpx0bb10lIx7epOE8ogGSmLS\r\n" + "w0rMbmcKAlTLAJ/0nX1ierZkb+Ay21F398tKL3igEfnaB9CzuOHF8XhbsTqeGFDJ\r\n" + "HFBMXxTbo4kfkUmZNYxwTombzkkCgYEA2m9teqjEOVZItqghOicWZ68JhWxBJFme\r\n" + "oSfzJKLd8atfUOnBLqAhPzvg1PvdIxjLWBqy28tEJf+jdSQCNe9BmhlptOwbFrJy\r\n" + "IyCXj6QTApSKTxyzIjMvzQkv1m8CxeCq5T64hvJ2++i7dlhumh21c7oL8aLeTnoe\r\n" + "AG1dBLJ9UCUCgYAhSlDJsyvB/Ct/nt0qRgCYCLzEEZakWwI9Nr8wBr41iSZi/fdF\r\n" + "zZC9J/qRqr9cPq4hl4sk/fTUWhUhAZjS4NY3HuWJs6d6ikhpNKm1MCMx5TqGA+ti\r\n" + "VtHc63g7edZjwczxliWr2EgBMIxZmoQByhrZxKis8vbMeUrSsiyFQstjoQKBgD3k\r\n" + "2Paqn39Hra7kERYzQSv78wy1UfgE1OgBM+orpAv4bTe2JKEbipAqXVi8TTrGqce7\r\n" + "OPcCr7q8pwpoO6AgvUv263byd/KEecbuU0PGUASpJk+oaDHGo0LL2Zw/NF/xezsd\r\n" + "/JdwWLqkhYnRIPXWeTXjf8LmTWubOqkQVA0irlNpAoGAJ+9N/NF3XAW0BroiVYLZ\r\n" + "p0Btcgt+b4LWrBlm0XqHhzKUlqhfibAr3OtUkFjo/509ncYPiuOzVSNosyjXFJ86\r\n" + "2kQ88fB3eeLnBAcbBXQKiOBPU2y6bCCfgdo+JEOK/cxVslaxMAyKSnFi9gdgzScd\r\n" + "k+hOlkflXQVkic3W358kFto=\r\n" + "-----END PRIVATE KEY-----\r\n" + ; +static int key_bytes = sizeof(key_ctx); + +const static unsigned char cert_ctx[] = + "-----BEGIN CERTIFICATE-----\r\n" + "MIID7jCCAtYCAQEwDQYJKoZIhvcNAQELBQAwgbwxCzAJBgNVBAYTAkNOMRAwDgYD\r\n" + "VQQIDAdKaWFuZ3N1MQ0wCwYDVQQHDARXdXhpMSYwJAYDVQQKDB1Fc3ByZXNzaWYg\r\n" + "Um9vdCBSU0EyMDQ4IHNoYTI1NjEcMBoGA1UECwwTUm9vdCBSU0EyMDQ4IHNoYTI1\r\n" + "NjEfMB0GA1UEAwwWcm9vdGNlcnQuZXNwcmVzc2lmLmNvbTElMCMGCSqGSIb3DQEJ\r\n" + "ARYWcm9vdGNlcnRAZXNwcmVzc2lmLmNvbTAeFw0xNjA2MjgwMjMxMjlaFw0yNjA2\r\n" + "MjYwMjMxMjlaMIG8MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHSmlhbmdzdTENMAsG\r\n" + "A1UEBwwEV3V4aTEmMCQGA1UECgwdRXNwcmVzc2lmIFJvb3QgUlNBMjA0OCBzaGEy\r\n" + "NTYxHDAaBgNVBAsME1Jvb3QgUlNBMjA0OCBzaGEyNTYxHzAdBgNVBAMMFnJvb3Rj\r\n" + "ZXJ0LmVzcHJlc3NpZi5jb20xJTAjBgkqhkiG9w0BCQEWFnJvb3RjZXJ0QGVzcHJl\r\n" + "c3NpZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQyyF0WBb2\r\n" + "XbkLwYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijB\r\n" + "R0Rr0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe\r\n" + "6eFGAGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wH\r\n" + "zywADvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwb\r\n" + "rdcDHNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gm\r\n" + "pqfhCwEPoKCNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABTYZLiFHq51lqaa0nHI\r\n" + "aDMAb29DfO93fqp+oHZYO4xKyEeLr8EhD39GjnQmhz710wO0TBCYV7nD+xnJ1h5F\r\n" + "IbQUAQZO9NIy3ns4mYVRUWjnWYAo+evGeKgRrxvh7sjNLPBPzs9tg/u7XjBp/nor\r\n" + "8JnnFFT0wXPyi/qg8J3QutqJvWRQGRRx2AP93F44+Zcj7ReFMVSmOXyzT4aNJL0+\r\n" + "Ls+baKwA4pnyVRoAaKbs/JYDgd0/DunuktVKuhyvK/qOGjJSRLPhdrXbvSAegpiM\r\n" + "4xIm6ZWKtTv8VvkGgXUVQ7RpruP6nV6506gDcUgecbEq7H2VDhEzUYcMmGCUQZlG\r\n" + "sJ8=\r\n" + "-----END CERTIFICATE-----\r\n" + ; +static int cert_bytes = sizeof(cert_ctx); + + + +#endif From 64a2f0ee0bf31f7921069f601515f9bc638ccfff Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 15 Nov 2016 10:29:52 +0800 Subject: [PATCH 078/131] Amend gpio driver to work around SoC bug with some pullups/pulldowns in the GPIO peripheral; mark existing function that does not always work as deprecated --- components/driver/gpio.c | 92 ++++++++++++++++++++--- components/driver/include/driver/gpio.h | 84 +++++++++++++++++++-- components/esp32/gdbstub.c | 3 +- components/esp32/include/soc/io_mux_reg.h | 39 +++++++++- 4 files changed, 194 insertions(+), 24 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index b445d3df03..ddbcb352c0 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -69,6 +69,74 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; +const gpio_pu_pd_desc_t gpio_pu_pd_desc[GPIO_PIN_COUNT]={ + {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M}, + {PERIPHS_IO_MUX_U0TXD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M}, + {PERIPHS_IO_MUX_U0RXD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M}, + {PERIPHS_IO_MUX_GPIO5_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_CLK_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA0_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA1_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA2_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA3_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_CMD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M}, + {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M}, + {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M}, + {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M}, + {PERIPHS_IO_MUX_GPIO16_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO17_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO18_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO19_U, FUN_PU, FUN_PD}, + {0,0,0}, + {PERIPHS_IO_MUX_GPIO21_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO22_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO23_U, FUN_PU, FUN_PD}, + {0,0,0}, + {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M}, + {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M}, + {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M}, + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M}, + {PERIPHS_IO_MUX_GPIO34_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO35_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO36_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO37_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO38_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO39_U, FUN_PU, FUN_PD} +}; + + +esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + return ESP_OK; +} + +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + return ESP_OK; +} + +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); + return ESP_OK; +} + +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); + return ESP_OK; +} + esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); @@ -152,20 +220,20 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: - PIN_PULLUP_EN(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_PULLDOWN_ONLY: - PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[gpio_num]); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_PULLUP_PULLDOWN: - PIN_PULLUP_EN(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[gpio_num]); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_FLOATING: - PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; default: ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull); @@ -253,15 +321,15 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) } if(pGPIOConfig->pull_up_en) { pu_en = 1; - PIN_PULLUP_EN(io_reg); + REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } else { - PIN_PULLUP_DIS(io_reg); + REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } if(pGPIOConfig->pull_down_en) { pd_en = 1; - PIN_PULLDWN_EN(io_reg); + REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } else { - PIN_PULLDWN_DIS(io_reg); + REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 73efeaa342..5dad11f2ff 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -117,6 +117,29 @@ extern const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT]; #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num < GPIO_PIN_COUNT && GPIO_PIN_MUX_REG[gpio_num] != 0)) //to decide whether it is a valid GPIO number #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((GPIO_IS_VALID_GPIO(gpio_num)) && (gpio_num < 34)) //to decide whether it can be a valid GPIO number of output mode +typedef struct { + uint32_t reg; /*!< Register to modify to enable or disable pullups or pulldowns */ + uint32_t pu; /*!< Bit to set or clear in the above register to enable or disable the pullup, respectively */ + uint32_t pd; /*!< Bit to set or clear in the above register to enable or disable the pulldown, respectively */ +} gpio_pu_pd_desc_t; + + +/** + * Per-GPIO pullup/pulldown information + * On the ESP32, some GPIOs need their pullups and pulldowns enabled and disabled in the RTC + * peripheral instead of in the GPIO peripheral. This array documents for every GPIO what bit + * to set or clear. + * + * This array is non-static, so if you need a very quick way of toggling the pull-up/downs, you can just + * do e.g. REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); inline. + * + * ToDo: Functions using the contents of this array will do a read/modify/write on GPIO as well as RTC + * registers. We may need to look into muxes/locks for other code that accesses these RTC registers when we + * write drivers for the RTC stuff. + */ +extern const gpio_pu_pd_desc_t gpio_pu_pd_desc[GPIO_PIN_COUNT]; + + typedef enum { GPIO_NUM_0 = 0, /*!< GPIO0, input and output */ GPIO_NUM_1 = 1, /*!< GPIO1, input and output */ @@ -220,7 +243,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig); /** * @brief GPIO set interrupt trigger type * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set the trigger type of e.g. of GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param intr_type Interrupt type, select from gpio_int_type_t * * @return @@ -233,7 +256,7 @@ esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** * @brief Enable GPIO module interrupt signal * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to enable an interrupt on e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - ESP_OK Success @@ -245,7 +268,7 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); /** * @brief Disable GPIO module interrupt signal * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to disable the interrupt of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - ESP_OK success @@ -257,7 +280,7 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); /** * @brief GPIO set output level * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set the output level of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param level Output level. 0: low ; 1: high * * @return @@ -270,7 +293,7 @@ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); /** * @brief GPIO get input level * - * @param gpio_num GPIO number. If you want to get level of pin GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to get the logic level of e.g. pin GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - 0 the GPIO input level is 0 @@ -284,7 +307,7 @@ int gpio_get_level(gpio_num_t gpio_num); * * Configure GPIO direction,such as output_only,input_only,output_and_input * - * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param mode GPIO direction * * @return @@ -299,7 +322,7 @@ esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode); * * User this Function,configure GPIO pull mode,such as pull-up,pull-down * - * @param gpio_num GPIO number. If you want to set pull up or down mode for GPIO16,gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set pull up or down mode for e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param pull GPIO pull up/down mode. * * @return @@ -354,6 +377,53 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); */ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); + + +/** + * @brief Enable pull-up on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pullup_en(gpio_num_t gpio_num); + +/** + * @brief Disable pull-up on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num); + +/** + * @brief Enable pull-down on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); + +/** + * @brief Disable pull-down on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); + + /** * *************** ATTENTION ********************/ /** diff --git a/components/esp32/gdbstub.c b/components/esp32/gdbstub.c index a43793f835..819944a902 100644 --- a/components/esp32/gdbstub.c +++ b/components/esp32/gdbstub.c @@ -25,6 +25,7 @@ #include "soc/io_mux_reg.h" #include "esp_gdbstub.h" +#include "driver/gpio.h" //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which //implies a minimum size of about 320 bytes. @@ -354,7 +355,7 @@ static int gdbReadCommand() { void esp_gdbstub_panic_handler(XtExcFrame *frame) { dumpHwToRegfile(frame); //Make sure txd/rxd are enabled - PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + gpio_pullup_dis(1); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); diff --git a/components/esp32/include/soc/io_mux_reg.h b/components/esp32/include/soc/io_mux_reg.h index 208a60703a..de8fe7ec99 100644 --- a/components/esp32/include/soc/io_mux_reg.h +++ b/components/esp32/include/soc/io_mux_reg.h @@ -34,10 +34,41 @@ #define PIN_INPUT_ENABLE(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME,FUN_IE) #define PIN_INPUT_DISABLE(PIN_NAME) CLEAR_PERI_REG_MASK(PIN_NAME,FUN_IE) #define PIN_SET_DRV(PIN_NAME, drv) REG_SET_FIELD(PIN_NAME, FUN_DRV, (drv)); -#define PIN_PULLUP_DIS(PIN_NAME) REG_CLR_BIT(PIN_NAME, FUN_PU) -#define PIN_PULLUP_EN(PIN_NAME) REG_SET_BIT(PIN_NAME, FUN_PU) -#define PIN_PULLDWN_DIS(PIN_NAME) REG_CLR_BIT(PIN_NAME, FUN_PD) -#define PIN_PULLDWN_EN(PIN_NAME) REG_SET_BIT(PIN_NAME, FUN_PD) + +/* + * @attention + * The PIN_PULL[UP|DWN]_[EN|DIS]() functions used to exist as macros in previous SDK versions. + * Unfortunately, however, they do not work for some GPIOs on the ESP32 chip, which needs pullups + * and -downs turned on and off through RTC registers. The functions still exist for compatibility + * with older code, but are marked as deprecated in order to generate a warning. + * Please replace them in this fashion: (make sure to include driver/gpio.h as well) + * PIN_PULLUP_EN(GPIO_PIN_MUX_REG[x]) -> gpio_pullup_en(x) + * PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[x]) -> gpio_pullup_dis(x) + * PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[x]) -> gpio_pulldown_en(x) + * PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[x]) -> gpio_pulldown_dis(x) + * +*/ +static inline void __attribute__ ((deprecated)) PIN_PULLUP_DIS(uint32_t PIN_NAME) +{ + REG_CLR_BIT(PIN_NAME, FUN_PU); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLUP_EN(uint32_t PIN_NAME) +{ + REG_SET_BIT(PIN_NAME, FUN_PU); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLDWN_DIS(uint32_t PIN_NAME) +{ + REG_CLR_BIT(PIN_NAME, FUN_PD); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME) +{ + REG_SET_BIT(PIN_NAME, FUN_PD); +} + + #define PIN_FUNC_SELECT(PIN_NAME, FUNC) REG_SET_FIELD(PIN_NAME, MCU_SEL, FUNC) #define PIN_FUNC_GPIO 2 From 858fe9815b37068bef9abd8b99e534876d7e3caa Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 11:12:58 +0800 Subject: [PATCH 079/131] feature/openssl: add openssl client demo --- examples/09_openssl/README.md | 21 - examples/09_openssl/main/Kconfig.projbuild | 27 -- examples/09_openssl/main/openssl_demo.c | 430 ------------------ examples/09_openssl/main/openssl_demo.h | 66 --- .../Makefile | 2 +- examples/09_openssl_client/README.md | 16 + .../09_openssl_client/main/Kconfig.projbuild | 28 ++ .../main/component.mk | 2 +- .../09_openssl_client/main/openssl_client.c | 235 ++++++++++ .../09_openssl_client/main/openssl_client.h | 34 ++ 10 files changed, 315 insertions(+), 546 deletions(-) delete mode 100644 examples/09_openssl/README.md delete mode 100644 examples/09_openssl/main/Kconfig.projbuild delete mode 100644 examples/09_openssl/main/openssl_demo.c delete mode 100644 examples/09_openssl/main/openssl_demo.h rename examples/{09_openssl => 09_openssl_client}/Makefile (83%) create mode 100644 examples/09_openssl_client/README.md create mode 100644 examples/09_openssl_client/main/Kconfig.projbuild rename examples/{09_openssl => 09_openssl_client}/main/component.mk (86%) create mode 100644 examples/09_openssl_client/main/openssl_client.c create mode 100644 examples/09_openssl_client/main/openssl_client.h diff --git a/examples/09_openssl/README.md b/examples/09_openssl/README.md deleted file mode 100644 index 54b0133420..0000000000 --- a/examples/09_openssl/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Openssl Example - -The Example contains of OpenSSL client and server demo. - -First you should config the project by "menuconfig": - Example Configuration -> - 1. Openssl demo: select your demo (client or server) - 2. WiFi SSID: you own wifi, which you pc is connected to alse. - 3. WiFi Password: wifi password - -If you want to test the OpenSSL client demo: - 1. compile the code and load the firmware - 2. you can see it will download the "www.baidu.com" main page and print the context - -IF you want to test the openSSL client demo: - 1. compile the code and load the firmware - 2. You should input the context of "https://192.168.17.128", the IP of your module may not be 192.168.17.128, you should input your module's IP - 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" - 4. You should wait for a moment until your see the "OpenSSL server demo!" in your IE page - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl/main/Kconfig.projbuild b/examples/09_openssl/main/Kconfig.projbuild deleted file mode 100644 index b42529ca19..0000000000 --- a/examples/09_openssl/main/Kconfig.projbuild +++ /dev/null @@ -1,27 +0,0 @@ -menu "Example Configuration" - -choice OPENSSL_DEMO - prompt "Openssl demo" - default OPENSSL_CLIENT_DEMO - help - Openssl test demo mode, client or server. - -config OPENSSL_CLIENT_DEMO - bool "client demo" -config OPENSSL_SERVER_DEMO - bool "server demon" -endchoice - -config WIFI_SSID - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. - -config WIFI_PASSWORD - string "WiFi Password" - default "myssid" - help - WiFi password (WPA or WPA2) for the example to use. - -endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/openssl_demo.c b/examples/09_openssl/main/openssl_demo.c deleted file mode 100644 index d541b26253..0000000000 --- a/examples/09_openssl/main/openssl_demo.c +++ /dev/null @@ -1,430 +0,0 @@ -#include -#include - -#include "openssl_demo.h" -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "lwip/sockets.h" -#include "lwip/api.h" - -#include "nvs_flash.h" - - -#define os_printf(fmt, ...) ESP_LOGI("openssl_demo", fmt, ##__VA_ARGS__) - -#define IP_ADDR(ip) ip.u_addr.ip4.addr - -/* The examples use simple WiFi configuration that you can set via - 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" -*/ -#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID -#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD - -#define OPENSSL_DEMO_THREAD_STACK_WORDS 8192 -#define OPENSSL_DEMO_THREAD_PRORIOTY 6 - -#define OPENSSL_DEMO_FRAGMENT_SIZE 8192 - -#define OPENSSL_DEMO_RECV_BUF_LEN 1024 - -#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 - -#ifdef CONFIG_OPENSSL_CLIENT_DEMO - -#define OPENSSL_DEMO_THREAD_NAME "ssl_client" - -#define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" -#define OPENSSL_DEMO_TARGET_TCP_PORT 443 - -#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" - -LOCAL void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket; - struct sockaddr_in sock_addr; - - ip_addr_t target_ip; - - int recv_bytes = 0; - - LOCAL char send_data[] = OPENSSL_DEMO_REQUEST; - LOCAL int send_bytes = sizeof(send_data); - - LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - os_printf("OpenSSL demo thread start\n"); - - do { - ret = netconn_gethostbyname(OPENSSL_DEMO_TARGET_NAME, &target_ip); - } while(ret); - os_printf("get target IP is %d.%d.%d.%d\n", - (unsigned char)((IP_ADDR(target_ip) & 0x000000ff) >> 0), - (unsigned char)((IP_ADDR(target_ip) & 0x0000ff00) >> 8), - (unsigned char)((IP_ADDR(target_ip) & 0x00ff0000) >> 16), - (unsigned char)((IP_ADDR(target_ip) & 0xff000000) >> 24)); - - os_printf("create SSL context ......"); - ctx = SSL_CTX_new(TLSv1_1_client_method()); - if (!ctx) { - os_printf("failed\n"); - goto failed1; - } - os_printf("OK\n"); - - /** - * The openssl does not support "SSL_CTX_set_default_read_buffer_len" - * at the platform of ESP32 esp_idf now. - * - * So you should not care it now. And We my let it work later. - */ - os_printf("set SSL context read buffer size ......"); - SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); - ret = 0; - if (ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("bind socket ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed4; - } - os_printf("OK\n"); - - os_printf("socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = IP_ADDR(target_ip); - sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); - ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed5; - } - os_printf("OK\n"); - - os_printf("create SSL ......"); - ssl = SSL_new(ctx); - if (!ssl) { - os_printf("failed\n"); - goto failed6; - } - os_printf("OK\n"); - - SSL_set_fd(ssl, socket); - - os_printf("SSL connected to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_connect(ssl); - if (!ret) { - os_printf("failed, return [-0x%x]\n", -ret); - goto failed7; - } - os_printf("OK\n"); - - os_printf("send https request to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_write(ssl, send_data, send_bytes); - if (ret <= 0) { - os_printf("failed\n"); - goto failed8; - } - os_printf("OK\n\n"); - - do { - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - recv_bytes += ret; - os_printf("%s", recv_buf); - } while (1); - - os_printf("\r\ntotaly read %d bytes data from %s ......\n", recv_bytes, OPENSSL_DEMO_TARGET_NAME); - -failed8: - SSL_shutdown(ssl); -failed7: - SSL_free(ssl); - ssl = NULL; -failed6: -failed5: -failed4: - close(socket); - socket = -1; -failed3: -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - - os_printf("task exit\n"); - - return ; -} - -#elif defined(CONFIG_OPENSSL_SERVER_DEMO) - -#define OPENSSL_DEMO_THREAD_NAME "openssl_server" - -#define OPENSSL_DEMO_CLIENT_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" - -#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/html\r\n" \ - "Content-Length: 98\r\n" \ - "\r\n" \ - "\r\n" \ - "openSSL demo\r\n" \ - "OpenSSL server demo!\r\n" \ - "\r\n" \ - "\r\n" - -LOCAL void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket, new_socket; - socklen_t addr_len; - struct sockaddr_in sock_addr; - - LOCAL char send_data[] = OPENSSL_DEMO_SERVER_ACK; - LOCAL int send_bytes = sizeof(send_data); - - LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - os_printf("server SSL context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); - if (!ctx) { - os_printf("failed\n"); - goto failed1; - } - os_printf("OK\n"); - - /** - * The openssl does not support "SSL_CTX_set_default_read_buffer_len" - * at the platform of ESP32 esp_idf now. - * - * So you should not care it now. And We my let it work later. - */ - os_printf("server SSL context set fragment ......"); - SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); - ret = 0; - if (ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server SSL context set own certification......"); - ret = SSL_CTX_use_certificate_ASN1(ctx, cert_bytes, cert_ctx); - if (!ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server SSL context set private key......"); - ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, key_ctx, key_bytes); - if (!ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - os_printf("failed\n"); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server socket bind ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("server socket listen ......"); - ret = listen(socket, 32); - if (ret) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - -reconnect: - os_printf("server SSL create ......"); - ssl = SSL_new(ctx); - if (!ssl) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("server socket accept client ......"); - new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); - if (new_socket < 0) { - os_printf("failed, return [-0x%x]\n", -new_socket); - goto failed4; - } - os_printf("OK\n"); - - SSL_set_fd(ssl, new_socket); - - os_printf("server SSL accept client ......"); - ret = SSL_accept(ssl); - if (!ret) { - os_printf("failed\n"); - goto failed5; - } - os_printf("OK\n"); - - os_printf("server SSL read message ......"); - do { - memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); - break; - } - } while (1); - - os_printf("result %d\n", ret); - - SSL_shutdown(ssl); -failed5: - close(new_socket); - new_socket = -1; -failed4: - SSL_free(ssl); - ssl = NULL; - goto reconnect; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - - return ; -} - -#else - #error "you must choose the right demo type" -#endif - -LOCAL void demo_init(void) -{ - int ret = pdFALSE; - -#if defined(CONFIG_OPENSSL_CLIENT_DEMO) || defined(CONFIG_OPENSSL_SERVER_DEMO) - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); -#endif - - if (ret != pdPASS) { - os_printf("create thread %s failed\n", OPENSSL_DEMO_THREAD_NAME); - return ; - } -} - -LOCAL esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - demo_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - break; - default: - break; - } - return ESP_OK; -} - -LOCAL void wifi_conn_init(void) -{ - tcpip_adapter_init(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - os_printf("start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} - diff --git a/examples/09_openssl/main/openssl_demo.h b/examples/09_openssl/main/openssl_demo.h deleted file mode 100644 index 89cd264b46..0000000000 --- a/examples/09_openssl/main/openssl_demo.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef _OPENSSL_DEMO_H_ -#define _OPENSSL_DEMO_H_ - -const static unsigned char key_ctx[] = - "-----BEGIN PRIVATE KEY-----\r\n" - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQyyF0WBb2XbkL\r\n" - "wYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijBR0Rr\r\n" - "0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe6eFG\r\n" - "AGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wHzywA\r\n" - "DvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwbrdcD\r\n" - "HNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gmpqfh\r\n" - "CwEPoKCNAgMBAAECggEBAIVr+LHXBL1kQLZhbiKC5t1FrMY8FLKYGM3JCevciMlk\r\n" - "lhIXwR7p29vsRYZfHBv7zWWyI9/C04JG31MiGOMaCbBxfnA2HBrVEqFwwxqnKVi8\r\n" - "CxzwGQkfwu3Y9j7TEj0PipfQYo+aKzmDrN/FrXnHjyEJryxAQbAZPVLW+Z7OR41R\r\n" - "ZOwtZLeVqmbeARGpu2Xd9SKAhbjdLSz96IdUcNrwbP/lzUgrKaiUioBMVFfIG5ce\r\n" - "4Mm2seCwaWxI8k24q0keSjsjV+5IxatVUNtJ9vYv6Tzo+3oqGvPeUBO7w9xhbLKf\r\n" - "jw1uEykcs0wcftWb1iB7r78bMPZ/KYhnSFsjT+vnIOECgYEA9LM5p63sn6OWrULd\r\n" - "doBBLaIw1ZK9rDUFLLRwv0e6+7tuuB08ok6D7PSfOpx0bb10lIx7epOE8ogGSmLS\r\n" - "w0rMbmcKAlTLAJ/0nX1ierZkb+Ay21F398tKL3igEfnaB9CzuOHF8XhbsTqeGFDJ\r\n" - "HFBMXxTbo4kfkUmZNYxwTombzkkCgYEA2m9teqjEOVZItqghOicWZ68JhWxBJFme\r\n" - "oSfzJKLd8atfUOnBLqAhPzvg1PvdIxjLWBqy28tEJf+jdSQCNe9BmhlptOwbFrJy\r\n" - "IyCXj6QTApSKTxyzIjMvzQkv1m8CxeCq5T64hvJ2++i7dlhumh21c7oL8aLeTnoe\r\n" - "AG1dBLJ9UCUCgYAhSlDJsyvB/Ct/nt0qRgCYCLzEEZakWwI9Nr8wBr41iSZi/fdF\r\n" - "zZC9J/qRqr9cPq4hl4sk/fTUWhUhAZjS4NY3HuWJs6d6ikhpNKm1MCMx5TqGA+ti\r\n" - "VtHc63g7edZjwczxliWr2EgBMIxZmoQByhrZxKis8vbMeUrSsiyFQstjoQKBgD3k\r\n" - "2Paqn39Hra7kERYzQSv78wy1UfgE1OgBM+orpAv4bTe2JKEbipAqXVi8TTrGqce7\r\n" - "OPcCr7q8pwpoO6AgvUv263byd/KEecbuU0PGUASpJk+oaDHGo0LL2Zw/NF/xezsd\r\n" - "/JdwWLqkhYnRIPXWeTXjf8LmTWubOqkQVA0irlNpAoGAJ+9N/NF3XAW0BroiVYLZ\r\n" - "p0Btcgt+b4LWrBlm0XqHhzKUlqhfibAr3OtUkFjo/509ncYPiuOzVSNosyjXFJ86\r\n" - "2kQ88fB3eeLnBAcbBXQKiOBPU2y6bCCfgdo+JEOK/cxVslaxMAyKSnFi9gdgzScd\r\n" - "k+hOlkflXQVkic3W358kFto=\r\n" - "-----END PRIVATE KEY-----\r\n" - ; -static int key_bytes = sizeof(key_ctx); - -const static unsigned char cert_ctx[] = - "-----BEGIN CERTIFICATE-----\r\n" - "MIID7jCCAtYCAQEwDQYJKoZIhvcNAQELBQAwgbwxCzAJBgNVBAYTAkNOMRAwDgYD\r\n" - "VQQIDAdKaWFuZ3N1MQ0wCwYDVQQHDARXdXhpMSYwJAYDVQQKDB1Fc3ByZXNzaWYg\r\n" - "Um9vdCBSU0EyMDQ4IHNoYTI1NjEcMBoGA1UECwwTUm9vdCBSU0EyMDQ4IHNoYTI1\r\n" - "NjEfMB0GA1UEAwwWcm9vdGNlcnQuZXNwcmVzc2lmLmNvbTElMCMGCSqGSIb3DQEJ\r\n" - "ARYWcm9vdGNlcnRAZXNwcmVzc2lmLmNvbTAeFw0xNjA2MjgwMjMxMjlaFw0yNjA2\r\n" - "MjYwMjMxMjlaMIG8MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHSmlhbmdzdTENMAsG\r\n" - "A1UEBwwEV3V4aTEmMCQGA1UECgwdRXNwcmVzc2lmIFJvb3QgUlNBMjA0OCBzaGEy\r\n" - "NTYxHDAaBgNVBAsME1Jvb3QgUlNBMjA0OCBzaGEyNTYxHzAdBgNVBAMMFnJvb3Rj\r\n" - "ZXJ0LmVzcHJlc3NpZi5jb20xJTAjBgkqhkiG9w0BCQEWFnJvb3RjZXJ0QGVzcHJl\r\n" - "c3NpZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQyyF0WBb2\r\n" - "XbkLwYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijB\r\n" - "R0Rr0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe\r\n" - "6eFGAGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wH\r\n" - "zywADvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwb\r\n" - "rdcDHNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gm\r\n" - "pqfhCwEPoKCNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABTYZLiFHq51lqaa0nHI\r\n" - "aDMAb29DfO93fqp+oHZYO4xKyEeLr8EhD39GjnQmhz710wO0TBCYV7nD+xnJ1h5F\r\n" - "IbQUAQZO9NIy3ns4mYVRUWjnWYAo+evGeKgRrxvh7sjNLPBPzs9tg/u7XjBp/nor\r\n" - "8JnnFFT0wXPyi/qg8J3QutqJvWRQGRRx2AP93F44+Zcj7ReFMVSmOXyzT4aNJL0+\r\n" - "Ls+baKwA4pnyVRoAaKbs/JYDgd0/DunuktVKuhyvK/qOGjJSRLPhdrXbvSAegpiM\r\n" - "4xIm6ZWKtTv8VvkGgXUVQ7RpruP6nV6506gDcUgecbEq7H2VDhEzUYcMmGCUQZlG\r\n" - "sJ8=\r\n" - "-----END CERTIFICATE-----\r\n" - ; -static int cert_bytes = sizeof(cert_ctx); - - - -#endif diff --git a/examples/09_openssl/Makefile b/examples/09_openssl_client/Makefile similarity index 83% rename from examples/09_openssl/Makefile rename to examples/09_openssl_client/Makefile index 8987be554c..7e2f4fe7f8 100644 --- a/examples/09_openssl/Makefile +++ b/examples/09_openssl_client/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := openssl +PROJECT_NAME := openssl_client include $(IDF_PATH)/make/project.mk diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md new file mode 100644 index 0000000000..85d9575d6e --- /dev/null +++ b/examples/09_openssl_client/README.md @@ -0,0 +1,16 @@ +# Openssl Example + +The Example contains of OpenSSL client demo. + +First you should config the project by "make menuconfig": + Example Configuration -> + 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". + 2. Target port number : the port number of the target domain, and default is 443. + 3. WiFi SSID : you own wifi, which you pc is connected to alse, and default is "myssid". + 4. WiFi Password : wifi password, and default is "mypassword" + +If you want to test the OpenSSL client demo: + 1. compile the code and load the firmware + 2. open the UART TTY, then you can see it print the context of target domain + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl_client/main/Kconfig.projbuild b/examples/09_openssl_client/main/Kconfig.projbuild new file mode 100644 index 0000000000..1767923ad1 --- /dev/null +++ b/examples/09_openssl_client/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Example Configuration" + +config TARGET_DOMAIN + string "Target Domain" + default "www.baidu.com" + help + Target domain for the example to connect to. + +config TARGET_PORT_NUMBER + int "Target port number" + range 0 65535 + default 433 + help + Target port number for the example to connect to. + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/component.mk b/examples/09_openssl_client/main/component.mk similarity index 86% rename from examples/09_openssl/main/component.mk rename to examples/09_openssl_client/main/component.mk index 24356f23ed..973de1d6f0 100644 --- a/examples/09_openssl/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -1,7 +1,7 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, # this will take the sources in the src/ directory, compile them and link them into # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c new file mode 100644 index 0000000000..9d4b8b395d --- /dev/null +++ b/examples/09_openssl_client/main/openssl_client.c @@ -0,0 +1,235 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_client.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +void openssl_demo_thread(void *p) +{ + int ret; + SSL_CTX *ctx; + SSL *ssl; + int socket; + struct sockaddr_in sock_addr; + struct hostent *hp; + struct ip4_addr *ip4_addr; + + int recv_bytes = 0; + char send_data[] = OPENSSL_DEMO_REQUEST; + int send_bytes = sizeof(send_data); + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + ESP_LOGI(TAG, "OpenSSL demo thread start OK"); + + ESP_LOGI(TAG, "get target IP address"); + hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); + if (!hp) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); + + ESP_LOGI(TAG, "create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = ip4_addr->addr; + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, socket); + + ESP_LOGI(TAG, "SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed " ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + ESP_LOGI(TAG, "%s", recv_buf); + } while (1); + + ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed5: + SSL_shutdown(ssl); +failed4: + SSL_free(ssl); + ssl = NULL; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + extern void openssl_demo_thread(void *p); + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/09_openssl_client/main/openssl_client.h b/examples/09_openssl_client/main/openssl_client.h new file mode 100644 index 0000000000..5bc69a0aca --- /dev/null +++ b/examples/09_openssl_client/main/openssl_client.h @@ -0,0 +1,34 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +/* The examples use domain of "www.baidu.com" and port number of 433 that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" + and ie #define OPENSSL_DEMO_TARGET_TCP_PORT 433 +*/ +#define OPENSSL_DEMO_TARGET_NAME CONFIG_TARGET_DOMAIN +#define OPENSSL_DEMO_TARGET_TCP_PORT CONFIG_TARGET_PORT_NUMBER + +#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +#define OPENSSL_DEMO_THREAD_NAME "OpenSSL_demo" +#define OPENSSL_DEMO_THREAD_STACK_WORDS 10240 +#define OPENSSL_DEMO_THREAD_PRORIOTY 8 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#endif + From adb4be859cb74cefbe81527a23effaeb05abe355 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Tue, 15 Nov 2016 11:19:15 +0800 Subject: [PATCH 080/131] wps: update wifi lib -- add wps lib --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 84af0ed366..01f5c068e1 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 84af0ed366e1ba38984f7df517a77f8ec4fa27ed +Subproject commit 01f5c068e1ac3968add98439ee2f1748b9e391fa From fa1d5bfbc78797ac261ab5d63662cdbf1b68f254 Mon Sep 17 00:00:00 2001 From: liuhan Date: Thu, 3 Nov 2016 13:30:32 +0800 Subject: [PATCH 081/131] tcpip_adapter: add set hostname interface --- .../tcpip_adapter/include/tcpip_adapter.h | 13 +++++++++ components/tcpip_adapter/tcpip_adapter_lwip.c | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index e847016884..45d6ade305 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -372,6 +372,19 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev); */ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); +#define TCPIP_HOSTNAME_MAX_SIZE 31 +/** + * @brief Set the hostname to the interface + * + * @param[in] tcpip_if: the interface which we will set the hostname + * @param[in] hostname: the host name for set the interfce + * + * @return ESP_OK:success + * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error + * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS:parameter error + */ +esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname); + #ifdef __cplusplus } #endif diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 9b6e9d94fa..3edc90509e 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -607,4 +607,32 @@ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapt return ESP_OK; } +esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname) +{ + struct netif *p_netif; + static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1]; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + if (strlen(hostname) >= TCPIP_HOSTNAME_MAX_SIZE) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + p_netif = esp_netif[tcpip_if]; + if (p_netif != NULL) { + if (netif_is_up(p_netif)) { + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; + } else { + memset(hostinfo, 0, sizeof(hostinfo)); + memcpy(hostinfo, hostname, strlen(hostname)); + p_netif->hostname = hostinfo; + return ESP_OK; + } + } else { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } +} + #endif From 9a2887c458c20b05ffad8f7ef8dbd54a20b97920 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 15 Nov 2016 12:10:02 +0800 Subject: [PATCH 082/131] Add comments/headers according to merge request comments --- components/esp32/freertos_hooks.c | 15 ++++++++++ components/esp32/include/esp_freertos_hooks.h | 29 +++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c index 50ebd3d054..d59a20363d 100644 --- a/components/esp32/freertos_hooks.c +++ b/components/esp32/freertos_hooks.c @@ -1,3 +1,18 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + #include #include #include diff --git a/components/esp32/include/esp_freertos_hooks.h b/components/esp32/include/esp_freertos_hooks.h index 1bb5ab194a..45a1649723 100644 --- a/components/esp32/include/esp_freertos_hooks.h +++ b/components/esp32/include/esp_freertos_hooks.h @@ -1,9 +1,27 @@ -#ifndef ESP_FREERTOS_HOOKS_H -#define ESP_FREERTOS_HOOKS_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_FREERTOS_HOOKS_H__ +#define __ESP_FREERTOS_HOOKS_H__ #include #include "esp_err.h" +#ifdef __cplusplus +extern "C" +{ +#endif /* Definitions for the tickhook and idlehook callbacks @@ -17,6 +35,9 @@ typedef void (*esp_freertos_tick_cb_t)(); * sleep until an interrupt (or FreeRTOS tick) happens and false * if it should be called again as fast as possible. * + * @warning Idle callbacks MUST NOT, UNDER ANY CIRCUMSTANCES, CALL + * A FUNCTION THAT MIGHT BLOCK. + * * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be called * * @return ESP_OK : Callback registered @@ -54,5 +75,9 @@ void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb); */ void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb); +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file From a8a51a2786a3b2f1814a111af10f66d45f47a4c1 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 15 Nov 2016 13:47:51 +0800 Subject: [PATCH 083/131] bugfix/uart_isr_switch_context: add switching context in uart ISR. 1. add switching context in uart ISR 2. remove duplicated #include in uart.c 3. modify example in uart.h(will later add examples to idf/examples) --- components/driver/include/driver/uart.h | 13 ++++++---- components/driver/uart.c | 34 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 7dccf1666c..906bdcb9e9 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -686,13 +686,14 @@ esp_err_t uart_flush(uart_port_t uart_num); * { * int uart_num = (int)pvParameters; * uart_event_t event; - * uint8_t dtmp[1000]; + * size_t size = 1024; + * uint8_t* dtmp = (uint8_t*)malloc(size); * for(;;) { * //Waiting for UART event. * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { - * memset(dtmp, 0, sizeof(dtmp)); + * memset(dtmp, 0, size); * //Event of UART receving data * case UART_DATA: * ESP_LOGI(TAG,"data, len: %d", event.size); @@ -727,6 +728,8 @@ esp_err_t uart_flush(uart_port_t uart_num); * } * } * } + * free(dtmp); + * dtmp = NULL; * vTaskDelete(NULL); * } * @@ -744,13 +747,13 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART parameters * uart_param_config(uart_num, &uart_config); * //Set UART pins,(-1: default pin, no change.) - * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); * //Set UART log level * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue); * //Create a task to handler UART event from ISR - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); + * xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL); * } * @endcode * diff --git a/components/driver/uart.c b/components/driver/uart.c index d9e3fd64ca..a8d28ff295 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -23,11 +23,9 @@ #include "freertos/task.h" #include "freertos/ringbuf.h" #include "soc/dport_reg.h" -#include "rom/ets_sys.h" #include "soc/uart_struct.h" #include "driver/uart.h" #include "driver/gpio.h" -#include "soc/uart_struct.h" static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ @@ -458,17 +456,20 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); if(p_uart->tx_waiting_brk) { - return; + continue; } //TX semaphore will only be used when tx_buf_size is zero. if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) { p_uart->tx_waiting_fifo = false; - xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); + xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { - //We don't use TX ring buffer, because the size if zero. + //We don't use TX ring buffer, because the size is zero. if(p_uart->tx_buf_size == 0) { - return; + continue; } int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; bool en_tx_flg = false; @@ -492,6 +493,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } //We have saved the data description from the 1st item, return buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } }else if(p_uart->tx_ptr == NULL) { //Update the TX item pointer, we will need this to return item to buffer. p_uart->tx_ptr = (uint8_t*) p_uart->tx_head; @@ -501,7 +505,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else { //Can not get data from ring buffer, return; - return; + break; } } if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) { @@ -516,6 +520,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_len_cur == 0) { //Return item to ring buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } p_uart->tx_head = NULL; p_uart->tx_ptr = NULL; //Sending item done, now we need to send break if there is a record. @@ -529,7 +536,6 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.tx_brk_done = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); p_uart->tx_waiting_brk = 1; - return; } else { //enable TX empty interrupt en_tx_flg = true; @@ -576,6 +582,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else { uart_event.type = UART_DATA; } + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; @@ -614,6 +623,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) p_uart->tx_waiting_brk = 0; } else { xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); @@ -626,6 +638,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.tx_done = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ @@ -634,6 +649,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) { xQueueSendFromISR(p_uart->xQueueUart, (void * )&uart_event, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } uart_intr_status = uart_reg->int_st.val; } From dfaac25a37340a37ae808d2c12c9a37d268cbc48 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 15:04:21 +0800 Subject: [PATCH 084/131] feature/openssl: add openssl server demo and remove some check function --- components/openssl/platform/ssl_pm.c | 4 ---- examples/09_openssl_client/main/component.mk | 2 -- 2 files changed, 6 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 091402cda4..a5986dc3ee 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -90,10 +90,6 @@ int ssl_pm_new(SSL *ssl) if (!ssl_pm) SSL_ERR(ret, failed1, "ssl_mem_zalloc\n"); - if (ssl->ctx->read_buffer_len < 2048 || - ssl->ctx->read_buffer_len > 8192) - return -1; - max_content_len = ssl->ctx->read_buffer_len; mbedtls_net_init(&ssl_pm->fd); diff --git a/examples/09_openssl_client/main/component.mk b/examples/09_openssl_client/main/component.mk index 973de1d6f0..9c21f4a8d4 100644 --- a/examples/09_openssl_client/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -6,5 +6,3 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # - -include $(IDF_PATH)/make/component_common.mk From a2b1f4221b39e61d0ad41c55f523b08d4a6a2605 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 15:08:51 +0800 Subject: [PATCH 085/131] feature/openssl: add the openssl server demo --- examples/10_openssl_server/Makefile | 9 + examples/10_openssl_server/README.md | 20 ++ .../10_openssl_server/main/Kconfig.projbuild | 15 + examples/10_openssl_server/main/cacert.pem | 21 ++ examples/10_openssl_server/main/component.mk | 11 + .../10_openssl_server/main/openssl_server.c | 258 ++++++++++++++++++ .../10_openssl_server/main/openssl_server.h | 22 ++ examples/10_openssl_server/main/prvtkey.pem | 27 ++ 8 files changed, 383 insertions(+) create mode 100644 examples/10_openssl_server/Makefile create mode 100644 examples/10_openssl_server/README.md create mode 100644 examples/10_openssl_server/main/Kconfig.projbuild create mode 100644 examples/10_openssl_server/main/cacert.pem create mode 100644 examples/10_openssl_server/main/component.mk create mode 100644 examples/10_openssl_server/main/openssl_server.c create mode 100644 examples/10_openssl_server/main/openssl_server.h create mode 100644 examples/10_openssl_server/main/prvtkey.pem diff --git a/examples/10_openssl_server/Makefile b/examples/10_openssl_server/Makefile new file mode 100644 index 0000000000..f65f11a562 --- /dev/null +++ b/examples/10_openssl_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := openssl_server + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md new file mode 100644 index 0000000000..ae5c8da0c7 --- /dev/null +++ b/examples/10_openssl_server/README.md @@ -0,0 +1,20 @@ +# Openssl Example + +The Example contains of OpenSSL server demo. + +First you should configure the project by "make menuconfig": + Example Configuration -> + 1. WiFi SSID: you own wifi that you pc is connected to alse. + 1. WiFi Password: wifi password + +IF you want to test the OpenSSL server demo: + 1. compile the code and load the firmware + 2. input the context of "https://192.168.17.128" into your web browser, the IP of your module may not be 192.168.17.128, you should input your module's IP + 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" + 4. You should wait for a moment until your see the "OpenSSL server demo!" in your web browser + +Note: + The private key and certification at the example are not trusted by web browser, because they are not created by CA official, just by ourselves. + You can alse create your own private key and ceritification by "openssl at ubuntu or others". + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/10_openssl_server/main/Kconfig.projbuild b/examples/10_openssl_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..7a9cb97a0e --- /dev/null +++ b/examples/10_openssl_server/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/10_openssl_server/main/cacert.pem b/examples/10_openssl_server/main/cacert.pem new file mode 100644 index 0000000000..e09c3989cd --- /dev/null +++ b/examples/10_openssl_server/main/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIJAPMMNobNczaUMA0GCSqGSIb3DQEBBAUAMHQxEzARBgNV +BAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEcMBoGCSqG +SIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNjExMTUwNTA0MThaFw0xOTExMTUwNTA0MThaMHQx +EzARBgNVBAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEc +MBoGCSqGSIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALDjSPDlomepHCzbw4MUrquQAU0xTV4/Npb27k9I5TRVTjIoOs/5hNI2LPFW +e4CREx09ZrT8K3NFOBoSy7bhPAsjGaFxCYYWc9tiX1m5gq3ToVRSmbZ65fE3kvnI +8E/d5VyzA0OMmWbfaolBSTMoWgqRynEaT+z1Eh2yDTzVFy9eov1DdQFUqGDqbH5b +QYvTY5Fyem7UcKWAe2yS0j3H4dVtVBKNY7qV3Px08yGAs5fQFgUwhyB5+qwhvkeL +JdgapGaSTwLgoQKWHbe/lA3NiBIB9hznFUGKo3hmniAvYZbrQcn3tc0l/J4I39v2 +Pm29FAyjWvQyBkGktz2q4elOZYkCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOCAQEAJCJ+97oae/FcOLbPpjCpUQnWqYydgSChgalkZNvr4fVp +TnuNg471l0Y2oTJLoWn2YcbPSFVOEeKkU47mpjMzucHHp0zGaW9SdzhZalWwmbgK +q2ijecIbuFHFNedYTk/03K7eaAcjVhD8e0oOJImeLOL6DAFivA1LUnSgXsdGPDtD +zhISsCPTu+cL1j0yP6HBvLeAyb8kaCWJ05RtiVLRANNHQn/keHajJYpMwnEEbJdG +cqN3whfJoGVbZ6isEf2RQJ0pYRnP7uGLW3wGkLWxfdto8uER8HVDx7fZpevLIqGd +1OoSEi3cIJXWBAjx0TLzzhtb6aeIxBJWQqHThtkKdg== +-----END CERTIFICATE----- diff --git a/examples/10_openssl_server/main/component.mk b/examples/10_openssl_server/main/component.mk new file mode 100644 index 0000000000..4a891d52f7 --- /dev/null +++ b/examples/10_openssl_server/main/component.mk @@ -0,0 +1,11 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_EMBED_TXTFILES := cacert.pem +COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c new file mode 100644 index 0000000000..4fc841ac79 --- /dev/null +++ b/examples/10_openssl_server/main/openssl_server.c @@ -0,0 +1,258 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_server.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "OpenSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +static void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + char send_data[] = OPENSSL_DEMO_SERVER_ACK; + int send_bytes = sizeof(send_data); + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; + + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; + + ESP_LOGI(TAG, "SSL server context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + +reconnect: + ESP_LOGI(TAG, "SSL server create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + ESP_LOGI(TAG, "failed" ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, new_socket); + + ESP_LOGI(TAG, "SSL server accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + ESP_LOGI(TAG, "result %d", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + extern void openssl_demo_thread(void *p); + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h new file mode 100644 index 0000000000..e87f5e482e --- /dev/null +++ b/examples/10_openssl_server/main/openssl_server.h @@ -0,0 +1,22 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define OPENSSL_DEMO_THREAD_NAME "OpenSSL_demo" +#define OPENSSL_DEMO_THREAD_STACK_WORDS 10240 +#define OPENSSL_DEMO_THREAD_PRORIOTY 8 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#endif + diff --git a/examples/10_openssl_server/main/prvtkey.pem b/examples/10_openssl_server/main/prvtkey.pem new file mode 100644 index 0000000000..4ead61f6ff --- /dev/null +++ b/examples/10_openssl_server/main/prvtkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsONI8OWiZ6kcLNvDgxSuq5ABTTFNXj82lvbuT0jlNFVOMig6 +z/mE0jYs8VZ7gJETHT1mtPwrc0U4GhLLtuE8CyMZoXEJhhZz22JfWbmCrdOhVFKZ +tnrl8TeS+cjwT93lXLMDQ4yZZt9qiUFJMyhaCpHKcRpP7PUSHbINPNUXL16i/UN1 +AVSoYOpsfltBi9NjkXJ6btRwpYB7bJLSPcfh1W1UEo1jupXc/HTzIYCzl9AWBTCH +IHn6rCG+R4sl2BqkZpJPAuChApYdt7+UDc2IEgH2HOcVQYqjeGaeIC9hlutByfe1 +zSX8ngjf2/Y+bb0UDKNa9DIGQaS3Parh6U5liQIDAQABAoIBAB9K9jp3xXVlO3DM +KBhmbkg3n6NSV4eW00d9w8cO9E1/0eeZql3knJS7tNO1IwApqiIAHM1j1yP7WONz +88oUqpSlzwD6iF7KVhC3pHqxEOdDi0Tpn/viXg+Ab2X1IF5guRTfLnKiyviiCazi +edqtBtDb3d6Icx9Oc7gBKcpbQFDGt++wSOb5L+xhRm9B5B4l/6byikiPeKqIK5tC +SoP9Zr1mvpNoGm1P4LvEunFJcRBqVI010VNwfO9P98oVyzJu9/FZZrQxXoY9JdXF +OM6nbl+hMDM3TkEOda9NvBhImozEAvuc97CaaXyR3XivxMqNqNIb4+syUPa2PCS3 +ZztI5qECgYEA1gbVG6ifpvpbBkDPi3Im8fM3F7FLLrQc48FdFjdMvDhHD9lVKucD +Uaa8PF9dbbvlu2cwMyfBOKSuWaXxRxRsiqiPmTunS1MvPzQcSrGwUrL2AogGucn6 ++NrLQf5P4H5IpkDQ9ih3zwjO6xKFK1WeYnYpHM8qUBtl6q0YFyVBPu0CgYEA05Pn +StWA4D7VSbNnVi6lvFyEOUsTrK3v419598TFiq4eXLq6aV8/CQYzKsSzoG+aOZhX +Li+0uyT5cNzUcXYhTsW1hA/pNhMfxMrYiB1x14zlLp2WRGg4vd/+SxX6d9Yd3acX +7QzPKgdDicXs9QN8ozJOICKvNbUI53AJdATVEY0CgYEAwvpGeoQLrdq1weSZLrg3 +soOX1QW3MDz1dKdbXjnStkWut0mOxR7fbysuoPFf8/ARQcCnsHKvHCMqkpESVWbN +2yPkbfxiU8Tcbf/TJljqAOz4ISY6ula/RKZONTixHBrvpEW4GAiV3Q5xMsYUe33s +ZFaw7YXtTj0ng7tdDvjpj6ECgYEApHdUU9ejVq2BHslWiqe4LbO9FMxHfvO2hgix +xugupp6y+2Irhb2EQn+PRq+g8hXOzPaezkhHNTKItDL08T3iplkJwJ6dqmszRsZn +i2dYFzZu8M2PAZ4CfZahFbz/9id7D9HTx3EtmH4NAgvZJpyPRkzUbiaIDDettDpj +Hsyi1AECgYAPLvjBzQj4kPF8Zo9pQEUcz4pmupRVfv3aRfjnahDK4qZHEePDRj+J +W7pzayrs1dyN9QLB8pTc424z7f8MB3llCICN+ohs8CR/eW0NEobE9ldDOeoCr1Vh +NhNSbrN1iZ8U4oLkRTMaDKkVngGffvjGi/q0tOU7hJdZOqNlk2Iahg== +-----END RSA PRIVATE KEY----- From d7599ab16d1504e2d75211f05562548665d905d3 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Wed, 16 Nov 2016 01:31:02 +0800 Subject: [PATCH 086/131] driver: fix header file warnings for Doxygen. --- components/driver/include/driver/gpio.h | 2 +- components/driver/include/driver/ledc.h | 2 + components/driver/include/driver/uart.h | 233 ++++++++++++------------ 3 files changed, 122 insertions(+), 115 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 73efeaa342..2d92dc50a9 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -314,7 +314,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); * * @param gpio_num GPIO number. * - * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL\GPIO_INTR_HIGH_LEVEL can be used. + * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL or GPIO_INTR_HIGH_LEVEL can be used. * * @return * - ESP_OK Success diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 3ab0ebff1e..317a599fbc 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -150,6 +150,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * * @param channel LEDC channel(0-7), select from ledc_channel_t * + * @param idle_level Set output idle level after LEDC stops. + * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 906bdcb9e9..687ae71aad 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -113,11 +113,11 @@ typedef struct { } uart_event_t; /** - * @brief Set UART data bits. + * @brief Set UART data bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param data_bit UART data bits + * @param data_bit UART data bits * * @return * - ESP_OK Success @@ -126,9 +126,11 @@ typedef struct { esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); /** - * @brief Get UART data bits. + * @brief Get UART data bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param data_bit Pointer to accept value of UART data bits. * * @return * - ESP_FAIL Parameter error @@ -137,22 +139,24 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** - * @brief Set UART stop bits. + * @brief Set UART stop bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param bit_num UART stop bits + * @param bit_num UART stop bits * * @return * - ESP_OK Success * - ESP_FAIL Fail */ -esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t bit_num); /** - * @brief Set UART stop bits. + * @brief Set UART stop bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param stop_bit Pointer to accept value of UART stop bits. * * @return * - ESP_FAIL Parameter error @@ -161,22 +165,24 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** - * @brief Set UART parity. + * @brief Set UART parity. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param parity_mode the enum of uart parity configuration + * @param parity_mode the enum of uart parity configuration * * @return * - ESP_FAIL Parameter error * - ESP_OK Success */ -esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode); /** - * @brief Get UART parity mode. + * @brief Get UART parity mode. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param parity_mode Pointer to accept value of UART parity mode. * * @return * - ESP_FAIL Parameter error @@ -186,22 +192,24 @@ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** - * @brief Set UART baud rate. + * @brief Set UART baud rate. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param baud_rate UART baud-rate. + * @param baud_rate UART baud-rate. * * @return * - ESP_FAIL Parameter error * - ESP_OK Success */ -esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate); /** - * @brief Get UART bit-rate. + * @brief Get UART bit-rate. * - * @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param baudrate Pointer to accept value of UART baud rate * * @return * - ESP_FAIL Parameter error @@ -211,11 +219,11 @@ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** - * @brief Set UART line inverse mode + * @brief Set UART line inverse mode * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param inverse_mask Choose the wires that need to be inversed. + * @param Inverse_mask Choose the wires that need to be inversed. * * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) * @@ -223,16 +231,16 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask); /** - * @brief Set hardware flow control. + * @brief Set hardware flow control. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param flow_ctrl Hardware flow control mode + * @param flow_ctrl Hardware flow control mode * - * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. * @@ -240,12 +248,14 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); /** - * @brief Get hardware flow control mode + * @brief Get hardware flow control mode * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param flow_ctrl Option for different flow control mode. * * @return * - ESP_FAIL Parameter error @@ -254,11 +264,11 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** - * @brief Clear UART interrupt status + * @brief Clear UART interrupt status * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param clr_mask Bit mask of the status that to be cleared. + * @param clr_mask Bit mask of the status that to be cleared. * * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) * @@ -269,11 +279,11 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); /** - * @brief Set UART interrupt enable + * @brief Set UART interrupt enable * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param enable_mask Bit mask of the enable bits. + * @param enable_mask Bit mask of the enable bits. * * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) * @@ -284,11 +294,11 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); /** - * @brief Clear UART interrupt enable bits + * @brief Clear UART interrupt enable bits * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param disable_mask Bit mask of the disable bits. + * @param disable_mask Bit mask of the disable bits. * * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) * @@ -300,9 +310,9 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); /** - * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -311,9 +321,9 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); esp_err_t uart_enable_rx_intr(uart_port_t uart_num); /** - * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -322,9 +332,9 @@ esp_err_t uart_enable_rx_intr(uart_port_t uart_num); esp_err_t uart_disable_rx_intr(uart_port_t uart_num); /** - * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -333,13 +343,13 @@ esp_err_t uart_disable_rx_intr(uart_port_t uart_num); esp_err_t uart_disable_tx_intr(uart_port_t uart_num); /** - * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param enable 1: enable; 0: disable + * @param enable 1: enable; 0: disable * - * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return * - ESP_OK Success @@ -348,21 +358,20 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** -* @brief register UART interrupt handler(ISR). +* @brief register UART interrupt handler(ISR). * @note * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * - * @param fn Interrupt handler function. + * @param fn Interrupt handler function. * @attention * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. - * @param arg parameter for handler function + * @param arg parameter for handler function * * @return * - ESP_OK Success @@ -371,21 +380,21 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); /** - * @brief Set UART pin number + * @brief Set UART pin number * * @note * Internal signal can be output to multiple GPIO pads * Only one GPIO pad can connect with input signal * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return * - ESP_OK Success @@ -394,12 +403,12 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); /** - * @brief UART set RTS level (before inverse) + * @brief UART set RTS level (before inverse) * UART rx hardware flow control should not be set. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param level 1: RTS output low(active); 0: RTS output high(block) + * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return * - ESP_OK Success @@ -408,11 +417,11 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level); /** - * @brief UART set DTR level (before inverse) + * @brief UART set DTR level (before inverse) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param level 1: DTR output low; 0: DTR output high + * @param level 1: DTR output low; 0: DTR output high * * @return * - ESP_OK Success @@ -421,11 +430,11 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); esp_err_t uart_set_dtr(uart_port_t uart_num, int level); /** -* @brief UART parameter configure +* @brief UART parameter configure * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_config UART parameter settings + * @param uart_config UART parameter settings * * @return * - ESP_OK Success @@ -434,11 +443,11 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config); /** -* @brief UART interrupt configure +* @brief UART interrupt configure * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param intr_conf UART interrupt settings + * @param intr_conf UART interrupt settings * * @return * - ESP_OK Success @@ -447,25 +456,25 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf); /** - * @brief Install UART driver. + * @brief Install UART driver. * * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param rx_buffer_size UART RX ring buffer size + * @param rx_buffer_size UART RX ring buffer size * - * @param tx_buffer_size UART TX ring buffer size. + * @param tx_buffer_size UART TX ring buffer size. * * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * - * @param queue_size UART event queue size/depth. + * @param queue_size UART event queue size/depth. * - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return * - ESP_OK Success @@ -474,9 +483,9 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); /** - * @brief Uninstall UART driver. + * @brief Uninstall UART driver. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -485,11 +494,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b esp_err_t uart_driver_delete(uart_port_t uart_num); /** - * @brief Wait UART TX FIFO empty + * @brief Wait UART TX FIFO empty * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param ticks_to_wait Timeout, count in RTOS ticks + * @param ticks_to_wait Timeout, count in RTOS ticks * * @return * - ESP_OK Success @@ -499,26 +508,25 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * @note * This function should only be used when UART TX buffer is not enabled. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buffer data buffer address * - * @param buffer data buffer address - * - * @param len data length to send + * @param len data length to send * * @return * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); +int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * * If parameter tx_buffer_size is set to zero: * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. @@ -526,11 +534,11 @@ int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, * then, UART ISR will move data from ring buffer to TX FIFO gradually. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param src data buffer address + * @param src data buffer address * - * @param size data length to send + * @param size data length to send * * @return * - (-1) Parameter error @@ -539,7 +547,7 @@ int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * * If parameter tx_buffer_size is set to zero: * This function will not return until all the data and the break signal have been sent out. @@ -549,15 +557,13 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); * then, UART ISR will move data from ring buffer to TX FIFO gradually. * After all data send out, send a break signal. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * + * @param src data buffer address * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param size data length to send * - * @param src data buffer address - * - * @param size data length to send - * - * @param brk_len break signal length (unit: one bit's time@current_baudrate) + * @param brk_len break signal length (unit: time of one data bit at current_baudrate) * * @return * - (-1) Parameter error @@ -567,16 +573,15 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** -* @brief UART read bytes from UART buffer + * @brief UART read bytes from UART buffer * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param buf pointer to the buffer. + * @param buf pointer to the buffer. * - * @param length data length - * - * @param ticks_to_wait sTimeout, count in RTOS ticks + * @param length data length * + * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return * - (-1) Error @@ -585,9 +590,9 @@ int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t si int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); /** - * @brief UART ring buffer flush + * @brief UART ring buffer flush * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success From 656543c5ca9838497da76673ebd408e22d3d3baf Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 10:46:03 +0800 Subject: [PATCH 087/131] feature/openssl: fixup some insufficient --- examples/09_openssl_client/README.md | 2 +- examples/09_openssl_client/main/component.mk | 5 ----- examples/09_openssl_client/main/openssl_client.c | 7 +++---- examples/10_openssl_server/README.md | 2 +- examples/10_openssl_server/main/component.mk | 5 ----- examples/10_openssl_server/main/openssl_server.c | 7 +++---- 6 files changed, 8 insertions(+), 20 deletions(-) diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md index 85d9575d6e..a131cb2472 100644 --- a/examples/09_openssl_client/README.md +++ b/examples/09_openssl_client/README.md @@ -6,7 +6,7 @@ First you should config the project by "make menuconfig": Example Configuration -> 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". 2. Target port number : the port number of the target domain, and default is 443. - 3. WiFi SSID : you own wifi, which you pc is connected to alse, and default is "myssid". + 3. WiFi SSID : you own wifi, which is connected to the Internet, and default is "myssid". 4. WiFi Password : wifi password, and default is "mypassword" If you want to test the OpenSSL client demo: diff --git a/examples/09_openssl_client/main/component.mk b/examples/09_openssl_client/main/component.mk index 9c21f4a8d4..44bd2b5273 100644 --- a/examples/09_openssl_client/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -1,8 +1,3 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index 9d4b8b395d..890962f5d9 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -55,9 +55,10 @@ void openssl_demo_thread(void *p) struct ip4_addr *ip4_addr; int recv_bytes = 0; - char send_data[] = OPENSSL_DEMO_REQUEST; - int send_bytes = sizeof(send_data); char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_REQUEST; + const int send_bytes = sizeof(send_data); ESP_LOGI(TAG, "OpenSSL demo thread start OK"); @@ -171,7 +172,6 @@ static void openssl_client_init(void) { int ret; xTaskHandle openssl_handle; - extern void openssl_demo_thread(void *p); ret = xTaskCreate(openssl_demo_thread, OPENSSL_DEMO_THREAD_NAME, @@ -182,7 +182,6 @@ static void openssl_client_init(void) if (ret != pdPASS) { ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - return ; } } diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md index ae5c8da0c7..a8c16d4ed0 100644 --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -4,7 +4,7 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> - 1. WiFi SSID: you own wifi that you pc is connected to alse. + 1. WiFi SSID: WiFi network to which your PC is also connected to. 1. WiFi Password: wifi password IF you want to test the OpenSSL server demo: diff --git a/examples/10_openssl_server/main/component.mk b/examples/10_openssl_server/main/component.mk index 4a891d52f7..80af01cb53 100644 --- a/examples/10_openssl_server/main/component.mk +++ b/examples/10_openssl_server/main/component.mk @@ -1,11 +1,6 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# COMPONENT_EMBED_TXTFILES := cacert.pem COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 4fc841ac79..6dc28182d7 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -65,10 +65,11 @@ static void openssl_demo_thread(void *p) socklen_t addr_len; struct sockaddr_in sock_addr; - char send_data[] = OPENSSL_DEMO_SERVER_ACK; - int send_bytes = sizeof(send_data); char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + const char send_data[] = OPENSSL_DEMO_SERVER_ACK; + const int send_bytes = sizeof(send_data); + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; @@ -194,7 +195,6 @@ static void openssl_client_init(void) { int ret; xTaskHandle openssl_handle; - extern void openssl_demo_thread(void *p); ret = xTaskCreate(openssl_demo_thread, OPENSSL_DEMO_THREAD_NAME, @@ -205,7 +205,6 @@ static void openssl_client_init(void) if (ret != pdPASS) { ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - return ; } } From 2ed9e2d9a8712df9bf8f00d9d23d6f3522fe6e50 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 11:11:01 +0800 Subject: [PATCH 088/131] feature/openssl: fixup the file and code style --- examples/09_openssl_client/README.md | 4 +- .../09_openssl_client/main/openssl_client.c | 464 ++++++++-------- examples/10_openssl_server/README.md | 7 +- .../10_openssl_server/main/openssl_server.c | 510 +++++++++--------- 4 files changed, 489 insertions(+), 496 deletions(-) diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md index a131cb2472..2a6ac19881 100644 --- a/examples/09_openssl_client/README.md +++ b/examples/09_openssl_client/README.md @@ -6,8 +6,8 @@ First you should config the project by "make menuconfig": Example Configuration -> 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". 2. Target port number : the port number of the target domain, and default is 443. - 3. WiFi SSID : you own wifi, which is connected to the Internet, and default is "myssid". - 4. WiFi Password : wifi password, and default is "mypassword" + 3. WIFI SSID : your own WIFI, which is connected to the Internet, and default is "myssid". + 4. WIFI Password : WIFI password, and default is "mypassword" If you want to test the OpenSSL client demo: 1. compile the code and load the firmware diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index 890962f5d9..c804b6c4fd 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -1,234 +1,230 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "openssl_client.h" - -#include - -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_log.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "nvs_flash.h" -#include "tcpip_adapter.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const static int CONNECTED_BIT = BIT0; - -const static char *TAG = "Openssl_demo"; - -void openssl_demo_thread(void *p) -{ - int ret; - SSL_CTX *ctx; - SSL *ssl; - int socket; - struct sockaddr_in sock_addr; - struct hostent *hp; - struct ip4_addr *ip4_addr; - - int recv_bytes = 0; - char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - const char send_data[] = OPENSSL_DEMO_REQUEST; - const int send_bytes = sizeof(send_data); - - ESP_LOGI(TAG, "OpenSSL demo thread start OK"); - - ESP_LOGI(TAG, "get target IP address"); - hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); - if (!hp) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ip4_addr = (struct ip4_addr *)hp->h_addr; - ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); - - ESP_LOGI(TAG, "create SSL context ......"); - ctx = SSL_CTX_new(TLSv1_1_client_method()); - if (!ctx) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "bind socket ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = ip4_addr->addr; - sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); - ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "create SSL ......"); - ssl = SSL_new(ctx); - if (!ssl) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - SSL_set_fd(ssl, socket); - - ESP_LOGI(TAG, "SSL connected to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_connect(ssl); - if (!ret) { - ESP_LOGI(TAG, "failed " ); - goto failed4; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "send https request to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_write(ssl, send_data, send_bytes); - if (ret <= 0) { - ESP_LOGI(TAG, "failed"); - goto failed5; - } - ESP_LOGI(TAG, "OK"); - - do { - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - recv_bytes += ret; - ESP_LOGI(TAG, "%s", recv_buf); - } while (1); - - ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); - -failed5: - SSL_shutdown(ssl); -failed4: - SSL_free(ssl); - ssl = NULL; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - return ; -} - -static void openssl_client_init(void) -{ - int ret; - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); - - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - } -} - -static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - openssl_client_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void wifi_conn_init(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_client.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +void openssl_demo_thread(void *p) +{ + int ret; + SSL_CTX *ctx; + SSL *ssl; + int socket; + struct sockaddr_in sock_addr; + struct hostent *hp; + struct ip4_addr *ip4_addr; + + int recv_bytes = 0; + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_REQUEST; + const int send_bytes = sizeof(send_data); + + ESP_LOGI(TAG, "OpenSSL demo thread start OK"); + + ESP_LOGI(TAG, "get target IP address"); + hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); + if (!hp) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); + + ESP_LOGI(TAG, "create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = ip4_addr->addr; + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, socket); + + ESP_LOGI(TAG, "SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed " ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + ESP_LOGI(TAG, "%s", recv_buf); + } while (1); + + ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed5: + SSL_shutdown(ssl); +failed4: + SSL_free(ssl); + ssl = NULL; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md index a8c16d4ed0..333cb3d6a6 100644 --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -4,8 +4,8 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> - 1. WiFi SSID: WiFi network to which your PC is also connected to. - 1. WiFi Password: wifi password + 1. WIFI SSID: WIFI network to which your PC is also connected to. + 1. WIFI Password: WIFI password IF you want to test the OpenSSL server demo: 1. compile the code and load the firmware @@ -15,6 +15,7 @@ IF you want to test the OpenSSL server demo: Note: The private key and certification at the example are not trusted by web browser, because they are not created by CA official, just by ourselves. - You can alse create your own private key and ceritification by "openssl at ubuntu or others". + You can alse create your own private key and ceritification by "openssl at ubuntu or others". + We have the document of "ESP8266_SDKSSL_User_Manual_EN_v1.4.pdf" at "http://www.espressif.com/en/support/download/documents". By it you can gernerate the private key and certification with the fomate of ".pem" See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 6dc28182d7..7f4b7d6b6e 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -1,257 +1,253 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "openssl_server.h" - -#include - -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_log.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "nvs_flash.h" -#include "tcpip_adapter.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const static int CONNECTED_BIT = BIT0; - -const static char *TAG = "Openssl_demo"; - -#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/html\r\n" \ - "Content-Length: 98\r\n" \ - "\r\n" \ - "\r\n" \ - "OpenSSL demo\r\n" \ - "OpenSSL server demo!\r\n" \ - "\r\n" \ - "\r\n" - -static void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket, new_socket; - socklen_t addr_len; - struct sockaddr_in sock_addr; - - char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - const char send_data[] = OPENSSL_DEMO_SERVER_ACK; - const int send_bytes = sizeof(send_data); - - extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); - extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); - const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; - - extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); - extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); - const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; - - ESP_LOGI(TAG, "SSL server context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); - if (!ctx) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server context set own certification......"); - ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server context set private key......"); - ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket bind ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket listen ......"); - ret = listen(socket, 32); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - -reconnect: - ESP_LOGI(TAG, "SSL server create ......"); - ssl = SSL_new(ctx); - if (!ssl) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket accept client ......"); - new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); - if (new_socket < 0) { - ESP_LOGI(TAG, "failed" ); - goto failed4; - } - ESP_LOGI(TAG, "OK"); - - SSL_set_fd(ssl, new_socket); - - ESP_LOGI(TAG, "SSL server accept client ......"); - ret = SSL_accept(ssl); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed5; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server read message ......"); - do { - memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); - break; - } - } while (1); - - ESP_LOGI(TAG, "result %d", ret); - - SSL_shutdown(ssl); -failed5: - close(new_socket); - new_socket = -1; -failed4: - SSL_free(ssl); - ssl = NULL; - goto reconnect; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - return ; -} - -static void openssl_client_init(void) -{ - int ret; - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); - - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - } -} - -static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - openssl_client_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void wifi_conn_init(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_server.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "OpenSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +static void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_SERVER_ACK; + const int send_bytes = sizeof(send_data); + + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; + + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; + + ESP_LOGI(TAG, "SSL server context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + +reconnect: + ESP_LOGI(TAG, "SSL server create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + ESP_LOGI(TAG, "failed" ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, new_socket); + + ESP_LOGI(TAG, "SSL server accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + ESP_LOGI(TAG, "result %d", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} From 9ae07082ff97ed712a4ee99c38c3a8d874454c17 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 14:20:47 +1100 Subject: [PATCH 089/131] examples: Update component.mk for new conventions --- examples/01_hello_world/main/component.mk | 2 +- examples/02_blink/main/component.mk | 2 +- examples/03_http_request/main/component.mk | 2 +- examples/04_https_request/main/component.mk | 2 +- examples/05_ble_adv/main/component.mk | 2 +- examples/06_sntp/main/component.mk | 2 +- examples/07_nvs_rw_value/main/component.mk | 5 ++++- examples/08_nvs_rw_blob/main/component.mk | 5 ++++- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/01_hello_world/main/component.mk b/examples/01_hello_world/main/component.mk index 4d3b30caf3..0b9d7585e7 100644 --- a/examples/01_hello_world/main/component.mk +++ b/examples/01_hello_world/main/component.mk @@ -1,5 +1,5 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/02_blink/main/component.mk b/examples/02_blink/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/02_blink/main/component.mk +++ b/examples/02_blink/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/03_http_request/main/component.mk b/examples/03_http_request/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/03_http_request/main/component.mk +++ b/examples/03_http_request/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index f4502c25db..818e2a1822 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,5 +1,5 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/05_ble_adv/main/component.mk b/examples/05_ble_adv/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/05_ble_adv/main/component.mk +++ b/examples/05_ble_adv/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/06_sntp/main/component.mk b/examples/06_sntp/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/06_sntp/main/component.mk +++ b/examples/06_sntp/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/07_nvs_rw_value/main/component.mk b/examples/07_nvs_rw_value/main/component.mk index d33485c26c..0b9d7585e7 100644 --- a/examples/07_nvs_rw_value/main/component.mk +++ b/examples/07_nvs_rw_value/main/component.mk @@ -1,2 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk index d33485c26c..0b9d7585e7 100644 --- a/examples/08_nvs_rw_blob/main/component.mk +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -1,2 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk From 60f29236f607c81929c391da263e10f2cc495063 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 15:42:38 +1100 Subject: [PATCH 090/131] Build system: Raise warning level Default esp-idf builds now show -Wextra warnings (except for a few: signed/unsigned comparison, unused parameters, old-style C declarations.) CI building of examples runs with that level raised to -Werror, to catch those changes going into the main repo. --- .../bootloader_support/src/esp_image_format.c | 2 +- .../src/secure_boot_signatures.c | 7 ++- components/expat/port/minicheck.c | 63 ++++++++++--------- components/freertos/ringbuf.c | 4 +- components/freertos/tasks.c | 4 +- make/build_examples.sh | 9 ++- make/project.mk | 16 +++-- 7 files changed, 58 insertions(+), 47 deletions(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 42a3e4ffe2..ca75f6ae45 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -17,7 +17,7 @@ #include #include -const static char *TAG = "esp_image"; +static const char *TAG = "esp_image"; #define SIXTEEN_MB 0x1000000 #define ESP_ROM_CHECKSUM_INITIAL 0xEF diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 6d47651b2f..f7e435e86d 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -45,10 +45,13 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) sha_context sha; uint8_t digest[32]; ptrdiff_t keylen; - const uint8_t *data, *digest_data; - uint32_t digest_len; + const uint8_t *data; const signature_block_t *sigblock; bool is_valid; +#ifdef BOOTLOADER_BUILD + const uint8_t *digest_data; + uint32_t digest_len; +#endif ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); diff --git a/components/expat/port/minicheck.c b/components/expat/port/minicheck.c index 5a1f5ed0e2..95a4939bdb 100644 --- a/components/expat/port/minicheck.c +++ b/components/expat/port/minicheck.c @@ -108,44 +108,45 @@ add_failure(SRunner *runner, int verbosity) } } +static void run_test(SRunner *runner, int verbosity, TCase *tc, int i) +{ + if (tc->setup != NULL) { + /* setup */ + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + tc->setup(); + } + /* test */ + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + (tc->tests[i])(); + + /* teardown */ + if (tc->teardown != NULL) { + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + tc->teardown(); + } +} + void srunner_run_all(SRunner *runner, int verbosity) { - Suite *suite; - TCase *tc; assert(runner != NULL); - suite = runner->suite; - tc = suite->tests; + assert(runner->suite != NULL); + TCase *tc = runner->suite->tests; while (tc != NULL) { - int i; - for (i = 0; i < tc->ntests; ++i) { + for (int i = 0; i < tc->ntests; ++i) { runner->nchecks++; - - if (tc->setup != NULL) { - /* setup */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->setup(); - } - /* test */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - (tc->tests[i])(); - - /* teardown */ - if (tc->teardown != NULL) { - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->teardown(); - } + run_test(runner, verbosity, tc, i); + tc = tc->next_tcase; } - tc = tc->next_tcase; } if (verbosity) { int passed = runner->nchecks - runner->nfailures; diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index a4205d88dc..062c52023f 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -495,8 +495,8 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, //we will need to wait some more. ticks_to_wait = ticks_end - xTaskGetTickCount(); } - } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + } while (ringbufferFreeMem(rb) < needed_size && ticks_end >= xTaskGetTickCount()); + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 88aa8d3ef5..26103ee236 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -2704,7 +2704,7 @@ void vTaskSwitchContext( void ) taskENTER_CRITICAL_ISR(&xTaskQueueMutex); unsigned portBASE_TYPE foundNonExecutingWaiter = pdFALSE, ableToSchedule = pdFALSE, resetListHead; - unsigned portBASE_TYPE uxDynamicTopReady = uxTopReadyPriority; + portBASE_TYPE uxDynamicTopReady = uxTopReadyPriority; unsigned portBASE_TYPE holdTop=pdFALSE; /* @@ -2717,8 +2717,6 @@ void vTaskSwitchContext( void ) while ( ableToSchedule == pdFALSE && uxDynamicTopReady >= 0 ) { - configASSERT( uxTopReadyPriority>=0 ); - configASSERT( uxDynamicTopReady>=0 ); resetListHead = pdFALSE; // Nothing to do for empty lists if (!listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxDynamicTopReady ] ) )) { diff --git a/make/build_examples.sh b/make/build_examples.sh index a522666a98..8345dab502 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -20,8 +20,13 @@ for example in ${IDF_PATH}/examples/*; do mkdir ${EXAMPLE_NUM} cp -r ${example} ${EXAMPLE_NUM} pushd ${EXAMPLE_NUM}/`basename ${example}` - # build non-verbose first, only build verbose if there's an error - make defconfig all || (RESULT=$?; make V=1) + + # be stricter in the CI build than the default IDF settings + export EXTRA_CFLAGS="-Werror -Werror=deprecated-declarations" + export EXTRA_CXXFLAGS=${EXTRA_CFLAGS} + + # build non-verbose first, only build verbose if there's an error + (make clean defconfig && make all ) || (RESULT=$?; make V=1) popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done diff --git a/make/project.mk b/make/project.mk index 08c7dd89a9..a081992804 100644 --- a/make/project.mk +++ b/make/project.mk @@ -158,14 +158,16 @@ LDFLAGS ?= -nostdlib \ # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) +CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) $(EXTRA_CPPFLAGS) # Warnings-related flags relevant both for C and C++ -COMMON_WARNING_FLAGS = -Wall -Werror \ +COMMON_WARNING_FLAGS = -Wall -Werror=all \ -Wno-error=unused-function \ -Wno-error=unused-but-set-variable \ -Wno-error=unused-variable \ - -Wno-error=deprecated-declarations + -Wno-error=deprecated-declarations \ + -Wextra \ + -Wno-unused-parameter -Wno-sign-compare # Flags which control code generation and dependency generation, both for C and C++ COMMON_FLAGS = \ @@ -192,8 +194,9 @@ CFLAGS := $(strip \ -std=gnu99 \ $(OPTIMIZATION_FLAGS) \ $(COMMON_FLAGS) \ - $(COMMON_WARNING_FLAGS) \ - $(CFLAGS)) + $(COMMON_WARNING_FLAGS) -Wno-old-style-declaration \ + $(CFLAGS) \ + $(EXTRA_CFLAGS)) # List of flags to pass to C++ compiler # If any flags are defined in application Makefile, add them at the end. @@ -204,7 +207,8 @@ CXXFLAGS := $(strip \ $(OPTIMIZATION_FLAGS) \ $(COMMON_FLAGS) \ $(COMMON_WARNING_FLAGS) \ - $(CXXFLAGS)) + $(CXXFLAGS) \ + $(EXTRA_CXXFLAGS)) export CFLAGS CPPFLAGS CXXFLAGS From 7dbf01b2107376e6f6c047bd64221b4f16976b19 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 13:20:49 +0800 Subject: [PATCH 091/131] esp32: softap supports bridge Softap supports bridge, then the stations associated with esp32 softap can send/receive traffic from each other --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 01f5c068e1..41da160a5d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 01f5c068e1ac3968add98439ee2f1748b9e391fa +Subproject commit 41da160a5dbf9e13b4fb51f31acf372f50c28270 From 37cbb0bdea302783f3bca43356f806402845322f Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Wed, 16 Nov 2016 14:44:06 +0800 Subject: [PATCH 092/131] driver: bugfix/fix uart parity and frame error bug 1. modify definition for uart_parity_t 2. fix bugs in uart interrupt handler for parity err and frame err. --- components/driver/include/driver/uart.h | 4 ++-- components/driver/uart.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 687ae71aad..749cd65648 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -68,8 +68,8 @@ typedef enum { typedef enum { UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ - UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/ - UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/ + UART_PARITY_EVEN = 0x2, /*!< Enable UART even parity*/ + UART_PARITY_ODD = 0x3 /*!< Enable UART odd parity*/ } uart_parity_t; typedef enum { diff --git a/components/driver/uart.c b/components/driver/uart.c index a8d28ff295..02610e7b49 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -603,10 +603,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) { uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; - } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { + } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M ) { uart_reg->int_clr.parity_err = 1; uart_event.type = UART_FRAME_ERR; - } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { + } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { @@ -964,7 +964,8 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b | UART_RXFIFO_TOUT_INT_ENA_M | UART_FRM_ERR_INT_ENA_M | UART_RXFIFO_OVF_INT_ENA_M - | UART_BRK_DET_INT_ENA_M, + | UART_BRK_DET_INT_ENA_M + | UART_PARITY_ERR_INT_ENA_M, .rxfifo_full_thresh = UART_FULL_THRESH_DEFAULT, .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT From 8b7d1cdc272acce928f053ec64eefb55a46f44c4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 17:15:55 +1100 Subject: [PATCH 093/131] ringbuf: Fix case where xTaskGetTickCount() overflows but task timeout point doesn't --- components/freertos/ringbuf.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 062c52023f..67323e3276 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -471,7 +471,8 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ringbuf_t *rb=(ringbuf_t *)ringbuf; size_t needed_size=dataSize+sizeof(buf_entry_hdr_t); BaseType_t done=pdFALSE; - portTickType ticks_end=xTaskGetTickCount() + ticks_to_wait; + TickType_t ticks_end = xTaskGetTickCount() + ticks_to_wait; + TickType_t ticks_remaining = ticks_to_wait; configASSERT(rb); @@ -486,16 +487,25 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, if (ringbufferFreeMem(rb) < needed_size) { //Data does not fit yet. Wait until the free_space_sem is given, then re-evaluate. - BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_to_wait); + BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_remaining); if (r == pdFALSE) { //Timeout. return pdFALSE; } - //Adjust ticks_to_wait; we may have waited less than that and in the case the free memory still is not enough, + //Adjust ticks_remaining; we may have waited less than that and in the case the free memory still is not enough, //we will need to wait some more. - ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (ticks_to_wait != portMAX_DELAY) { + ticks_remaining = ticks_end - xTaskGetTickCount(); + } + + // ticks_remaining will always be less than or equal to the original ticks_to_wait, + // unless the timeout is reached - in which case it unsigned underflows to a much + // higher value. + // + // (Check is written this non-intuitive way to allow for the case where xTaskGetTickCount() + // has overflowed but the ticks_end value has not overflowed.) } - } while (ringbufferFreeMem(rb) < needed_size && ticks_end >= xTaskGetTickCount()); + } while (ringbufferFreeMem(rb) < needed_size && ticks_remaining > 0 && ticks_remaining <= ticks_to_wait); //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); From 69dbc36a1c9338d5829256c8bf69cffaebbb83a8 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 16:24:41 +0800 Subject: [PATCH 094/131] lwip: add option to memcopy packet from L2 to L3 Menuconfig add an option to copy the packet from layer2 (WIFI driver) to layer3 (LWIP), default not copy --- components/lwip/Kconfig | 10 +++++ components/lwip/include/lwip/lwip/opt.h | 7 ++++ components/lwip/include/lwip/port/lwipopts.h | 1 + components/lwip/port/netif/wlanif.c | 41 +++++++++----------- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 801fa0b51c..90b7c5b5c7 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1,5 +1,15 @@ menu "LWIP" +config L2_TO_L3_COPY + bool "Enable copy between Layer2 and Layer3 packets" + default 0 + help + If this feature is enabled, then all traffic from layer2(WIFI Driver) + to layer3(LWIP stack) will make a copy, the layer2 buffer will be + freed and the copy will be sent to layer3. Please make sure you fully + understand this feature before you enable this feature. + + config LWIP_MAX_SOCKETS int "Max number of open sockets" range 1 16 diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 51d340e00b..9a1d10afbf 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -3008,6 +3008,13 @@ #define LWIP_PERF 0 #endif +/** + * ESP_L2_TO_L3_COPY: enable memcpy when receiving packet from L2 + */ +#ifndef ESP_L2_TO_L3_COPY +#define ESP_L2_TO_L3_COPY 1 +#endif + #ifndef ESP_THREAD_SAFE_DEBUG #define ESP_THREAD_SAFE_DEBUG 0 #endif diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index d06e756850..26bdc3a4e9 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -525,6 +525,7 @@ extern unsigned long os_random(void); #define ESP_RANDOM_TCP_PORT 1 #define ESP_IP4_ATON 1 #define ESP_LIGHT_SLEEP 1 +#define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY #define TCP_WND_DEFAULT (4*TCP_MSS) #define TCP_SND_BUF_DEFAULT (2*TCP_MSS) diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index ffad69cd46..3fd2a3192a 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -161,40 +161,37 @@ low_level_output(struct netif *netif, struct pbuf *p) * @param netif the lwip network interface structure for this ethernetif */ void -#if ESP_LWIP wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) -#else -wlanif_input(struct netif *netif, void *buffer, uint16 len) -#endif { struct pbuf *p; -#if ESP_LWIP - if(buffer== NULL) + if(!buffer || !netif) goto _exit; - if(netif == NULL) - goto _exit; -#endif -#if ESP_LWIP - p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); - if (p == NULL){ -#if ESP_PERF - g_rx_alloc_pbuf_fail_cnt++; -#endif - return; - } - p->payload = buffer; - p->eb = eb; -#else - p = pbuf_alloc(PBUF_IP, len, PBUF_POOL); +#if (ESP_L2_TO_L3_COPY == 1) + //p = pbuf_alloc(PBUF_IP, len, PBUF_POOL); + p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { + #if ESP_PERF + g_rx_alloc_pbuf_fail_cnt++; + #endif + esp_wifi_internal_free_rx_buffer(eb); return; } memcpy(p->payload, buffer, len); + esp_wifi_internal_free_rx_buffer(eb); +#else + p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); + if (p == NULL){ + #if ESP_PERF + g_rx_alloc_pbuf_fail_cnt++; + #endif + return; + } + p->payload = buffer; + p->eb = eb; #endif - /* full packet send to tcpip_thread to process */ if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); From a023b005c67c80c7cf5cc4e2666ff34fe99e5baf Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 16 Nov 2016 17:53:50 +0800 Subject: [PATCH 095/131] taskwdt fixes: better handling of empty/emptying wdt task list, lock task struct while feeding --- components/esp32/task_wdt.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 549b7f58b2..de5f9f54f9 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -22,6 +22,8 @@ #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" #include #include "esp_err.h" #include "esp_intr.h" @@ -45,6 +47,7 @@ struct wdt_task_t { }; static wdt_task_t *wdt_task_list=NULL; +static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; @@ -55,13 +58,23 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { TIMERG0.wdt_wprotect=0; //Ack interrupt TIMERG0.int_clr_timers.wdt=1; + //We are taking a spinlock while doing I/O (ets_printf) here. Normally, that is a pretty + //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case, + //something bad already happened and reporting this is considered more important + //than the badness caused by a spinlock here. + portENTER_CRITICAL(&taskwdt_spinlock); + if (!wdt_task_list) { + //No task on list. Maybe none registered yet. + portEXIT_CRITICAL(&taskwdt_spinlock); + return; + } //Watchdog got triggered because at least one task did not report in. ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } ets_printf("Tasks currently running:\n"); @@ -73,6 +86,7 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { ets_printf("Aborting.\n"); abort(); #endif + portEXIT_CRITICAL(&taskwdt_spinlock); } @@ -80,6 +94,8 @@ void esp_task_wdt_feed() { wdt_task_t *wdttask=wdt_task_list; bool found_task=false, do_feed_wdt=true; TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + portENTER_CRITICAL(&taskwdt_spinlock); + //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed //the real watchdog timer. for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { @@ -114,14 +130,18 @@ void esp_task_wdt_feed() { //Reset fed_watchdog status for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; } + portEXIT_CRITICAL(&taskwdt_spinlock); } void esp_task_wdt_delete() { TaskHandle_t handle=xTaskGetCurrentTaskHandle(); wdt_task_t *wdttask=wdt_task_list; + portENTER_CRITICAL(&taskwdt_spinlock); + //Wdt task list can't be empty if (!wdt_task_list) { ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); + portEXIT_CRITICAL(&taskwdt_spinlock); return; } if (handle==wdt_task_list) { @@ -130,15 +150,25 @@ void esp_task_wdt_delete() { free(wdttask); } else { //Find current task in list + if (wdt_task_list->task_handle==handle) { + //Task is the very first one. + wdt_task_t *freeme=wdt_task_list; + wdt_task_list=wdt_task_list->next; + free(freeme); + portEXIT_CRITICAL(&taskwdt_spinlock); + return; + } while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; if (!wdttask->next) { ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); + portEXIT_CRITICAL(&taskwdt_spinlock); return; } wdt_task_t *freeme=wdttask->next; wdttask->next=wdttask->next->next; free(freeme); } + portEXIT_CRITICAL(&taskwdt_spinlock); } From 0b2cea6e9f757191be0bfc83a6fe3ed15981ccf2 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 21:11:17 +0800 Subject: [PATCH 096/131] lwip: modify menuconfig comments according to review --- components/lwip/Kconfig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 90b7c5b5c7..7661fe6cb7 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -6,9 +6,14 @@ config L2_TO_L3_COPY help If this feature is enabled, then all traffic from layer2(WIFI Driver) to layer3(LWIP stack) will make a copy, the layer2 buffer will be - freed and the copy will be sent to layer3. Please make sure you fully - understand this feature before you enable this feature. - + freed and the copy will be sent to layer3. Please be notified that the + total layer2 receiving buffer is fixed and ESP32 currently supports 25 + layer2 receiving buffer, when layer2 buffer runs out of memory, then the + incoming packets will be dropped in hardware. The layer3 buffer is + allocated from the heap, so the total layer3 receiving buffer depends + on the available heap size, when heap runs out of memory, no copy will + be sent to layer3 and packet will be dropped in layer2. Please make sure + you fully understand the impact of this feature before enabling it. config LWIP_MAX_SOCKETS int "Max number of open sockets" From 8713155e669fd19db23e9b20364910240c3f15c9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 14 Nov 2016 15:40:54 +0800 Subject: [PATCH 097/131] docs: add description of ESP-IDF application startup flow and memory layout --- docs/general-notes.rst | 127 +++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 128 insertions(+) create mode 100644 docs/general-notes.rst diff --git a/docs/general-notes.rst b/docs/general-notes.rst new file mode 100644 index 0000000000..519a03b287 --- /dev/null +++ b/docs/general-notes.rst @@ -0,0 +1,127 @@ +General Notes About ESP-IDF Programming +======================================= + +Application startup flow +------------------------ + +This note explains various steps which happen before ``app_main`` function of an ESP-IDF application is called. + +The high level view of startup process is as follows: + +1. First-stage bootloader in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset 0x1000. +2. Second-stage bootloader loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache. +3. Main app image executes. At this point the second CPU and RTOS scheduler can be started. + +This process is explained in detail in the following sections. + +First stage bootloader +^^^^^^^^^^^^^^^^^^^^^^ + +After SoC reset, PRO CPU will start running immediately, executing reset vector code, while APP CPU will be held in reset. During startup process, PRO CPU does all the initialization. APP CPU reset is de-asserted in the ``call_start_cpu0`` function of application startup code. Reset vector code is located at address 0x40000400 in the mask ROM of the ESP32 chip and can not be modified. + +Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place: + +1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see deep sleep documentation for this: deep-sleep-stub_. + +2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset. + +3. For software CPU reset and watchdog CPU reset: configure SPI flash based on EFUSE values, and attempt to load the code from flash. This step is described in more detail in the next paragraphs. If loading code from flash fails, unpack BASIC interpreter into the RAM and start it. Note that RTC watchdog is still enabled when this happens, so unless any input is received by the interpreter, watchdog will reset the SOC in a few hundred milliseconds, repeating the whole process. If the interpreter receives any input from the UART, it disables the watchdog. + +Application binary image is loaded from flash starting at address 0x1000. First 4kB sector of flash is used to store secure boot IV and signature of the application image. Please check secure boot documentation for details about this. + +.. TODO: describe application binary image format, describe optional flash configuration commands. + +Second stage bootloader +^^^^^^^^^^^^^^^^^^^^^^^ + +In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the second stage bootloader. Second stage bootloader source code is available in components/bootloader directory of ESP-IDF. Note that this arrangement is not the only one possible with the ESP32 chip. It is possible to write a fully featured application which would work when flashed to offset 0x1000, but this is out of scope of this document. Second stage bootloader is used in ESP-IDF to add flexibility to flash layout (using partition tables), and allow for various flows associated with flash encryption, secure boot, and over-the-air updates (OTA) to take place. + +When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header. + +Second stage bootloader reads the partition table found at offset 0x8000. For more information about partition tables, see partition-tables_. It finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. + +For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header. + +Currently it is not possible to add application-defined hooks to the bootloader to customize application partition selection logic. This may be required to load different application image depending on a state of a GPIO, for example. Such customization features will be added to ESP-IDF in the future. For now, bootloader can be customized by copying bootloader component into application directory and making necessary changes there. ESP-IDF build system will compile the component in application directory instead of ESP-IDF components directory in this case. + +Application startup +^^^^^^^^^^^^^^^^^^^ + +ESP-IDF application entry point is ``call_start_cpu0`` function found in ``components/esp32/cpu_start.c``. Two main things this function does are to enable heap allocator and to make APP CPU jump to its entry point, ``call_start_cpu1``. The code on PRO CPU sets the entry point for APP CPU, de-asserts APP CPU reset, and waits for a global flag to be set by the code running on APP CPU, indicating that it has started. Once this is done, PRO CPU jumps to ``start_cpu0`` function, and APP CPU jumps to ``start_cpu1`` function. + +Both ``start_cpu0`` and ``start_cpu1`` are weak functions, meaning that they can be overridden in the application, if some application-specific change to initialization sequence is needed. Default implementation of ``start_cpu0`` enables or initializes components depending on choices made in ``menuconfig``. Please see source code of this function in ``components/esp32/cpu_start.c`` for an up to date list of steps performed. Note that any C++ global constructors present in the application will be called at this stage. Once all essential components are initialized, *main task* is created and FreeRTOS scheduler is started. + +While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in ``start_cpu1`` function, waiting for the scheduler to be started on the PRO CPU. Once the scheduler is started on the PRO CPU, code on the APP CPU starts the scheduler as well. + +Main task is the task which runs ``app_main`` function. Main task stack size and priority can be configured in ``menuconfig``. Application can use this task for initial application-specific setup, for example to launch other tasks. Application can also use main task for event loops and other general purpose activities. If ``app_main`` function returns, main task is deleted. + + +Application memory layout +------------------------- + +ESP32 chip has flexible memory mapping features. This section describes how ESP-IDF uses these features by default. + +Application code in ESP-IDF can be placed into one of the following memory regions. + +IRAM (instruction RAM) +^^^^^^^^^^^^^^^^^^^^^^ + +ESP-IDF allocates part of `Internal SRAM0` region (defined in the Technical Reference Manual) for instruction RAM. Except for the first 64 kB block which is used for PRO and APP CPU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of application which need to run from RAM. + +A few components of ESP-IDF and parts of WiFi stack are placed into this region using the linker script. + +If some application code needs to be placed into IRAM, it can be done using ``IRAM_ATTR`` define:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +Here are the cases when parts of application may or should be placed into IRAM. + +- ISR handlers must always be placed into IRAM. Furthermore, ISR handlers may only call functions placed into IRAM or functions present in ROM. *Note 1:* all FreeRTOS APIs are currently placed into IRAM, so are safe to call from ISR handlers. *Note 1:* all constant data used by ISR handlers and functions called from ISR handlers (including, but not limited to, ``const char`` arrays), must be placed into DRAM using ``DRAM_ATTR``. + +- Some timing critical code may be placed into IRAM to reduce the penalty associated with loading the code from flash. ESP32 reads code and data from flash via a 32 kB cache. In some cases, placing a function into IRAM may reduce delays caused by a cache miss. + +IROM (code executed from Flash) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a function is not explicitly placed into IRAM or RTC memory, it is placed into flash. The mechanism by which Flash MMU is used to allow code execution from flash is described in the Technical Reference Manual. ESP-IDF places the code which should be executed from flash starting from the beginning of ``0x400D0000 — 0x40400000`` region. Upon startup, second stage bootloader initializes Flash MMU to map the location in flash where code is located into the beginning of this region. Access to this region is transparently cached using two 32kB blocks in ``0x40070000`` — ``0x40080000`` range. + +Note that the code outside ``0x40000000 — 0x40400000`` region may not be reachable with Window ABI ``CALLx`` instructions, so special care is required if ``0x40400000 — 0x40800000`` or ``0x40800000 — 0x40C00000`` regions are used by the application. ESP-IDF doesn't use these regions by default. + +RTC fast memory +^^^^^^^^^^^^^^^ + +The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in deep-sleep-stub_. + +DRAM (data RAM) +^^^^^^^^^^^^^^^ + +Non-constant static data and zero-initialized data is placed by the linker into 200 kB ``0x3FFB0000 — 0x3FFF0000`` region. Note that this region is reduced by 64kB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 kB or 32kB if trace memory is used. All space which is left in this region after placing static data there is used for the runtime heap. + +Constant data may also be placed into DRAM, for example if it is used in an ISR handler (see notes in IRAM section above). To do that, ``DRAM_ATTR`` define can be used:: + + DRAM_ATTR const char[] format_string = "%p %x"; + char buffer[64]; + sprintf(buffer, format_string, ptr, val); + +Needless to say, it is not advised to use ``printf`` and other output functions in ISR handlers. For debugging purposes, use ``ESP_EARLY_LOGx`` macros when logging from ISR handlers. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case. + +DROM (data stored in Flash) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, constant data is placed by the linker into a 4 MB region (``0x3F400000 — 0x3F800000``) which is used to access external flash memory via Flash MMU and cache. Exceptions to this are literal constants which are embedded by the compiler into application code. + +RTC slow memory +^^^^^^^^^^^^^^^ + +Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check the detailed description in deep-sleep-stub_. + + +.. _deep-sleep-stub: deep-sleep-stub.rst +.. _partition-tables: partition-tables.rst + + diff --git a/docs/index.rst b/docs/index.rst index 1ca6e28eec..fb5e0c7c9e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,6 +28,7 @@ Contents: :caption: What Else? :maxdepth: 1 + General Notes partition-tables build_system openocd From ca202cbb4605f97bedd884b3acd807e6b4102797 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 16 Nov 2016 21:33:04 +0800 Subject: [PATCH 098/131] docs: fix broken links, formatting, add SPI flash and partition APIs --- docs/Doxyfile | 8 ++++- docs/api/nvs_flash.rst | 5 ++- docs/api/spi_flash.rst | 67 +++++++++++++++++++++++++++++++++++ docs/doxygen_xml_to_rst.xslt | 56 +++++++++++++++++++++++++++++ docs/general-notes.rst | 10 +++--- docs/index.rst | 2 +- docs/security/secure-boot.rst | 12 +++---- 7 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 docs/api/spi_flash.rst create mode 100644 docs/doxygen_xml_to_rst.xslt diff --git a/docs/Doxyfile b/docs/Doxyfile index 6ff4c45860..a46f014a5b 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,12 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h \ + ../components/driver/include/driver \ + ../components/bt/include \ + ../components/nvs_flash/include \ + ../components/log/include \ + ../components/vfs/include \ + ../components/spi_flash/include WARN_NO_PARAMDOC = YES diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst index 0768fa5597..5d36343552 100644 --- a/docs/api/nvs_flash.rst +++ b/docs/api/nvs_flash.rst @@ -3,7 +3,10 @@ Application Example ------------------- -`Instructions `_ +Two examples are provided in ESP-IDF examples directory: + +- `07_nvs_rw_value `_ demostrates how to read and write integer values +- `08_nvs_rw_blob `_ demostrates how to read and write variable length binary values API Reference ------------- diff --git a/docs/api/spi_flash.rst b/docs/api/spi_flash.rst new file mode 100644 index 0000000000..5903c0902d --- /dev/null +++ b/docs/api/spi_flash.rst @@ -0,0 +1,67 @@ +.. include:: ../../components/spi_flash/README.rst + +Application Example +------------------- + +`Instructions`_ + +.. _Instructions: template.html + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `spi_flash/include/esp_spi_flash.h `_ + * `spi_flash/include/esp_partition.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: ESP_ERR_FLASH_BASE +.. doxygendefine:: ESP_ERR_FLASH_OP_FAIL +.. doxygendefine:: ESP_ERR_FLASH_OP_TIMEOUT +.. doxygendefine:: SPI_FLASH_SEC_SIZE +.. doxygendefine:: ESP_PARTITION_SUBTYPE_OTA + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: spi_flash_mmap_handle_t +.. doxygentypedef:: esp_partition_iterator_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: spi_flash_mmap_memory_t +.. doxygenenum:: esp_partition_type_t +.. doxygenenum:: esp_partition_subtype_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_partition_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: spi_flash_init +.. doxygenfunction:: spi_flash_get_chip_size +.. doxygenfunction:: spi_flash_erase_sector +.. doxygenfunction:: spi_flash_erase_range +.. doxygenfunction:: spi_flash_write +.. doxygenfunction:: spi_flash_read +.. doxygenfunction:: spi_flash_mmap +.. doxygenfunction:: spi_flash_munmap +.. doxygenfunction:: spi_flash_mmap_dump +.. doxygenfunction:: esp_partition_find +.. doxygenfunction:: esp_partition_find_first +.. doxygenfunction:: esp_partition_get +.. doxygenfunction:: esp_partition_next +.. doxygenfunction:: esp_partition_iterator_release +.. doxygenfunction:: esp_partition_read +.. doxygenfunction:: esp_partition_write +.. doxygenfunction:: esp_partition_erase_range +.. doxygenfunction:: esp_partition_mmap + diff --git a/docs/doxygen_xml_to_rst.xslt b/docs/doxygen_xml_to_rst.xslt new file mode 100644 index 0000000000..c4570a9df6 --- /dev/null +++ b/docs/doxygen_xml_to_rst.xslt @@ -0,0 +1,56 @@ + + + + + + + + + Macros + ^^^^^^ + + .. doxygendefine:: + + + + + + Type Definitions + ^^^^^^^^^^^^^^^^ + + .. doxygentypedef:: + + + + + + Enumerations + ^^^^^^^^^^^^ + + .. doxygenenum:: + + + + + + + Structures + ^^^^^^^^^^ + + .. doxygenstruct:: + + + + + + Functions + ^^^^^^^^^ + + .. doxygenfunction:: + + + + + + + diff --git a/docs/general-notes.rst b/docs/general-notes.rst index 519a03b287..a683467007 100644 --- a/docs/general-notes.rst +++ b/docs/general-notes.rst @@ -21,7 +21,7 @@ After SoC reset, PRO CPU will start running immediately, executing reset vector Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place: -1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see deep sleep documentation for this: deep-sleep-stub_. +1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see :doc:`deep sleep ` documentation for this. 2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset. @@ -38,7 +38,7 @@ In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the seco When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header. -Second stage bootloader reads the partition table found at offset 0x8000. For more information about partition tables, see partition-tables_. It finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. +Second stage bootloader reads the partition table found at offset 0x8000. See :doc:`partition tables ` documentation for more information. The bootloader finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header. @@ -95,7 +95,7 @@ Note that the code outside ``0x40000000 — 0x40400000`` region may not be reach RTC fast memory ^^^^^^^^^^^^^^^ -The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in deep-sleep-stub_. +The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in :doc:`deep sleep ` documentation. DRAM (data RAM) ^^^^^^^^^^^^^^^ @@ -118,10 +118,8 @@ By default, constant data is placed by the linker into a 4 MB region (``0x3F4000 RTC slow memory ^^^^^^^^^^^^^^^ -Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check the detailed description in deep-sleep-stub_. +Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check detailed description in :doc:`deep sleep ` documentation. -.. _deep-sleep-stub: deep-sleep-stub.rst -.. _partition-tables: partition-tables.rst diff --git a/docs/index.rst b/docs/index.rst index fb5e0c7c9e..f777d10caf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -97,7 +97,7 @@ Contents: api/gpio api/uart api/ledc - + SPI Flash and Partition APIs Logging Non-Volatile Storage Virtual Filesystem diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index bdc1b71699..b352b3964c 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -14,7 +14,7 @@ Background - Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. -- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. +- To understand the secure boot process, first familiarise yourself with the standard :doc:`ESP-IDF boot process <../general-notes>`. - Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship. @@ -30,10 +30,11 @@ This is a high level overview of the secure boot process. Step by step instructi 2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader follows the following process to enable secure boot: - - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - - The secure digest is flashed at offset 0x0 in the flash. - - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) + + - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - The secure digest is flashed at offset 0x0 in the flash. + - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) 4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. @@ -175,5 +176,4 @@ Deterministic ECDSA as specified by `RFC6979`. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. -.. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 From 13d47343992a861de7e04ae8191a630ee5b21016 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 16 Nov 2016 01:35:09 +0800 Subject: [PATCH 099/131] docs: fix Doxygen warnings, fail CI build on Doxygen warnings --- .gitlab-ci.yml | 3 + components/bt/include/bt.h | 7 +- components/driver/include/driver/gpio.h | 6 + components/driver/include/driver/ledc.h | 6 + components/driver/include/driver/uart.h | 155 +++---- components/esp32/include/esp_wifi.h | 433 +++++++++---------- components/nvs_flash/include/nvs.h | 107 ++++- components/spi_flash/include/esp_partition.h | 95 ++-- components/spi_flash/include/esp_spi_flash.h | 6 +- components/vfs/include/esp_vfs.h | 10 +- docs/Doxyfile | 3 +- 11 files changed, 444 insertions(+), 387 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..1352bd6c93 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,6 +118,9 @@ build_docs: - build_docs script: - cd docs + - doxygen + # If not building master branch, and there are Doxygen warnings, print them and bail out + - test "${CI_BUILD_REF_NAME}" = "master" || test $(cat doxygen-warning-log.txt | wc -l) -eq 0 || ( echo "Doxygen pass had some warnings:" && cat doxygen-warning-log.txt && false ) - make html artifacts: paths: diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index f476334b12..d7e0496c55 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -32,13 +32,10 @@ void bt_controller_init(void); /** @brief vhci_host_callback * used for vhci call host function to notify what host need to do - * - * notify_host_send_available: notify host can send packet to controller - * notify_host_recv: notify host that controller has packet send to host */ typedef struct vhci_host_callback { - void (*notify_host_send_available)(void); - int (*notify_host_recv)(uint8_t *data, uint16_t len); + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ } vhci_host_callback_t; /** @brief API_vhci_host_check_send_available diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 03eb7588f8..de34ac4e35 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -117,6 +117,9 @@ extern const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT]; #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num < GPIO_PIN_COUNT && GPIO_PIN_MUX_REG[gpio_num] != 0)) //to decide whether it is a valid GPIO number #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((GPIO_IS_VALID_GPIO(gpio_num)) && (gpio_num < 34)) //to decide whether it can be a valid GPIO number of output mode +/** + * @brief Pullup/pulldown information for a single GPIO pad + */ typedef struct { uint32_t reg; /*!< Register to modify to enable or disable pullups or pulldowns */ uint32_t pu; /*!< Bit to set or clear in the above register to enable or disable the pullup, respectively */ @@ -208,6 +211,9 @@ typedef enum { GPIO_PULLDOWN_ENABLE = 0x1, /*!< Enable GPIO pull-down resistor */ } gpio_pulldown_t; +/** + * @brief Configuration parameters of GPIO pad for gpio_config function + */ typedef struct { uint64_t pin_bit_mask; /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */ gpio_mode_t mode; /*!< GPIO mode: set input/output mode */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 317a599fbc..e07787b2b1 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -78,6 +78,9 @@ typedef enum { LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; +/** + * @brief Configuration parameters of LEDC channel for ledc_channel_config function + */ typedef struct { int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16*/ ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ @@ -87,6 +90,9 @@ typedef struct { uint32_t duty; /*!< LEDC channel duty, the duty range is [0, (2**bit_num) - 1], */ } ledc_channel_config_t; +/** + * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function + */ typedef struct { ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ ledc_timer_bit_t bit_num; /*!< LEDC channel duty depth*/ diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 749cd65648..905c288260 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -32,11 +32,11 @@ extern "C" { #include "freertos/ringbuf.h" #include -#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */ -#define UART_INTR_MASK 0x1ff -#define UART_LINE_INV_MASK (0x3f << 19) -#define UART_BITRATE_MAX 5000000 -#define UART_PIN_NO_CHANGE (-1) +#define UART_FIFO_LEN (128) /*!< Length of the hardware FIFO buffers */ +#define UART_INTR_MASK 0x1ff /*!< mask of all UART interrupts */ +#define UART_LINE_INV_MASK (0x3f << 19) /*!< TBD */ +#define UART_BITRATE_MAX 5000000 /*!< Max bit rate supported by UART */ +#define UART_PIN_NO_CHANGE (-1) /*!< Constant for uart_set_pin function which indicates that UART pin should not be changed */ #define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/ #define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/ @@ -44,6 +44,9 @@ extern "C" { #define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/ #define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/ +/** + * @brief UART word length constants + */ typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ @@ -52,6 +55,9 @@ typedef enum { UART_DATA_BITS_MAX = 0X4, } uart_word_length_t; +/** + * @brief UART stop bits number + */ typedef enum { UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/ @@ -59,6 +65,9 @@ typedef enum { UART_STOP_BITS_MAX = 0x4, } uart_stop_bits_t; +/** + * @brief UART peripheral number + */ typedef enum { UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/ UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/ @@ -66,12 +75,18 @@ typedef enum { UART_NUM_MAX, } uart_port_t; +/** + * @brief UART parity constants + */ typedef enum { UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ UART_PARITY_EVEN = 0x2, /*!< Enable UART even parity*/ UART_PARITY_ODD = 0x3 /*!< Enable UART odd parity*/ } uart_parity_t; +/** + * @brief UART hardware flow control modes + */ typedef enum { UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/ UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/ @@ -80,6 +95,9 @@ typedef enum { UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; +/** + * @brief UART configuration parameters for uart_param_config function + */ typedef struct { int baud_rate; /*!< UART baudrate*/ uart_word_length_t data_bits; /*!< UART byte size*/ @@ -89,6 +107,9 @@ typedef struct { uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/ } uart_config_t; +/** + * @brief UART interrupt configuration parameters for uart_intr_config function + */ typedef struct { uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/ uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/ @@ -96,6 +117,9 @@ typedef struct { uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/ } uart_intr_config_t; +/** + * @brief UART event types used in the ringbuffer + */ typedef enum { UART_DATA, /*!< UART data event*/ UART_BREAK, /*!< UART break event*/ @@ -107,6 +131,9 @@ typedef enum { UART_EVENT_MAX, /*!< UART event max index*/ } uart_event_type_t; +/** + * @brief Event structure used in UART event queue + */ typedef struct { uart_event_type_t type; /*!< UART event type */ size_t size; /*!< UART data size for UART_DATA event*/ @@ -116,7 +143,6 @@ typedef struct { * @brief Set UART data bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param data_bit UART data bits * * @return @@ -129,7 +155,6 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit * @brief Get UART data bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param data_bit Pointer to accept value of UART data bits. * * @return @@ -142,7 +167,6 @@ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bi * @brief Set UART stop bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param bit_num UART stop bits * * @return @@ -155,7 +179,6 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t bit_num); * @brief Set UART stop bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param stop_bit Pointer to accept value of UART stop bits. * * @return @@ -168,7 +191,6 @@ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); * @brief Set UART parity. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param parity_mode the enum of uart parity configuration * * @return @@ -181,7 +203,6 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode); * @brief Get UART parity mode. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param parity_mode Pointer to accept value of UART parity mode. * * @return @@ -195,7 +216,6 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); * @brief Set UART baud rate. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param baud_rate UART baud-rate. * * @return @@ -208,7 +228,6 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate); * @brief Get UART bit-rate. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param baudrate Pointer to accept value of UART baud rate * * @return @@ -222,10 +241,8 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * @brief Set UART line inverse mode * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @param Inverse_mask Choose the wires that need to be inversed. - * - * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) + * @param inverse_mask Choose the wires that need to be inversed. + * Inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR operation. * * @return * - ESP_OK Success @@ -237,12 +254,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask); * @brief Set hardware flow control. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param flow_ctrl Hardware flow control mode - * - * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) - * - * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN). + * Only when UART_HW_FLOWCTRL_RTS is set, will the rx_thresh value be set. * * @return * - ESP_OK Success @@ -254,7 +268,6 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow * @brief Get hardware flow control mode * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param flow_ctrl Option for different flow control mode. * * @return @@ -267,10 +280,8 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo * @brief Clear UART interrupt status * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param clr_mask Bit mask of the status that to be cleared. - * - * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) + * enable_mask should be chosen from the fields of register UART_INT_CLR_REG. * * @return * - ESP_OK Success @@ -282,10 +293,8 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); * @brief Set UART interrupt enable * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param enable_mask Bit mask of the enable bits. - * - * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * enable_mask should be chosen from the fields of register UART_INT_ENA_REG. * * @return * - ESP_OK Success @@ -297,10 +306,8 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); * @brief Clear UART interrupt enable bits * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param disable_mask Bit mask of the disable bits. - * - * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * disable_mask should be chosen from the fields of register UART_INT_ENA_REG. * * @return * - ESP_OK Success @@ -346,9 +353,7 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param enable 1: enable; 0: disable - * * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return @@ -358,19 +363,17 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** -* @brief register UART interrupt handler(ISR). -* @note - * UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. + * @brief register UART interrupt handler(ISR). + * + * @note UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * * @param fn Interrupt handler function. - * @attention - * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * @param arg parameter for handler function * * @return @@ -382,18 +385,13 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* /** * @brief Set UART pin number * - * @note - * Internal signal can be output to multiple GPIO pads - * Only one GPIO pad can connect with input signal + * @note Internal signal can be output to multiple GPIO pads. + * Only one GPIO pad can connect with input signal. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return @@ -407,7 +405,6 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * UART rx hardware flow control should not be set. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return @@ -420,7 +417,6 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); * @brief UART set DTR level (before inverse) * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param level 1: DTR output low; 0: DTR output high * * @return @@ -433,7 +429,6 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * @brief UART parameter configure * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param uart_config UART parameter settings * * @return @@ -446,7 +441,6 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf * @brief UART interrupt configure * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param intr_conf UART interrupt settings * * @return @@ -458,22 +452,16 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ /** * @brief Install UART driver. * - * UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param rx_buffer_size UART RX ring buffer size - * * @param tx_buffer_size UART TX ring buffer size. - * - * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. - * + * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return @@ -497,7 +485,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @brief Wait UART TX FIFO empty * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -508,15 +495,13 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** - * @brief Send data to the UART port from a given buffer and length, - * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. - * @note - * This function should only be used when UART TX buffer is not enabled. + * @brief Send data to the UART port from a given buffer and length. + * + * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * @note This function should only be used when UART TX buffer is not enabled. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param buffer data buffer address - * * @param len data length to send * * @return @@ -528,16 +513,14 @@ int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, * - * If parameter tx_buffer_size is set to zero: - * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. + * If parameter tx_buffer_size is set to zero: + * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * - * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, - * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param src data buffer address - * * @param size data length to send * * @return @@ -549,20 +532,17 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, * - * If parameter tx_buffer_size is set to zero: - * This function will not return until all the data and the break signal have been sent out. - * After all data send out, send a break signal. + * If parameter tx_buffer_size is set to zero: + * This function will not return until all the data and the break signal have been sent out. + * After all data send out, send a break signal. * - * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, - * then, UART ISR will move data from ring buffer to TX FIFO gradually. - * After all data send out, send a break signal. + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * After all data send out, send a break signal. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param src data buffer address - * * @param size data length to send - * * @param brk_len break signal length (unit: time of one data bit at current_baudrate) * * @return @@ -576,11 +556,8 @@ int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t si * @brief UART read bytes from UART buffer * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param buf pointer to the buffer. - * * @param length data length - * * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 88ea0d9a65..65a91929dd 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -88,6 +88,9 @@ extern "C" { #define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Passord is invalid */ #define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 11) /*!< Timeout error */ +/** + * @brief WiFi stack configuration parameters passed to esp_wifi_init call. + */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ } wifi_init_config_t; @@ -108,12 +111,12 @@ typedef struct { * will post station connected event to this queue. If the queue is not initialized, WiFi * will not post any events * - * @param wifi_init_config_t *config : provide WiFi init configuration + * @param config provide WiFi init configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NO_MEM : out of memory - * - others : refer to error code esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NO_MEM: out of memory + * - others: refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); @@ -123,7 +126,7 @@ esp_err_t esp_wifi_init(wifi_init_config_t *config); * * @attention 1. This API should be called if you want to remove WiFi driver from the system * - * @return ESP_OK : succeed + * @return ESP_OK: succeed */ esp_err_t esp_wifi_deinit(void); @@ -133,25 +136,25 @@ esp_err_t esp_wifi_deinit(void); * Set the WiFi operating mode as station, soft-AP or station+soft-AP, * The default mode is soft-AP mode. * - * @param wifi_mode_t mode : WiFi operating modes: + * @param mode WiFi operating mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_mode(wifi_mode_t mode); /** * @brief Get current operating mode of WiFi * - * @param wifi_mode_t *mode : store current WiFi mode + * @param[out] mode store current WiFi mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); @@ -161,29 +164,25 @@ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); * If mode is WIFI_MODE_AP, it create soft-AP control block and start soft-AP * If mode is WIFI_MODE_APSTA, it create soft-AP and station control block and start soft-AP and station * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory - * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong - * - ESP_ERR_WIFI_FAIL : other WiFi internal errors + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory + * - ESP_ERR_WIFI_CONN: WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_start(void); /** * @brief Stop WiFi - If mode is WIFI_MODE_STA, it stop station and free station control block + * If mode is WIFI_MODE_STA, it stop station and free station control block * If mode is WIFI_MODE_AP, it stop soft-AP and free soft-AP control block * If mode is WIFI_MODE_APSTA, it stop station/soft-AP and free station/soft-AP control block * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_stop(void); @@ -193,52 +192,47 @@ esp_err_t esp_wifi_stop(void); * @attention 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode * @attention 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong - * - ESP_ERR_WIFI_SSID : SSID of AP which station connects is invalid + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_CONN: WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_SSID: SSID of AP which station connects is invalid */ esp_err_t esp_wifi_connect(void); /** * @brief Disconnect the ESP32 WiFi station from the AP. * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_FAIL : other WiFi internal errors + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_disconnect(void); /** * @brief Currently this API is just an stub API * - * @param null - * + * @return - * - ESP_OK : succeed - * - others : fail + * - ESP_OK: succeed + * - others: fail */ esp_err_t esp_wifi_clear_fast_connect(void); /** * @brief deauthenticate all stations or associated id equals to aid * - * @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid + * @param aid when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong */ esp_err_t esp_wifi_deauth_sta(uint16_t aid); @@ -249,88 +243,87 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * will be freed in esp_wifi_get_ap_list, so generally, call esp_wifi_get_ap_list to cause * the memory to be freed once the scan is done * - * @param struct scan_config *config : configuration of scanning - * @param bool block : if block is true, this API will block the caller until the scan is done, otherwise + * @param config configuration of scanning + * @param block if block is true, this API will block the caller until the scan is done, otherwise * it will return immediately * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_TIMEOUT : blocking scan is timeout - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_TIMEOUT: blocking scan is timeout + * - others: refer to error code in esp_err.h */ -esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf, bool block); +esp_err_t esp_wifi_scan_start(wifi_scan_config_t *config, bool block); /** * @brief Stop the scan in process * - * @param null * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start */ esp_err_t esp_wifi_scan_stop(void); /** * @brief Get number of APs found in last scan * - * @param uint16_t *number : store number of APIs found in last scan + * @param[out] number store number of APIs found in last scan * - * @attention This API can only be called when the scan is completed, otherwise it may get wrong value + * @attention This API can only be called when the scan is completed, otherwise it may get wrong value. * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); /** * @brief Get AP list found in last scan * - * @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store - the actual AP number this API returns - * @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs + * @param[inout] number As input param, it stores max AP number ap_records can hold. + * As output param, it receives the actual AP number this API returns. + * @param ap_records wifi_ap_record_t array to hold the found APs * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory */ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); /** - * @brief Get information of AP associated with ESP32 station + * @brief Get information of AP which the ESP32 station is associated with * - * @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP + * @param ap_info the wifi_ap_record_t to hold AP information * * @return - * - ESP_OK : succeed - * - others : fail + * - ESP_OK: succeed + * - others: fail */ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); /** * @brief Set current power save type * - * @param wifi_ps_type_t type : power save type + * @param type power save type * - * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet + * @return ESP_ERR_WIFI_NOT_SUPPORT: not supported yet */ esp_err_t esp_wifi_set_ps(wifi_ps_type_t type); /** * @brief Get current power save type * - * @param wifi_ps_type_t *type : store current power save type + * @param[out] type: store current power save type * - * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet + * @return ESP_ERR_WIFI_NOT_SUPPORT: not supported yet */ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); @@ -340,47 +333,47 @@ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); * * @attention Currently we only support 802.11b or 802.11bg or 802.11bgn mode * - * @param wifi_interface_t ifx : interfaces - * @param uint8_t protocol : WiFi protocol bitmap + * @param ifx interfaces + * @param protocol_bitmap WiFi protocol bitmap * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - others : refer to erro code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap); /** - * @brief Get the current protocol bitmap of specified ifx + * @brief Get the current protocol bitmap of the specified interface * - * @param wifi_interface_t ifx : interfaces - * @param uint8_t protocol : store current WiFi protocol bitmap of interface ifx + * @param ifx interface + * @param[out] protocol_bitmap store current WiFi protocol bitmap of interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap); /** * @brief Set the bandwidth of ESP32 specified interface * - * @attention 1. API return false if try to configure a interface that is not enable + * @attention 1. API return false if try to configure an interface that is not enabled * @attention 2. WIFI_BW_HT40 is supported only when the interface support 11N * - * @param wifi_interface_t ifx : interface to be configured - * @param wifi_bandwidth_t bw : bandwidth + * @param ifx interface to be configured + * @param bw bandwidth * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); @@ -389,45 +382,45 @@ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); * * @attention 1. API return false if try to get a interface that is not enable * - * @param wifi_interface_t ifx : interface to be configured - * @param wifi_bandwidth_t *bw : store bandwidth of interface ifx + * @param ifx interface to be configured + * @param[out] bw store bandwidth of interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); /** - * @brief Set primary/second channel of ESP32 + * @brief Set primary/secondary channel of ESP32 * * @attention 1. This is a special API for sniffer * - * @param uint8_t primary : for HT20, primary is the channel number, for HT40, primary is the primary channel - * @param wifi_second_chan_t second : for HT20, second is ignored, for HT40, second is the second channel + * @param primary for HT20, primary is the channel number, for HT40, primary is the primary channel + * @param second for HT20, second is ignored, for HT40, second is the second channel * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second); /** - * @brief Get the primary/second channel of ESP32 + * @brief Get the primary/secondary channel of ESP32 * * @attention 1. API return false if try to get a interface that is not enable * - * @param uint8_t *primary : store current primary channel - * @param wifi_second_chan_t *second : store current second channel + * @param primary store current primary channel + * @param[out] second store current second channel * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); @@ -435,25 +428,25 @@ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); * @brief Set country code * The default value is WIFI_COUNTRY_CN * - * @param wifi_country_t country : country type + * @param country country type * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_country(wifi_country_t country); /** * @brief Get country code * - * @param wifi_country_t country : store current country + * @param country store current country * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_country(wifi_country_t *country); @@ -462,43 +455,44 @@ esp_err_t esp_wifi_get_country(wifi_country_t *country); * * @attention 1. This API can only be called when the interface is disabled * @attention 2. ESP32 soft-AP and station have different MAC addresses, do not set them to be the same. - * - The bit0 of the first byte of ESP32 MAC address can not be 1. For example, the MAC address + * @attention 3. The bit 0 of the first byte of ESP32 MAC address can not be 1. For example, the MAC address * can set to be "1a:XX:XX:XX:XX:XX", but can not be "15:XX:XX:XX:XX:XX". * - * @param wifi_interface_t ifx : interface - * @param uint8 mac[6]: the MAC address. + * @param ifx interface + * @param mac the MAC address * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_MAC : invalid mac address - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_MAC: invalid mac address + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, uint8_t mac[6]); /** * @brief Get mac of specified interface * - * @param uint8_t mac[6] : store mac of this interface ifx + * @param ifx interface + * @param[out] mac store mac of the interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface */ esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); /** * @brief The RX callback function in the promiscuous mode. * - * Each time a packet is received, the callback function will be called. + * Each time a packet is received, the callback function will be called. * - * @param void *buf : the data received - * @param uint16_t len : data length + * @param buf the data received + * @param len data length * * @return none */ @@ -507,36 +501,36 @@ typedef void (* wifi_promiscuous_cb_t)(void *buf, uint16_t len); /** * @brief Register the RX callback function in the promiscuous mode. * - * Each time a packet is received, the registered callback function will be called. + * Each time a packet is received, the registered callback function will be called. * - * @param wifi_promiscuous_cb_t cb : callback + * @param cb callback * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); /** * @brief Enable the promiscuous mode. * - * @param bool promiscuous : false - disable / true - enable + * @param en false - disable, true - enable * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous(bool en); /** * @brief Get the promiscuous mode. * - * @param bool *enable : store the current status of promiscuous mode + * @param[out] en store the current status of promiscuous mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_promiscuous(bool *en); @@ -548,32 +542,32 @@ esp_err_t esp_wifi_get_promiscuous(bool *en); * @attention 3. ESP32 is limited to only one channel, so when in the soft-AP+station mode, the soft-AP will adjust its channel automatically to be the same as * the channel of the ESP32 station. * - * @param wifi_interface_t ifx : interface - * @param wifi_config_t *conf : station or soft-AP configuration + * @param ifx interface + * @param conf station or soft-AP configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_MODE : invalid mode - * - ESP_ERR_WIFI_PASSWORD : invalid password - * - ESP_ERR_WIFI_NVS : WiFi internal NVS error - * - others : refer to the erro code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_MODE: invalid mode + * - ESP_ERR_WIFI_PASSWORD: invalid password + * - ESP_ERR_WIFI_NVS: WiFi internal NVS error + * - others: refer to the erro code in esp_err.h */ esp_err_t esp_wifi_set_config(wifi_interface_t ifx, wifi_config_t *conf); /** * @brief Get configuration of specified interface * - * @param wifi_interface_t ifx : interface - * @param wifi_config_t *conf : station or soft-AP configuration + * @param ifx interface + * @param[out] conf station or soft-AP configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface */ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); @@ -582,14 +576,14 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @attention SSC only API * - * @param wifi_sta_list_t *sta: station list + * @param[out] sta station list * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong - * - ESP_ERR_WIFI_CONN : WiFi internal error, the station/soft-AP control block is invalid + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong + * - ESP_ERR_WIFI_CONN: WiFi internal error, the station/soft-AP control block is invalid */ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); @@ -599,12 +593,12 @@ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); * * @attention 1. The default value is WIFI_STORAGE_FLASH * - * @param wifi_storage_t storage : storage type + * @param storage : storage type * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_set_storage(wifi_storage_t storage); @@ -612,72 +606,63 @@ esp_err_t esp_wifi_set_storage(wifi_storage_t storage); * @brief Set auto connect * The default value is true * - * @param bool en : true - enable auto connect / false - disable auto connect + * @param en : true - enable auto connect / false - disable auto connect * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_MODE : WiFi internal error, the station/soft-AP control block is invalid - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_MODE: WiFi internal error, the station/soft-AP control block is invalid + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_auto_connect(bool en); /** * @brief Get the auto connect flag * - * @param bool *en : store current auto connect configuration + * @param[out] en store current auto connect configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_auto_connect(bool *en); /** * @brief Set vendor specific element * - * @param bool enable : enable or not - * @param wifi_vendor_ie_type_t type : 0 - WIFI_VND_IE_TYPE_BEACON - * 1 - WIFI_VND_IE_TYPE_PROBE_REQ - * 2 - WIFI_VND_IE_TYPE_PROBE_RESP - * 3 - WIFI_VND_IE_TYPE_ASSOC_REQ - * 4 - WIFI_VND_IE_TYPE_ASSOC_RESP - * @param wifi_vendor_ie_id_t idx : 0 - WIFI_VND_IE_ID_0 - 1 - WIFI_VND_IE_ID_1 - * @param uint8_t *vnd_ie : pointer to a vendor specific element + * @param enable enable or not + * @param type information element type + * @param idx information element index + * @param vnd_ie pointer to a vendor specific element * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory */ esp_err_t esp_wifi_set_vendor_ie(bool enable, wifi_vendor_ie_type_t type, wifi_vendor_ie_id_t idx, uint8_t *vnd_ie); /** * @brief Define function pointer for vendor specific element callback - * @param void *ctx : reserved - * @param wifi_vendor_ie_type_t type : 0 - WIFI_VND_IE_TYPE_BEACON - * 1 - WIFI_VND_IE_TYPE_PROBE_REQ - * 2 - WIFI_VND_IE_TYPE_PROBE_RESP - * 3 - WIFI_VND_IE_TYPE_ASSOC_REQ - * 4 - WIFI_VND_IE_TYPE_ASSOC_RESP - * @param const uint8_t sa[6] : source address - * @param const uint8_t *vnd_ie : pointer to a vendor specific element - * @param int rssi : received signal strength indication + * @param ctx reserved + * @param type information element type + * @param sa source address + * @param vnd_ie pointer to a vendor specific element + * @param rssi received signal strength indication */ typedef void (*esp_vendor_ie_cb_t) (void *ctx, wifi_vendor_ie_type_t type, const uint8_t sa[6], const uint8_t *vnd_ie, int rssi); /** * @brief Set vendor specific element callback * - * @param esp_vendor_ie_cb_t cb : callback function - * @param void *ctx : reserved + * @param cb callback function + * @param ctx reserved * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_vendor_ie_cb(esp_vendor_ie_cb_t cb, void *ctx); diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 8418959793..5f7a93a7b0 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -77,8 +77,9 @@ typedef enum { */ esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_handle); +/**@{*/ /** - * @brief nvs_set_X - set value for given key + * @brief set value for given key * * This family of functions set value for the key, given its name. Note that * actual storage will not be updated until nvs_commit function is called. @@ -89,7 +90,6 @@ esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_ha * implementation, but is guaranteed to be at least * 16 characters. Shouldn't be empty. * @param[in] value The value to set. - * @param[in] length For nvs_set_blob: length of binary value to set, in bytes. * * @return * - ESP_OK if value was set successfully @@ -112,10 +112,39 @@ esp_err_t nvs_set_u32 (nvs_handle handle, const char* key, uint32_t value); esp_err_t nvs_set_i64 (nvs_handle handle, const char* key, int64_t value); esp_err_t nvs_set_u64 (nvs_handle handle, const char* key, uint64_t value); esp_err_t nvs_set_str (nvs_handle handle, const char* key, const char* value); -esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length); +/**@}*/ /** - * @brief nvs_get_X - get value for given key + * @brief set variable length binary value for given key + * + * This family of functions set value for the key, given its name. Note that + * actual storage will not be updated until nvs_commit function is called. + * + * @param[in] handle Handle obtained from nvs_open function. + * Handles that were opened read only cannot be used. + * @param[in] key Key name. Maximal length is determined by the underlying + * implementation, but is guaranteed to be at least + * 16 characters. Shouldn't be empty. + * @param[in] value The value to set. + * @param[in] length length of binary value to set, in bytes. + * + * @return + * - ESP_OK if value was set successfully + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL + * - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only + * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints + * - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the + * underlying storage to save the value + * - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash + * write operation has failed. The value was written however, and + * update will be finished after re-initialization of nvs, provided that + * flash operation doesn't fail again. + */ +esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length); + +/**@{*/ +/** + * @brief get value for given key * * These functions retrieve value for the key, given its name. If key does not * exist, or the requested variable type doesn't match the type which was used @@ -125,7 +154,55 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * * All functions expect out_value to be a pointer to an already allocated variable * of the given type. - * Additionally, nvs_get_str and nvs_get_blob support WinAPI-style length queries. + * + * \code{c} + * // Example of using nvs_get_i32: + * int32_t max_buffer_size = 4096; // default value + * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); + * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); + * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still + * // have its default value. + * + * \endcode + * + * @param[in] handle Handle obtained from nvs_open function. + * @param[in] key Key name. Maximal length is determined by the underlying + * implementation, but is guaranteed to be at least + * 16 characters. Shouldn't be empty. + * @param out_value Pointer to the output value. + * May be NULL for nvs_get_str and nvs_get_blob, in this + * case required length will be returned in length argument. + * + * @return + * - ESP_OK if the value was retrieved successfully + * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL + * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints + * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data + */ +esp_err_t nvs_get_i8 (nvs_handle handle, const char* key, int8_t* out_value); +esp_err_t nvs_get_u8 (nvs_handle handle, const char* key, uint8_t* out_value); +esp_err_t nvs_get_i16 (nvs_handle handle, const char* key, int16_t* out_value); +esp_err_t nvs_get_u16 (nvs_handle handle, const char* key, uint16_t* out_value); +esp_err_t nvs_get_i32 (nvs_handle handle, const char* key, int32_t* out_value); +esp_err_t nvs_get_u32 (nvs_handle handle, const char* key, uint32_t* out_value); +esp_err_t nvs_get_i64 (nvs_handle handle, const char* key, int64_t* out_value); +esp_err_t nvs_get_u64 (nvs_handle handle, const char* key, uint64_t* out_value); +/**@}*/ + +/** + * @brief get value for given key + * + * These functions retrieve value for the key, given its name. If key does not + * exist, or the requested variable type doesn't match the type which was used + * when setting a value, an error is returned. + * + * In case of any error, out_value is not modified. + * + * All functions expect out_value to be a pointer to an already allocated variable + * of the given type. + * + * nvs_get_str and nvs_get_blob functions support WinAPI-style length queries. * To get the size necessary to store the value, call nvs_get_str or nvs_get_blob * with zero out_value and non-zero pointer to length. Variable pointed to * by length argument will be set to the required length. For nvs_get_str, @@ -136,13 +213,6 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * nvs_get/set_blob used for arbitrary data structures. * * \code{c} - * // Example of using nvs_get_i32: - * int32_t max_buffer_size = 4096; // default value - * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); - * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); - * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still - * // have its default value. - * * // Example (without error checking) of using nvs_get_str to get a string into dynamic array: * size_t required_size; * nvs_get_str(my_handle, "server_name", NULL, &required_size); @@ -163,8 +233,7 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * @param out_value Pointer to the output value. * May be NULL for nvs_get_str and nvs_get_blob, in this * case required length will be returned in length argument. - * @param[inout] length For nvs_get_str and nvs_get_blob, non-zero pointer - * to the variable holding the length of out_value. + * @param[inout] length A non-zero pointer to the variable holding the length of out_value. * In case out_value a zero, will be set to the length * required to hold the value. In case out_value is not * zero, will be set to the actual length of the value @@ -177,16 +246,10 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data */ -esp_err_t nvs_get_i8 (nvs_handle handle, const char* key, int8_t* out_value); -esp_err_t nvs_get_u8 (nvs_handle handle, const char* key, uint8_t* out_value); -esp_err_t nvs_get_i16 (nvs_handle handle, const char* key, int16_t* out_value); -esp_err_t nvs_get_u16 (nvs_handle handle, const char* key, uint16_t* out_value); -esp_err_t nvs_get_i32 (nvs_handle handle, const char* key, int32_t* out_value); -esp_err_t nvs_get_u32 (nvs_handle handle, const char* key, uint32_t* out_value); -esp_err_t nvs_get_i64 (nvs_handle handle, const char* key, int64_t* out_value); -esp_err_t nvs_get_u64 (nvs_handle handle, const char* key, uint64_t* out_value); +/**@{*/ esp_err_t nvs_get_str (nvs_handle handle, const char* key, char* out_value, size_t* length); esp_err_t nvs_get_blob(nvs_handle handle, const char* key, void* out_value, size_t* length); +/**@}*/ /** * @brief Erase key-value pair with given key name. diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index ae0185dcd7..13b803e10f 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -25,57 +25,78 @@ extern "C" { #endif +/** + * @file esp_partition.h + * @brief Partition APIs + */ + + +/** + * @brief Partition type + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ typedef enum { - ESP_PARTITION_TYPE_APP = 0x00, - ESP_PARTITION_TYPE_DATA = 0x01, - ESP_PARTITION_TYPE_FILESYSTEM = 0x02, + ESP_PARTITION_TYPE_APP = 0x00, //!< Application partition type + ESP_PARTITION_TYPE_DATA = 0x01, //!< Data partition type } esp_partition_type_t; +/** + * @brief Partition subtype + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ typedef enum { - ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, - ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, - ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, - ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, - ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, - ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, - ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, - ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, - ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, - ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, - ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, - ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, - ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10, - ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11, - ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12, - ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13, - ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14, - ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15, - ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, - ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, //!< Factory application partition + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, //!< Base for OTA partition subtypes + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, //!< OTA partition 0 + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, //!< OTA partition 1 + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, //!< OTA partition 2 + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, //!< OTA partition 3 + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, //!< OTA partition 4 + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, //!< OTA partition 5 + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, //!< OTA partition 6 + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, //!< OTA partition 7 + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, //!< OTA partition 8 + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, //!< OTA partition 9 + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10,//!< OTA partition 10 + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11,//!< OTA partition 11 + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12,//!< OTA partition 12 + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13 + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14 + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15 + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, //!< Max subtype of OTA partition + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition - ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, - ESP_PARTITION_SUBTYPE_DATA_RF = 0x01, - ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition + ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition - ESP_PARTITION_SUBTYPE_FILESYSTEM_ESPHTTPD = 0x00, - ESP_PARTITION_SUBTYPE_FILESYSTEM_FAT = 0x01, - ESP_PARTITION_SUBTYPE_FILESYSTEM_SPIFFS = 0x02, + ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition + ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition + ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition - ESP_PARTITION_SUBTYPE_ANY = 0xff, + ESP_PARTITION_SUBTYPE_ANY = 0xff, //!< Used to search for partitions with any subtype } esp_partition_subtype_t; +/** + * @brief Convenience macro to get esp_partition_subtype_t value for the i-th OTA partition + */ #define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) - +/** + * @brief Opaque partition iterator type + */ typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; +/** + * @brief partition information structure + */ typedef struct { - esp_partition_type_t type; - esp_partition_subtype_t subtype; - uint32_t address; - uint32_t size; - char label[17]; - bool encrypted; + esp_partition_type_t type; /*!< partition type (app/data) */ + esp_partition_subtype_t subtype; /*!< partition subtype */ + uint32_t address; /*!< starting address of the partition in flash */ + uint32_t size; /*!< size of the partition, in bytes */ + char label[17]; /*!< partition label, zero-terminated ASCII string */ + bool encrypted; /*!< flag is set to true if partition is encrypted */ } esp_partition_t; /** diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 840bbc4971..5d124da6b2 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -62,13 +62,13 @@ esp_err_t spi_flash_erase_sector(size_t sector); /** * @brief Erase a range of flash sectors * - * @param uint32_t start_address : Address where erase operation has to start. + * @param start_address Address where erase operation has to start. * Must be 4kB-aligned - * @param uint32_t size : Size of erased range, in bytes. Must be divisible by 4kB. + * @param size Size of erased range, in bytes. Must be divisible by 4kB. * * @return esp_err_t */ -esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); +esp_err_t spi_flash_erase_range(size_t start_address, size_t size); /** diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 2d9e52c5af..7dd273fb00 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -57,15 +57,15 @@ extern "C" { * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer * to esp_vfs_register function. * If the implementation doesn't use this extra argument, populate the - * members without _p suffix and set flags memeber to ESP_VFS_FLAG_DEFAULT. + * members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT. * * If the FS driver doesn't provide some of the functions, set corresponding * members to NULL. */ typedef struct { - int fd_offset; - int flags; + int fd_offset; /*!< file descriptor offset, determined by the FS driver */ + int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */ union { size_t (*write_p)(void* p, int fd, const void * data, size_t size); size_t (*write)(int fd, const void * data, size_t size); @@ -135,7 +135,7 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct * These functions are to be used in newlib syscall table. They will be called by * newlib when it needs to use any of the syscalls. */ - +/**@{*/ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode); ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); @@ -146,7 +146,7 @@ int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st); int esp_vfs_link(struct _reent *r, const char* n1, const char* n2); int esp_vfs_unlink(struct _reent *r, const char *path); int esp_vfs_rename(struct _reent *r, const char *src, const char *dst); - +/**@}*/ #ifdef __cplusplus diff --git a/docs/Doxyfile b/docs/Doxyfile index a46f014a5b..0e4549a10f 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -20,9 +20,8 @@ XML_OUTPUT = xml GENERATE_HTML = NO HAVE_DOT = NO GENERATE_LATEX = NO -GENERATE_MAN = NO +GENERATE_MAN = YES GENERATE_RTF = NO -QUIET = YES WARN_LOGFILE = "doxygen-warning-log.txt" From 4eeb2bc41e43c850d468ed9132c0202f0ddbf638 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 10:35:47 +1100 Subject: [PATCH 100/131] build system: Fix embedding files which are themselves generated by the build system Used by secure boot, which generates the secure boot signing key inside build/. --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 55a135158a..6f903f9fce 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -194,7 +194,7 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect define GenerateEmbedTarget $(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) $(summary) EMBED $$@ - $$(if $$(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ rm $$(notdir $$<) From 24b4c17ead152119736716347b29e9266462ab75 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:12:48 +1100 Subject: [PATCH 101/131] build system: Refactor SubmoduleCheck to work project-wide Required at project level because some components use header files in other components' submodules, and one component with a submodule (esptool.py) doesn't have or need a component.mk. --- components/bt/component.mk | 3 +- components/esp32/component.mk | 4 +-- components/esptool_py/Makefile.projbuild | 4 ++- docs/build_system.rst | 8 +++++ make/common.mk | 25 ---------------- make/component_wrapper.mk | 5 ++-- make/project.mk | 37 +++++++++++++++++++++--- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/components/bt/component.mk b/components/bt/component.mk index 91620ddc14..d6bde0c5b6 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -15,5 +15,4 @@ COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) -# automatically trigger a git submodule update if BT library is missing -$(eval $(call SubmoduleCheck,$(ALL_LIB_FILES),$(COMPONENT_PATH)/lib)) +COMPONENT_SUBMODULES += lib diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 040c686e5e..8059d157c5 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -17,9 +17,7 @@ COMPONENT_ADD_LDFLAGS := -lesp32 \ ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) -# automatically trigger a git submodule update -# if any libraries are missing -$(eval $(call SubmoduleCheck,$(ALL_LIB_FILES),$(COMPONENT_PATH)/lib)) +COMPONENT_SUBMODULES += lib # this is a hack to make sure the app is re-linked if the binary # libraries change or are updated. If they change, the main esp32 diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index aa3bb2bfa9..acbada7244 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -54,4 +54,6 @@ app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) -$(eval $(call SubmoduleCheck,$(ESPTOOLPY_SRC),$(COMPONENT_PATH)/esptool)) +# Submodules normally added in component.mk, but can be added +# at the project level as long as qualified path +COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool diff --git a/docs/build_system.rst b/docs/build_system.rst index 8168cb76ef..aa14cdda56 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -186,6 +186,14 @@ The following variables can be set inside ``component.mk`` to control build sett generates an include file which you then want to include in another component. Most components do not need to set this variable. +The following variable only works for components that are part of esp-idf itself: + +- ``COMPONENT_SUBMODULES``: Optional list of git submodule paths + (relative to COMPONENT_PATH) used by the component. These will be + checked (and initialised if necessary) by the build process. This + variable is ignored if the component is outside the IDF_PATH + directory. + Optional Component-Specific Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/make/common.mk b/make/common.mk index 6e4fa75ab5..4d40f7abc3 100644 --- a/make/common.mk +++ b/make/common.mk @@ -26,31 +26,6 @@ details := @true MAKEFLAGS += --silent endif -# Pseudo-target to check a git submodule has been properly initialised -# -# $(eval $(call SubmoduleCheck,FILENAMES,SUBMODULE_PATH)) to create a target that -# automatically runs 'git submodule update --init SUBMODULE_PATH' if any of -# the files in FILENAMES are missing, and fails if this is not possible. -# -# Will also print a WARNING if the submodule at SUBMODULE_PATH appears -# to require an update. -define SubmoduleCheck -$(1): - @echo "WARNING: Missing submodule $(2) for $$@..." - [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) - [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) - @echo "Attempting 'git submodule update --init' in esp-idf root directory..." - cd ${IDF_PATH} && git submodule update --init $(2) - -# Parse 'git submodule status' output for out-of-date submodule. -# Status output prefixes status line with '+' if the submodule commit doesn't match -ifneq ("$(shell cd ${IDF_PATH} && git submodule status $(2) | grep '^+')","") -$$(info WARNING: git submodule $2 may be out of date. Run 'git submodule update' to update.) -endif -endef - - - # General make utilities # convenience variable for printing an 80 asterisk wide separator line diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 6f903f9fce..005c996712 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -103,8 +103,8 @@ endef # component_project_vars.mk target for the component. This is used to # take component.mk variables COMPONENT_ADD_INCLUDEDIRS, -# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject those into -# the project make pass. +# COMPONENT_ADD_LDFLAGS, COMPONENT_DEPENDS and COMPONENT_SUBMODULES +# and inject those into the project make pass. # # The target here has no dependencies, as the parent target in # project.mk evaluates dependencies before calling down to here. See @@ -119,6 +119,7 @@ component_project_vars.mk:: @echo '# Automatically generated build file. Do not edit.' > $@ @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ @echo 'COMPONENT_LDFLAGS += $(call MakeVariablePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo 'COMPONENT_SUBMODULES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_SUBMODULES)))' >> $@ @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ diff --git a/make/project.mk b/make/project.mk index a081992804..870db55f97 100644 --- a/make/project.mk +++ b/make/project.mk @@ -10,7 +10,7 @@ # where this file is located. # -.PHONY: build-components menuconfig defconfig all build clean all_binaries +.PHONY: build-components menuconfig defconfig all build clean all_binaries check-submodules all: all_binaries # see below for recipe of 'all' target # @@ -94,13 +94,16 @@ COMPONENT_PATHS += $(abspath $(SRCDIRS)) # A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) -# Initialise a project-wide list of include dirs (COMPONENT_INCLUDES), -# and LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# Initialise project-wide variables which can be added to by +# each component. # # These variables are built up via the component_project_vars.mk # generated makefiles (one per component). +# +# See docs/build-system.rst for more details. COMPONENT_INCLUDES := COMPONENT_LDFLAGS := +COMPONENT_SUBMODULES := # COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles # for each component. @@ -289,7 +292,7 @@ endef define GenerateComponentTargets .PHONY: $(2)-build $(2)-clean -$(2)-build: +$(2)-build: check-submodules $(call ComponentMake,$(1),$(2)) build $(2)-clean: @@ -332,4 +335,30 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) config-clean: app-clean clean: config-clean +# phony target to check if any git submodule listed in COMPONENT_SUBMODULES are missing +# or out of date, and exit if so. Components can add paths to this variable. +# +# This only works for components inside IDF_PATH +check-submodules: +# Generate a target to check this submodule +# $(1) - submodule directory, relative to IDF_PATH +define GenerateSubmoduleCheckTarget +check-submodules: $(IDF_PATH)/$(1)/.git +$(IDF_PATH)/$(1)/.git: + @echo "WARNING: Missing submodule $(1)..." + [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) + [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule init $(1)' in esp-idf root directory."; exit 1) + @echo "Attempting 'git submodule update --init $(1)' in esp-idf root directory..." + cd ${IDF_PATH} && git submodule update --init $(1) + +# Parse 'git submodule status' output for out-of-date submodule. +# Status output prefixes status line with '+' if the submodule commit doesn't match +ifneq ("$(shell cd ${IDF_PATH} && git submodule status $(1) | grep '^+')","") +$$(info WARNING: git submodule $(1) may be out of date. Run 'git submodule update' to update.) +endif +endef + +# filter/subst in expression ensures all submodule paths begin with $(IDF_PATH), and then strips that prefix +# so the argument is suitable for use with 'git submodule' commands +$(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule)))) From 84635a160e0b6765e031c562c6a38f0d078c69f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:18:40 +1100 Subject: [PATCH 102/131] micro-ecc: Add submodule dependency Closes github #100 --- components/micro-ecc/component.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/micro-ecc/component.mk b/components/micro-ecc/component.mk index df73f7a3b2..8c569df597 100644 --- a/components/micro-ecc/component.mk +++ b/components/micro-ecc/component.mk @@ -4,3 +4,5 @@ COMPONENT_SRCDIRS := micro-ecc COMPONENT_OBJS := micro-ecc/uECC.o COMPONENT_ADD_INCLUDEDIRS := micro-ecc + +COMPONENT_SUBMODULES := micro-ecc From 872a481cf1c526263c0bf90afc0ebf7c25aa6a47 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:45:39 +1100 Subject: [PATCH 103/131] build system: When embedding binary files, fix re-generating on partial builds When embedding a generated file (ie secure boot public key data), the file was being re-generated each time. --- make/component_wrapper.mk | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 005c996712..2c073af761 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -189,18 +189,29 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # txt files are null-terminated before being embedded (otherwise # identical behaviour.) # -# Files are temporarily copied to the build directory before objcopy, -# because objcopy generates the symbol name from the full command line -# path to the input file. define GenerateEmbedTarget -$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) + +# copy the input file into the build dir (using a subdirectory +# in case the file already exists elsewhere in the build dir) +embed_bin/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_bin + cp $$< $$@ + +embed_txt/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_txt + cp $$< $$@ + echo -ne '\0' >> $$@ # null-terminate text files + +# messing about with the embed_X subdirectory then using 'cd' for objcopy is because the +# full path passed to OBJCOPY makes it into the name of the symbols in the .o file +$(1).$(2).o: embed_$(2)/$$(notdir $(1)) | $$(dir $(1)) $(summary) EMBED $$@ - $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir - $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output - $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ - rm $$(notdir $$<) + cd embed_$(2); $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) ../$$@ + +CLEAN_FILES += embed_$(2)/$$(notdir $(1)) endef +embed_txt embed_bin: + mkdir -p $@ + # generate targets to embed binary & text files $(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin))) From 78d084942670c1703af3da58d0fa91f07fd19953 Mon Sep 17 00:00:00 2001 From: Benjamin Vernoux Date: Fri, 28 Oct 2016 22:25:14 +0200 Subject: [PATCH 104/131] Simplified "Installing OpenOCD" with git clone --recursive --- docs/openocd.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/openocd.rst b/docs/openocd.rst index 2dcb55f0c5..642e3067e1 100644 --- a/docs/openocd.rst +++ b/docs/openocd.rst @@ -40,10 +40,8 @@ Installing OpenOCD The sources for the ESP32-enabled variant of OpenOCD are available from `Espressifs Github `_. To download the source, use the following commands:: - git clone https://github.com/espressif/openocd-esp32.git + git clone --recursive https://github.com/espressif/openocd-esp32.git cd openocd-esp32 - git submodule init - git submodule update For compilation of OpenOCD, please refer to the README, README.OSX and README.Windows file in the openocd-esp32 directory. You can skip the ``make install`` step if you want. From ceea97495f5070e50ba3b36de3b9f1a0afcf5226 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 10:22:20 +0800 Subject: [PATCH 105/131] lwip:refractor to the description about this menuconfig option --- components/lwip/Kconfig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 7661fe6cb7..bf7bff15b4 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -4,16 +4,17 @@ config L2_TO_L3_COPY bool "Enable copy between Layer2 and Layer3 packets" default 0 help - If this feature is enabled, then all traffic from layer2(WIFI Driver) - to layer3(LWIP stack) will make a copy, the layer2 buffer will be - freed and the copy will be sent to layer3. Please be notified that the - total layer2 receiving buffer is fixed and ESP32 currently supports 25 - layer2 receiving buffer, when layer2 buffer runs out of memory, then the - incoming packets will be dropped in hardware. The layer3 buffer is - allocated from the heap, so the total layer3 receiving buffer depends - on the available heap size, when heap runs out of memory, no copy will - be sent to layer3 and packet will be dropped in layer2. Please make sure - you fully understand the impact of this feature before enabling it. + If this feature is enabled, all traffic from layer2(WIFI Driver) will be + copied to a new buffer before sending it to layer3(LWIP stack), freeing + the layer2 buffer. + Please be notified that the total layer2 receiving buffer is fixed and + ESP32 currently supports 25 layer2 receiving buffer, when layer2 buffer + runs out of memory, then the incoming packets will be dropped in hardware. + The layer3 buffer is allocated from the heap, so the total layer3 receiving + buffer depends on the available heap size, when heap runs out of memory, + no copy will be sent to layer3 and packet will be dropped in layer2. + Please make sure you fully understand the impact of this feature before + enabling it. config LWIP_MAX_SOCKETS int "Max number of open sockets" From 5e428f21b6e07825ce041acc9a4b84bdeddb1dc9 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 10:53:00 +0800 Subject: [PATCH 106/131] lwip: default ESP_L2_TO_L3_COPY to 0 --- components/lwip/include/lwip/lwip/opt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 9a1d10afbf..c42f3cd735 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -3012,7 +3012,7 @@ * ESP_L2_TO_L3_COPY: enable memcpy when receiving packet from L2 */ #ifndef ESP_L2_TO_L3_COPY -#define ESP_L2_TO_L3_COPY 1 +#define ESP_L2_TO_L3_COPY 0 #endif #ifndef ESP_THREAD_SAFE_DEBUG From f12be7ebd8c5b0006a94c1ddaa492555ffc73d78 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:22:20 +0800 Subject: [PATCH 107/131] nvs_flash: adjust the nvs_flash start sector number Modify the nvs flash start sector from 6 to 9 --- components/nvs_flash/src/nvs_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index c1a910260e..f6c6c588aa 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -63,7 +63,7 @@ extern "C" void nvs_dump() extern "C" esp_err_t nvs_flash_init(void) { - return nvs_flash_init_custom(6, 3); + return nvs_flash_init_custom(9, 3); } extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) From 12dd886ee194004ce8f9e74c1b3329b3b40129be Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 18:03:55 +1100 Subject: [PATCH 108/131] build system: Use correct objcopy arguments for object format Avoid ambiguous argument error on some platforms Ref internal discussion !198 squash! build system: Use correct objcopy --input-target argument not --input --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 2c073af761..3018c18b55 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -180,7 +180,7 @@ $(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(sr ## Support for embedding binary files into the ELF as symbols -OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architecture xtensa --rename-section .data=.rodata.embedded +OBJCOPY_EMBED_ARGS := --input-target binary --output-target elf32-xtensa-le --binary-architecture xtensa --rename-section .data=.rodata.embedded # Generate pattern for embedding text or binary files into the app # $(1) is name of file (as relative path inside component) From 936b02216b49af02488d058e80c2b58dc771fd51 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:44:39 +0800 Subject: [PATCH 109/131] docs/nvs_flash: update the nvs flash offset comment according review --- components/nvs_flash/include/nvs_flash.h | 2 +- docs/partition-tables.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index 1cade0e956..d6e1990250 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -21,7 +21,7 @@ extern "C" { /** Initialise NVS flash storage with default flash sector layout Temporarily, this region is hardcoded as a 12KB (0x3000 byte) - region starting at 24KB (0x6000 byte) offset in flash. + region starting at 36KB (0x9000 byte) offset in flash. @return ESP_OK if flash was successfully initialised. */ diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 5f5911bd52..a1a46866ee 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -23,7 +23,7 @@ Known Issues The below design document outlines the goals for the partition table system. At the moment, only some features are used: - data partition types "rf" & "wifi" are unused and can be entirely omitted to save space. -- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x6000. +- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x9000. Once a full user API is in place for partition access, these limitations will be resolved and you'll be able to use the partition mechanism fully for storing data in flash. From d19d7107d34541bf6edc9d9e80daeff394904cf4 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:56:22 +0800 Subject: [PATCH 110/131] esp32: tw8706 softap join ap return wrong code In softap/null mode, esp_wifi_connect will return ESP_ERR_WIFI_MODE --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 41da160a5d..e188536a63 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 41da160a5dbf9e13b4fb51f31acf372f50c28270 +Subproject commit e188536a6315cc3ce4f1006ac3a4450faea6abc6 From d116adf260358180807045ad047dc95f302a2f52 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 12:09:08 +0800 Subject: [PATCH 111/131] Add documentation --- components/esp32/include/esp_int_wdt.h | 3 -- components/esp32/include/esp_task_wdt.h | 9 ---- docs/Doxyfile | 4 +- docs/api/wdts.rst | 72 +++++++++++++++++++++++++ docs/index.rst | 5 +- 5 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 docs/api/wdts.rst diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h index 4387400396..b32d0219fb 100644 --- a/components/esp32/include/esp_int_wdt.h +++ b/components/esp32/include/esp_int_wdt.h @@ -41,9 +41,6 @@ This uses the TIMERG1 WDT. * @brief Initialize the interrupt watchdog. This is called in the init code if * the interrupt watchdog is enabled in menuconfig. * - * @param null - * - * @return null */ void esp_int_wdt_init(); diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h index bbc4995674..eb77377009 100644 --- a/components/esp32/include/esp_task_wdt.h +++ b/components/esp32/include/esp_task_wdt.h @@ -42,9 +42,6 @@ This uses the TIMERG0 WDT. * @brief Initialize the task watchdog. This is called in the init code, if the * task watchdog is enabled in menuconfig. * - * @param null - * - * @return null */ void esp_task_wdt_init(); @@ -52,9 +49,6 @@ void esp_task_wdt_init(); * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling * task to keep feeding the watchdog until task_wdt_delete() is called. * - * @param null - * - * @return null */ void esp_task_wdt_feed(); @@ -63,9 +57,6 @@ void esp_task_wdt_feed(); /** * @brief Delete the watchdog for the current task. * - * @param null - * - * @return null */ void esp_task_wdt_delete(); diff --git a/docs/Doxyfile b/docs/Doxyfile index 6ff4c45860..d5970ee5e9 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,8 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include \ + ../components/nvs_flash/include ../components/log/include ../components/vfs/include \ + ../components/esp32/include/esp_int_wdt.h ../components/esp32/include/esp_task_wdt.h WARN_NO_PARAMDOC = YES diff --git a/docs/api/wdts.rst b/docs/api/wdts.rst new file mode 100644 index 0000000000..1b476f2f79 --- /dev/null +++ b/docs/api/wdts.rst @@ -0,0 +1,72 @@ +Watchdogs +========= + +Overview +-------- + +Esp-idf has support for two types of watchdogs: a task watchdog as well as an interrupt watchdog. Both can be +enabled using ``make menuconfig`` and selecting the appropriate options. + +Interrupt watchdog +^^^^^^^^^^^^^^^^^^ + +The interrupt watchdog makes sure the FreeRTOS task switching interrupt isn't blocked for a long time. This +is bad because no other tasks, including potentially important ones like the WiFi task and the idle task, +can't get any CPU runtime. A blocked task switching interrupt can happen because a program runs into an +infinite loop with interrupts disabled or hangs in an interrupt. + +The default action of the interrupt watchdog is to invoke the panic handler. causing a register dump and an opportunity +for the programmer to find out, using either OpenOCD or gdbstub, what bit of code is stuck with interrupts +disabled. Depending on the configuration of the panic handler, it can also blindly reset the CPU, which may be +preferred in a production environment. + +The interrupt watchdog is built around the hardware watchdog in timer group 1. If this watchdog for some reason +cannot execute the NMI handler that invokes the panic handler (e.g. because IRAM is overwritten by garbage), +it will hard-reset the SOC. + +Task watchdog +^^^^^^^^^^^^^ + +Any tasks can elect to be watched by the task watchdog. If such a task does not feed the watchdog within the time +specified by the task watchdog timeout (which is configurable using ``make menuconfig``), the watchdog will +print out a warning with information about which processes are running on the ESP32 CPUs and which processes +failed to feed the watchdog. + +By default, the task watchdog watches the idle tasks. The usual cause of idle tasks not feeding the watchdog +is a higher-priority process looping without yielding to the lower-priority processes, and can be an indicator +of badly-written code that spinloops on a peripheral or a task that is stuck in an infinite loop. + +Other task can elect to be watched by the task watchdog by calling ``esp_task_wdt_feed()``. Calling this routine +for the first time will register the task to the task watchdog; calling it subsequent times will feed +the watchdog. If a task does not want to be watched anymore (e.g. because it is finished and will call +``vTaskDelete()`` on itself), it needs to call ``esp_task_wdt_delete()``. + +The task watchdog is built around the hardware watchdog in timer group 0. If this watchdog for some reason +cannot execute the interrupt handler that prints the task data (e.g. because IRAM is overwritten by garbage +or interrupts are disabled entirely) it will hard-reset the SOC. + +JTAG and watchdogs +^^^^^^^^^^^^^^^^^^ + +While debugging using OpenOCD, if the CPUs are halted the watchdogs will keep running, eventually resetting the +CPU. This makes it very hard to debug code; that is why the OpenOCD config will disable both watchdogs on startup. +This does mean that you will not get any warnings or panics from either the task or interrupt watchdog when the ESP32 +is connected to OpenOCD via JTAG. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp32/include/esp_int_wdt.h `_ + * `esp32/include/esp_task_wdt.h `_ + + +Functions +--------- + +.. doxygenfunction:: esp_int_wdt_init +.. doxygenfunction:: esp_task_wdt_init +.. doxygenfunction:: esp_task_wdt_feed +.. doxygenfunction:: esp_task_wdt_delete diff --git a/docs/index.rst b/docs/index.rst index 1ca6e28eec..c19fc52c9a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -42,9 +42,9 @@ Contents: 1.2. Application startup flow - TBA 1.3. Flash encryption and secure boot: how they work and APIs - TBA 1.4. Lower Power Coprocessor - TBA - 1.5. Watchdogs + 1.5. Watchdogs 1.6. ... - 2. Memeory - TBA + 2. Memory - TBA 2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA 2.2. Flash layout and partitions - TBA 2.3. Flash access APIs - TBA @@ -92,6 +92,7 @@ Contents: Wi-Fi Bluetooth + Watchdogs api/gpio api/uart From 02f5bb9442e7dac64086b1573030b9ba9b814466 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 12:33:31 +0800 Subject: [PATCH 112/131] Add docs build artifacts to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 5ec57a167f..2870b4a805 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ GPATH examples/*/sdkconfig examples/*/sdkconfig.old examples/*/build + +#Doc build artifacts +docs/_build/ +docs/doxygen-warning-log.txt +docs/xml/ From 9e6e6b8ff649a682f4d693cd23e7c5cf97cb784d Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 17 Nov 2016 13:45:53 +0800 Subject: [PATCH 113/131] save logs as test_report artifacts --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..9aa23f27bf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -159,6 +159,7 @@ test_report: when: always paths: - $REPORT_PATH + - $LOG_PATH expire_in: 12 mos script: # clone test bench From b0ca2feabcc1e3aefccd289eec5a6cf4807e85b2 Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 17 Nov 2016 14:02:46 +0800 Subject: [PATCH 114/131] support build multiple bin for SSC --- .gitlab-ci.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..ad1750faf7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,10 +63,7 @@ build_ssc: <<: *build_template artifacts: paths: - - ./SSC/build/*.bin - - ./SSC/build/*.elf - - ./SSC/build/*.map - - ./SSC/build/bootloader/*.bin + - ./SSC/ssc_bin expire_in: 6 mos script: @@ -229,7 +226,7 @@ deploy_docs: variables: # LOCAL_ENV_CONFIG_PATH: define in template and jobs can overwrite if required LOCAL_ENV_CONFIG_PATH: /home/gitlab-runner/LocalConfig/ESP32_IDF - BIN_PATH: "$CI_PROJECT_DIR/SSC/build/" + BIN_PATH: "$CI_PROJECT_DIR/SSC/ssc_bin/SSC" APP_NAME: "ssc" LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" # append test level folder to TEST_CASE_FILE_PATH in before_script of test job From d73448f57f2c8ab44de4dad373cbc1a2e28fd9af Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 18:05:47 +0800 Subject: [PATCH 115/131] Make sure task wdt also does the right thing when triggered when flash is disabled --- components/esp32/include/esp_attr.h | 4 ++++ components/esp32/task_wdt.c | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 78aa3bd190..7ef2920d98 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -26,6 +26,10 @@ // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) +// Forces a string into DRAM instrad of flash +// Use as ets_printf(DRAM_STR("Hello world!\n")); +#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) + // Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" #define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index de5f9f54f9..6f39591259 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -49,6 +49,7 @@ struct wdt_task_t { static wdt_task_t *wdt_task_list=NULL; static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; + static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; @@ -69,21 +70,21 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { return; } //Watchdog got triggered because at least one task did not report in. - ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); + ets_printf(DRAM_STR("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n")); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { - cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; - if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1"); + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1"); + ets_printf(DRAM_STR(" - %s (%s)\n"), pcTaskGetTaskName(wdttask->task_handle), cpu); } } - ets_printf("Tasks currently running:\n"); + ets_printf(DRAM_STR("Tasks currently running:\n")); for (int x=0; x Date: Fri, 18 Nov 2016 10:07:34 +0800 Subject: [PATCH 116/131] components/openssl_demo: remove the apache license header and add cc license head --- .../09_openssl_client/main/openssl_client.c | 19 +++++++------------ .../09_openssl_client/main/openssl_client.h | 9 +++++++++ .../10_openssl_server/main/openssl_server.c | 19 +++++++------------ .../10_openssl_server/main/openssl_server.h | 9 +++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index c804b6c4fd..c6b0e449ac 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -1,16 +1,11 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +/* OpenSSL client Example -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ #include "openssl_client.h" diff --git a/examples/09_openssl_client/main/openssl_client.h b/examples/09_openssl_client/main/openssl_client.h index 5bc69a0aca..f5ab887ad4 100644 --- a/examples/09_openssl_client/main/openssl_client.h +++ b/examples/09_openssl_client/main/openssl_client.h @@ -1,3 +1,12 @@ +/* OpenSSL client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + #ifndef _OPENSSL_DEMO_H_ #define _OPENSSL_DEMO_H_ diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 7f4b7d6b6e..53b6050d57 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -1,16 +1,11 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +/* OpenSSL server Example -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ #include "openssl_server.h" diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h index e87f5e482e..5f49de35f2 100644 --- a/examples/10_openssl_server/main/openssl_server.h +++ b/examples/10_openssl_server/main/openssl_server.h @@ -1,3 +1,12 @@ +/* OpenSSL server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + #ifndef _OPENSSL_DEMO_H_ #define _OPENSSL_DEMO_H_ From 6b687b43f48252649af40f26ccca1e1599b65296 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 20:37:51 +0800 Subject: [PATCH 117/131] mbedtls hardware RSA: Fix "mbedtls_mpi_exp_mod" hardware calculations --- components/mbedtls/port/esp_bignum.c | 158 ++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 4 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 0a835c9e8d..401d3fc24b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -53,12 +53,11 @@ void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) static char buf[1024]; size_t n; memset(buf, 0, sizeof(buf)); - printf("%s = 0x", name); mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); if(n) { - puts(buf); + ESP_LOGI(TAG, "%s = 0x%s", name, buf); } else { - puts("TOOLONG"); + ESP_LOGI(TAG, "TOOLONG"); } } @@ -278,6 +277,7 @@ int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi /* * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) */ + #if 0 int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) { int ret; @@ -336,6 +336,155 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi return ret; } +#else + +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b + */ +static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) +{ + int i; + uint64_t t = 1; + uint64_t two_2_i_minus_1 = 2; /* 2^(i-1) */ + uint64_t two_2_i = 4; /* 2^i */ + uint64_t N = M->p[0]; + + for (i = 2; i <= 32; i++) { + if ((mbedtls_mpi_uint) N * t % two_2_i >= two_2_i_minus_1) { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (mbedtls_mpi_uint)(UINT32_MAX - t + 1); +} + +static int bignum_param_init(const mbedtls_mpi *M, mbedtls_mpi *_RR, mbedtls_mpi *r, mbedtls_mpi_uint *Mi, size_t num_words) +{ + int ret = 0; + size_t num_bits; + mbedtls_mpi RR; + + /* Calculate number of bits */ + num_bits = num_words * 32; + ESP_LOGI(TAG, "num_bits = %d\n", num_bits); + + /* + * R = b^n where b = 2^32, n=num_words, + * R = 2^N (where N=num_bits) + * RR(R^2) = 2^(2*N) (where N=num_bits) + * + * r = RR(R^2) mod M + * + * Get the RR(RR == r) value from up level if RR and RR->p is not NULL + */ + ESP_LOGI(TAG, "r = RR(R^2) mod M\n"); + if (_RR == NULL || _RR->p == NULL) { + ESP_LOGI(TAG, "RR(R^2) = 2^(2*N) (where N=num_bits)\n"); + mbedtls_mpi_init(&RR); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); + mbedtls_mpi_printf("RR", &RR); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(r, &RR, M)); + + if (_RR != NULL) + memcpy(_RR, r, sizeof( mbedtls_mpi ) ); + } else { + memcpy(r, _RR, sizeof( mbedtls_mpi ) ); + } + mbedtls_mpi_printf("r", r); + + *Mi = modular_inverse(M); + +cleanup: + mbedtls_mpi_free(&RR); + + return ret; +} + +static void bignum_param_deinit(mbedtls_mpi *_RR, mbedtls_mpi *r) +{ + if (_RR == NULL || _RR->p == NULL) + mbedtls_mpi_free(r); +} + +/* + * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +{ + int ret = 0; + size_t z_words = hardware_words_needed(Z); + size_t x_words = hardware_words_needed(X); + size_t y_words = hardware_words_needed(Y); + size_t m_words = hardware_words_needed(M); + size_t num_words; + + mbedtls_mpi r; + mbedtls_mpi_uint Mi = 0; + + /* "all numbers must be the same length", so choose longest number + as cardinal length of operation... + */ + num_words = z_words; + if (x_words > num_words) { + num_words = x_words; + } + if (y_words > num_words) { + num_words = y_words; + } + if (m_words > num_words) { + num_words = m_words; + } + ESP_LOGI(TAG, "num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); + + if (num_words * 32 > 4096) + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + + mbedtls_mpi_init(&r); + ret = bignum_param_init(M, _RR, &r, &Mi, num_words); + if (ret != 0) { + return ret; + } + + mbedtls_mpi_printf("X",X); + mbedtls_mpi_printf("Y",Y); + + esp_mpi_acquire_hardware(); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); + + /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &r, num_words); + REG_WRITE(RSA_M_DASH_REG, Mi); + + execute_op(RSA_START_MODEXP_REG); + + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + + esp_mpi_release_hardware(); + + mbedtls_mpi_printf("Z",Z); + ESP_LOGI(TAG, "print (Z == (X ** Y) %% M)\n"); + + bignum_param_deinit(_RR, &r); + + return ret; +} + + +#endif + #endif /* MBEDTLS_MPI_EXP_MOD_ALT */ @@ -385,7 +534,7 @@ static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t /* Block of debugging data, output suitable to paste into Python TODO remove */ - mbedtls_mpi_printf("R", &RR); + mbedtls_mpi_printf("RR", &RR); mbedtls_mpi_printf("M", M); mbedtls_mpi_printf("Rinv", &Rinv); mbedtls_mpi_printf("Mprime", &Mprime); @@ -463,6 +612,7 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication doesn't have the same restriction, so result is simply the number of bits in X plus number of bits in in Y.) */ + //ESP_LOGE(TAG, "INFO: %d bit result (%d bits * %d bits)\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); if (words_mult * 32 > 2048) { /* Calculate new length of Z */ words_z = words_x + words_y; From f87be70d51e60ea62ed5d16cf93563e000b96702 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 13:44:37 +1100 Subject: [PATCH 118/131] mbedtls hardware RSA: Combine methods for calculating M' & r inverse Remove redundant gcd calculation, use consistent terminology. Also remove leftover debugging code --- components/mbedtls/port/esp_bignum.c | 586 +++++++++------------------ 1 file changed, 191 insertions(+), 395 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 401d3fc24b..076234b55b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -39,46 +39,10 @@ static const char *TAG = "bignum"; #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) -/* Constants from mbedTLS bignum.c */ -#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */ -#define biL (ciL << 3) /* bits in limb */ - static _lock_t mpi_lock; -/* Temporary debugging function to print an MPI number to - stdout. Happens to be in a format compatible with Python. -*/ -void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) -{ - static char buf[1024]; - size_t n; - memset(buf, 0, sizeof(buf)); - mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); - if(n) { - ESP_LOGI(TAG, "%s = 0x%s", name, buf); - } else { - ESP_LOGI(TAG, "TOOLONG"); - } -} - -/* Temporary debug function to dump a memory block's contents to stdout - TODO remove - */ -static void __attribute__((unused)) dump_memory_block(const char *label, uint32_t addr) -{ - printf("Dumping %s @ %08x\n", label, addr); - for(int i = 0; i < (4096 / 8); i += 4) { - if(i % 32 == 0) { - printf("\n %04x:", i); - } - printf("%08x ", REG_READ(addr + i)); - } - printf("Done\n"); -} - /* At the moment these hardware locking functions aren't exposed publically - for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, - please raise a feature request. + for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. */ static void esp_mpi_acquire_hardware( void ) { @@ -116,6 +80,14 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } +/* Convert number of bits to number of words, rounded up to nearest + 512 bit (16 word) block count. +*/ +static inline size_t bits_to_hardware_words(size_t num_bits) +{ + return ((num_bits + 511) / 512) * 16; +} + /* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'. If num_words is higher than the number of words in the bignum then @@ -156,194 +128,14 @@ static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_wo return ret; } -/* Given a & b, determine u & v such that - - gcd(a,b) = d = au - bv - - This is suitable for calculating values for montgomery multiplication: - - gcd(R, M) = R * Rinv - M * Mprime = 1 - - Conditions which must be true: - - argument 'a' (R) is a power of 2. - - argument 'b' (M) is odd. - - Underlying algorithm comes from: - http://www.hackersdelight.org/hdcodetxt/mont64.c.txt - http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf - */ -static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, - mbedtls_mpi *u, mbedtls_mpi *v) -{ - mbedtls_mpi a_, ta; - - /* These checks degrade performance, TODO remove them... */ - assert(b->p[0] & 1); - assert(mbedtls_mpi_bitlen(a) == mbedtls_mpi_lsb(a)+1); - assert(mbedtls_mpi_cmp_mpi(a, b) > 0); - - mbedtls_mpi_lset(u, 1); - mbedtls_mpi_lset(v, 0); - - /* 'a' needs to be half its real value for this algorithm - TODO see if we can halve the number in the caller to avoid - allocating a bignum here. - */ - mbedtls_mpi_init(&a_); - mbedtls_mpi_copy(&a_, a); - mbedtls_mpi_shift_r(&a_, 1); - - mbedtls_mpi_init(&ta); - mbedtls_mpi_copy(&ta, &a_); - - //mbedtls_mpi_printf("a", &a_); - //mbedtls_mpi_printf("b", b); - - /* Loop invariant: - 2*ta = u*2*a - v*b. - - Loop until ta == 0 - */ - while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { - //mbedtls_mpi_printf("ta", &ta); - //mbedtls_mpi_printf("u", u); - //mbedtls_mpi_printf("v", v); - //printf("2*ta == u*2*a - v*b\n"); - - mbedtls_mpi_shift_r(&ta, 1); - if (mbedtls_mpi_get_bit(u, 0) == 0) { - // Remove common factor of 2 in u & v - mbedtls_mpi_shift_r(u, 1); - mbedtls_mpi_shift_r(v, 1); - } - else { - /* u = (u + b) >> 1 */ - mbedtls_mpi_add_mpi(u, u, b); - mbedtls_mpi_shift_r(u, 1); - /* v = (v - a) >> 1 */ - mbedtls_mpi_shift_r(v, 1); - mbedtls_mpi_add_mpi(v, v, &a_); - } - } - mbedtls_mpi_free(&ta); - mbedtls_mpi_free(&a_); -} - -/* Execute RSA operation. op_reg specifies which 'START' register - to write to. -*/ -static inline void execute_op(uint32_t op_reg) -{ - /* Clear interrupt status, start operation */ - REG_WRITE(RSA_INTERRUPT_REG, 1); - REG_WRITE(op_reg, 1); - - /* TODO: use interrupt instead of busywaiting */ - while(REG_READ(RSA_INTERRUPT_REG) != 1) - { } - - /* clear the interrupt */ - REG_WRITE(RSA_INTERRUPT_REG, 1); -} - -/* Sub-stages of modulo multiplication/exponentiation operations */ -static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words); -inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); - -/* Z = (X * Y) mod M - - Not an mbedTLS function - */ -int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) -{ - int ret; - size_t num_words = hardware_words_needed(M); - - /* Calculate and load the first stage montgomery multiplication */ - MBEDTLS_MPI_CHK( modular_op_prepare(X, M, num_words) ); - - execute_op(RSA_MULT_START_REG); - - MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - - esp_mpi_release_hardware(); - - cleanup: - return ret; -} - -#if defined(MBEDTLS_MPI_EXP_MOD_ALT) - -/* - * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) - */ - #if 0 -int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) -{ - int ret; - size_t z_words = hardware_words_needed(Z); - size_t x_words = hardware_words_needed(X); - size_t y_words = hardware_words_needed(Y); - size_t m_words = hardware_words_needed(M); - size_t num_words; - - mbedtls_mpi_printf("X",X); - mbedtls_mpi_printf("Y",Y); - mbedtls_mpi_printf("M",M); - - /* "all numbers must be the same length", so choose longest number - as cardinal length of operation... - */ - num_words = z_words; - if (x_words > num_words) { - num_words = x_words; - } - if (y_words > num_words) { - num_words = y_words; - } - if (m_words > num_words) { - num_words = m_words; - } - printf("num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); - - /* TODO: _RR parameter currently ignored */ - - ret = modular_op_prepare(X, M, num_words); - if (ret != 0) { - return ret; - } - - mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); - - //dump_memory_block("X_BLOCK", RSA_MEM_X_BLOCK_BASE); - //dump_memory_block("Y_BLOCK", RSA_MEM_Y_BLOCK_BASE); - //dump_memory_block("M_BLOCK", RSA_MEM_M_BLOCK_BASE); - - REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); - - execute_op(RSA_START_MODEXP_REG); - - //dump_memory_block("Z_BLOCK", RSA_MEM_Z_BLOCK_BASE); - - /* TODO: only need to read m_words not num_words, provided result is correct... */ - ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); - - esp_mpi_release_hardware(); - - mbedtls_mpi_printf("Z",Z); - printf("print (Z == (X ** Y) %% M)\n"); - - return ret; -} - -#else /** - * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, - * where B^-1(B-1) mod N=1. Actually, only the least significant part of - * N' is needed, hence the definition N0'=N' mod b. We reproduce below the - * simple algorithm from an article by Dusse and Kaliski to efficiently - * find N0' from N0 and b + * + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) { @@ -365,59 +157,104 @@ static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) return (mbedtls_mpi_uint)(UINT32_MAX - t + 1); } -static int bignum_param_init(const mbedtls_mpi *M, mbedtls_mpi *_RR, mbedtls_mpi *r, mbedtls_mpi_uint *Mi, size_t num_words) +/* Calculate Rinv = RR^2 mod M, where: + * + * R = b^n where b = 2^32, n=num_words, + * R = 2^N (where N=num_bits) + * RR = R^2 = 2^(2*N) (where N=num_bits=num_words*32) + * + * This calculation is computationally expensive (mbedtls_mpi_mod_mpi) + * so caller should cache the result where possible. + * + * DO NOT call this function while holding esp_mpi_acquire_hardware(). + * + */ +static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words) { - int ret = 0; - size_t num_bits; + int ret; + size_t num_bits = num_words * 32; mbedtls_mpi RR; + mbedtls_mpi_init(&RR); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(Rinv, &RR, M)); - /* Calculate number of bits */ - num_bits = num_words * 32; - ESP_LOGI(TAG, "num_bits = %d\n", num_bits); - - /* - * R = b^n where b = 2^32, n=num_words, - * R = 2^N (where N=num_bits) - * RR(R^2) = 2^(2*N) (where N=num_bits) - * - * r = RR(R^2) mod M - * - * Get the RR(RR == r) value from up level if RR and RR->p is not NULL - */ - ESP_LOGI(TAG, "r = RR(R^2) mod M\n"); - if (_RR == NULL || _RR->p == NULL) { - ESP_LOGI(TAG, "RR(R^2) = 2^(2*N) (where N=num_bits)\n"); - mbedtls_mpi_init(&RR); - MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); - mbedtls_mpi_printf("RR", &RR); - - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(r, &RR, M)); - - if (_RR != NULL) - memcpy(_RR, r, sizeof( mbedtls_mpi ) ); - } else { - memcpy(r, _RR, sizeof( mbedtls_mpi ) ); - } - mbedtls_mpi_printf("r", r); - - *Mi = modular_inverse(M); - -cleanup: + cleanup: mbedtls_mpi_free(&RR); - return ret; } -static void bignum_param_deinit(mbedtls_mpi *_RR, mbedtls_mpi *r) + +/* Execute RSA operation. op_reg specifies which 'START' register + to write to. +*/ +static inline void execute_op(uint32_t op_reg) { - if (_RR == NULL || _RR->p == NULL) - mbedtls_mpi_free(r); + /* Clear interrupt status, start operation */ + REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); + + /* TODO: use interrupt instead of busywaiting */ + while(REG_READ(RSA_INTERRUPT_REG) != 1) + { } + + /* clear the interrupt */ + REG_WRITE(RSA_INTERRUPT_REG, 1); } +/* Sub-stages of modulo multiplication/exponentiation operations */ +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = (X * Y) mod M + + Not an mbedTLS function +*/ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) +{ + int ret; + size_t num_words = hardware_words_needed(M); + mbedtls_mpi Rinv; + mbedtls_mpi_uint Mprime; + + /* Calculate and load the first stage montgomery multiplication */ + mbedtls_mpi_init(&Rinv); + MBEDTLS_MPI_CHK(calculate_rinv(&Rinv, M, num_words)); + Mprime = modular_inverse(M); + + esp_mpi_acquire_hardware(); + + /* Load M, X, Rinv, Mprime (Mprime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, (uint32_t)Mprime); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + /* Execute first stage montgomery multiplication */ + execute_op(RSA_MULT_START_REG); + + /* execute second stage */ + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + + esp_mpi_release_hardware(); + + cleanup: + mbedtls_mpi_free(&Rinv); + return ret; +} + +#if defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + * + * _Rinv is optional pre-calculated version of Rinv (via calculate_rinv()). + * + * (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv) + * */ -int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _Rinv ) { int ret = 0; size_t z_words = hardware_words_needed(Z); @@ -426,8 +263,9 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi size_t m_words = hardware_words_needed(M); size_t num_words; - mbedtls_mpi r; - mbedtls_mpi_uint Mi = 0; + mbedtls_mpi Rinv_new; /* used if _Rinv == NULL */ + mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) othwerwise &RR_new */ + mbedtls_mpi_uint Mprime; /* "all numbers must be the same length", so choose longest number as cardinal length of operation... @@ -442,19 +280,24 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi if (m_words > num_words) { num_words = m_words; } - ESP_LOGI(TAG, "num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); - if (num_words * 32 > 4096) + if (num_words * 32 > 4096) { return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - - mbedtls_mpi_init(&r); - ret = bignum_param_init(M, _RR, &r, &Mi, num_words); - if (ret != 0) { - return ret; } - mbedtls_mpi_printf("X",X); - mbedtls_mpi_printf("Y",Y); + /* Determine RR pointer, either _RR for cached value + or local RR_new */ + if (_Rinv == NULL) { + mbedtls_mpi_init(&Rinv_new); + Rinv = &Rinv_new; + } else { + Rinv = _Rinv; + } + if (Rinv->p == NULL) { + MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, num_words)); + } + + Mprime = modular_inverse(M); esp_mpi_acquire_hardware(); @@ -465,8 +308,8 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &r, num_words); - REG_WRITE(RSA_M_DASH_REG, Mi); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, Mprime); execute_op(RSA_START_MODEXP_REG); @@ -474,91 +317,16 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi esp_mpi_release_hardware(); - mbedtls_mpi_printf("Z",Z); - ESP_LOGI(TAG, "print (Z == (X ** Y) %% M)\n"); - - bignum_param_deinit(_RR, &r); - - return ret; -} - - -#endif - -#endif /* MBEDTLS_MPI_EXP_MOD_ALT */ - - -/* The common parts of modulo multiplication and modular sliding - * window exponentiation: - * - * @param X first multiplication factor and/or base of exponent. - * @param M modulo value for result - * @param num_words size of modulo operation, in words (limbs). - * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. - * - * Steps: - * Calculate Rinv & Mprime based on M & num_words - * Load all coefficients to memory - * Set mode register - * - * @note This function calls esp_mpi_acquire_hardware. If successful, - * returns 0 and it becomes the callers responsibility to call - * esp_mpi_release_hardware(). If failure is returned, the caller does - * not need to call esp_mpi_release_hardware(). - */ -static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words) -{ - int ret = 0; - mbedtls_mpi RR, Rinv, Mprime; - size_t num_bits; - - /* Calculate number of bits */ - num_bits = num_words * 32; - - if(num_bits > 4096) { - return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + cleanup: + if (_Rinv == NULL) { + mbedtls_mpi_free(&Rinv_new); } - /* Rinv & Mprime are calculated via extended binary gcd - algorithm, see references on extended_binary_gcd() above. - */ - mbedtls_mpi_init(&Rinv); - mbedtls_mpi_init(&RR); - mbedtls_mpi_init(&Mprime); - - mbedtls_mpi_set_bit(&RR, num_bits, 1); /* R = b^n where b = 2^32, n=num_words, - ie R = 2^N (where N=num_bits) */ - /* calculate Rinv & Mprime */ - extended_binary_gcd(&RR, M, &Rinv, &Mprime); - - /* Block of debugging data, output suitable to paste into Python - TODO remove - */ - mbedtls_mpi_printf("RR", &RR); - mbedtls_mpi_printf("M", M); - mbedtls_mpi_printf("Rinv", &Rinv); - mbedtls_mpi_printf("Mprime", &Mprime); - printf("print (R * Rinv - M * Mprime == 1)\n"); - printf("print (Rinv == (R * R) %% M)\n"); - - esp_mpi_acquire_hardware(); - - /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ - mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); - REG_WRITE(RSA_M_DASH_REG, Mprime.p[0]); - - /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); - - mbedtls_mpi_free(&Rinv); - mbedtls_mpi_free(&RR); - mbedtls_mpi_free(&Mprime); - return ret; } +#endif /* MBEDTLS_MPI_EXP_MOD_ALT */ + /* Second & final step of a modular multiply - load second multiplication * factor Y, run the multiply, read back the result into Z. * @@ -594,17 +362,43 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) { int ret; - size_t words_x, words_y, words_mult, words_z; + size_t bits_x, bits_y, words_x, words_y, words_mult, words_z; /* Count words needed for X & Y in hardware */ - words_x = hardware_words_needed(X); - words_y = hardware_words_needed(Y); + bits_x = mbedtls_mpi_bitlen(X); + bits_y = mbedtls_mpi_bitlen(Y); + /* Convert bit counts to words, rounded up to 512-bit + (16 word) blocks */ + words_x = bits_to_hardware_words(bits_x); + words_y = bits_to_hardware_words(bits_y); + + /* Short-circuit eval if either argument is 0 or 1. + + This is needed as the mpi modular division + argument will sometimes call in here when one + argument is too large for the hardware unit, but the other + argument is zero or one. + + This leaks some timing information, although overall there is a + lot less timing variation than a software MPI approach. + */ + if (bits_x == 0 || bits_y == 0) { + mbedtls_mpi_lset(Z, 0); + return 0; + } + if (bits_x == 1) { + return mbedtls_mpi_copy(Z, Y); + } + if (bits_y == 1) { + return mbedtls_mpi_copy(Z, X); + } words_mult = (words_x > words_y ? words_x : words_y); /* Result Z has to have room for double the larger factor */ words_z = words_mult * 2; + /* If either factor is over 2048 bits, we can't use the standard hardware multiplier (it assumes result is double longest factor, and result is max 4096 bits.) @@ -612,12 +406,11 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication doesn't have the same restriction, so result is simply the number of bits in X plus number of bits in in Y.) */ - //ESP_LOGE(TAG, "INFO: %d bit result (%d bits * %d bits)\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); if (words_mult * 32 > 2048) { /* Calculate new length of Z */ - words_z = words_x + words_y; + words_z = bits_to_hardware_words(bits_x + bits_y); if (words_z * 32 > 4096) { - ESP_LOGE(TAG, "ERROR: %d bit result (%d bits * %d bits) too large for hardware unit\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); + ESP_LOGE(TAG, "ERROR: %d bit result %d bits * %d bits too large for hardware unit\n", words_z * 32, bits_x, bits_y); return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; } else { @@ -640,7 +433,7 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi /* "mode" register loaded with number of 512-bit blocks in result, plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8)) - */ + */ REG_WRITE(RSA_MULT_MODE_REG, (words_z / 16) + 7); execute_op(RSA_MULT_START_REG); @@ -656,54 +449,57 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi } /* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod - multiplication to solve the case where A or B are >2048 bits so - can't use the standard multiplication method. + multiplication to calculate an mbedtls_mpi_mult_mpi result where either + A or B are >2048 bits so can't use the standard multiplication method. - This case is simpler than esp_mpi_mul_mpi_mod() as we control the arguments: + Result (A bits + B bits) must still be less than 4096 bits. + + This case is simpler than the general case modulo multiply of + esp_mpi_mul_mpi_mod() because we can control the other arguments: * Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output - isn't actually modulo anything. - * Therefore of of M' and Rinv are predictable as follows: - M' = 1 - Rinv = 1 + isn't actually modulo anything. + * Mprime and Rinv are therefore predictable as follows: + Mprime = 1 + Rinv = 1 - (See RSA Accelerator section in Technical Reference * - extended_binary_gcd() function above for more about M', Rinv) + (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv) */ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) - { - int ret = 0; +{ + int ret = 0; - /* Load coefficients to hardware */ - esp_mpi_acquire_hardware(); + /* Load coefficients to hardware */ + esp_mpi_acquire_hardware(); - /* M = 2^num_words - 1, so block is entirely FF */ - for(int i = 0; i < num_words; i++) { - REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); - } - /* Mprime = 1 */ - REG_WRITE(RSA_M_DASH_REG, 1); + /* M = 2^num_words - 1, so block is entirely FF */ + for(int i = 0; i < num_words; i++) { + REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); + } + /* Mprime = 1 */ + REG_WRITE(RSA_M_DASH_REG, 1); - /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); - /* Load X */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + /* Load X */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - /* Rinv = 1 */ - REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); - for(int i = 1; i < num_words; i++) { - REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); - } + /* Rinv = 1 */ + REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); + for(int i = 1; i < num_words; i++) { + REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); + } - execute_op(RSA_MULT_START_REG); + execute_op(RSA_MULT_START_REG); - MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + /* finish the modular multiplication */ + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - esp_mpi_release_hardware(); + esp_mpi_release_hardware(); cleanup: - return ret; + return ret; } #endif /* MBEDTLS_MPI_MUL_MPI_ALT */ From 68d370542a19c3d1b406a2ba6da3b8f088e541ae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 14:26:02 +1100 Subject: [PATCH 119/131] mbedtls hardware RSA: Put into menuconfig, squash warnings All combinations of enabling/disabling hardware acceleration no longer show unused warnings. --- components/mbedtls/Kconfig | 21 ++++++++++++++++++- components/mbedtls/library/bignum.c | 8 ++++++- components/mbedtls/port/esp_bignum.c | 8 ++++--- .../mbedtls/port/include/mbedtls/esp_config.h | 4 ++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 60facc7d27..bb2aa8f6a3 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -22,7 +22,7 @@ config MBEDTLS_SSL_MAX_CONTENT_LEN config MBEDTLS_DEBUG bool "Enable mbedTLS debugging" - default "no" + default n help Enable mbedTLS debugging functions. @@ -34,4 +34,23 @@ config MBEDTLS_DEBUG functionality. See the "https_request_main" example for a sample function which connects the two together. +config MBEDTLS_HARDWARE_AES + bool "Enable hardware AES acceleration" + default y + help + Enable hardware accelerated AES encryption & decryption. + +config MBEDTLS_HARDWARE_MPI + bool "Enable hardware MPI (bignum) acceleration" + default y + help + Enable hardware accelerated multiple precision integer operations. + + Hardware accelerated multiplication, modulo multiplication, + and modular exponentiation for up to 4096 bit results. + + These operations are used by RSA. + + + endmenu diff --git a/components/mbedtls/library/bignum.c b/components/mbedtls/library/bignum.c index e739bc1d38..04ff9e07b2 100644 --- a/components/mbedtls/library/bignum.c +++ b/components/mbedtls/library/bignum.c @@ -1092,6 +1092,8 @@ int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint return( mbedtls_mpi_sub_mpi( X, A, &_B ) ); } +#if !defined(MBEDTLS_MPI_MUL_MPI_ALT) || !defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Helper for mbedtls_mpi multiplication */ @@ -1103,6 +1105,7 @@ static */ __attribute__ ((noinline)) #endif + void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b ) { mbedtls_mpi_uint c = 0, t = 0; @@ -1164,6 +1167,8 @@ void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mp while( c != 0 ); } +#endif + #if !defined(MBEDTLS_MPI_MUL_MPI_ALT) /* * Baseline multiplication: X = A * B (HAC 14.12) @@ -1526,6 +1531,8 @@ int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_ return( 0 ); } +#if !defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Fast Montgomery initialization (thanks to Tom St Denis) */ @@ -1600,7 +1607,6 @@ static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint m return( mpi_montmul( A, &U, N, mm, T ) ); } -#if !defined(MBEDTLS_MPI_EXP_MOD_ALT) /* * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) */ diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 076234b55b..11a1c63973 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -35,8 +35,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -static const char *TAG = "bignum"; - #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) static _lock_t mpi_lock; @@ -330,6 +328,8 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi /* Second & final step of a modular multiply - load second multiplication * factor Y, run the multiply, read back the result into Z. * + * Called from both mbedtls_mpi_exp_mod and mbedtls_mpi_mod_mpi. + * * @param Z result value * @param X first multiplication factor (used to set sign of result). * @param Y second multiplication factor. @@ -338,7 +338,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi * * Caller must have already called esp_mpi_acquire_hardware(). */ -inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) +static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) { int ret; /* Load Y to X input memory block, rerun */ @@ -356,6 +356,8 @@ inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, #if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ +static const char *TAG = "bignum"; + static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); /* Z = X * Y */ diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 2b47d84ea4..db87c6ef31 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -239,7 +239,9 @@ /* The following units have ESP32 hardware support, uncommenting each _ALT macro will use the hardware-accelerated implementation. */ +#ifdef CONFIG_MBEDTLS_HARDWARE_AES #define MBEDTLS_AES_ALT +#endif /* Currently hardware SHA does not work with TLS handshake, due to concurrency issue. Internal TW#7111. */ @@ -251,8 +253,10 @@ Uncommenting these macros will use the hardware-accelerated implementations. */ +#ifdef CONFIG_MBEDTLS_HARDWARE_MPI #define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT +#endif /** * \def MBEDTLS_MD2_PROCESS_ALT From 36f29017b62336ecf58d52b91f88b3c11ab36fe8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 15:53:00 +1100 Subject: [PATCH 120/131] mbedtls hardware bignum: Support "RSA" interrupt for end of operation Allows CPU to do other things which bignum operation is in progress. --- components/mbedtls/Kconfig | 16 ++++++++++ components/mbedtls/port/esp_bignum.c | 48 +++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index bb2aa8f6a3..d6e2a2dcb7 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -51,6 +51,22 @@ config MBEDTLS_HARDWARE_MPI These operations are used by RSA. +config MBEDTLS_MPI_USE_INTERRUPT + bool "Use interrupt for MPI operations" + depends on MBEDTLS_HARDWARE_MPI + default y + help + Use an interrupt to coordinate MPI operations. + This allows other code to run on the CPU while an MPI operation is pending. + Otherwise the CPU busy-waits. + +config MBEDTLS_MPI_INTERRUPT_NUM + int "MPI Interrupt number" + depends on MBEDTLS_MPI_USE_INTERRUPT + default 18 + help + CPU interrupt number for MPI interrupt to connect to. Must be otherwise unused. + Eventually this assignment will be handled automatically at runtime. endmenu diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 11a1c63973..2133e7cae6 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -31,12 +31,43 @@ #include "soc/hwcrypto_reg.h" #include "esp_system.h" #include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) +static const char *TAG = "bignum"; + +#if defined(CONFIG_MBEDTLS_MPI_USE_INTERRUPT) +static SemaphoreHandle_t op_complete_sem; + +static IRAM_ATTR void rsa_complete_isr(void *arg) +{ + BaseType_t higher_woken; + REG_WRITE(RSA_INTERRUPT_REG, 1); + xSemaphoreGiveFromISR(op_complete_sem, &higher_woken); + if (higher_woken) { + portYIELD_FROM_ISR(); + } +} + +static void rsa_isr_initialise() +{ + if (op_complete_sem == NULL) { + op_complete_sem = xSemaphoreCreateBinary(); + intr_matrix_set(xPortGetCoreID(), ETS_RSA_INTR_SOURCE, CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + xt_set_interrupt_handler(CONFIG_MBEDTLS_MPI_INTERRUPT_NUM, &rsa_complete_isr, NULL); + xthal_set_intclear(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + xt_ints_on(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + } +} + +#endif /* CONFIG_MBEDTLS_MPI_USE_INTERRUPT */ + static _lock_t mpi_lock; /* At the moment these hardware locking functions aren't exposed publically @@ -47,6 +78,9 @@ static void esp_mpi_acquire_hardware( void ) /* newlib locks lazy initialize on ESP-IDF */ _lock_acquire(&mpi_lock); ets_bigint_enable(); +#ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT + rsa_isr_initialise(); +#endif } static void esp_mpi_release_hardware( void ) @@ -187,13 +221,21 @@ static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words */ static inline void execute_op(uint32_t op_reg) { - /* Clear interrupt status, start operation */ + /* Clear interrupt status */ REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); - /* TODO: use interrupt instead of busywaiting */ +#ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT + if (!xSemaphoreTake(op_complete_sem, 2000 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "Timed out waiting for RSA operation (op_reg 0x%x int_reg 0x%x)", + op_reg, REG_READ(RSA_INTERRUPT_REG)); + abort(); /* indicates a fundamental problem with driver */ + } +#else while(REG_READ(RSA_INTERRUPT_REG) != 1) { } +#endif /* clear the interrupt */ REG_WRITE(RSA_INTERRUPT_REG, 1); @@ -356,8 +398,6 @@ static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const m #if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ -static const char *TAG = "bignum"; - static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); /* Z = X * Y */ From 1cc0b3000b699e9ea451a18c9f255da1e590a62d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 16:38:22 +1100 Subject: [PATCH 121/131] mbedtls hardware bignum: Expose ESP-only bignum API in wrapper mbedtls/bignum.h --- components/mbedtls/port/esp_bignum.c | 14 +--- components/mbedtls/port/include/aes_alt.h | 3 +- .../mbedtls/port/include/mbedtls/bignum.h | 78 +++++++++++++++++++ components/mbedtls/port/include/sha1_alt.h | 17 +++- components/mbedtls/port/include/sha256_alt.h | 18 +++-- components/mbedtls/port/include/sha512_alt.h | 19 +++-- 6 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 components/mbedtls/port/include/mbedtls/bignum.h diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 2133e7cae6..68fd8bec98 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -26,7 +26,6 @@ #include #include #include "mbedtls/bignum.h" -#include "mbedtls/bn_mul.h" #include "rom/bigint.h" #include "soc/hwcrypto_reg.h" #include "esp_system.h" @@ -38,9 +37,7 @@ #include "freertos/task.h" #include "freertos/semphr.h" -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) - -static const char *TAG = "bignum"; +static const __attribute__((unused)) char *TAG = "bignum"; #if defined(CONFIG_MBEDTLS_MPI_USE_INTERRUPT) static SemaphoreHandle_t op_complete_sem; @@ -70,10 +67,7 @@ static void rsa_isr_initialise() static _lock_t mpi_lock; -/* At the moment these hardware locking functions aren't exposed publically - for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. -*/ -static void esp_mpi_acquire_hardware( void ) +void esp_mpi_acquire_hardware( void ) { /* newlib locks lazy initialize on ESP-IDF */ _lock_acquire(&mpi_lock); @@ -83,7 +77,7 @@ static void esp_mpi_acquire_hardware( void ) #endif } -static void esp_mpi_release_hardware( void ) +void esp_mpi_release_hardware( void ) { ets_bigint_disable(); _lock_release(&mpi_lock); @@ -546,5 +540,3 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, #endif /* MBEDTLS_MPI_MUL_MPI_ALT */ -#endif /* MBEDTLS_MPI_MUL_MPI_ALT || MBEDTLS_MPI_EXP_MOD_ALT */ - diff --git a/components/mbedtls/port/include/aes_alt.h b/components/mbedtls/port/include/aes_alt.h index 7161b282c2..d4da6ca878 100644 --- a/components/mbedtls/port/include/aes_alt.h +++ b/components/mbedtls/port/include/aes_alt.h @@ -20,7 +20,6 @@ * * */ - #ifndef AES_ALT_H #define AES_ALT_H @@ -56,4 +55,4 @@ typedef esp_aes_context mbedtls_aes_context; } #endif -#endif /* aes.h */ +#endif diff --git a/components/mbedtls/port/include/mbedtls/bignum.h b/components/mbedtls/port/include/mbedtls/bignum.h new file mode 100644 index 0000000000..23cd56348a --- /dev/null +++ b/components/mbedtls/port/include/mbedtls/bignum.h @@ -0,0 +1,78 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_MBEDTLS_BIGNUM_H__ +#define __ESP_MBEDTLS_BIGNUM_H__ + +#include_next "mbedtls/bignum.h" + +/** + * This is a wrapper for the main mbedtls/bignum.h. This wrapper + * provides a few additional ESP32-only functions. + * + * This is because we don't set MBEDTLS_BIGNUM_ALT in the same way we + * do for AES, SHA, etc. Because we still use most of the bignum.h + * implementation and just replace a few hardware accelerated + * functions (see MBEDTLS_MPI_EXP_MOD_ALT & MBEDTLS_MPI_MUL_MPI_ALT in + * esp_config.h). + * + * @note Unlike the other hardware accelerator support functions in esp32/hwcrypto, there is no + * generic "hwcrypto/bignum.h" header for using these functions without mbedTLS. The reason for this + * is that all of the function implementations depend strongly upon the mbedTLS MPI implementation. + */ + +/** + * @brief Lock access to RSA Accelerator (MPI/bignum operations) + * + * RSA Accelerator hardware unit can only be used by one + * consumer at a time. + * + * @note This function is non-recursive (do not call it twice from the + * same task.) + * + * @note You do not need to call this if you are using the mbedTLS bignum.h + * API or esp_mpi_xxx functions. This function is only needed if you + * want to call ROM RSA functions or access the registers directly. + * + */ +void esp_mpi_acquire_hardware(void); + +/** + * @brief Unlock access to RSA Accelerator (MPI/bignum operations) + * + * Has to be called once for each call to esp_mpi_acquire_hardware(). + * + * @note You do not need to call this if you are using the mbedTLS bignum.h + * API or esp_mpi_xxx functions. This function is only needed if you + * want to call ROM RSA functions or access the registers directly. + */ +void esp_mpi_release_hardware(void); + +/* @brief MPI modular mupltiplication function + * + * Calculates Z = (X * Y) mod M using MPI hardware acceleration. + * + * This is not part of the standard mbedTLS bignum API. + * + * @note All of X, Y & Z should be less than 4096 bit long or an error is returned. + * + * @param Z Result bignum, should be pre-initialised with mbedtls_mpi_init(). + * @param X First multiplication argument. + * @param Y Second multiplication argument. + * @param M Modulus value for result. + * + * @return 0 on success, mbedTLS MPI error codes on failure. + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M); + +#endif diff --git a/components/mbedtls/port/include/sha1_alt.h b/components/mbedtls/port/include/sha1_alt.h index 60297b9fbf..f5e69b3f95 100644 --- a/components/mbedtls/port/include/sha1_alt.h +++ b/components/mbedtls/port/include/sha1_alt.h @@ -1,7 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA1_ALT_H_ #define _SHA1_ALT_H_ diff --git a/components/mbedtls/port/include/sha256_alt.h b/components/mbedtls/port/include/sha256_alt.h index 6d9986b3a1..143d8c75e1 100644 --- a/components/mbedtls/port/include/sha256_alt.h +++ b/components/mbedtls/port/include/sha256_alt.h @@ -1,8 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA256_ALT_H_ #define _SHA256_ALT_H_ @@ -30,4 +38,4 @@ typedef esp_sha_context mbedtls_sha256_context; } #endif -#endif /* sha256.h */ +#endif diff --git a/components/mbedtls/port/include/sha512_alt.h b/components/mbedtls/port/include/sha512_alt.h index 241f2be3b3..8044b42754 100644 --- a/components/mbedtls/port/include/sha512_alt.h +++ b/components/mbedtls/port/include/sha512_alt.h @@ -1,9 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - * esf Link List Descriptor - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA512_ALT_H_ #define _SHA512_ALT_H_ @@ -30,4 +37,4 @@ typedef esp_sha_context mbedtls_sha512_context; } #endif -#endif /* sha512.h */ +#endif From 51021b06f8c7e49e937f8c295df95807bf505eb3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:23:29 +0800 Subject: [PATCH 122/131] nvs: initialize using layout from partition table --- components/nvs_flash/include/nvs_flash.h | 30 ++++++++++-------------- components/nvs_flash/src/nvs_api.cpp | 12 +++++++++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index d6e1990250..eca7f99edd 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -18,25 +18,21 @@ extern "C" { #endif -/** Initialise NVS flash storage with default flash sector layout - - Temporarily, this region is hardcoded as a 12KB (0x3000 byte) - region starting at 36KB (0x9000 byte) offset in flash. - - @return ESP_OK if flash was successfully initialised. -*/ +/** + * @brief Initialize NVS flash storage with layout given in the partition table. + * + * @return ESP_OK if storage was successfully initialized. + */ esp_err_t nvs_flash_init(void); -/** Initialise NVS flash storage with custom flash sector layout - - @param baseSector Flash sector (units of 4096 bytes) offset to start NVS. - @param sectorCount Length (in flash sectors) of NVS region. - - @return ESP_OK if flash was successfully initialised. - - @note Use this parameter if you're not using the options in menuconfig for - configuring flash layout & partition table. -*/ +/** + * @brief Initialize NVS flash storage with custom flash sector layout + * @note Make sure specified sectors don't fall within ranges occupied + * by other partitions. + * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS + * @param sectorCount Length (in flash sectors) of NVS region + * @return ESP_OK if flash was successfully initialized + */ esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index f6c6c588aa..b2cb5e7ad4 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -16,6 +16,7 @@ #include "nvs_storage.hpp" #include "intrusive_list.h" #include "nvs_platform.hpp" +#include "esp_partition.h" #include "sdkconfig.h" #ifdef ESP_PLATFORM @@ -61,10 +62,19 @@ extern "C" void nvs_dump() s_nvs_storage.debugDump(); } +#ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { - return nvs_flash_init_custom(9, 3); + const esp_partition_t* partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + if (partition == NULL) { + return ESP_ERR_NOT_FOUND; + } + + return nvs_flash_init_custom(partition->address / SPI_FLASH_SEC_SIZE, + partition->size / SPI_FLASH_SEC_SIZE); } +#endif extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) { From 6e97936bace204d802cee8d34f6cbfed3a5dee0c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:24:56 +0800 Subject: [PATCH 123/131] nvs: allow nvs_flash_init to be called more than once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also don’t assert in nvs_* functions if nvs_flash_init wasn’t called, and make nvs_flash_init_custom an internal API for unit tests. --- components/nvs_flash/include/nvs_flash.h | 10 ----- components/nvs_flash/src/nvs_api.cpp | 9 +++-- components/nvs_flash/src/nvs_platform.hpp | 21 +++++----- components/nvs_flash/src/nvs_storage.cpp | 5 +++ components/nvs_flash/src/nvs_storage.hpp | 2 + components/nvs_flash/src/nvs_test_api.h | 47 +++++++++++++++++++++++ components/nvs_flash/test/test_nvs.cpp | 2 +- 7 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 components/nvs_flash/src/nvs_test_api.h diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index eca7f99edd..0162a8f8ac 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -25,16 +25,6 @@ extern "C" { */ esp_err_t nvs_flash_init(void); -/** - * @brief Initialize NVS flash storage with custom flash sector layout - * @note Make sure specified sectors don't fall within ranges occupied - * by other partitions. - * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS - * @param sectorCount Length (in flash sectors) of NVS region - * @return ESP_OK if flash was successfully initialized - */ -esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); - #ifdef __cplusplus } diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index b2cb5e7ad4..751542ee12 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -65,6 +65,11 @@ extern "C" void nvs_dump() #ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { + Lock::init(); + Lock lock; + if (s_nvs_storage.isValid()) { + return ESP_OK; + } const esp_partition_t* partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); if (partition == NULL) { @@ -78,9 +83,7 @@ extern "C" esp_err_t nvs_flash_init(void) extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) { - Lock::init(); - Lock lock; - ESP_LOGD(TAG, "init start=%d count=%d", baseSector, sectorCount); + ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); s_nvs_handles.clear(); return s_nvs_storage.init(baseSector, sectorCount); } diff --git a/components/nvs_flash/src/nvs_platform.hpp b/components/nvs_flash/src/nvs_platform.hpp index 374dbca6cc..0973c4875c 100644 --- a/components/nvs_flash/src/nvs_platform.hpp +++ b/components/nvs_flash/src/nvs_platform.hpp @@ -16,9 +16,6 @@ #ifdef ESP_PLATFORM -#define NVS_DEBUGV(...) ets_printf(__VA_ARGS__) - -#include "rom/ets_sys.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -30,19 +27,23 @@ class Lock public: Lock() { - assert(mSemaphore); - xSemaphoreTake(mSemaphore, portMAX_DELAY); + if (mSemaphore) { + xSemaphoreTake(mSemaphore, portMAX_DELAY); + } } ~Lock() { - assert(mSemaphore); - xSemaphoreGive(mSemaphore); + if (mSemaphore) { + xSemaphoreGive(mSemaphore); + } } static esp_err_t init() { - assert(mSemaphore == nullptr); + if (mSemaphore) { + return ESP_OK; + } mSemaphore = xSemaphoreCreateMutex(); if (!mSemaphore) { return ESP_ERR_NO_MEM; @@ -52,7 +53,9 @@ public: static void uninit() { - vSemaphoreDelete(mSemaphore); + if (mSemaphore) { + vSemaphoreDelete(mSemaphore); + } mSemaphore = nullptr; } diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index cacfbd4022..f8da28fa24 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -69,6 +69,11 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) return ESP_OK; } +bool Storage::isValid() const +{ + return mState == StorageState::ACTIVE; +} + esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item) { for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index f8cee9f2a9..ecf2651ae5 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -47,6 +47,8 @@ public: esp_err_t init(uint32_t baseSector, uint32_t sectorCount); + bool isValid() const; + esp_err_t createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex); esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize); diff --git a/components/nvs_flash/src/nvs_test_api.h b/components/nvs_flash/src/nvs_test_api.h new file mode 100644 index 0000000000..97940092d5 --- /dev/null +++ b/components/nvs_flash/src/nvs_test_api.h @@ -0,0 +1,47 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nvs_flash.h" + +/** + * @brief Initialize NVS flash storage with custom flash sector layout + * + * @note This API is intended to be used in unit tests. + * + * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS + * @param sectorCount Length (in flash sectors) of NVS region. + NVS partition must be at least 3 sectors long. + * @return ESP_OK if flash was successfully initialized + */ +esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); + + +/** + * @brief Dump contents of NVS storage to stdout + * + * This function may be used for debugging purposes to inspect the state + * of NVS pages. For each page, list of entries is also dumped. + */ +void nvs_dump(void); + + +#ifdef __cplusplus +} +#endif diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 81bf7fd216..282d4de48e 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "catch.hpp" #include "nvs.hpp" -#include "nvs_flash.h" +#include "nvs_test_api.h" #include "spi_flash_emulation.h" #include #include From a0feea8daa21d301a19283304ef635008eeff75c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:35:10 +0800 Subject: [PATCH 124/131] partition_table: update layouts, remove unused subtypes, sync with header file --- components/partition_table/gen_esp32part.py | 10 +++++++--- components/partition_table/partitions_singleapp.csv | 4 ++-- components/partition_table/partitions_two_ota.csv | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index cb6a5f24a1..5ead13adc7 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -116,7 +116,8 @@ class PartitionDefinition(object): "app" : APP_TYPE, "data" : DATA_TYPE, } - + + # Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h SUBTYPES = { APP_TYPE : { "factory" : 0x00, @@ -124,8 +125,11 @@ class PartitionDefinition(object): }, DATA_TYPE : { "ota" : 0x00, - "rf" : 0x01, - "wifi" : 0x02, + "phy" : 0x01, + "nvs" : 0x02, + "esphttpd" : 0x80, + "fat" : 0x81, + "spiffs" : 0x82, }, } diff --git a/components/partition_table/partitions_singleapp.csv b/components/partition_table/partitions_singleapp.csv index 940b8a76c4..8517b1196f 100644 --- a/components/partition_table/partitions_singleapp.csv +++ b/components/partition_table/partitions_singleapp.csv @@ -1,4 +1,4 @@ # Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M -rfdata, data, rf, , 256K -wifidata, data, wifi, , 256K diff --git a/components/partition_table/partitions_two_ota.csv b/components/partition_table/partitions_two_ota.csv index 8e064e14b2..58c1127d8c 100644 --- a/components/partition_table/partitions_two_ota.csv +++ b/components/partition_table/partitions_two_ota.csv @@ -1,7 +1,7 @@ # Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 factory, 0, 0, 0x10000, 1M ota_0, 0, ota_0, , 1M ota_1, 0, ota_1, , 1M -rfdata, data, rf, , 256K -wifidata, data, wifi, , 256K -otadata, data, ota, , 256K From 4db29f74a0cf2ac950628bd4cf56d08451d333a6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:36:18 +0800 Subject: [PATCH 125/131] add PHY init support --- components/esp32/Kconfig | 54 +++++ components/esp32/Makefile.projbuild | 42 ++++ components/esp32/cpu_start.c | 15 ++ components/esp32/include/esp_phy_init.h | 170 +++++++++++++++ components/esp32/lib | 2 +- components/esp32/phy.h | 51 +++++ components/esp32/phy_init.c | 264 ++++++++++++++++++++++++ components/esp32/phy_init_data.h | 139 +++++++++++++ 8 files changed, 736 insertions(+), 1 deletion(-) create mode 100644 components/esp32/Makefile.projbuild create mode 100644 components/esp32/include/esp_phy_init.h create mode 100644 components/esp32/phy.h create mode 100644 components/esp32/phy_init.c create mode 100644 components/esp32/phy_init_data.h diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 1f04cf4bb7..6bf6499112 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -364,4 +364,58 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL depends on DOCUMENTATION_FOR_RTC_CNTL endchoice +config ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + bool "Store PHY calibration data in NVS" + default y + help + Choose whether to use non-volatile storage library (NVS) + to store PHY calibration data obtained at run time. + If enabled, this will use approximately 2kB of NVS storage + for PHY calibration data. + If this option is not enabled, calibration data will not be stored, + unless application provides its own implementations of + esp_phy_store_cal_data and esp_phy_load_cal_data functions. + See esp_phy_init.h for details. + + If unsure, choose 'y'. + + +config ESP32_PHY_AUTO_INIT + bool "Initialize PHY in startup code" + default y + help + If enabled, PHY will be initialized in startup code, before + app_main function runs. + If this is undesired, disable this option and call esp_phy_init + from the application before enabling WiFi or BT. + + If this option is enabled along with ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS, + startup code will also initialize NVS prior to initializing PHY. + + If unsure, choose 'y'. + +config ESP32_PHY_INIT_DATA_IN_PARTITION + bool "Use a partition to store PHY init data" + default n + help + If enabled, PHY init data will be loaded from a partition. + When using a custom partition table, make sure that PHY data + partition is included (type: 'data', subtype: 'phy'). + With default partition tables, this is done automatically. + If PHY init data is stored in a partition, it has to be flashed there, + otherwise runtime error will occur. + + If this option is not enabled, PHY init data will be embedded + into the application binary. + + If unsure, choose 'n'. + +config ESP32_PHY_MAX_TX_POWER + int "Max TX power (dBm)" + range 0 20 + default 20 + help + Set maximum transmit power. Actual transmit power for high + data rates may be lower than this setting. + endmenu diff --git a/components/esp32/Makefile.projbuild b/components/esp32/Makefile.projbuild new file mode 100644 index 0000000000..1b54f1844d --- /dev/null +++ b/components/esp32/Makefile.projbuild @@ -0,0 +1,42 @@ +ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o +PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin + +PARTITION_TABLE_COMPONENT_PATH := $(COMPONENT_PATH)/../partition_table +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) + +GEN_ESP32PART := $(PYTHON) $(PARTITION_TABLE_COMPONENT_PATH)/gen_esp32part.py -q + +# Path to partition CSV file is relative to project path for custom +# partition CSV files, but relative to component dir otherwise. +PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(PARTITION_TABLE_COMPONENT_PATH))) +PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(subst $(quote),,$(CONFIG_PARTITION_TABLE_FILENAME)))) +PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) + +# Parse partition table and get offset of PHY init data partition +PHY_INIT_GET_ADDR_CMD := $(GEN_ESP32PART) $(PARTITION_TABLE_CSV_PATH) | $(GEN_ESP32PART) - | sed -n -e "s/[^,]*,data,phy,\\([^,]*\\),.*/\\1/p" +PHY_INIT_DATA_ADDR = $(shell $(PHY_INIT_GET_ADDR_CMD)) + +# Command to flash PHY init data partition +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) + +$(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h + $(summary) CC $(notdir $@) + printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - + +$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) + $(summary) BIN $(notdir $@) + $(OBJCOPY) -O binary $< $@ + +phy_init_data: $(PHY_INIT_DATA_BIN) + +phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin + @echo "Flashing PHY init data..." + $(PHY_INIT_DATA_FLASH_CMD) + +phy_init_data-clean: + rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) + +endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index a96fdee950..df839069f9 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -50,6 +50,7 @@ #include "esp_brownout.h" #include "esp_int_wdt.h" #include "esp_task_wdt.h" +#include "esp_phy_init.h" #include "trax.h" void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); @@ -187,6 +188,20 @@ void start_cpu0_default(void) esp_ipc_init(); spi_flash_init(); +#if CONFIG_ESP32_PHY_AUTO_INIT +#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + nvs_flash_init(); +#endif + esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; + if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { + calibration_mode = PHY_RF_CAL_NONE; + } + if (esp_phy_init(calibration_mode) != ESP_OK) { + ESP_LOGD(TAG, "phy init has failed"); + abort(); + } +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h new file mode 100644 index 0000000000..f911276dff --- /dev/null +++ b/components/esp32/include/esp_phy_init.h @@ -0,0 +1,170 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t param_ver_id; /*!< init_data structure version */ + uint8_t crystal_select; /*!< 0: 40MHz, 1: 26 MHz, 2: 24 MHz, 3: auto */ + uint8_t wifi_rx_gain_swp_step_1; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_2; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_3; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_4; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_5; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_6; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_7; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_8; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_9; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_10; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_11; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_12; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_13; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_14; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_15; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_1; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_2; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_3; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_4; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_5; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_6; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_7; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_8; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_9; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_10; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_11; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_12; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_13; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_14; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_15; /*!< do not change */ + uint8_t gain_cmp_1; /*!< do not change */ + uint8_t gain_cmp_6; /*!< do not change */ + uint8_t gain_cmp_11; /*!< do not change */ + uint8_t gain_cmp_ext2_1; /*!< do not change */ + uint8_t gain_cmp_ext2_6; /*!< do not change */ + uint8_t gain_cmp_ext2_11; /*!< do not change */ + uint8_t gain_cmp_ext3_1; /*!< do not change */ + uint8_t gain_cmp_ext3_6; /*!< do not change */ + uint8_t gain_cmp_ext3_11; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_1; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_6; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_11; /*!< do not change */ + uint8_t target_power_qdb_0; /*!< 78 means target power is 78/4=19.5dbm */ + uint8_t target_power_qdb_1; /*!< 76 means target power is 76/4=19dbm */ + uint8_t target_power_qdb_2; /*!< 74 means target power is 74/4=18.5dbm */ + uint8_t target_power_qdb_3; /*!< 68 means target power is 68/4=17dbm */ + uint8_t target_power_qdb_4; /*!< 64 means target power is 64/4=16dbm */ + uint8_t target_power_qdb_5; /*!< 52 means target power is 52/4=13dbm */ + uint8_t target_power_index_mcs0; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (1m,2m,5.5m,11m,6m,9m) */ + uint8_t target_power_index_mcs1; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (12m) */ + uint8_t target_power_index_mcs2; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (18m) */ + uint8_t target_power_index_mcs3; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (24m) */ + uint8_t target_power_index_mcs4; /*!< target power index is 2, means target power is target_power_qdb_2 18.5dbm; (36m) */ + uint8_t target_power_index_mcs5; /*!< target power index is 3, means target power is target_power_qdb_3 17dbm; (48m) */ + uint8_t target_power_index_mcs6; /*!< target power index is 4, means target power is target_power_qdb_4 16dbm; (54m) */ + uint8_t target_power_index_mcs7; /*!< target power index is 5, means target power is target_power_qdb_5 13dbm */ + uint8_t pwr_ind_11b_en; /*!< 0: 11b power is same as mcs0 and 6m, 1: 11b power different with OFDM */ + uint8_t pwr_ind_11b_0; /*!< 1m, 2m power index [0~5] */ + uint8_t pwr_ind_11b_1; /*!< 5.5m, 11m power index [0~5] */ + uint8_t chan_backoff_en; /*!< 0: channel backoff disable, 1:channel backoff enable */ + uint8_t chan1_power_backoff_qdb; /*!< 4 means backoff is 1db */ + uint8_t chan2_power_backoff_qdb; /*!< see chan1_power_backoff_qdb */ + uint8_t chan3_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan4_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan5_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan6_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan7_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan8_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan9_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan10_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan11_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan12_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan13_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan14_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan1_rate_backoff_index; /*!< if bit i is set, backoff data rate is target_power_qdb_i */ + uint8_t chan2_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan3_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan4_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan5_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan6_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan7_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan8_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan9_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan10_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan11_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan12_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan13_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan14_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t spur_freq_cfg_msb_1; /*!< first spur: */ + uint8_t spur_freq_cfg_1; /*!< spur_freq_cfg = (spur_freq_cfg_msb_1 <<8) | spur_freq_cfg_1 */ + uint8_t spur_freq_cfg_div_1; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_1 */ + uint8_t spur_freq_en_h_1; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_1; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */ + uint8_t spur_freq_cfg_msb_2; /*!< second spur: */ + uint8_t spur_freq_cfg_2; /*!< spur_freq_cfg = (spur_freq_cfg_msb_2 <<8) | spur_freq_cfg_2 */ + uint8_t spur_freq_cfg_div_2; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_2 */ + uint8_t spur_freq_en_h_2; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_2; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */ + uint8_t spur_freq_cfg_msb_3; /*!< third spur: */ + uint8_t spur_freq_cfg_3; /*!< spur_freq_cfg = (spur_freq_cfg_msb_3 <<8) | spur_freq_cfg_3 */ + uint8_t spur_freq_cfg_div_3; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_3 */ + uint8_t spur_freq_en_h_3; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_3; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority, */ + uint8_t reserved[23]; /*!< reserved for future expansion */ +} esp_phy_init_data_t; + +typedef struct { + uint8_t opaque[1904]; /*!< opaque calibration data */ +} esp_phy_calibration_data_t; + +typedef enum { + PHY_RF_CAL_PARTIAL = 0x00000000, /*!< Do part of RF calibration. This should be used after power-on reset. */ + PHY_RF_CAL_NONE = 0x00000001, /*!< Don't do any RF calibration. This mode is only suggested to be used after deep sleep reset. */ + PHY_RF_CAL_FULL = 0x00000002 /*!< Do full RF calibration. Produces best results, but also consumes a lot of time and current. Suggested to be used once. */ +} esp_phy_calibration_mode_t; + +/** + * + * @param mode + * @return + */ +esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode); + +#ifndef CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +/** + * + * @param cal_data + * @return + */ +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data); + +/** + * + * @param out_cal_data + * @return + */ +esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data); + +#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +#ifdef __cplusplus +} +#endif + diff --git a/components/esp32/lib b/components/esp32/lib index e188536a63..db867fe912 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit e188536a6315cc3ce4f1006ac3a4450faea6abc6 +Subproject commit db867fe9128cc1fc273d76af5a412f6743519149 diff --git a/components/esp32/phy.h b/components/esp32/phy.h new file mode 100644 index 0000000000..ad6b9003ec --- /dev/null +++ b/components/esp32/phy.h @@ -0,0 +1,51 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "esp_phy_init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file phy.h + * @brief Declarations for functions provided by libphy.a + */ + +/** + * @brief Initialize function pointer table in PHY library. + * @note This function should be called before register_chipv7_phy. + */ +void phy_get_romfunc_addr(void); + +/** + * @brief Initialize PHY module and do RF calibration + * @param[in] init_data Initialization parameters to be used by the PHY + * @param[inout] cal_data As input, calibration data previously obtained. As output, will contain new calibration data. + * @param[in] cal_mode RF calibration mode + * @return reserved for future use + */ +int register_chipv7_phy(const esp_phy_init_data_t* init_data, esp_phy_calibration_data_t *cal_data, esp_phy_calibration_mode_t cal_mode); + +/** + * @brief Get the format version of calibration data used by PHY library. + * @return Format version number + */ +uint32_t phy_get_rf_cal_version(); + +#ifdef __cplusplus +} +#endif + diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c new file mode 100644 index 0000000000..11d571f04e --- /dev/null +++ b/components/esp32/phy_init.c @@ -0,0 +1,264 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "rom/ets_sys.h" +#include "soc/dport_reg.h" + +#include "esp_err.h" +#include "esp_phy_init.h" +#include "esp_system.h" +#include "phy.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "phy_init_data.h" + +static const char* TAG = "phy_init"; + +static const esp_phy_init_data_t* phy_get_init_data(); +static void phy_release_init_data(const esp_phy_init_data_t*); + +esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode) +{ + ESP_LOGD(TAG, "esp_phy_init, mode=%d", mode); + esp_err_t err; + const esp_phy_init_data_t* init_data = phy_get_init_data(); + if (init_data == NULL) { + ESP_LOGE(TAG, "failed to obtain PHY init data"); + return ESP_FAIL; + } + esp_phy_calibration_data_t* cal_data = + (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); + if (cal_data == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); + return ESP_ERR_NO_MEM; + } + // Initialize PHY function pointer table + phy_get_romfunc_addr(); + // Enable WiFi peripheral clock + SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); + // If full calibration is requested, don't need to load previous calibration data + if (mode != PHY_RF_CAL_FULL) { + err = esp_phy_load_cal_data(cal_data); + if (err != ESP_OK) { + ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); + mode = PHY_RF_CAL_FULL; + } + } + ESP_LOGV(TAG, "calling register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, cal_data, mode); + register_chipv7_phy(init_data, cal_data, mode); + if (mode != PHY_RF_CAL_NONE) { + err = esp_phy_store_cal_data(cal_data); + } else { + err = ESP_OK; + } + phy_release_init_data(init_data); + free(cal_data); // PHY maintains a copy of calibration data, so we can free this + return err; +} + +// PHY init data handling functions + +#if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION +#define NO_DEFAULT_INIT_DATA +#include "esp_partition.h" + +static const esp_phy_init_data_t* phy_get_init_data() +{ + const esp_partition_t* partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL); + if (partition == NULL) { + ESP_LOGE(TAG, "PHY data partition not found"); + return NULL; + } + ESP_LOGD(TAG, "loading PHY init data from partition at offset 0x%x", partition->address); + size_t init_data_store_length = sizeof(phy_init_magic_pre) + + sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post); + uint8_t* init_data_store = (uint8_t*) malloc(init_data_store_length); + if (init_data_store == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for PHY init data"); + return NULL; + } + esp_err_t err = esp_partition_read(partition, 0, init_data_store, init_data_store_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to read PHY data partition (%d)", err); + return NULL; + } + if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre)) != 0 || + memcmp(init_data_store + init_data_store_length - sizeof(phy_init_magic_post), + PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0) { + ESP_LOGE(TAG, "failed to validate PHY data partition"); + return NULL; + } + ESP_LOGE(TAG, "PHY data partition validated"); + return (const esp_phy_init_data_t*) (init_data_store + sizeof(phy_init_magic_pre)); +} + +static void phy_release_init_data(const esp_phy_init_data_t* init_data) +{ + free((uint8_t*) init_data - sizeof(phy_init_magic_pre)); +} + +#else // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +// phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data + +static const esp_phy_init_data_t* phy_get_init_data() +{ + ESP_LOGD(TAG, "loading PHY init data from application binary"); + return &phy_init_data; +} + +static void phy_release_init_data(const esp_phy_init_data_t* init_data) +{ + // no-op +} +#endif // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + + +// PHY calibration data handling functions + +#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS +#include "nvs.h" + +static const char* PHY_NAMESPACE = "phy"; +static const char* PHY_CAL_VERSION_KEY = "cal_version"; +static const char* PHY_CAL_MAC_KEY = "cal_mac"; +static const char* PHY_CAL_DATA_KEY = "cal_data"; + +static esp_err_t load_cal_data_from_nvs(nvs_handle handle, + esp_phy_calibration_data_t* out_cal_data); + +static esp_err_t store_cal_data_to_nvs(nvs_handle handle, + const esp_phy_calibration_data_t* cal_data); + +esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err); + return err; + } + else { + err = load_cal_data_from_nvs(handle, out_cal_data); + nvs_close(handle); + return err; + } +} + +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err); + return err; + } + else { + err = store_cal_data_to_nvs(handle, cal_data); + nvs_close(handle); + return err; + } +} + +static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data) +{ + esp_err_t err; + uint32_t cal_data_version; + err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err); + return err; + } + uint32_t cal_format_version = phy_get_rf_cal_version(); + if (cal_data_version != cal_format_version) { + ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d", + __func__, cal_format_version, cal_data_version); + return ESP_FAIL; + } + uint8_t cal_data_mac[6]; + size_t length = sizeof(cal_data_mac); + err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to get cal_mac (%d)", __func__, err); + return err; + } + if (length != sizeof(cal_data_mac)) { + ESP_LOGD(TAG, "%s: invalid length of cal_mac (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + uint8_t sta_mac[6]; + system_efuse_read_mac(sta_mac); + if (memcmp(sta_mac, cal_data_mac, sizeof(sta_mac)) != 0) { + ESP_LOGE(TAG, "%s: calibration data MAC check failed: expected " \ + MACSTR ", found " MACSTR, + __func__, MAC2STR(sta_mac), MAC2STR(cal_data_mac)); + return ESP_FAIL; + } + length = sizeof(*out_cal_data); + err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data, &length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: failed to get cal_data(%d)", __func__, err); + return err; + } + if (length != sizeof(*out_cal_data)) { + ESP_LOGD(TAG, "%s: invalid length of cal_data (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + return ESP_OK; +} + +static esp_err_t store_cal_data_to_nvs(nvs_handle handle, + const esp_phy_calibration_data_t* cal_data) +{ + esp_err_t err; + uint32_t cal_format_version = phy_get_rf_cal_version(); + err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version); + if (err != ESP_OK) { + return err; + } + uint8_t sta_mac[6]; + system_efuse_read_mac(sta_mac); + err = nvs_set_blob(handle, PHY_CAL_MAC_KEY, sta_mac, sizeof(sta_mac)); + if (err != ESP_OK) { + return err; + } + err = nvs_set_blob(handle, PHY_CAL_DATA_KEY, cal_data, sizeof(*cal_data)); + return err; +} + +#else // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +// Default implementation: don't store or load calibration data. +// These functions are defined as weak and can be overridden in the application. + +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +{ + // pretend that calibration data is stored + return ESP_OK; +} + +esp_err_t esp_phy_load_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +{ + // nowhere to load data from + return ESP_ERR_NOT_SUPPORTED; +} + +#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + diff --git a/components/esp32/phy_init_data.h b/components/esp32/phy_init_data.h new file mode 100644 index 0000000000..206598f97c --- /dev/null +++ b/components/esp32/phy_init_data.h @@ -0,0 +1,139 @@ +// Copyright 2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "esp_phy_init.h" +#include "sdkconfig.h" + +// constrain a value between 'low' and 'high', inclusive +#define LIMIT(val, low, high) ((val < low) ? low : (val > high) ? high : val) + +#define PHY_INIT_MAGIC "PHYINIT" + +static const char phy_init_magic_pre[] = PHY_INIT_MAGIC; + +/** + * @brief Structure containing default recommended PHY initialization parameters. + */ +static const esp_phy_init_data_t phy_init_data= { + .param_ver_id = 0, + .crystal_select = 3, + .wifi_rx_gain_swp_step_1 = 0x05, + .wifi_rx_gain_swp_step_2 = 0x04, + .wifi_rx_gain_swp_step_3 = 0x06, + .wifi_rx_gain_swp_step_4 = 0x05, + .wifi_rx_gain_swp_step_5 = 0x01, + .wifi_rx_gain_swp_step_6 = 0x06, + .wifi_rx_gain_swp_step_7 = 0x05, + .wifi_rx_gain_swp_step_8 = 0x04, + .wifi_rx_gain_swp_step_9 = 0x06, + .wifi_rx_gain_swp_step_10 = 0x04, + .wifi_rx_gain_swp_step_11 = 0x05, + .wifi_rx_gain_swp_step_12 = 0x00, + .wifi_rx_gain_swp_step_13 = 0x00, + .wifi_rx_gain_swp_step_14 = 0x00, + .wifi_rx_gain_swp_step_15 = 0x00, + .bt_rx_gain_swp_step_1 = 0x05, + .bt_rx_gain_swp_step_2 = 0x04, + .bt_rx_gain_swp_step_3 = 0x06, + .bt_rx_gain_swp_step_4 = 0x05, + .bt_rx_gain_swp_step_5 = 0x01, + .bt_rx_gain_swp_step_6 = 0x06, + .bt_rx_gain_swp_step_7 = 0x05, + .bt_rx_gain_swp_step_8 = 0x00, + .bt_rx_gain_swp_step_9 = 0x00, + .bt_rx_gain_swp_step_10 = 0x00, + .bt_rx_gain_swp_step_11 = 0x00, + .bt_rx_gain_swp_step_12 = 0x00, + .bt_rx_gain_swp_step_13 = 0x00, + .bt_rx_gain_swp_step_14 = 0x00, + .bt_rx_gain_swp_step_15 = 0x00, + .gain_cmp_1 = 0x0a, + .gain_cmp_6 = 0x0a, + .gain_cmp_11 = 0x0c, + .gain_cmp_ext2_1 = 0xf0, + .gain_cmp_ext2_6 = 0xf0, + .gain_cmp_ext2_11 = 0xf0, + .gain_cmp_ext3_1 = 0xe0, + .gain_cmp_ext3_6 = 0xe0, + .gain_cmp_ext3_11 = 0xe0, + .gain_cmp_bt_ofs_1 = 0x18, + .gain_cmp_bt_ofs_6 = 0x18, + .gain_cmp_bt_ofs_11 = 0x18, + .target_power_qdb_0 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 78), + .target_power_qdb_1 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 76), + .target_power_qdb_2 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 74), + .target_power_qdb_3 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 68), + .target_power_qdb_4 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 64), + .target_power_qdb_5 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 52), + .target_power_index_mcs0 = 0, + .target_power_index_mcs1 = 0, + .target_power_index_mcs2 = 1, + .target_power_index_mcs3 = 1, + .target_power_index_mcs4 = 2, + .target_power_index_mcs5 = 3, + .target_power_index_mcs6 = 4, + .target_power_index_mcs7 = 5, + .pwr_ind_11b_en = 0, + .pwr_ind_11b_0 = 0, + .pwr_ind_11b_1 = 0, + .chan_backoff_en = 0, + .chan1_power_backoff_qdb = 0, + .chan2_power_backoff_qdb = 0, + .chan3_power_backoff_qdb = 0, + .chan4_power_backoff_qdb = 0, + .chan5_power_backoff_qdb = 0, + .chan6_power_backoff_qdb = 0, + .chan7_power_backoff_qdb = 0, + .chan8_power_backoff_qdb = 0, + .chan9_power_backoff_qdb = 0, + .chan10_power_backoff_qdb = 0, + .chan11_power_backoff_qdb = 0, + .chan12_power_backoff_qdb = 0, + .chan13_power_backoff_qdb = 0, + .chan14_power_backoff_qdb = 0, + .chan1_rate_backoff_index = 0, + .chan2_rate_backoff_index = 0, + .chan3_rate_backoff_index = 0, + .chan4_rate_backoff_index = 0, + .chan5_rate_backoff_index = 0, + .chan6_rate_backoff_index = 0, + .chan7_rate_backoff_index = 0, + .chan8_rate_backoff_index = 0, + .chan9_rate_backoff_index = 0, + .chan10_rate_backoff_index = 0, + .chan11_rate_backoff_index = 0, + .chan12_rate_backoff_index = 0, + .chan13_rate_backoff_index = 0, + .chan14_rate_backoff_index = 0, + .spur_freq_cfg_msb_1 = 0, + .spur_freq_cfg_1 = 0, + .spur_freq_cfg_div_1 = 0, + .spur_freq_en_h_1 = 0, + .spur_freq_en_l_1 = 0, + .spur_freq_cfg_msb_2 = 0, + .spur_freq_cfg_2 = 0, + .spur_freq_cfg_div_2 = 0, + .spur_freq_en_h_2 = 0, + .spur_freq_en_l_2 = 0, + .spur_freq_cfg_msb_3 = 0, + .spur_freq_cfg_3 = 0, + .spur_freq_cfg_div_3 = 0, + .spur_freq_en_h_3 = 0, + .spur_freq_en_l_3 = 0, + .reserved = {0} +}; + +static const char phy_init_magic_post[] = PHY_INIT_MAGIC; + From 6d4ab76db2a9f24371864c9f4238ea635b598eed Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 12:22:32 +1100 Subject: [PATCH 126/131] phy init data: Read PHY init data partition offset from menuconfig --- components/esp32/Makefile.projbuild | 30 +++----- components/partition_table/Kconfig.projbuild | 72 +++++++++++-------- .../partition_table/partitions_singleapp.csv | 1 + .../partition_table/partitions_two_ota.csv | 1 + 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/components/esp32/Makefile.projbuild b/components/esp32/Makefile.projbuild index 1b54f1844d..01f8d03c5a 100644 --- a/components/esp32/Makefile.projbuild +++ b/components/esp32/Makefile.projbuild @@ -3,34 +3,21 @@ ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin -PARTITION_TABLE_COMPONENT_PATH := $(COMPONENT_PATH)/../partition_table -ESP32_COMPONENT_PATH := $(COMPONENT_PATH) - -GEN_ESP32PART := $(PYTHON) $(PARTITION_TABLE_COMPONENT_PATH)/gen_esp32part.py -q - -# Path to partition CSV file is relative to project path for custom -# partition CSV files, but relative to component dir otherwise. -PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(PARTITION_TABLE_COMPONENT_PATH))) -PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(subst $(quote),,$(CONFIG_PARTITION_TABLE_FILENAME)))) -PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) - -# Parse partition table and get offset of PHY init data partition -PHY_INIT_GET_ADDR_CMD := $(GEN_ESP32PART) $(PARTITION_TABLE_CSV_PATH) | $(GEN_ESP32PART) - | sed -n -e "s/[^,]*,data,phy,\\([^,]*\\),.*/\\1/p" -PHY_INIT_DATA_ADDR = $(shell $(PHY_INIT_GET_ADDR_CMD)) - # Command to flash PHY init data partition -PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) -ESPTOOL_ALL_FLASH_ARGS += $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) + +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) $(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h $(summary) CC $(notdir $@) printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - - + $(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) $(summary) BIN $(notdir $@) - $(OBJCOPY) -O binary $< $@ + $(OBJCOPY) -O binary $< $@ -phy_init_data: $(PHY_INIT_DATA_BIN) +phy_init_data: $(PHY_INIT_DATA_BIN) phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin @echo "Flashing PHY init data..." @@ -39,4 +26,7 @@ phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin phy_init_data-clean: rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) +all: phy_init_data +flash: phy_init_data + endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index fa2685d7a1..1f019a6e3f 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -1,49 +1,63 @@ menu "Partition Table" choice - prompt "Partition Table" - default PARTITION_TABLE_SINGLE_APP - help - The partition table to flash to the ESP32. The partition table - determines where apps, data and other resources are expected to - be found. + prompt "Partition Table" + default PARTITION_TABLE_SINGLE_APP + help + The partition table to flash to the ESP32. The partition table + determines where apps, data and other resources are expected to + be found. - The predefined partition table CSV descriptions can be found - in the components/partition_table directory. Otherwise it's - possible to create a new custom partition CSV for your application. + The predefined partition table CSV descriptions can be found + in the components/partition_table directory. Otherwise it's + possible to create a new custom partition CSV for your application. config PARTITION_TABLE_SINGLE_APP - bool "Single factory app, no OTA" + bool "Single factory app, no OTA" config PARTITION_TABLE_TWO_OTA - bool "Factory app, two OTA definitions" + bool "Factory app, two OTA definitions" config PARTITION_TABLE_CUSTOM - bool "Custom partition table CSV" + bool "Custom partition table CSV" endchoice config PARTITION_TABLE_CUSTOM_FILENAME - string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM - default partitions.csv - help - Name of the custom partition CSV filename. This path is evaluated - relative to the project root directory. + string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM + default partitions.csv + help + Name of the custom partition CSV filename. This path is evaluated + relative to the project root directory. config PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET - hex "App offset in flash" if PARTITION_TABLE_CUSTOM - default 0x10000 - help - If using a custom partition table, specify the offset in the flash - where 'make flash' should write the built app. + hex "Factory app partition offset" if PARTITION_TABLE_CUSTOM + default 0x10000 + help + If using a custom partition table, specify the offset in the flash + where 'make flash' should write the built app. + +config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET + hex "PHY data partition offset" if PARTITION_TABLE_CUSTOM + depends on ESP32_PHY_INIT_DATA_IN_PARTITION + default 0xf000 + help + If using a custom partition table, specify the offset in the flash + where 'make flash' should write the initial PHY data file. + config PARTITION_TABLE_FILENAME - string - default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP - default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA - default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM + string + default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP + default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA + default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config APP_OFFSET - hex - default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM - default 0x10000 # this is the factory app offset used by the default tables + hex + default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM + default 0x10000 # this is the factory app offset used by the default tables + +config PHY_DATA_OFFSET + hex + default PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET if PARTITION_TABLE_CUSTOM + default 0xf000 # this is the factory app offset used by the default tables endmenu diff --git a/components/partition_table/partitions_singleapp.csv b/components/partition_table/partitions_singleapp.csv index 8517b1196f..e1647008ee 100644 --- a/components/partition_table/partitions_singleapp.csv +++ b/components/partition_table/partitions_singleapp.csv @@ -1,4 +1,5 @@ # Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M diff --git a/components/partition_table/partitions_two_ota.csv b/components/partition_table/partitions_two_ota.csv index 58c1127d8c..afb43967a3 100644 --- a/components/partition_table/partitions_two_ota.csv +++ b/components/partition_table/partitions_two_ota.csv @@ -1,4 +1,5 @@ # Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 From 541b1426546a4ee22089e78177ad13d380d3a3d6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 01:18:39 +0800 Subject: [PATCH 127/131] phy_init: reduce the amount of hardwired logic, add coexist init --- components/esp32/Kconfig | 19 +--- components/esp32/cpu_start.c | 45 +++++++--- components/esp32/include/esp_phy_init.h | 107 ++++++++++++++++++++--- components/esp32/include/soc/dport_reg.h | 1 + components/esp32/lib | 2 +- components/esp32/phy.h | 15 +++- components/esp32/phy_init.c | 104 +++++++--------------- components/nvs_flash/src/nvs_api.cpp | 14 +-- 8 files changed, 185 insertions(+), 122 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 6bf6499112..206ea669e1 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -364,21 +364,6 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL depends on DOCUMENTATION_FOR_RTC_CNTL endchoice -config ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - bool "Store PHY calibration data in NVS" - default y - help - Choose whether to use non-volatile storage library (NVS) - to store PHY calibration data obtained at run time. - If enabled, this will use approximately 2kB of NVS storage - for PHY calibration data. - If this option is not enabled, calibration data will not be stored, - unless application provides its own implementations of - esp_phy_store_cal_data and esp_phy_load_cal_data functions. - See esp_phy_init.h for details. - - If unsure, choose 'y'. - config ESP32_PHY_AUTO_INIT bool "Initialize PHY in startup code" @@ -389,8 +374,8 @@ config ESP32_PHY_AUTO_INIT If this is undesired, disable this option and call esp_phy_init from the application before enabling WiFi or BT. - If this option is enabled along with ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS, - startup code will also initialize NVS prior to initializing PHY. + If this option is enabled, startup code will also initialize + NVS prior to initializing PHY. If unsure, choose 'y'. diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index df839069f9..1eb0a5355e 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -63,6 +63,7 @@ static bool app_cpu_started = false; #endif //!CONFIG_FREERTOS_UNICORE static void do_global_ctors(void); +static void do_phy_init(); static void main_task(void* args); extern void app_main(void); @@ -189,17 +190,8 @@ void start_cpu0_default(void) spi_flash_init(); #if CONFIG_ESP32_PHY_AUTO_INIT -#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS nvs_flash_init(); -#endif - esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; - if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { - calibration_mode = PHY_RF_CAL_NONE; - } - if (esp_phy_init(calibration_mode) != ESP_OK) { - ESP_LOGD(TAG, "phy init has failed"); - abort(); - } + do_phy_init(); #endif xTaskCreatePinnedToCore(&main_task, "main", @@ -239,3 +231,36 @@ static void main_task(void* args) vTaskDelete(NULL); } +static void do_phy_init() +{ + esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; + if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { + calibration_mode = PHY_RF_CAL_NONE; + } + const esp_phy_init_data_t* init_data = esp_phy_get_init_data(); + if (init_data == NULL) { + ESP_LOGE(TAG, "failed to obtain PHY init data"); + abort(); + } + esp_phy_calibration_data_t* cal_data = + (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); + if (cal_data == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); + abort(); + } + esp_err_t err = esp_phy_load_cal_data_from_nvs(cal_data); + if (err != ESP_OK) { + ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); + calibration_mode = PHY_RF_CAL_FULL; + } + + esp_phy_init(init_data, calibration_mode, cal_data); + + if (calibration_mode != PHY_RF_CAL_NONE) { + err = esp_phy_store_cal_data_to_nvs(cal_data); + } else { + err = ESP_OK; + } + esp_phy_release_init_data(init_data); + free(cal_data); // PHY maintains a copy of calibration data, so we can free this +} diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h index f911276dff..7bc0536103 100644 --- a/components/esp32/include/esp_phy_init.h +++ b/components/esp32/include/esp_phy_init.h @@ -20,6 +20,14 @@ extern "C" { #endif +/** + * @file PHY init parameters and API + */ + + +/** + * @brief Structure holding PHY init parameters + */ typedef struct { uint8_t param_ver_id; /*!< init_data structure version */ uint8_t crystal_select; /*!< 0: 40MHz, 1: 26 MHz, 2: 24 MHz, 3: auto */ @@ -129,8 +137,11 @@ typedef struct { uint8_t reserved[23]; /*!< reserved for future expansion */ } esp_phy_init_data_t; +/** + * @brief Opaque PHY calibration data + */ typedef struct { - uint8_t opaque[1904]; /*!< opaque calibration data */ + uint8_t opaque[1904]; /*!< calibration data */ } esp_phy_calibration_data_t; typedef enum { @@ -140,29 +151,97 @@ typedef enum { } esp_phy_calibration_mode_t; /** + * @brief Get PHY init data * - * @param mode - * @return + * If "Use a partition to store PHY init data" option is set in menuconfig, + * This function will load PHY init data from a partition. Otherwise, + * PHY init data will be compiled into the application itself, and this function + * will return a pointer to PHY init data located in read-only memory (DROM). + * + * If "Use a partition to store PHY init data" option is enabled, this function + * may return NULL if the data loaded from flash is not valid. + * + * @note Call esp_phy_release_init_data to release the pointer obtained using + * this function after the call to esp_wifi_init. + * + * @return pointer to PHY init data structure */ -esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode); - -#ifndef CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS +const esp_phy_init_data_t* esp_phy_get_init_data(); /** - * - * @param cal_data - * @return + * @brief Release PHY init data + * @param data pointer to PHY init data structure obtained from + * esp_phy_get_init_data function */ -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data); +void esp_phy_release_init_data(const esp_phy_init_data_t* data); /** + * @brief Function called by esp_phy_init to load PHY calibration data * - * @param out_cal_data - * @return + * This is a convenience function which can be used to load PHY calibration + * data from NVS. Data can be stored to NVS using esp_phy_store_cal_data_to_nvs + * function. + * + * If calibration data is not present in the NVS, or + * data is not valid (was obtained for a chip with a different MAC address, + * or obtained for a different version of software), this function will + * return an error. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to load calibration data. To provide a different + * mechanism for loading calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. For an example usage of esp_phy_init and + * this function, see do_phy_init function in cpu_start.c + * + * @param out_cal_data pointer to calibration data structure to be filled with + * loaded data. + * @return ESP_OK on success */ -esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data); +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data); + +/** + * @brief Function called by esp_phy_init to store PHY calibration data + * + * This is a convenience function which can be used to store PHY calibration + * data to the NVS. Calibration data is returned by esp_phy_init function. + * Data saved using this function to the NVS can later be loaded using + * esp_phy_store_cal_data_to_nvs function. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to store calibration data. To provide a different + * mechanism for storing calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. + * + * @param cal_data pointer to calibration data which has to be saved. + * @return ESP_OK on success + */ +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data); + +/** + * @brief Initialize PHY module + * + * PHY module should be initialized in order to use WiFi or BT. + * If "Initialize PHY in startup code" option is set in menuconfig, + * this function will be called automatically before app_main is called, + * using parameters obtained from esp_phy_get_init_data. + * + * Applications which don't need to enable PHY on every start up should + * disable this menuconfig option and call esp_phy_init before calling + * esp_wifi_init or bt_controller_init. See do_phy_init function in + * cpu_start.c for an example of using this function. + * + * @param init_data PHY parameters. Default set of parameters can + * be obtained by calling esp_phy_get_default_init_data + * function. + * @param mode Calibration mode (Full, partial, or no calibration) + * @param[inout] calibration_data + * @return ESP_OK on success. + */ +esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data, + esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data); -#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS #ifdef __cplusplus } diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index 0c43c08740..1be0fdee14 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -1028,6 +1028,7 @@ #define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) /* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: */ +#define DPORT_MAC_RST (BIT(2)) #define DPORT_WIFI_RST 0xFFFFFFFF #define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) #define DPORT_WIFI_RST_V 0xFFFFFFFF diff --git a/components/esp32/lib b/components/esp32/lib index db867fe912..a580f70a64 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit db867fe9128cc1fc273d76af5a412f6743519149 +Subproject commit a580f70a64872a7cc291b1f22455f6adbc2e35cf diff --git a/components/esp32/phy.h b/components/esp32/phy.h index ad6b9003ec..81990f2e36 100644 --- a/components/esp32/phy.h +++ b/components/esp32/phy.h @@ -41,10 +41,23 @@ int register_chipv7_phy(const esp_phy_init_data_t* init_data, esp_phy_calibratio /** * @brief Get the format version of calibration data used by PHY library. - * @return Format version number + * @return Format version number, OR'ed with BIT(16) if PHY is in WIFI only mode. */ uint32_t phy_get_rf_cal_version(); +/** + * @brief Set RF/BB for only WIFI mode or coexist(WIFI & BT) mode + * @param[in] true is for only WIFI mode, false is for coexist mode. default is 0. + * @return NULL + */ +void phy_set_wifi_mode_only(bool wifi_only); + +/** + * @brief Set BT the highest priority in coexist mode. + * @return NULL + */ +void coex_bt_high_prio(void); + #ifdef __cplusplus } #endif diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index 11d571f04e..32d8f64edf 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -25,60 +25,37 @@ #include "esp_system.h" #include "phy.h" #include "esp_log.h" +#include "nvs.h" #include "sdkconfig.h" #include "phy_init_data.h" static const char* TAG = "phy_init"; -static const esp_phy_init_data_t* phy_get_init_data(); -static void phy_release_init_data(const esp_phy_init_data_t*); -esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode) +esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data, + esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data) { - ESP_LOGD(TAG, "esp_phy_init, mode=%d", mode); - esp_err_t err; - const esp_phy_init_data_t* init_data = phy_get_init_data(); - if (init_data == NULL) { - ESP_LOGE(TAG, "failed to obtain PHY init data"); - return ESP_FAIL; - } - esp_phy_calibration_data_t* cal_data = - (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); - if (cal_data == NULL) { - ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); - return ESP_ERR_NO_MEM; - } - // Initialize PHY function pointer table + assert(init_data); + assert(calibration_data); + // Initialize PHY pointer table phy_get_romfunc_addr(); + REG_SET_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); + REG_CLR_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); // Enable WiFi peripheral clock SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); - // If full calibration is requested, don't need to load previous calibration data - if (mode != PHY_RF_CAL_FULL) { - err = esp_phy_load_cal_data(cal_data); - if (err != ESP_OK) { - ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); - mode = PHY_RF_CAL_FULL; - } - } - ESP_LOGV(TAG, "calling register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, cal_data, mode); - register_chipv7_phy(init_data, cal_data, mode); - if (mode != PHY_RF_CAL_NONE) { - err = esp_phy_store_cal_data(cal_data); - } else { - err = ESP_OK; - } - phy_release_init_data(init_data); - free(cal_data); // PHY maintains a copy of calibration data, so we can free this - return err; + ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", + init_data, calibration_data, mode); + phy_set_wifi_mode_only(0); + register_chipv7_phy(init_data, calibration_data, mode); + coex_bt_high_prio(); + return ESP_OK; } // PHY init data handling functions - #if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION -#define NO_DEFAULT_INIT_DATA #include "esp_partition.h" -static const esp_phy_init_data_t* phy_get_init_data() +const esp_phy_init_data_t* esp_phy_get_init_data() { const esp_partition_t* partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL); @@ -109,7 +86,7 @@ static const esp_phy_init_data_t* phy_get_init_data() return (const esp_phy_init_data_t*) (init_data_store + sizeof(phy_init_magic_pre)); } -static void phy_release_init_data(const esp_phy_init_data_t* init_data) +void esp_phy_release_init_data(const esp_phy_init_data_t* init_data) { free((uint8_t*) init_data - sizeof(phy_init_magic_pre)); } @@ -118,13 +95,13 @@ static void phy_release_init_data(const esp_phy_init_data_t* init_data) // phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data -static const esp_phy_init_data_t* phy_get_init_data() +const esp_phy_init_data_t* esp_phy_get_init_data() { ESP_LOGD(TAG, "loading PHY init data from application binary"); return &phy_init_data; } -static void phy_release_init_data(const esp_phy_init_data_t* init_data) +void esp_phy_release_init_data(const esp_phy_init_data_t* init_data) { // no-op } @@ -132,22 +109,18 @@ static void phy_release_init_data(const esp_phy_init_data_t* init_data) // PHY calibration data handling functions - -#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS -#include "nvs.h" - static const char* PHY_NAMESPACE = "phy"; static const char* PHY_CAL_VERSION_KEY = "cal_version"; static const char* PHY_CAL_MAC_KEY = "cal_mac"; static const char* PHY_CAL_DATA_KEY = "cal_data"; -static esp_err_t load_cal_data_from_nvs(nvs_handle handle, +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data); -static esp_err_t store_cal_data_to_nvs(nvs_handle handle, +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, const esp_phy_calibration_data_t* cal_data); -esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data) { nvs_handle handle; esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle); @@ -156,13 +129,13 @@ esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) return err; } else { - err = load_cal_data_from_nvs(handle, out_cal_data); + err = load_cal_data_from_nvs_handle(handle, out_cal_data); nvs_close(handle); return err; } } -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data) { nvs_handle handle; esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle); @@ -171,13 +144,14 @@ esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) return err; } else { - err = store_cal_data_to_nvs(handle, cal_data); + err = store_cal_data_to_nvs_handle(handle, cal_data); nvs_close(handle); return err; } } -static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data) +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, + esp_phy_calibration_data_t* out_cal_data) { esp_err_t err; uint32_t cal_data_version; @@ -186,7 +160,8 @@ static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_d ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err); return err; } - uint32_t cal_format_version = phy_get_rf_cal_version(); + uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16)); + ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version); if (cal_data_version != cal_format_version) { ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d", __func__, cal_format_version, cal_data_version); @@ -224,11 +199,12 @@ static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_d return ESP_OK; } -static esp_err_t store_cal_data_to_nvs(nvs_handle handle, +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, const esp_phy_calibration_data_t* cal_data) { esp_err_t err; - uint32_t cal_format_version = phy_get_rf_cal_version(); + uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16)); + ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version); err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version); if (err != ESP_OK) { return err; @@ -243,22 +219,6 @@ static esp_err_t store_cal_data_to_nvs(nvs_handle handle, return err; } -#else // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - -// Default implementation: don't store or load calibration data. -// These functions are defined as weak and can be overridden in the application. - -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +void register_chipv7_phy_stub() { - // pretend that calibration data is stored - return ESP_OK; } - -esp_err_t esp_phy_load_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) -{ - // nowhere to load data from - return ESP_ERR_NOT_SUPPORTED; -} - -#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 751542ee12..7c9ec89a6f 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -62,6 +62,13 @@ extern "C" void nvs_dump() s_nvs_storage.debugDump(); } +extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) +{ + ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); + s_nvs_handles.clear(); + return s_nvs_storage.init(baseSector, sectorCount); +} + #ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { @@ -81,13 +88,6 @@ extern "C" esp_err_t nvs_flash_init(void) } #endif -extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) -{ - ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); - s_nvs_handles.clear(); - return s_nvs_storage.init(baseSector, sectorCount); -} - static esp_err_t nvs_find_ns_handle(nvs_handle handle, HandleEntry& entry) { auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool { From 0b265dc2a71247eccf0574b3a60fcebb7f99ceee Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 19:17:13 +0800 Subject: [PATCH 128/131] nvs, spi_flash: handle case when source data is in DROM --- components/nvs_flash/src/nvs_page.cpp | 25 +++++++++++++++++++- components/spi_flash/flash_ops.c | 6 +++++ components/spi_flash/include/esp_spi_flash.h | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index d2ca225352..80ccb1f6d0 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -114,7 +114,30 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) assert(mFirstUsedEntry != INVALID_ENTRY); const uint16_t count = size / ENTRY_SIZE; - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); + const uint8_t* buf = data; + +#ifdef ESP_PLATFORM + /* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write + * function. To work around this, we copy the data to heap if it came from DROM. + * Hopefully this won't happen very often in practice. For data from DRAM, we should + * still be able to write it to flash directly. + * TODO: figure out how to make this platform-specific check nicer (probably by introducing + * a platform-specific flash layer). + */ + if ((uint32_t) data < 0x3ff00000) { + buf = (uint8_t*) malloc(size); + if (!buf) { + return ESP_ERR_NO_MEM; + } + memcpy((void*)buf, data, size); + } +#endif //ESP_PLATFORM + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), buf, size); +#ifdef ESP_PLATFORM + if (buf != data) { + free((void*)buf); + } +#endif //ESP_PLATFORM if (rc != ESP_OK) { mState = PageState::INVALID; return rc; diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 134e1fe65b..3358c550f2 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -135,6 +135,12 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t si if (size % 4 != 0) { return ESP_ERR_INVALID_SIZE; } + if ((uint32_t) src < 0x3ff00000) { + // if source address is in DROM, we won't be able to read it + // from within SPIWrite + // TODO: consider buffering source data using heap and writing it anyway? + return ESP_ERR_INVALID_ARG; + } // Out of bound writes are checked in ROM code, but we can give better // error code here if (dest_addr + size > g_rom_flashchip.chip_size) { diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 5d124da6b2..f940c0ad50 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -76,6 +76,8 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * * @note Address in flash, dest, has to be 4-byte aligned. * This is a temporary limitation which will be removed. + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. * * @param dest destination address in Flash * @param src pointer to the source buffer From 1f6585dd4ff67c9d3feef78691478c9e64967f88 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 20:34:54 +0800 Subject: [PATCH 129/131] docs: update partition tables documentation --- docs/partition-tables.rst | 46 ++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index a1a46866ee..c9709029d8 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -17,40 +17,30 @@ The simplest way to use the partition table is to `make menuconfig` and choose o In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table. -Known Issues ------------- - -The below design document outlines the goals for the partition table system. At the moment, only some features are used: - -- data partition types "rf" & "wifi" are unused and can be entirely omitted to save space. -- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x9000. - -Once a full user API is in place for partition access, these limitations will be resolved and you'll be able to use the partition mechanism fully for storing data in flash. - Built-in Partition Tables ------------------------- Here is the summary printed for the "Single factory app, no OTA" configuration:: # Espressif ESP32 Partition Table - # Name, Type, SubType, Offset, Size - factory, app, factory, 0x10000, 1M - rfdata, data, rf, 0x110000, 256K - wifidata,data, wifi, 0x150000, 256K + # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M * At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default. -* There are also two data regions defined in the partition table for storing RF & Wifi calibration data. +* There are also two data regions defined in the partition table for storing NVS library partition and PHY init data. Here is the summary printed for the "Factory app, two OTA definitions" configuration:: # Espressif ESP32 Partition Table - # Name, Type, SubType, Offset, Size - factory, app, factory, 0x10000, 1M - ota_0, app, ota_0, 0x110000, 1M - ota_1, app, ota_1, 0x210000, 1M - rfdata, data, rf, 0x310000, 256K - wifidata,data, wifi, 0x350000, 256K - otadata, data, ota, 0x390000, 256K + # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x4000 + otadata, data, ota, 0xd000, 0x2000 + phy_init, data, phy, 0xf000, 0x1000 + factory, 0, 0, 0x10000, 1M + ota_0, 0, ota_0, , 1M + ota_1, 0, ota_1, , 1M * There are now three app partition definitions. * The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps. @@ -65,13 +55,13 @@ If you choose "Custom partition table CSV" in menuconfig then you can also enter The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table:: # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x4000 + otadata, data, ota, 0xd000, 0x2000 + phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M ota_0, app, ota_0, , 1M ota_1, app, ota_1, , 1M - rfdata, data, rf, , 256K - wifidata, data, wifi, , 256K - otadata, data, ota, , 256K - + * Whitespace between fields is ignored, and so is any line starting with # (comments). * Each non-comment line in the CSV file is a partition definition. * Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition. @@ -93,7 +83,7 @@ Subtype When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot -When type is "data", the subtype field can be specified as ota (0), rf (1), wifi (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Other "data" subtypes are reserved for Espressif use. To create custom data partition subtypes then use a custom type value, and choose any subtype 0x00-0xFF. +When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Subtypes 0-0x7f are reserved for Espressif use. To create custom data partition subtypes use "data" type, and choose any unused subtype in 0x80-0xFF range. If you are porting a filesystem to the ESP-IDF, consider opening a PR to add the new subtype to esp_partition.h file. Offset & Size ~~~~~~~~~~~~~ @@ -104,6 +94,8 @@ App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes). +NVS data partition has to be at least 0x3000 bytes long, and OTA data parition has to be 0x2000 bytes long. If you are using NVS in your application to store a lot of data, consider using a custom partition table with larger NVS partition. + Generating Binary Partition Table --------------------------------- From 659ec8facbedd9863a437626af6fb96c42c1f9b3 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 18 Nov 2016 21:11:54 +0800 Subject: [PATCH 130/131] esp32: add phy_printf 1. add phy_printf into libcore 2. update libphy.a which has important optimization for ht40 traffic --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index a580f70a64..ea9c156e8a 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a580f70a64872a7cc291b1f22455f6adbc2e35cf +Subproject commit ea9c156e8a67d27623eab6f98ce5a55a00c8fb19 From 1d4775558814b6673d59078cc37c8c65274b5d49 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 21 Nov 2016 18:08:22 +1100 Subject: [PATCH 131/131] mbedtls hardware bignum: Use memcpy instead of REG_WRITE/REG_READ in a loop Removes memory barriers for better performance, thanks Ivan for pointing this out. Manually unrolling the loop further seemed like diminishing returns. --- components/mbedtls/port/esp_bignum.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 68fd8bec98..7570820e3b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -121,12 +121,16 @@ static inline size_t bits_to_hardware_words(size_t num_bits) */ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t num_words) { - for(size_t i = 0; i < mpi->n && i < num_words; i++) { - REG_WRITE(mem_base + i * 4, mpi->p[i]); - } - for(size_t i = mpi->n; i < num_words; i++) { - REG_WRITE(mem_base + i * 4, 0); - } + uint32_t *pbase = (uint32_t *)mem_base; + uint32_t copy_words = num_words < mpi->n ? num_words : mpi->n; + + /* Copy MPI data to memory block registers */ + memcpy(pbase, mpi->p, copy_words * 4); + + /* Zero any remaining memory block data */ + bzero(pbase + copy_words, (num_words - copy_words) * 4); + + /* Note: not executing memw here, can do it before we start a bignum operation */ } /* Read mbedTLS MPI bignum back from hardware memory block. @@ -141,15 +145,16 @@ static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_wo MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, num_words) ); - for(int i = 0; i < num_words; i++) { - x->p[i] = REG_READ(mem_base + i * 4); - } + /* Copy data from memory block registers */ + memcpy(x->p, (uint32_t *)mem_base, num_words * 4); + /* Zero any remaining limbs in the bignum, if the buffer is bigger than num_words */ for(size_t i = num_words; i < x->n; i++) { x->p[i] = 0; } + asm volatile ("memw"); cleanup: return ret; } @@ -218,6 +223,9 @@ static inline void execute_op(uint32_t op_reg) /* Clear interrupt status */ REG_WRITE(RSA_INTERRUPT_REG, 1); + /* Note: above REG_WRITE includes a memw, so we know any writes + to the memory blocks are also complete. */ + REG_WRITE(op_reg, 1); #ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT