From c30149e4bd8b51d457cc57d445561e6c8248e430 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 17 Jul 2017 15:38:19 +0800 Subject: [PATCH 01/26] feature(gpio): add APIs to set and get pad drive capability. RTC pads use RTC registers to contol drive capability. --- components/driver/gpio.c | 26 ++++++ components/driver/include/driver/gpio.h | 31 +++++++ components/driver/include/driver/rtc_io.h | 25 ++++++ components/driver/rtc_module.c | 103 +++++++++++++--------- 4 files changed, 145 insertions(+), 40 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 0477d5ebb2..97ad4224c7 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -456,3 +456,29 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num) GPIO.pin[gpio_num].wakeup_enable = 0; return ESP_OK; } + +esp_err_t gpio_set_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t strength) +{ + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(strength < GPIO_DRIVE_CAP_MAX, "GPIO drive capability error", ESP_ERR_INVALID_ARG); + + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + rtc_gpio_set_drive_capability(gpio_num, strength); + } else { + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[gpio_num], FUN_DRV_V, strength, FUN_DRV_S); + } + return ESP_OK; +} + +esp_err_t gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t* strength) +{ + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(strength != NULL, "GPIO drive capability pointer error", ESP_ERR_INVALID_ARG); + + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + return rtc_gpio_get_drive_capability(gpio_num, strength); + } else { + *strength = GET_PERI_REG_BITS2(GPIO_PIN_MUX_REG[gpio_num], FUN_DRV_V, FUN_DRV_S); + } + return ESP_OK; +} diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index af727a8d74..c68caee79c 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -219,6 +219,14 @@ typedef enum { GPIO_FLOATING, /*!< Pad floating */ } gpio_pull_mode_t; +typedef enum { + GPIO_DRIVE_CAP_0 = 0, /*!< Pad drive capability: weak */ + GPIO_DRIVE_CAP_1 = 1, /*!< Pad drive capability: stronger */ + GPIO_DRIVE_CAP_2 = 2, /*!< Pad drive capability: default value */ + GPIO_DRIVE_CAP_DEFAULT = 2, /*!< Pad drive capability: default value */ + GPIO_DRIVE_CAP_3 = 3, /*!< Pad drive capability: strongest */ + GPIO_DRIVE_CAP_MAX, +} gpio_drive_cap_t; typedef void (*gpio_isr_t)(void*); typedef intr_handle_t gpio_isr_handle_t; @@ -481,6 +489,29 @@ esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void */ esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num); +/** + * @brief Set GPIO pad drive capability + * + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Drive capability of the pad + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_set_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t strength); + +/** + * @brief Get GPIO pad drive capability + * + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Pointer to accept drive capability of the pad + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t* strength); #ifdef __cplusplus } diff --git a/components/driver/include/driver/rtc_io.h b/components/driver/include/driver/rtc_io.h index 65de941a44..5abff18805 100644 --- a/components/driver/include/driver/rtc_io.h +++ b/components/driver/include/driver/rtc_io.h @@ -40,6 +40,8 @@ typedef struct { uint32_t slpie; /*!< Mask of input enable in sleep mode */ uint32_t hold; /*!< Mask of hold enable */ uint32_t hold_force;/*!< Mask of hold_force bit for RTC IO in RTC_CNTL_HOLD_FORCE_REG */ + uint32_t drv_v; /*!< Mask of drive capability */ + uint32_t drv_s; /*!< Offset of drive capability */ int rtc_num; /*!< RTC IO number, or -1 if not an RTC GPIO */ } rtc_gpio_desc_t; @@ -232,6 +234,29 @@ esp_err_t rtc_gpio_hold_dis(gpio_num_t gpio_num); */ void rtc_gpio_force_hold_dis_all(); +/** + * @brief Set RTC GPIO pad drive capability + * + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Drive capability of the pad + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t rtc_gpio_set_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t strength); + +/** + * @brief Get RTC GPIO pad drive capability + * + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Pointer to accept drive capability of the pad + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t rtc_gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t* strength); #ifdef __cplusplus } diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index c6d2727e02..39e5de72dd 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -27,6 +27,7 @@ #include "esp_intr_alloc.h" #include "sys/lock.h" #include "driver/rtc_cntl.h" +#include "driver/gpio.h" #ifndef NDEBUG // Enable built-in checks in queue.h in debug builds @@ -54,46 +55,46 @@ static xSemaphoreHandle rtc_touch_sem = NULL; //Reg,Mux,Fun,IE,Up,Down,Rtc_number const rtc_gpio_desc_t rtc_gpio_desc[GPIO_PIN_COUNT] = { - {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, RTC_IO_TOUCH_PAD1_HOLD_M, RTC_CNTL_TOUCH_PAD1_HOLD_FORCE_M, 11}, //0 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //1 - {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, RTC_IO_TOUCH_PAD2_SLP_SEL_M, RTC_IO_TOUCH_PAD2_SLP_IE_M, RTC_IO_TOUCH_PAD2_HOLD_M, RTC_CNTL_TOUCH_PAD2_HOLD_FORCE_M, 12}, //2 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //3 - {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, RTC_IO_TOUCH_PAD0_SLP_SEL_M, RTC_IO_TOUCH_PAD0_SLP_IE_M, RTC_IO_TOUCH_PAD0_HOLD_M, RTC_CNTL_TOUCH_PAD0_HOLD_FORCE_M, 10}, //4 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //5 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //6 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //7 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //8 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //9 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //10 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //11 - {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, RTC_IO_TOUCH_PAD5_SLP_SEL_M, RTC_IO_TOUCH_PAD5_SLP_IE_M, RTC_IO_TOUCH_PAD5_HOLD_M, RTC_CNTL_TOUCH_PAD5_HOLD_FORCE_M, 15}, //12 - {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, RTC_IO_TOUCH_PAD4_SLP_SEL_M, RTC_IO_TOUCH_PAD4_SLP_IE_M, RTC_IO_TOUCH_PAD4_HOLD_M, RTC_CNTL_TOUCH_PAD4_HOLD_FORCE_M, 14}, //13 - {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, RTC_IO_TOUCH_PAD6_SLP_SEL_M, RTC_IO_TOUCH_PAD6_SLP_IE_M, RTC_IO_TOUCH_PAD6_HOLD_M, RTC_CNTL_TOUCH_PAD6_HOLD_FORCE_M, 16}, //14 - {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, RTC_IO_TOUCH_PAD3_SLP_SEL_M, RTC_IO_TOUCH_PAD3_SLP_IE_M, RTC_IO_TOUCH_PAD3_HOLD_M, RTC_CNTL_TOUCH_PAD3_HOLD_FORCE_M, 13}, //15 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //16 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //17 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //18 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //19 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //20 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //21 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //22 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //23 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //24 - {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, RTC_IO_PDAC1_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 6}, //25 - {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_IO_PDAC2_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, 7}, //26 - {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, RTC_IO_TOUCH_PAD7_HOLD_M, RTC_CNTL_TOUCH_PAD7_HOLD_FORCE_M, 17}, //27 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //28 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //29 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //30 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //31 - {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, RTC_IO_X32P_SLP_SEL_M, RTC_IO_X32P_SLP_IE_M, RTC_IO_X32P_HOLD_M, RTC_CNTL_X32P_HOLD_FORCE_M, 9}, //32 - {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, RTC_IO_X32N_SLP_SEL_M, RTC_IO_X32N_SLP_IE_M, RTC_IO_X32N_HOLD_M, RTC_CNTL_X32N_HOLD_FORCE_M, 8}, //33 - {RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, RTC_IO_ADC1_SLP_SEL_M, RTC_IO_ADC1_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC1_HOLD_FORCE_M, 4}, //34 - {RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, RTC_IO_ADC2_SLP_SEL_M, RTC_IO_ADC2_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC2_HOLD_FORCE_M, 5}, //35 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, RTC_IO_SENSE1_SLP_SEL_M, RTC_IO_SENSE1_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE1_HOLD_FORCE_M, 0}, //36 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, RTC_IO_SENSE2_SLP_SEL_M, RTC_IO_SENSE2_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE2_HOLD_FORCE_M, 1}, //37 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, RTC_IO_SENSE3_SLP_SEL_M, RTC_IO_SENSE3_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE3_HOLD_FORCE_M, 2}, //38 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, RTC_IO_SENSE4_SLP_SEL_M, RTC_IO_SENSE4_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE4_HOLD_FORCE_M, 3}, //39 + {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, RTC_IO_TOUCH_PAD1_HOLD_M, RTC_CNTL_TOUCH_PAD1_HOLD_FORCE_M, RTC_IO_TOUCH_PAD1_DRV_V, RTC_IO_TOUCH_PAD1_DRV_S, 11}, //0 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //1 + {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, RTC_IO_TOUCH_PAD2_SLP_SEL_M, RTC_IO_TOUCH_PAD2_SLP_IE_M, RTC_IO_TOUCH_PAD2_HOLD_M, RTC_CNTL_TOUCH_PAD2_HOLD_FORCE_M, RTC_IO_TOUCH_PAD2_DRV_V, RTC_IO_TOUCH_PAD2_DRV_S, 12}, //2 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //3 + {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, RTC_IO_TOUCH_PAD0_SLP_SEL_M, RTC_IO_TOUCH_PAD0_SLP_IE_M, RTC_IO_TOUCH_PAD0_HOLD_M, RTC_CNTL_TOUCH_PAD0_HOLD_FORCE_M, RTC_IO_TOUCH_PAD0_DRV_V, RTC_IO_TOUCH_PAD0_DRV_S, 10}, //4 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //5 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //6 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //7 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //8 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //9 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //10 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //11 + {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, RTC_IO_TOUCH_PAD5_SLP_SEL_M, RTC_IO_TOUCH_PAD5_SLP_IE_M, RTC_IO_TOUCH_PAD5_HOLD_M, RTC_CNTL_TOUCH_PAD5_HOLD_FORCE_M, RTC_IO_TOUCH_PAD5_DRV_V, RTC_IO_TOUCH_PAD5_DRV_S, 15}, //12 + {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, RTC_IO_TOUCH_PAD4_SLP_SEL_M, RTC_IO_TOUCH_PAD4_SLP_IE_M, RTC_IO_TOUCH_PAD4_HOLD_M, RTC_CNTL_TOUCH_PAD4_HOLD_FORCE_M, RTC_IO_TOUCH_PAD4_DRV_V, RTC_IO_TOUCH_PAD4_DRV_S, 14}, //13 + {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, RTC_IO_TOUCH_PAD6_SLP_SEL_M, RTC_IO_TOUCH_PAD6_SLP_IE_M, RTC_IO_TOUCH_PAD6_HOLD_M, RTC_CNTL_TOUCH_PAD6_HOLD_FORCE_M, RTC_IO_TOUCH_PAD6_DRV_V, RTC_IO_TOUCH_PAD6_DRV_S, 16}, //14 + {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, RTC_IO_TOUCH_PAD3_SLP_SEL_M, RTC_IO_TOUCH_PAD3_SLP_IE_M, RTC_IO_TOUCH_PAD3_HOLD_M, RTC_CNTL_TOUCH_PAD3_HOLD_FORCE_M, RTC_IO_TOUCH_PAD3_DRV_V, RTC_IO_TOUCH_PAD3_DRV_S, 13}, //15 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //16 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //17 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //18 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //19 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //20 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //21 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //22 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //23 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //24 + {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, RTC_IO_PDAC1_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, RTC_IO_PDAC1_DRV_V, RTC_IO_PDAC1_DRV_S, 6}, //25 + {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_IO_PDAC2_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, RTC_IO_PDAC2_DRV_V, RTC_IO_PDAC2_DRV_S, 7}, //26 + {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, RTC_IO_TOUCH_PAD7_HOLD_M, RTC_CNTL_TOUCH_PAD7_HOLD_FORCE_M, RTC_IO_TOUCH_PAD7_DRV_V, RTC_IO_TOUCH_PAD7_DRV_S, 17}, //27 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //28 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //29 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //30 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, //31 + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, RTC_IO_X32P_SLP_SEL_M, RTC_IO_X32P_SLP_IE_M, RTC_IO_X32P_HOLD_M, RTC_CNTL_X32P_HOLD_FORCE_M, RTC_IO_X32P_DRV_V, RTC_IO_X32P_DRV_S, 9}, //32 + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, RTC_IO_X32N_SLP_SEL_M, RTC_IO_X32N_SLP_IE_M, RTC_IO_X32N_HOLD_M, RTC_CNTL_X32N_HOLD_FORCE_M, RTC_IO_X32N_DRV_V, RTC_IO_X32N_DRV_S, 8}, //33 + {RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, RTC_IO_ADC1_SLP_SEL_M, RTC_IO_ADC1_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC1_HOLD_FORCE_M, 0, 0, 4}, //34 + {RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, RTC_IO_ADC2_SLP_SEL_M, RTC_IO_ADC2_SLP_IE_M, RTC_IO_ADC1_HOLD_M, RTC_CNTL_ADC2_HOLD_FORCE_M, 0, 0, 5}, //35 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, RTC_IO_SENSE1_SLP_SEL_M, RTC_IO_SENSE1_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE1_HOLD_FORCE_M, 0, 0, 0}, //36 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, RTC_IO_SENSE2_SLP_SEL_M, RTC_IO_SENSE2_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE2_HOLD_FORCE_M, 0, 0, 1}, //37 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, RTC_IO_SENSE3_SLP_SEL_M, RTC_IO_SENSE3_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE3_HOLD_FORCE_M, 0, 0, 2}, //38 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, RTC_IO_SENSE4_SLP_SEL_M, RTC_IO_SENSE4_SLP_IE_M, RTC_IO_SENSE1_HOLD_M, RTC_CNTL_SENSE4_HOLD_FORCE_M, 0, 0, 3}, //39 }; /*--------------------------------------------------------------- @@ -189,6 +190,28 @@ uint32_t rtc_gpio_get_level(gpio_num_t gpio_num) return ((level >> (RTC_GPIO_IN_NEXT_S + rtc_gpio_num)) & 0x01); } +esp_err_t rtc_gpio_set_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t strength) +{ + RTC_MODULE_CHECK(rtc_gpio_is_valid_gpio(gpio_num), "RTC_GPIO number error", ESP_ERR_INVALID_ARG); + RTC_MODULE_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "Output pad only", ESP_ERR_INVALID_ARG); + RTC_MODULE_CHECK(strength < GPIO_DRIVE_CAP_MAX, "GPIO drive capability error", ESP_ERR_INVALID_ARG); + + portENTER_CRITICAL(&rtc_spinlock); + SET_PERI_REG_BITS(rtc_gpio_desc[gpio_num].reg, rtc_gpio_desc[gpio_num].drv_v, strength, rtc_gpio_desc[gpio_num].drv_s); + portEXIT_CRITICAL(&rtc_spinlock); + return ESP_OK; +} + +esp_err_t rtc_gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t* strength) +{ + RTC_MODULE_CHECK(rtc_gpio_is_valid_gpio(gpio_num), "RTC_GPIO number error", ESP_ERR_INVALID_ARG); + RTC_MODULE_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "Output pad only", ESP_ERR_INVALID_ARG); + RTC_MODULE_CHECK(strength != NULL, "GPIO drive pointer error", ESP_ERR_INVALID_ARG); + + *strength = GET_PERI_REG_BITS2(rtc_gpio_desc[gpio_num].reg, rtc_gpio_desc[gpio_num].drv_v, rtc_gpio_desc[gpio_num].drv_s); + return ESP_OK; +} + esp_err_t rtc_gpio_set_direction(gpio_num_t gpio_num, rtc_gpio_mode_t mode) { RTC_MODULE_CHECK(rtc_gpio_is_valid_gpio(gpio_num), "RTC_GPIO number error", ESP_ERR_INVALID_ARG); From eeb8dc9c35302e969424097464532d40d9b87ed5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 20 Jul 2017 15:28:29 +1000 Subject: [PATCH 02/26] aws_iot: Expose common SDK compile-time configuration in menuconfig Closes https://github.com/espressif/esp-idf/issues/637 --- components/aws_iot/Kconfig | 53 +++++++++++++++++++++ components/aws_iot/include/aws_iot_config.h | 10 ++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/components/aws_iot/Kconfig b/components/aws_iot/Kconfig index 031def977d..76b587e9c0 100644 --- a/components/aws_iot/Kconfig +++ b/components/aws_iot/Kconfig @@ -30,3 +30,56 @@ config AWS_IOT_MQTT_PORT If you need per-device port numbers for different regions, you can override the default port number in your app. + +config AWS_IOT_MQTT_TX_BUF_LEN + int "MQTT TX Buffer Length" + depends on AWS_IOT_SDK + default 512 + range 32 65536 + help + Maximum MQTT transmit buffer size. This is the maximum MQTT + message length (including protocol overhead) which can be sent. + + Sending longer messages will fail. + +config AWS_IOT_MQTT_RX_BUF_LEN + int "MQTT RX Buffer Length" + depends on AWS_IOT_SDK + default 512 + range 32 65536 + help + Maximum MQTT receive buffer size. This is the maximum MQTT + message length (including protocol overhead) which can be + received. + + Longer messages are dropped. + + + +config AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS + int "Maximum MQTT Topic Filters" + depends on AWS_IOT_SDK + default 5 + range 1 100 + help + Maximum number of concurrent MQTT topic filters. + + +config AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + int "Auto reconnect initial interval (ms)" + depends on AWS_IOT_SDK + default 1000 + range 10 3600000 + help + Initial delay before making first reconnect attempt, if the AWS IoT connection fails. + Client will perform exponential backoff, starting from this value. + +config AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + int "Auto reconnect maximum interval (ms)" + depends on AWS_IOT_SDK + default 128000 + range 10 3600000 + help + Maximum delay between reconnection attempts. If the exponentially increased delay + interval reaches this value, the client will stop automatically attempting to reconnect. + diff --git a/components/aws_iot/include/aws_iot_config.h b/components/aws_iot/include/aws_iot_config.h index a19de46e08..2d10702af8 100644 --- a/components/aws_iot/include/aws_iot_config.h +++ b/components/aws_iot/include/aws_iot_config.h @@ -37,9 +37,9 @@ #define AWS_IOT_MY_THING_NAME "ESP32" ///< Thing Name of the Shadow this device is associated with // MQTT PubSub -#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow -#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. -#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow +#define AWS_IOT_MQTT_TX_BUF_LEN CONFIG_AWS_IOT_MQTT_TX_BUF_LEN ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_RX_BUF_LEN CONFIG_AWS_IOT_MQTT_RX_BUF_LEN ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS CONFIG_AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow // Thing Shadow specific configs #define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1) ///< Maximum size of the SHADOW buffer to store the received Shadow message @@ -54,7 +54,7 @@ #define MAX_SHADOW_TOPIC_LENGTH_BYTES (MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME) ///< This size includes the length of topic with Thing Name // Auto Reconnect specific config -#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm -#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL CONFIG_AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL CONFIG_AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. #endif /* _AWS_IOT_CONFIG_H_ */ From 99769f0b00952003354703d996edf96590f67a76 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 13 Jul 2017 17:40:57 +0800 Subject: [PATCH 03/26] fix(spi_master): fix the bug that VSPI no respond when host changed from HSPI to VSPI, and vice versa. fix the SPI control bits written wrong in the headers. TW#12123, Github#477 --- components/driver/test/test_spi_master.c | 61 ++++++++++++++++++++ components/soc/esp32/include/soc/dport_reg.h | 8 +-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index e58371f246..45314b7086 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -259,3 +259,64 @@ TEST_CASE("SPI Master test, interaction of multiple devs", "[spi][ignore]") { destroy_spi_bus(handle1); } +TEST_CASE("SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)", "[spi]") +{ + //spi config + spi_bus_config_t bus_config; + spi_device_interface_config_t device_config; + spi_device_handle_t spi; + spi_host_device_t host; + int dma = 1; + + memset(&bus_config, 0, sizeof(spi_bus_config_t)); + memset(&device_config, 0, sizeof(spi_device_interface_config_t)); + + bus_config.miso_io_num = -1; + bus_config.mosi_io_num = 26; + bus_config.sclk_io_num = 25; + bus_config.quadwp_io_num = -1; + bus_config.quadhd_io_num = -1; + + device_config.clock_speed_hz = 50000; + device_config.mode = 0; + device_config.spics_io_num = -1; + device_config.queue_size = 1; + device_config.flags = SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST; + + struct spi_transaction_t transaction = { + .flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA, + .length = 16, + .tx_buffer = NULL, + .rx_buffer = NULL, + .tx_data = {0x04, 0x00} + }; + + + //initialize for first host + host = 1; + + assert(spi_bus_initialize(host, &bus_config, dma) == ESP_OK); + assert(spi_bus_add_device(host, &device_config, &spi) == ESP_OK); + + printf("before first xmit\n"); + assert(spi_device_transmit(spi, &transaction) == ESP_OK); + printf("after first xmit\n"); + + assert(spi_bus_remove_device(spi) == ESP_OK); + assert(spi_bus_free(host) == ESP_OK); + + + //for second host and failed before + host = 2; + + assert(spi_bus_initialize(host, &bus_config, dma) == ESP_OK); + assert(spi_bus_add_device(host, &device_config, &spi) == ESP_OK); + + printf("before second xmit\n"); + // the original version (bit mis-written) stucks here. + assert(spi_device_transmit(spi, &transaction) == ESP_OK); + // test case success when see this. + printf("after second xmit\n"); + + +} diff --git a/components/soc/esp32/include/soc/dport_reg.h b/components/soc/esp32/include/soc/dport_reg.h index 852967f1c1..56e18246d7 100644 --- a/components/soc/esp32/include/soc/dport_reg.h +++ b/components/soc/esp32/include/soc/dport_reg.h @@ -958,7 +958,7 @@ #define DPORT_CAN_CLK_EN (BIT(19)) #define DPORT_I2C_EXT1_CLK_EN (BIT(18)) #define DPORT_PWM0_CLK_EN (BIT(17)) -#define DPORT_SPI_CLK_EN (BIT(16)) +#define DPORT_SPI_CLK_EN_2 (BIT(16)) #define DPORT_TIMERGROUP1_CLK_EN (BIT(15)) #define DPORT_EFUSE_CLK_EN (BIT(14)) #define DPORT_TIMERGROUP_CLK_EN (BIT(13)) @@ -968,7 +968,7 @@ #define DPORT_RMT_CLK_EN (BIT(9)) #define DPORT_UHCI0_CLK_EN (BIT(8)) #define DPORT_I2C_EXT0_CLK_EN (BIT(7)) -#define DPORT_SPI_CLK_EN_2 (BIT(6)) +#define DPORT_SPI_CLK_EN (BIT(6)) #define DPORT_UART1_CLK_EN (BIT(5)) #define DPORT_I2S0_CLK_EN (BIT(4)) #define DPORT_WDG_CLK_EN (BIT(3)) @@ -992,7 +992,7 @@ #define DPORT_CAN_RST (BIT(19)) #define DPORT_I2C_EXT1_RST (BIT(18)) #define DPORT_PWM0_RST (BIT(17)) -#define DPORT_SPI_RST (BIT(16)) +#define DPORT_SPI_RST_2 (BIT(16)) #define DPORT_TIMERGROUP1_RST (BIT(15)) #define DPORT_EFUSE_RST (BIT(14)) #define DPORT_TIMERGROUP_RST (BIT(13)) @@ -1002,7 +1002,7 @@ #define DPORT_RMT_RST (BIT(9)) #define DPORT_UHCI0_RST (BIT(8)) #define DPORT_I2C_EXT0_RST (BIT(7)) -#define DPORT_SPI_RST_2 (BIT(6)) +#define DPORT_SPI_RST (BIT(6)) #define DPORT_UART1_RST (BIT(5)) #define DPORT_I2S0_RST (BIT(4)) #define DPORT_WDG_RST (BIT(3)) From 967611cfaa2ddafacf367e05114eb8509d96d1c5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 24 Jul 2017 05:14:31 +0800 Subject: [PATCH 04/26] Revert "bootloader: Add option to build with Link Time Optimisation enabled" This reverts commit d1b66a08c1d9838b139e9fef6cc2d38e5d5ee57d. --- components/bootloader/Kconfig.projbuild | 13 ++----------- .../bootloader/subproject/main/Makefile.projbuild | 5 ----- make/project.mk | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index b4f2c904ac..5af223c0ad 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -43,16 +43,7 @@ config BOOTLOADER_SPI_WP_PIN The default value (GPIO 7) is correct for WP pin on ESP32-D2WD integrated flash. -config BOOTLOADER_LTO - bool "Build bootloader with Link Time Optimisation" - default n - help - Setting this option enables gcc Link Time Optimisation for the bootloader build & link pass. - - This gives a smaller bootloader binary (can be useful if secure boot & flash encryption & logging are all enabled), and can - give faster boot times, but it makes the bootloader harder to debug. - -endmenu # Bootloader config +endmenu # Bootloader menu "Security features" @@ -226,7 +217,7 @@ config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE config SECURE_BOOT_TEST_MODE bool "Secure boot test mode: don't permanently set any efuses" depends on SECURE_BOOT_INSECURE - default n + default N help If this option is set, all permanent secure boot changes (via Efuse) are disabled. diff --git a/components/bootloader/subproject/main/Makefile.projbuild b/components/bootloader/subproject/main/Makefile.projbuild index afd5282d83..c368c68416 100644 --- a/components/bootloader/subproject/main/Makefile.projbuild +++ b/components/bootloader/subproject/main/Makefile.projbuild @@ -2,8 +2,3 @@ # paths can be added at this level (we need binary librtc to be # available to link bootloader). COMPONENT_SUBMODULES += $(IDF_PATH)/components/esp32/lib - -ifdef CONFIG_BOOTLOADER_LTO -CFLAGS += -flto -EXTRA_LDFLAGS += -Wl,-flto -endif diff --git a/make/project.mk b/make/project.mk index ad7a055071..f6c958f2f6 100644 --- a/make/project.mk +++ b/make/project.mk @@ -290,7 +290,7 @@ export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY SIZE CC := $(call dequote,$(CONFIG_TOOLPREFIX))gcc CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++ LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld -AR := $(call dequote,$(CONFIG_TOOLPREFIX))gcc-ar +AR := $(call dequote,$(CONFIG_TOOLPREFIX))ar OBJCOPY := $(call dequote,$(CONFIG_TOOLPREFIX))objcopy SIZE := $(call dequote,$(CONFIG_TOOLPREFIX))size export CC CXX LD AR OBJCOPY SIZE From c2c9149a240bc83597bab75d74c94f2f3dfa1d00 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Mon, 24 Jul 2017 19:57:44 +0300 Subject: [PATCH 05/26] esp32: Apptrace API enhancement - User down buffer configuration support - bugfix: ring buf - avalable write size undeflow - SysView down buf support updated --- components/app_trace/Kconfig | 6 + components/app_trace/app_trace.c | 308 +++++++++++------- components/app_trace/app_trace_util.c | 43 ++- components/app_trace/include/esp_app_trace.h | 46 ++- .../app_trace/include/esp_app_trace_util.h | 98 +++--- .../Config/SEGGER_SYSVIEW_Config_FreeRTOS.c | 4 +- .../sys_view/esp32/SEGGER_RTT_esp32.c | 8 +- components/log/README.rst | 2 +- components/xtensa-debug-module/eri.c | 2 +- docs/api-guides/app_trace.rst | 49 ++- tools/unit-test-app/sdkconfig | 16 +- 11 files changed, 388 insertions(+), 194 deletions(-) diff --git a/components/app_trace/Kconfig b/components/app_trace/Kconfig index e88128f333..101fa97865 100644 --- a/components/app_trace/Kconfig +++ b/components/app_trace/Kconfig @@ -22,6 +22,12 @@ config ESP32_APPTRACE_ENABLE help Enables/disable application tracing module. +config ESP32_APPTRACE_LOCK_ENABLE + bool + default !SYSVIEW_ENABLE + help + Enables/disable application tracing module internal sync lock. + config ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO int "Timeout for flushing last trace data to host on panic" depends on ESP32_APPTRACE_ENABLE diff --git a/components/app_trace/app_trace.c b/components/app_trace/app_trace.c index 0a990b0d1d..939f3c4840 100644 --- a/components/app_trace/app_trace.c +++ b/components/app_trace/app_trace.c @@ -103,7 +103,8 @@ // that task/ISR will fail to complete filling its data chunk before the whole trace block is exposed to the host. To handle such conditions tracing // module prepends all user data chunks with header which contains allocated buffer size and actual data length within it. OpenOCD command // which reads application traces reports error when it reads incompleted user data block. -// Data which are transfered from host to target are also prepended with such header. +// Data which are transfered from host to target are also prepended with a header. Down channel data header is simple and consists of one two bytes field +// containing length of host data following the heder. // 4.3 Data Buffering // ------------------ @@ -159,14 +160,10 @@ #include "soc/dport_reg.h" #include "eri.h" #include "trax.h" -#include "freertos/FreeRTOS.h" -#include "freertos/portmacro.h" -#include "freertos/semphr.h" -#include "freertos/task.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" +#include "freertos/FreeRTOS.h" #include "esp_app_trace.h" -#include "esp_app_trace_util.h" #if CONFIG_ESP32_APPTRACE_ENABLE #define ESP_APPTRACE_MAX_VPRINTF_ARGS 256 @@ -174,7 +171,7 @@ #define ESP_APPTRACE_PRINT_LOCK 0 -#define LOG_LOCAL_LEVEL ESP_LOG_ERROR +#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include "esp_log.h" const static char *TAG = "esp_apptrace"; @@ -244,17 +241,13 @@ static volatile uint8_t *s_trax_blocks[] = { #define ESP_APPTRACE_TRAX_BLOCKS_NUM (sizeof(s_trax_blocks)/sizeof(s_trax_blocks[0])) -#define ESP_APPTRACE_TRAX_BLOCK_SIZE 0x4000UL - #define ESP_APPTRACE_TRAX_INBLOCK_START 0 #define ESP_APPTRACE_TRAX_INBLOCK_MARKER() (s_trace_buf.trax.state.markers[s_trace_buf.trax.state.in_block % 2]) #define ESP_APPTRACE_TRAX_INBLOCK_MARKER_UPD(_v_) do {s_trace_buf.trax.state.markers[s_trace_buf.trax.state.in_block % 2] += (_v_);}while(0) #define ESP_APPTRACE_TRAX_INBLOCK_GET() (&s_trace_buf.trax.blocks[s_trace_buf.trax.state.in_block % 2]) -//TODO: menuconfig -#define ESP_APPTRACE_DOWN_BUF_SIZE 32UL - +#define ESP_APPTRACE_TRAX_BLOCK_SIZE (0x4000UL) #if CONFIG_SYSVIEW_ENABLE #define ESP_APPTRACE_USR_DATA_LEN_MAX 255UL #else @@ -324,7 +317,6 @@ typedef struct { // ring buffer control struct for data from host (down buffer) esp_apptrace_rb_t rb_down; // storage for above ring buffer data - uint8_t down_buf[ESP_APPTRACE_DOWN_BUF_SIZE + 1]; esp_apptrace_trax_data_t trax; // TRAX HW transport data } esp_apptrace_buffer_t; @@ -334,13 +326,15 @@ static esp_apptrace_buffer_t s_trace_buf; static esp_apptrace_lock_t s_log_lock = {.irq_stat = 0, .portmux = portMUX_INITIALIZER_UNLOCKED}; #endif -static uint16_t esp_apptrace_trax_write_down_buffer_nolock(uint8_t *data, uint16_t size); -static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, uint32_t tmo); +static uint32_t esp_apptrace_trax_down_buffer_write_nolock(uint8_t *data, uint32_t size); +static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, esp_apptrace_tmo_t *tmo); static inline int esp_apptrace_log_lock() { #if ESP_APPTRACE_PRINT_LOCK - int ret = esp_apptrace_lock_take(&s_log_lock, ESP_APPTRACE_TMO_INFINITE); + esp_apptrace_tmo_t tmo; + esp_apptrace_tmo_init(&tmo, ESP_APPTRACE_TMO_INFINITE); + int ret = esp_apptrace_lock_take(&s_log_lock, &tmo); return ret; #else return 0; @@ -354,40 +348,40 @@ static inline void esp_apptrace_log_unlock() #endif } -esp_err_t esp_apptrace_lock_initialize() +static inline esp_err_t esp_apptrace_lock_initialize() { -#if CONFIG_SYSVIEW_ENABLE == 0 +#if CONFIG_ESP32_APPTRACE_LOCK_ENABLE esp_apptrace_lock_init(&s_trace_buf.lock); #endif return ESP_OK; } -esp_err_t inline esp_apptrace_lock_cleanup() +static inline esp_err_t esp_apptrace_lock_cleanup() { return ESP_OK; } -esp_err_t esp_apptrace_lock(uint32_t *tmo) +esp_err_t esp_apptrace_lock(esp_apptrace_tmo_t *tmo) { -#if CONFIG_SYSVIEW_ENABLE == 0 - unsigned cur, elapsed, start = xthal_get_ccount(); +#if CONFIG_ESP32_APPTRACE_LOCK_ENABLE + //unsigned cur, elapsed, start = xthal_get_ccount(); - esp_err_t ret = esp_apptrace_lock_take(&s_trace_buf.lock, *tmo); + esp_err_t ret = esp_apptrace_lock_take(&s_trace_buf.lock, tmo); if (ret != ESP_OK) { return ESP_FAIL; } // decrease tmo by actual waiting time - cur = xthal_get_ccount(); - if (start <= cur) { - elapsed = cur - start; - } else { - elapsed = ULONG_MAX - start + cur; - } - if (ESP_APPTRACE_CPUTICKS2US(elapsed) > *tmo) { - *tmo = 0; - } else { - *tmo -= ESP_APPTRACE_CPUTICKS2US(elapsed); - } + // cur = xthal_get_ccount(); + // if (start <= cur) { + // elapsed = cur - start; + // } else { + // elapsed = ULONG_MAX - start + cur; + // } + // if (ESP_APPTRACE_CPUTICKS2US(elapsed) > *tmo) { + // *tmo = 0; + // } else { + // *tmo -= ESP_APPTRACE_CPUTICKS2US(elapsed); + // } #endif return ESP_OK; } @@ -395,7 +389,7 @@ esp_err_t esp_apptrace_lock(uint32_t *tmo) esp_err_t esp_apptrace_unlock() { esp_err_t ret = ESP_OK; -#if CONFIG_SYSVIEW_ENABLE == 0 +#if CONFIG_ESP32_APPTRACE_LOCK_ENABLE ret = esp_apptrace_lock_give(&s_trace_buf.lock); #endif return ret; @@ -476,7 +470,8 @@ static esp_err_t esp_apptrace_trax_block_switch() uint32_t host_to_read = ESP_APPTRACE_TRAX_BLOCK_LEN_GET(ctrl_reg); if (host_to_read != 0 || acked_block != (s_trace_buf.trax.state.in_block & ESP_APPTRACE_TRAX_BLOCK_ID_MSK)) { ESP_APPTRACE_LOGD("HC[%d]: Can not switch %x %d %x %x/%lx, m %d", xPortGetCoreID(), ctrl_reg, host_to_read, acked_block, - s_trace_buf.trax.state.in_block & ESP_APPTRACE_TRAX_BLOCK_ID_MSK, s_trace_buf.trax.state.in_block, s_trace_buf.trax.state.markers[prev_block_num]); + s_trace_buf.trax.state.in_block & ESP_APPTRACE_TRAX_BLOCK_ID_MSK, s_trace_buf.trax.state.in_block, + s_trace_buf.trax.state.markers[prev_block_num]); res = ESP_ERR_NO_MEM; goto _on_func_exit; } @@ -491,12 +486,15 @@ static esp_err_t esp_apptrace_trax_block_switch() if (ctrl_reg & ESP_APPTRACE_TRAX_HOST_DATA && hdr->block_sz > 0) { // TODO: add support for multiple blocks from host, currently there is no need for that uint8_t *p = s_trace_buf.trax.blocks[new_block_num].start + s_trace_buf.trax.blocks[new_block_num].sz; - ESP_APPTRACE_LOGD("Recvd %d bytes from host [%x %x %x .. %x %x]", hdr->block_sz, + ESP_APPTRACE_LOGD("Recvd %d bytes from host [%x %x %x %x %x %x %x %x .. %x %x %x %x %x %x %x %x]", hdr->block_sz, *(s_trace_buf.trax.blocks[new_block_num].start+0), *(s_trace_buf.trax.blocks[new_block_num].start+1), - *(s_trace_buf.trax.blocks[new_block_num].start+2), *(p-2), *(p-1)); - uint32_t sz = esp_apptrace_trax_write_down_buffer_nolock((uint8_t *)(hdr+1), hdr->block_sz); + *(s_trace_buf.trax.blocks[new_block_num].start+2), *(s_trace_buf.trax.blocks[new_block_num].start+3), + *(s_trace_buf.trax.blocks[new_block_num].start+4), *(s_trace_buf.trax.blocks[new_block_num].start+5), + *(s_trace_buf.trax.blocks[new_block_num].start+6), *(s_trace_buf.trax.blocks[new_block_num].start+7), + *(p-8), *(p-7), *(p-6), *(p-5), *(p-4), *(p-3), *(p-2), *(p-1)); + uint32_t sz = esp_apptrace_trax_down_buffer_write_nolock((uint8_t *)(hdr+1), hdr->block_sz); if (sz != hdr->block_sz) { - ESP_APPTRACE_LOGE("Failed to write %d bytes to down buffer!", hdr->block_sz - sz); + ESP_APPTRACE_LOGE("Failed to write %d bytes to down buffer (%d %d)!", hdr->block_sz - sz, hdr->block_sz, sz); } hdr->block_sz = 0; } @@ -548,15 +546,12 @@ _on_func_exit: return res; } -static esp_err_t esp_apptrace_trax_block_switch_waitus(uint32_t tmo) +static esp_err_t esp_apptrace_trax_block_switch_waitus(esp_apptrace_tmo_t *tmo) { int res; - esp_apptrace_tmo_t sleeping_tmo; - - esp_apptrace_tmo_init(&sleeping_tmo, tmo); while ((res = esp_apptrace_trax_block_switch()) != ESP_OK) { - res = esp_apptrace_tmo_check(&sleeping_tmo); + res = esp_apptrace_tmo_check(tmo); if (res != ESP_OK) { break; } @@ -564,58 +559,79 @@ static esp_err_t esp_apptrace_trax_block_switch_waitus(uint32_t tmo) return res; } -static inline void esp_apptrace_trax_down_buf_init() +static uint8_t *esp_apptrace_trax_down_buffer_get(uint32_t *size, esp_apptrace_tmo_t *tmo) { - esp_apptrace_rb_init(&s_trace_buf.rb_down, s_trace_buf.down_buf, sizeof(s_trace_buf.down_buf)); -} + uint8_t *ptr = NULL; -static inline uint8_t *esp_apptrace_trax_get_down_rdptr(uint32_t *size, uint32_t *tmo) -{ int res = esp_apptrace_lock(tmo); if (res != ESP_OK) { return NULL; } - - // may need to flush - uint32_t ctrl_reg = eri_read(ESP_APPTRACE_TRAX_CTRL_REG); - if (ctrl_reg & ESP_APPTRACE_TRAX_HOST_DATA) { - ESP_APPTRACE_LOGD("force flush"); - res = esp_apptrace_trax_block_switch_waitus(*tmo); - if (res != ESP_OK) { - ESP_APPTRACE_LOGE("Failed to switch to another block to recv data from host!"); + while (1) { + uint32_t sz = esp_apptrace_rb_read_size_get(&s_trace_buf.rb_down); + if (sz != 0) { + ptr = esp_apptrace_rb_consume(&s_trace_buf.rb_down, sz > *size ? *size : sz); + if (!ptr) { + assert(false && "Failed to consume bytes from down buffer!"); + } + *size = sz; + break; + } + // may need to flush + uint32_t ctrl_reg = eri_read(ESP_APPTRACE_TRAX_CTRL_REG); + if (ctrl_reg & ESP_APPTRACE_TRAX_HOST_DATA) { + ESP_APPTRACE_LOGD("force flush"); + res = esp_apptrace_trax_block_switch_waitus(tmo); + if (res != ESP_OK) { + ESP_APPTRACE_LOGE("Failed to switch to another block to recv data from host!"); + /*do not return error because data can be in down buffer already*/ + } + } else { + // check tmo only if there is no data from host + res = esp_apptrace_tmo_check(tmo); + if (res != ESP_OK) { + return NULL; + } } } - uint8_t *ptr = NULL; - uint32_t sz = esp_apptrace_rb_read_size_get(&s_trace_buf.rb_down); - if (sz > 0) { - ptr = esp_apptrace_rb_consume(&s_trace_buf.rb_down, sz > *size ? *size : sz); - if (!ptr) { - assert(false && "Failed to consume bytes from down buffer!"); - } - } - *size = sz; - if (esp_apptrace_unlock() != ESP_OK) { assert(false && "Failed to unlock apptrace data!"); } return ptr; } -static inline esp_err_t esp_apptrace_trax_put_down_rdptr(uint8_t *ptr, uint32_t size, uint32_t *tmo) +static inline esp_err_t esp_apptrace_trax_down_buffer_put(uint8_t *ptr, esp_apptrace_tmo_t *tmo) { /* nothing todo */ return ESP_OK; } -static uint16_t esp_apptrace_trax_write_down_buffer_nolock(uint8_t *data, uint16_t size) +static uint32_t esp_apptrace_trax_down_buffer_write_nolock(uint8_t *data, uint32_t size) { - uint8_t *ptr = esp_apptrace_rb_produce(&s_trace_buf.rb_down, size); - if (ptr) { - memcpy(ptr, data, size); - } else { - return 0; + uint32_t total_sz = 0; + + while (total_sz < size) { + // ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock WRS %d-%d-%d %d", s_trace_buf.rb_down.wr, s_trace_buf.rb_down.rd, + // s_trace_buf.rb_down.cur_size, size); + uint32_t wr_sz = esp_apptrace_rb_write_size_get(&s_trace_buf.rb_down); + if (wr_sz == 0) { + break; + } + + if (wr_sz > size - total_sz) { + wr_sz = size - total_sz; + } + // ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d", wr_sz); + uint8_t *ptr = esp_apptrace_rb_produce(&s_trace_buf.rb_down, wr_sz); + if (!ptr) { + assert(false && "Failed to produce bytes to down buffer!"); + } + // ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d to 0x%x from 0x%x", wr_sz, ptr, data + total_sz + wr_sz); + memcpy(ptr, data + total_sz, wr_sz); + total_sz += wr_sz; + // ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d/%d", wr_sz, total_sz); } - return size; + return total_sz; } static inline uint8_t *esp_apptrace_data_header_init(uint8_t *ptr, uint16_t usr_size) @@ -626,7 +642,7 @@ static inline uint8_t *esp_apptrace_data_header_init(uint8_t *ptr, uint16_t usr_ return ptr + sizeof(esp_tracedata_hdr_t); } -static inline uint8_t *esp_apptrace_trax_wait4buf(uint16_t size, uint32_t tmo, int *pended) +static inline uint8_t *esp_apptrace_trax_wait4buf(uint16_t size, esp_apptrace_tmo_t *tmo, int *pended) { uint8_t *ptr = NULL; @@ -665,7 +681,7 @@ static inline uint8_t *esp_apptrace_trax_wait4buf(uint16_t size, uint32_t tmo, i return ptr; } -static uint8_t *esp_apptrace_trax_get_buffer(size_t size, uint32_t *tmo) +static uint8_t *esp_apptrace_trax_get_buffer(uint32_t size, esp_apptrace_tmo_t *tmo) { uint8_t *buf_ptr = NULL; @@ -691,14 +707,14 @@ static uint8_t *esp_apptrace_trax_get_buffer(size_t size, uint32_t *tmo) buf_ptr = esp_apptrace_rb_produce(&s_trace_buf.trax.rb_pend, ESP_APPTRACE_USR_BLOCK_RAW_SZ(size)); if (buf_ptr == NULL) { int pended_buf; - buf_ptr = esp_apptrace_trax_wait4buf(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size), *tmo, &pended_buf); + buf_ptr = esp_apptrace_trax_wait4buf(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size), tmo, &pended_buf); if (buf_ptr) { if (pended_buf) { #if CONFIG_ESP32_APPTRACE_PENDING_DATA_SIZE_MAX > ESP_APPTRACE_TRAX_BLOCK_SIZE esp_apptrace_trax_pend_chunk_sz_update(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size)); #endif } else { - ESP_APPTRACE_LOGD("Got %d bytes from TRAX buffer", size); + ESP_APPTRACE_LOGD("Get %d bytes from TRAX buffer", size); // update cur block marker ESP_APPTRACE_TRAX_INBLOCK_MARKER_UPD(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size)); } @@ -723,7 +739,7 @@ static uint8_t *esp_apptrace_trax_get_buffer(size_t size, uint32_t *tmo) if (buf_ptr == NULL) { int pended_buf; ESP_APPTRACE_LOGD("TRAX full. Get %d bytes from pend buffer", size); - buf_ptr = esp_apptrace_trax_wait4buf(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size), *tmo, &pended_buf); + buf_ptr = esp_apptrace_trax_wait4buf(ESP_APPTRACE_USR_BLOCK_RAW_SZ(size), tmo, &pended_buf); if (buf_ptr) { if (pended_buf) { #if CONFIG_ESP32_APPTRACE_PENDING_DATA_SIZE_MAX > ESP_APPTRACE_TRAX_BLOCK_SIZE @@ -737,7 +753,7 @@ static uint8_t *esp_apptrace_trax_get_buffer(size_t size, uint32_t *tmo) } } } else { - ESP_APPTRACE_LOGD("Get %d bytes from TRAX buffer!", size); + ESP_APPTRACE_LOGD("Get %d bytes from TRAX buffer", size); // fit to curr TRAX nlock buf_ptr = ESP_APPTRACE_TRAX_INBLOCK_GET()->start + ESP_APPTRACE_TRAX_INBLOCK_MARKER(); // update cur block marker @@ -755,7 +771,7 @@ static uint8_t *esp_apptrace_trax_get_buffer(size_t size, uint32_t *tmo) return buf_ptr; } -static esp_err_t esp_apptrace_trax_put_buffer(uint8_t *ptr, uint32_t *tmo) +static esp_err_t esp_apptrace_trax_put_buffer(uint8_t *ptr, esp_apptrace_tmo_t *tmo) { int res = ESP_OK; esp_tracedata_hdr_t *hdr = (esp_tracedata_hdr_t *)(ptr - sizeof(esp_tracedata_hdr_t)); @@ -772,7 +788,7 @@ static esp_err_t esp_apptrace_trax_put_buffer(uint8_t *ptr, uint32_t *tmo) return res; } -static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, uint32_t tmo) +static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, esp_apptrace_tmo_t *tmo) { int res = ESP_OK; @@ -782,7 +798,7 @@ static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, uint32_t tmo) } // switch TRAX block while size of data is more than min size while (ESP_APPTRACE_TRAX_INBLOCK_MARKER() > 0) { - ESP_APPTRACE_LOGD("Try to flush %d bytes. Wait until block switch for %u us", ESP_APPTRACE_TRAX_INBLOCK_MARKER(), tmo); + ESP_APPTRACE_LOGD("Try to flush %d bytes. Wait until block switch for %u us", ESP_APPTRACE_TRAX_INBLOCK_MARKER(), tmo->tmo); res = esp_apptrace_trax_block_switch_waitus(tmo); if (res != ESP_OK) { ESP_APPTRACE_LOGE("Failed to switch to another block!"); @@ -810,7 +826,6 @@ static esp_err_t esp_apptrace_trax_dest_init() sizeof(s_trace_buf.trax.pending_chunk_sz)); #endif #endif - esp_apptrace_trax_down_buf_init(); DPORT_WRITE_PERI_REG(DPORT_PRO_TRACEMEM_ENA_REG, DPORT_PRO_TRACEMEM_ENA_M); #if CONFIG_FREERTOS_UNICORE == 0 @@ -849,25 +864,31 @@ esp_err_t esp_apptrace_init() esp_apptrace_trax_init(); #endif + // disabled by default + esp_apptrace_rb_init(&s_trace_buf.rb_down, NULL, 0); + s_trace_buf.inited |= 1 << xPortGetCoreID(); // global and this CPU-specific data are inited return ESP_OK; } -esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, size_t *size, uint32_t user_tmo) +void esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size) +{ + esp_apptrace_rb_init(&s_trace_buf.rb_down, buf, size); +} + +esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, uint32_t *size, uint32_t user_tmo) { - uint8_t *ptr = NULL; - uint32_t tmo = user_tmo; int res = ESP_OK; - esp_apptrace_tmo_t sleeping_tmo; + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - uint8_t *(*apptrace_get_down_buffer)(uint32_t *, uint32_t *); - esp_err_t (*apptrace_put_down_buffer)(uint8_t *, uint32_t , uint32_t *); + uint8_t *(*apptrace_get_down_buffer)(uint32_t *, esp_apptrace_tmo_t *); + esp_err_t (*apptrace_put_down_buffer)(uint8_t *, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX - apptrace_get_down_buffer = esp_apptrace_trax_get_down_rdptr; - apptrace_put_down_buffer = esp_apptrace_trax_put_down_rdptr; + apptrace_get_down_buffer = esp_apptrace_trax_down_buffer_get; + apptrace_put_down_buffer = esp_apptrace_trax_down_buffer_put; #else ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!"); return ESP_ERR_NOT_SUPPORTED; @@ -878,31 +899,72 @@ esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, size_t *size, u } //TODO: callback system - esp_apptrace_tmo_init(&sleeping_tmo, tmo); + esp_apptrace_tmo_init(&tmo, user_tmo); uint32_t act_sz = *size; - while ((ptr = apptrace_get_down_buffer(&act_sz, &tmo)) == NULL ) { - res = esp_apptrace_tmo_check(&sleeping_tmo); - if (res != ESP_OK) { - break; - } - } + *size = 0; + uint8_t * ptr = apptrace_get_down_buffer(&act_sz, &tmo); if (ptr && act_sz > 0) { ESP_APPTRACE_LOGD("Read %d bytes from host", act_sz); memcpy(buf, ptr, act_sz); - res = apptrace_put_down_buffer(ptr, act_sz, &tmo); + res = apptrace_put_down_buffer(ptr, &tmo); *size = act_sz; } return res; } -esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, size_t size, uint32_t user_tmo) +uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size, uint32_t user_tmo) +{ + esp_apptrace_tmo_t tmo; + //TODO: use ptr to HW transport iface struct + uint8_t *(*apptrace_get_down_buffer)(uint32_t *, esp_apptrace_tmo_t *); + + if (dest == ESP_APPTRACE_DEST_TRAX) { +#if CONFIG_ESP32_APPTRACE_DEST_TRAX + apptrace_get_down_buffer = esp_apptrace_trax_down_buffer_get; +#else + ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!"); + return NULL; +#endif + } else { + ESP_APPTRACE_LOGE("Trace destinations other then TRAX are not supported yet!"); + return NULL; + } + + // ESP_APPTRACE_LOGE("esp_apptrace_down_buffer_get %d", *size); + esp_apptrace_tmo_init(&tmo, user_tmo); + return apptrace_get_down_buffer(size, &tmo); +} + +esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo) +{ + esp_apptrace_tmo_t tmo; + //TODO: use ptr to HW transport iface struct + esp_err_t (*apptrace_put_down_buffer)(uint8_t *, esp_apptrace_tmo_t *); + + if (dest == ESP_APPTRACE_DEST_TRAX) { +#if CONFIG_ESP32_APPTRACE_DEST_TRAX + apptrace_put_down_buffer = esp_apptrace_trax_down_buffer_put; +#else + ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!"); + return ESP_ERR_NOT_SUPPORTED; +#endif + } else { + ESP_APPTRACE_LOGE("Trace destinations other then TRAX are not supported yet!"); + return ESP_ERR_NOT_SUPPORTED; + } + + esp_apptrace_tmo_init(&tmo, user_tmo); + return apptrace_put_down_buffer(ptr, &tmo); +} + +esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_t size, uint32_t user_tmo) { uint8_t *ptr = NULL; - uint32_t tmo = user_tmo; + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - uint8_t *(*apptrace_get_buffer)(size_t, uint32_t *); - esp_err_t (*apptrace_put_buffer)(uint8_t *, uint32_t *); + uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *); + esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX @@ -917,6 +979,7 @@ esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, size_t return ESP_ERR_NOT_SUPPORTED; } + esp_apptrace_tmo_init(&tmo, user_tmo); ptr = apptrace_get_buffer(size, &tmo); if (ptr == NULL) { return ESP_ERR_NO_MEM; @@ -934,10 +997,10 @@ int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const c { uint16_t nargs = 0; uint8_t *pout, *p = (uint8_t *)fmt; - uint32_t tmo = user_tmo; + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - uint8_t *(*apptrace_get_buffer)(size_t, uint32_t *); - esp_err_t (*apptrace_put_buffer)(uint8_t *, uint32_t *); + uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *); + esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX @@ -952,6 +1015,7 @@ int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const c return ESP_ERR_NOT_SUPPORTED; } + esp_apptrace_tmo_init(&tmo, user_tmo); ESP_APPTRACE_LOGD("fmt %x", fmt); while ((p = (uint8_t *)strchr((char *)p, '%')) && nargs < ESP_APPTRACE_MAX_VPRINTF_ARGS) { p++; @@ -995,11 +1059,11 @@ int esp_apptrace_vprintf(const char *fmt, va_list ap) return esp_apptrace_vprintf_to(ESP_APPTRACE_DEST_TRAX, /*ESP_APPTRACE_TMO_INFINITE*/0, fmt, ap); } -uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, size_t size, uint32_t user_tmo) +uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32_t user_tmo) { - uint32_t tmo = user_tmo; + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - uint8_t *(*apptrace_get_buffer)(size_t, uint32_t *); + uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX @@ -1013,14 +1077,15 @@ uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, size_t size, uint32_t return NULL; } + esp_apptrace_tmo_init(&tmo, user_tmo); return apptrace_get_buffer(size, &tmo); } esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo) { - uint32_t tmo = user_tmo; + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - esp_err_t (*apptrace_put_buffer)(uint8_t *, uint32_t *); + esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX @@ -1034,13 +1099,15 @@ esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32 return ESP_ERR_NOT_SUPPORTED; } + esp_apptrace_tmo_init(&tmo, user_tmo); return apptrace_put_buffer(ptr, &tmo); } -esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, uint32_t tmo) +esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, uint32_t usr_tmo) { + esp_apptrace_tmo_t tmo; //TODO: use ptr to HW transport iface struct - esp_err_t (*apptrace_flush)(uint32_t, uint32_t); + esp_err_t (*apptrace_flush)(uint32_t, esp_apptrace_tmo_t *); if (dest == ESP_APPTRACE_DEST_TRAX) { #if CONFIG_ESP32_APPTRACE_DEST_TRAX @@ -1054,20 +1121,23 @@ esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, u return ESP_ERR_NOT_SUPPORTED; } - return apptrace_flush(min_sz, tmo); + esp_apptrace_tmo_init(&tmo, usr_tmo); + return apptrace_flush(min_sz, &tmo); } -esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t tmo) +esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t usr_tmo) { int res; + esp_apptrace_tmo_t tmo; + esp_apptrace_tmo_init(&tmo, usr_tmo); res = esp_apptrace_lock(&tmo); if (res != ESP_OK) { ESP_APPTRACE_LOGE("Failed to lock apptrace data (%d)!", res); return res; } - res = esp_apptrace_flush_nolock(dest, 0, tmo); + res = esp_apptrace_flush_nolock(dest, 0, esp_apptrace_tmo_remaining_us(&tmo)); if (res != ESP_OK) { ESP_APPTRACE_LOGE("Failed to flush apptrace data (%d)!", res); } diff --git a/components/app_trace/app_trace_util.c b/components/app_trace/app_trace_util.c index 17fe170931..078bd0d7f6 100644 --- a/components/app_trace/app_trace_util.c +++ b/components/app_trace/app_trace_util.c @@ -17,38 +17,39 @@ #include "esp_app_trace_util.h" /////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// LOCK //////////////////////////////////////// +///////////////////////////////// TIMEOUT ///////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -#define ESP_TEST_CPUTICKS2US(_t_) ((_t_)/(XT_CLOCK_FREQ/1000000)) +// TODO: get actual clock from PLL config +#define ESP_APPTRACE_CPUTICKS2US(_t_) ((_t_)/(XT_CLOCK_FREQ/1000000)) esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo) { - unsigned cur, elapsed; - - if (tmo->tmo != 0xFFFFFFFF) { - cur = portGET_RUN_TIME_COUNTER_VALUE(); + if (tmo->tmo != ESP_APPTRACE_TMO_INFINITE) { + unsigned cur = portGET_RUN_TIME_COUNTER_VALUE(); if (tmo->start <= cur) { - elapsed = cur - tmo->start; + tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(cur - tmo->start); } else { - elapsed = 0xFFFFFFFF - tmo->start + cur; + tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(0xFFFFFFFF - tmo->start + cur); } - if (ESP_TEST_CPUTICKS2US(elapsed) >= tmo->tmo) { + if (tmo->elapsed >= tmo->tmo) { return ESP_ERR_TIMEOUT; } } return ESP_OK; } -esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, uint32_t tmo) +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// LOCK //////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, esp_apptrace_tmo_t *tmo) { uint32_t res; #if CONFIG_SYSVIEW_ENABLE uint32_t recCnt; #endif - esp_apptrace_tmo_t sleeping_tmo; - esp_apptrace_tmo_init(&sleeping_tmo, tmo); while (1) { res = (xPortGetCoreID() << portMUX_VAL_SHIFT) | portMUX_MAGIC_VAL; // first disable IRQs on this CPU, this will prevent current task from been @@ -77,7 +78,7 @@ esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, uint32_t tmo) // if mux is locked by other task/ISR enable IRQs and let other guys work portEXIT_CRITICAL_NESTED(irq_stat); - int err = esp_apptrace_tmo_check(&sleeping_tmo); + int err = esp_apptrace_tmo_check(tmo); if (err != ESP_OK) { return err; } @@ -205,3 +206,19 @@ uint32_t esp_apptrace_rb_read_size_get(esp_apptrace_rb_t *rb) } return size; } + +uint32_t esp_apptrace_rb_write_size_get(esp_apptrace_rb_t *rb) +{ + uint32_t size = 0; + if (rb->rd <= rb->wr) { + // |?R......W??| + size = rb->size - rb->wr; + if (size && rb->rd == 0) { + size--; + } + } else { + // |?W......R??| + size = rb->rd - rb->wr - 1; + } + return size; +} diff --git a/components/app_trace/include/esp_app_trace.h b/components/app_trace/include/esp_app_trace.h index d043f3935b..aa35ea6a54 100644 --- a/components/app_trace/include/esp_app_trace.h +++ b/components/app_trace/include/esp_app_trace.h @@ -16,10 +16,7 @@ #include #include "esp_err.h" -#include "freertos/portmacro.h" - -/** Infinite waiting timeout */ -#define ESP_APPTRACE_TMO_INFINITE ((uint32_t)-1) +#include "esp_app_trace_util.h" // ESP_APPTRACE_TMO_INFINITE /** * Application trace data destinations bits. @@ -38,6 +35,16 @@ typedef enum { */ esp_err_t esp_apptrace_init(); +/** + * @brief Configures down buffer. + * @note Needs to be called before initiating any data transfer using esp_apptrace_buffer_get and esp_apptrace_write. + * This function does not protect internal data by lock. + * + * @param buf Address of buffer to use for down channel (host to target) data. + * @param size Size of the buffer. + */ +void esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size); + /** * @brief Allocates buffer for trace data. * After data in buffer are ready to be sent off esp_apptrace_buffer_put must be called to indicate it. @@ -48,11 +55,11 @@ esp_err_t esp_apptrace_init(); * * @return non-NULL on success, otherwise NULL. */ -uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, size_t size, uint32_t tmo); +uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32_t tmo); /** * @brief Indicates that the data in buffer are ready to be sent off. - * This function is a counterpart of must be preceeded by esp_apptrace_buffer_get. + * This function is a counterpart of and must be preceeded by esp_apptrace_buffer_get. * * @param dest Indicates HW interface to send data. Should be identical to the same parameter in call to esp_apptrace_buffer_get. * @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_buffer_get. @@ -72,7 +79,7 @@ esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32 * * @return ESP_OK on success, otherwise see esp_err_t */ -esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, size_t size, uint32_t tmo); +esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_t size, uint32_t tmo); /** * @brief vprintf-like function to sent log messages to host via specified HW interface. @@ -128,7 +135,30 @@ esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, u * * @return ESP_OK on success, otherwise see esp_err_t */ -esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *data, size_t *size, uint32_t tmo); +esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *data, uint32_t *size, uint32_t tmo); +/** + * @brief Rertrieves incoming data buffer if any. + * After data in buffer are processed esp_apptrace_down_buffer_put must be called to indicate it. + * + * @param dest Indicates HW interface to receive data. + * @param size Address to store size of available data in down buffer. Must be initializaed with requested value. + * @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly. + * + * @return non-NULL on success, otherwise NULL. + */ +uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size, uint32_t tmo); + +/** + * @brief Indicates that the data in down buffer are processesd. + * This function is a counterpart of and must be preceeded by esp_apptrace_down_buffer_get. + * + * @param dest Indicates HW interface to receive data. Should be identical to the same parameter in call to esp_apptrace_down_buffer_get. + * @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_down_buffer_get. + * @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly. + * + * @return ESP_OK on success, otherwise see esp_err_t + */ +esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t tmo); #endif diff --git a/components/app_trace/include/esp_app_trace_util.h b/components/app_trace/include/esp_app_trace_util.h index 83359b8b24..9793378694 100644 --- a/components/app_trace/include/esp_app_trace_util.h +++ b/components/app_trace/include/esp_app_trace_util.h @@ -14,9 +14,49 @@ #ifndef ESP_APP_TRACE_UTIL_H_ #define ESP_APP_TRACE_UTIL_H_ -#include "freertos/portmacro.h" +#include "freertos/FreeRTOS.h" #include "esp_err.h" +/** Infinite waiting timeout */ +#define ESP_APPTRACE_TMO_INFINITE ((uint32_t)-1) + +/** Structure which holds data necessary for measuring time intervals. + * + * After initialization via esp_apptrace_tmo_init() user needs to call esp_apptrace_tmo_check() + * periodically to check timeout for expiration. + */ +typedef struct { + uint32_t start; ///< time interval start (in CPU ticks) + uint32_t tmo; ///< timeout value (in us) + uint32_t elapsed; ///< elapsed time (in us) +} esp_apptrace_tmo_t; + +/** + * @brief Initializes timeout structure. + * + * @param tmo Pointer to timeout structure to be initialized. + * @param user_tmo Timeout value (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly. +*/ +static inline void esp_apptrace_tmo_init(esp_apptrace_tmo_t *tmo, uint32_t user_tmo) +{ + tmo->start = portGET_RUN_TIME_COUNTER_VALUE(); + tmo->tmo = user_tmo; +} + +/** + * @brief Checks timeout for expiration. + * + * @param tmo Pointer to timeout structure to be initialized. + * + * @return ESP_OK on success, otherwise \see esp_err_t + */ +esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo); + +static inline uint32_t esp_apptrace_tmo_remaining_us(esp_apptrace_tmo_t *tmo) +{ + return tmo->tmo != ESP_APPTRACE_TMO_INFINITE ? (tmo->elapsed - tmo->tmo) : ESP_APPTRACE_TMO_INFINITE; +} + /** Tracing module synchronization lock */ typedef struct { volatile unsigned int irq_stat; ///< local (on 1 CPU) IRQ state @@ -38,11 +78,11 @@ static inline void esp_apptrace_lock_init(esp_apptrace_lock_t *lock) * @brief Tries to acquire lock in specified time period. * * @param lock Pointer to lock structure. - * @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly. + * @param tmo Pointer to timeout struct. * * @return ESP_OK on success, otherwise \see esp_err_t */ -esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, uint32_t tmo); +esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, esp_apptrace_tmo_t *tmo); /** * @brief Releases lock. @@ -53,39 +93,6 @@ esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, uint32_t tmo); */ esp_err_t esp_apptrace_lock_give(esp_apptrace_lock_t *lock); - -/** Structure which holds data necessary for measuring time intervals. - * - * After initialization via esp_apptrace_tmo_init() user needs to call esp_apptrace_tmo_check() - * periodically to check timeout for expiration. - */ -typedef struct { - uint32_t start; ///< time interval start (in ticks) - uint32_t tmo; ///< timeout value (in us) -} esp_apptrace_tmo_t; - -/** - * @brief Initializes timeout structure. - * - * @param tmo Pointer to timeout structure to be initialized. - * @param user_tmo Timeout value (in us). -*/ -static inline void esp_apptrace_tmo_init(esp_apptrace_tmo_t *tmo, uint32_t user_tmo) -{ - tmo->start = portGET_RUN_TIME_COUNTER_VALUE(); - tmo->tmo = user_tmo; -} - -/** - * @brief Checks timeout for expiration. - * - * @param tmo Pointer to timeout structure to be initialized. - * - * @return ESP_OK on success, otherwise \see esp_err_t - */ -esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo); - - /** Ring buffer control structure. * * @note For purposes of application tracing module if there is no enough space for user data and write pointer can be wrapped @@ -93,10 +100,10 @@ esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo); */ typedef struct { uint8_t *data; ///< pointer to data storage - uint32_t size; ///< size of data storage - uint32_t cur_size; ///< current size of data storage - uint32_t rd; ///< read pointer - uint32_t wr; ///< write pointer + volatile uint32_t size; ///< size of data storage + volatile uint32_t cur_size; ///< current size of data storage + volatile uint32_t rd; ///< read pointer + volatile uint32_t wr; ///< write pointer } esp_apptrace_rb_t; /** @@ -145,4 +152,15 @@ uint8_t *esp_apptrace_rb_consume(esp_apptrace_rb_t *rb, uint32_t size); */ uint32_t esp_apptrace_rb_read_size_get(esp_apptrace_rb_t *rb); +/** + * @brief Gets size of memory which can produced with single call to esp_apptrace_rb_produce(). + * + * @param rb Pointer to ring buffer structure. + * + * @return Size of memory which can produced. + * + * @note Due to write pointer wrapping returned size can be less then the total size of available data. + */ +uint32_t esp_apptrace_rb_write_size_get(esp_apptrace_rb_t *rb); + #endif //ESP_APP_TRACE_UTIL_H_ \ No newline at end of file diff --git a/components/app_trace/sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c b/components/app_trace/sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c index aa018f507c..52bc27d066 100644 --- a/components/app_trace/sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c +++ b/components/app_trace/sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c @@ -322,7 +322,9 @@ void SEGGER_SYSVIEW_X_RTT_Unlock() void SEGGER_SYSVIEW_X_SysView_Lock() { - esp_apptrace_lock_take(&s_sys_view_lock, SEGGER_LOCK_WAIT_TMO); + esp_apptrace_tmo_t tmo; + esp_apptrace_tmo_init(&tmo, SEGGER_LOCK_WAIT_TMO); + esp_apptrace_lock_take(&s_sys_view_lock, &tmo); } void SEGGER_SYSVIEW_X_SysView_Unlock() diff --git a/components/app_trace/sys_view/esp32/SEGGER_RTT_esp32.c b/components/app_trace/sys_view/esp32/SEGGER_RTT_esp32.c index 795af78da1..fd674bbb6c 100644 --- a/components/app_trace/sys_view/esp32/SEGGER_RTT_esp32.c +++ b/components/app_trace/sys_view/esp32/SEGGER_RTT_esp32.c @@ -36,11 +36,14 @@ const static char *TAG = "segger_rtt"; #endif #endif -#define SEGGER_HOST_WAIT_TMO 500 //us -#define SEGGER_STOP_WAIT_TMO 1000000 //us +// size of down channel data buf +#define SYSVIEW_DOWN_BUF_SIZE 32 +#define SEGGER_HOST_WAIT_TMO 500 //us +#define SEGGER_STOP_WAIT_TMO 1000000 //us static uint8_t s_events_buf[SYSVIEW_EVENTS_BUF_SZ]; static uint16_t s_events_buf_filled; +static uint8_t s_down_buf[SYSVIEW_DOWN_BUF_SIZE]; /********************************************************************* * @@ -216,6 +219,7 @@ int SEGGER_RTT_ConfigUpBuffer(unsigned BufferIndex, const char* sName, void* pBu * Buffer name and flags can be reconfigured using the appropriate functions. */ int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + esp_apptrace_down_buffer_config(s_down_buf, sizeof(s_down_buf)); return 0; } diff --git a/components/log/README.rst b/components/log/README.rst index f732a9e90f..83db32ea60 100644 --- a/components/log/README.rst +++ b/components/log/README.rst @@ -120,7 +120,7 @@ To use logging via JTAG user needs to perform the following steps: 2. Build the program image and download it to target as described in :idf:`Developing With the ESP-IDF` section. 3. Run OpenOCD (see :idf:`OpenOCD setup for ESP32` section). 4. Connect to OpenOCD telnet server. On Linux it can be done using the following command in terminal ``telnet 4444``. If telnet session is opened on the same machine which runs OpenOCD you can use `localhost` as `` in the command. -5. Run the following command in OpenOCD telnet session: ``esp108 apptrace start /path/to/trace/file -1 -1 0 0 1``. This command will wait for board reset and transfer tracing data at the highest possible rate. +5. Run the following command in OpenOCD telnet session: ``esp108 apptrace start /path/to/trace/file 0 -1 -1 1``. This command will wait for board reset and transfer tracing data at the highest possible rate. 6. Reset the board. Logging to host will start automatically. 7. ``esp108 apptrace`` command with given arguments will never return (see other command options below), so you must stop it manually by resetting the board or pressing CTRL+C in OpenOCD window (not one with the telnet session). 8. Reset board or press CTRL+C in OpenOCD window (not one with the telnet session) when tracing is completed (for the example code above after the message `"Tracing is finished."` appears on UART). diff --git a/components/xtensa-debug-module/eri.c b/components/xtensa-debug-module/eri.c index fc96b531fe..8697572784 100644 --- a/components/xtensa-debug-module/eri.c +++ b/components/xtensa-debug-module/eri.c @@ -16,7 +16,7 @@ uint32_t eri_read(int addr) { uint32_t ret; - asm( + asm volatile ( "RER %0,%1" :"=r"(ret):"r"(addr) ); diff --git a/docs/api-guides/app_trace.rst b/docs/api-guides/app_trace.rst index 0c72ec9d94..c8e54a6142 100644 --- a/docs/api-guides/app_trace.rst +++ b/docs/api-guides/app_trace.rst @@ -13,7 +13,7 @@ Developers can use this library to send application specific state of execution Tracing components when working over JTAG interface are shown in the figure below. -.. figure:: ../_static/app_trace/overview.png +.. figure:: ../_static/app_trace/overview.jpg :align: center :alt: Tracing Components when Working Over JTAG :figclass: align-center @@ -80,13 +80,13 @@ In general user should decide what type of data should be transferred in every d #include "esp_app_trace.h" ... int number = 10; - char *ptr = (char *)esp_apptrace_buffer_get(32, 100/*tmo in us*/); + char *ptr = (char *)esp_apptrace_buffer_get(ESP_APPTRACE_DEST_TRAX, 32, 100/*tmo in us*/); if (ptr == NULL) { ESP_LOGE("Failed to get buffer!"); return ESP_FAIL; } sprintf(ptr, "Here is the number %d", number); - esp_err_t res = esp_apptrace_buffer_put(ptr, 100/*tmo in us*/); + esp_err_t res = esp_apptrace_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/); if (res != ESP_OK) { /* in case of error host tracing tool (e.g. OpenOCD) will report incomplete user buffer */ ESP_LOGE("Failed to put buffer!"); @@ -100,7 +100,11 @@ Also according to his needs user may want to receive data from the host. Piece o #include "esp_app_trace.h" ... char buf[32]; + char down_buf[32]; size_t sz = sizeof(buf); + + /* config down buffer */ + esp_apptrace_down_buffer_config(down_buf, sizeof(down_buf)); /* check for incoming data and read them if any */ esp_err_t res = esp_apptrace_read(ESP_APPTRACE_DEST_TRAX, buf, &sz, 0/*do not wait*/); if (res != ESP_OK) { @@ -112,6 +116,37 @@ Also according to his needs user may want to receive data from the host. Piece o ... } +``esp_apptrace_read()`` function uses memcpy to copy host data to user buffer. In some cases it can be more optimal to use ``esp_apptrace_down_buffer_get()`` and ``esp_apptrace_down_buffer_put()`` functions. +They allow developers to ocupy chunk of read buffer and process it in-place. The following piece of code shows how to do this. + +.. code-block:: c + + #include "esp_app_trace.h" + ... + char down_buf[32]; + uint32_t *number; + size_t sz = 32; + + /* config down buffer */ + esp_apptrace_down_buffer_config(down_buf, sizeof(down_buf)); + char *ptr = (char *)esp_apptrace_down_buffer_get(ESP_APPTRACE_DEST_TRAX, &sz, 100/*tmo in us*/); + if (ptr == NULL) { + ESP_LOGE("Failed to get buffer!"); + return ESP_FAIL; + } + if (sz > 4) { + number = (uint32_t *)ptr; + printf("Here is the number %d", *number); + } else { + printf("No data"); + } + esp_err_t res = esp_apptrace_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/); + if (res != ESP_OK) { + /* in case of error host tracing tool (e.g. OpenOCD) will report incomplete user buffer */ + ESP_LOGE("Failed to put buffer!"); + return res; + } + 2. The next step is to build the program image and download it to the target as described in :doc:`Build and Flash <../get-started/make-project>`. 3. Run OpenOCD (see :doc:`Debugging <../api-guides/openocd>`). 4. Connect to OpenOCD telnet server. On Linux it can be done using the following command in terminal ``telnet 4444``. If telnet session is opened on the same machine which runs OpenOCD you can use ``localhost`` as ```` in the command above. @@ -151,7 +186,7 @@ Sub-commands: Start command syntax: - ``start [outfile2] [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]`` + ``start [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]`` .. list-table:: :widths: 20 80 @@ -159,10 +194,8 @@ Start command syntax: * - Argument - Description - * - outfile1 - - Path to file to save data from PRO CPU. This argument should have the following format: ``file://path/to/file``. - * - outfile2 - - Path to file to save data from APP CPU. This argument should have the following format: ``file://path/to/file``. + * - outfile + - Path to file to save data from both CPUs. This argument should have the following format: ``file://path/to/file``. * - poll_period - Data polling period (in ms). If greater then 0 then command runs in non-blocking mode. By default 1 ms. * - trace_size diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index 29be2173dd..43ffa272b8 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -88,7 +88,9 @@ CONFIG_APP_OFFSET=0x10000 # CONFIG_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set -CONFIG_OPTIMIZATION_ASSERTIONS=y +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set # # Component config @@ -100,6 +102,7 @@ CONFIG_OPTIMIZATION_ASSERTIONS=y # CONFIG_ESP32_APPTRACE_DEST_TRAX is not set CONFIG_ESP32_APPTRACE_DEST_NONE=y # CONFIG_ESP32_APPTRACE_ENABLE is not set +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y # # FreeRTOS SystemView Tracing @@ -149,6 +152,16 @@ CONFIG_INT_WDT=y CONFIG_INT_WDT_TIMEOUT_MS=300 CONFIG_INT_WDT_CHECK_CPU1=y # CONFIG_TASK_WDT is not set +CONFIG_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set @@ -227,6 +240,7 @@ CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y # CONFIG_FREERTOS_ASSERT_DISABLE is not set CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y # CONFIG_ENABLE_MEMORY_DEBUG is not set +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 CONFIG_FREERTOS_ISR_STACKSIZE=1536 # CONFIG_FREERTOS_LEGACY_HOOKS is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 From 60c45ae2c45555a56666246041136243552d40a0 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Mon, 24 Jul 2017 20:07:21 +0300 Subject: [PATCH 06/26] Updated acc to comments --- components/app_trace/app_trace.c | 14 -------------- docs/api-guides/app_trace.rst | 10 +++++----- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/components/app_trace/app_trace.c b/components/app_trace/app_trace.c index 939f3c4840..679686a39a 100644 --- a/components/app_trace/app_trace.c +++ b/components/app_trace/app_trace.c @@ -364,24 +364,10 @@ static inline esp_err_t esp_apptrace_lock_cleanup() esp_err_t esp_apptrace_lock(esp_apptrace_tmo_t *tmo) { #if CONFIG_ESP32_APPTRACE_LOCK_ENABLE - //unsigned cur, elapsed, start = xthal_get_ccount(); - esp_err_t ret = esp_apptrace_lock_take(&s_trace_buf.lock, tmo); if (ret != ESP_OK) { return ESP_FAIL; } - // decrease tmo by actual waiting time - // cur = xthal_get_ccount(); - // if (start <= cur) { - // elapsed = cur - start; - // } else { - // elapsed = ULONG_MAX - start + cur; - // } - // if (ESP_APPTRACE_CPUTICKS2US(elapsed) > *tmo) { - // *tmo = 0; - // } else { - // *tmo -= ESP_APPTRACE_CPUTICKS2US(elapsed); - // } #endif return ESP_OK; } diff --git a/docs/api-guides/app_trace.rst b/docs/api-guides/app_trace.rst index c8e54a6142..0873decb2d 100644 --- a/docs/api-guides/app_trace.rst +++ b/docs/api-guides/app_trace.rst @@ -116,7 +116,7 @@ Also according to his needs user may want to receive data from the host. Piece o ... } -``esp_apptrace_read()`` function uses memcpy to copy host data to user buffer. In some cases it can be more optimal to use ``esp_apptrace_down_buffer_get()`` and ``esp_apptrace_down_buffer_put()`` functions. +``esp_apptrace_read()`` function uses memcpy to copy host data to user buffer. In some cases it can be more optimal to use ``esp_apptrace_down_buffer_get()`` and ``esp_apptrace_down_buffer_put()`` functions. They allow developers to ocupy chunk of read buffer and process it in-place. The following piece of code shows how to do this. .. code-block:: c @@ -135,12 +135,12 @@ They allow developers to ocupy chunk of read buffer and process it in-place. The return ESP_FAIL; } if (sz > 4) { - number = (uint32_t *)ptr; - printf("Here is the number %d", *number); + number = (uint32_t *)ptr; + printf("Here is the number %d", *number); } else { - printf("No data"); + printf("No data"); } - esp_err_t res = esp_apptrace_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/); + esp_err_t res = esp_apptrace_down_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/); if (res != ESP_OK) { /* in case of error host tracing tool (e.g. OpenOCD) will report incomplete user buffer */ ESP_LOGE("Failed to put buffer!"); From ee6d0631f4c2a6a512cbc3f043c70d45ff7ab68d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 26 Jul 2017 14:21:07 +0800 Subject: [PATCH 07/26] nghttp: build port directory Fixes https://github.com/espressif/esp-idf/issues/843 --- components/nghttp/component.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nghttp/component.mk b/components/nghttp/component.mk index b73161e47c..2a69cd5e1f 100644 --- a/components/nghttp/component.mk +++ b/components/nghttp/component.mk @@ -4,6 +4,6 @@ COMPONENT_ADD_INCLUDEDIRS := port/include nghttp2/lib/includes -COMPONENT_SRCDIRS := nghttp2/lib +COMPONENT_SRCDIRS := nghttp2/lib port COMPONENT_SUBMODULES := nghttp2 From fc05851b054477c1e54d7ae121e10502d3ce2329 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Wed, 19 Jul 2017 14:50:40 +0800 Subject: [PATCH 08/26] Component/bt: add set local privacy func callback --- .../bluedroid/api/include/esp_gap_ble_api.h | 7 ++++ components/bt/bluedroid/bta/dm/bta_dm_act.c | 2 +- components/bt/bluedroid/bta/dm/bta_dm_api.c | 4 +-- components/bt/bluedroid/bta/dm/bta_dm_int.h | 1 + components/bt/bluedroid/bta/include/bta_api.h | 6 ++-- .../btc/profile/std/gap/btc_gap_ble.c | 32 +++++++++++++++++-- .../bt/bluedroid/stack/btm/btm_ble_addr.c | 8 +++++ .../bt/bluedroid/stack/btm/btm_ble_gap.c | 8 ++++- .../bt/bluedroid/stack/include/btm_api.h | 6 +++- .../bt/bluedroid/stack/include/btm_ble_api.h | 2 +- .../bt/bluedroid/stack/include/btm_ble_int.h | 1 + 11 files changed, 66 insertions(+), 11 deletions(-) diff --git a/components/bt/bluedroid/api/include/esp_gap_ble_api.h b/components/bt/bluedroid/api/include/esp_gap_ble_api.h index 0492bd5d84..29be0982fa 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -93,6 +93,7 @@ typedef enum { ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT, /*!< When set the static rand address complete, the event comes */ ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT, /*!< When update connection parameters complete, the event comes */ ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT, /*!< When set pkt lenght complete, the event comes */ + ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT, /*!< When Enable/disable privacy on the local device complete, the event comes */ } esp_gap_ble_cb_event_t; /// Advertising data maximum length @@ -539,6 +540,12 @@ typedef union { esp_bt_status_t status; /*!< Indicate the set pkt data length operation success status */ esp_ble_pkt_data_length_params_t params; /*!< pkt data length value */ } pkt_data_lenth_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT + */ + struct ble_local_privacy_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate the set local privacy operation success status */ + } local_privacy_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT */ } esp_ble_gap_cb_param_t; /** diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index 39a5f06d35..49289a9ab4 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -4601,7 +4601,7 @@ void bta_dm_ble_stop_advertising(tBTA_DM_MSG *p_data) *******************************************************************************/ void bta_dm_ble_config_local_privacy (tBTA_DM_MSG *p_data) { - BTM_BleConfigPrivacy (p_data->ble_local_privacy.privacy_enable); + BTM_BleConfigPrivacy (p_data->ble_local_privacy.privacy_enable, p_data->ble_local_privacy.set_local_privacy_cback); } #endif diff --git a/components/bt/bluedroid/bta/dm/bta_dm_api.c b/components/bt/bluedroid/bta/dm/bta_dm_api.c index 7ef0887619..fcb27004f0 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_api.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_api.c @@ -1575,7 +1575,7 @@ void BTA_DmBleUpdateConnectionParam(BD_ADDR bd_addr, UINT16 min_int, ** Returns void ** *******************************************************************************/ -void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable) +void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) { ///This function used the irk to generate the resolve address #if BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE @@ -1586,7 +1586,7 @@ void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable) p_msg->hdr.event = BTA_DM_API_LOCAL_PRIVACY_EVT; p_msg->privacy_enable = privacy_enable; - + p_msg->set_local_privacy_cback = set_local_privacy_cback; bta_sys_sendmsg(p_msg); } #else diff --git a/components/bt/bluedroid/bta/dm/bta_dm_int.h b/components/bt/bluedroid/bta/dm/bta_dm_int.h index 7357781370..e4bf0fadb0 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/bta_dm_int.h @@ -444,6 +444,7 @@ typedef struct { typedef struct { BT_HDR hdr; BOOLEAN privacy_enable; + tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback; } tBTA_DM_API_LOCAL_PRIVACY; /* set scan parameter for BLE connections */ diff --git a/components/bt/bluedroid/bta/include/bta_api.h b/components/bt/bluedroid/bta/include/bta_api.h index f21803b1c9..1313b90c8b 100644 --- a/components/bt/bluedroid/bta/include/bta_api.h +++ b/components/bt/bluedroid/bta/include/bta_api.h @@ -404,6 +404,8 @@ typedef void (tBTA_START_ADV_CMPL_CBACK) (tBTA_STATUS status); typedef tBTM_SET_PKT_DATA_LENGTH_CBACK tBTA_SET_PKT_DATA_LENGTH_CBACK; +typedef tBTM_SET_LOCAL_PRIVACY_CBACK tBTA_SET_LOCAL_PRIVACY_CBACK; + /* advertising channel map */ #define BTA_BLE_ADV_CHNL_37 BTM_BLE_ADV_CHNL_37 #define BTA_BLE_ADV_CHNL_38 BTM_BLE_ADV_CHNL_38 @@ -2035,11 +2037,11 @@ extern void BTA_DmSetRandAddress(BD_ADDR rand_addr); ** Description Enable/disable privacy on the local device ** ** Parameters: privacy_enable - enable/disabe privacy on remote device. -** +** set_local_privacy_cback -callback to be called with result ** Returns void ** *******************************************************************************/ -extern void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable); +extern void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback); /******************************************************************************* ** diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 3863d5e3ca..1e551b6f88 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -145,6 +145,12 @@ static esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status) case BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED: esp_status = ESP_BT_STATUS_CONTROL_LE_DATA_LEN_UNSUPPORTED; break; + case BTM_SET_PRIVACY_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case BTM_SET_PRIVACY_FAIL: + esp_status = ESP_BT_STATUS_FAIL; + break; default: esp_status = ESP_BT_STATUS_FAIL; break; @@ -661,6 +667,23 @@ static void btc_set_pkt_length_callback(UINT8 status, tBTM_LE_SET_PKT_DATA_LENGT } } +static void btc_set_local_privacy_callback(UINT8 status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT; + param.local_privacy_cmpl.status = btc_btm_status_to_esp_status(status); + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + #if (SMP_INCLUDED == TRUE) static void btc_set_encryption_callback(BD_ADDR bd_addr, tBTA_TRANSPORT transport, tBTA_STATUS enc_status) @@ -756,9 +779,9 @@ static void btc_ble_set_rand_addr (BD_ADDR rand_addr) } } -static void btc_ble_config_local_privacy(bool privacy_enable) +static void btc_ble_config_local_privacy(bool privacy_enable, tBTA_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) { - BTA_DmBleConfigLocalPrivacy(privacy_enable); + BTA_DmBleConfigLocalPrivacy(privacy_enable, set_local_privacy_cback); } static void btc_ble_disconnect(BD_ADDR bd_addr) @@ -837,6 +860,9 @@ void btc_gap_ble_cb_handler(btc_msg_t *msg) case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: btc_gap_ble_cb_to_app(ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT, param); break; + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT, param); + break; default: break; @@ -1000,7 +1026,7 @@ void btc_gap_ble_call_handler(btc_msg_t *msg) break; } case BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY: - btc_ble_config_local_privacy(arg->cfg_local_privacy.privacy_enable); + btc_ble_config_local_privacy(arg->cfg_local_privacy.privacy_enable, btc_set_local_privacy_callback); break; case BTC_GAP_BLE_ACT_CFG_ADV_DATA_RAW: btc_ble_set_adv_data_raw(arg->cfg_adv_data_raw.raw_adv, diff --git a/components/bt/bluedroid/stack/btm/btm_ble_addr.c b/components/bt/bluedroid/stack/btm/btm_ble_addr.c index 4f6c443a5b..cfc9632750 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_addr.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_addr.c @@ -60,6 +60,10 @@ static void btm_gen_resolve_paddr_cmpl(tSMP_ENC *p) btsnd_hcic_ble_set_random_addr(p_cb->private_addr); p_cb->own_addr_type = BLE_ADDR_RANDOM; + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_SUCCESS); + p_cb->set_local_privacy_cback = NULL; + } /* start a periodical timer to refresh random addr */ btu_stop_timer_oneshot(&p_cb->raddr_timer_ent); @@ -73,6 +77,10 @@ static void btm_gen_resolve_paddr_cmpl(tSMP_ENC *p) } else { /* random address set failure */ BTM_TRACE_DEBUG("set random address failed"); + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_FAIL); + p_cb->set_local_privacy_cback = NULL; + } } } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index 523137aa0b..1029977cb2 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -640,10 +640,16 @@ void BTM_BleEnableMixedPrivacyMode(BOOLEAN mixed_on) ** Returns BOOLEAN privacy mode set success; otherwise failed. ** *******************************************************************************/ -BOOLEAN BTM_BleConfigPrivacy(BOOLEAN privacy_mode) +BOOLEAN BTM_BleConfigPrivacy(BOOLEAN privacy_mode, tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) { #if BLE_PRIVACY_SPT == TRUE tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_LE_RANDOM_CB *random_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + if (random_cb){ + random_cb->set_local_privacy_cback = set_local_privacy_cback; + }else{ + BTM_TRACE_ERROR("%s,random_cb = NULL", __func__); + } BTM_TRACE_EVENT ("%s\n", __func__); diff --git a/components/bt/bluedroid/stack/include/btm_api.h b/components/bt/bluedroid/stack/include/btm_api.h index 2976e497ed..4af3a48635 100644 --- a/components/bt/bluedroid/stack/include/btm_api.h +++ b/components/bt/bluedroid/stack/include/btm_api.h @@ -69,7 +69,9 @@ enum { BTM_REPEATED_ATTEMPTS, /* 19 repeated attempts for LE security requests */ BTM_MODE4_LEVEL4_NOT_SUPPORTED, /* 20 Secure Connections Only Mode can't be supported */ BTM_PEER_LE_DATA_LEN_UNSUPPORTED, /* 21 peer setting data length is unsupported*/ - BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED /* 22 controller setting data length is unsupported*/ + BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED,/* 22 controller setting data length is unsupported*/ + BTM_SET_PRIVACY_SUCCESS, /* 23 enable/disable local privacy success */ + BTM_SET_PRIVACY_FAIL, /* 24 enable/disable local privacy failed*/ }; typedef uint8_t tBTM_STATUS; @@ -175,6 +177,8 @@ typedef void (tBTM_UPDATE_CONN_PARAM_CBACK) (UINT8 status, BD_ADDR bd_addr, tBTM typedef void (tBTM_SET_PKT_DATA_LENGTH_CBACK) (UINT8 status, tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS *data_length_params); +typedef void (tBTM_SET_LOCAL_PRIVACY_CBACK) (UINT8 status); + /***************************************************************************** ** DEVICE DISCOVERY - Inquiry, Remote Name, Discovery, Class of Device diff --git a/components/bt/bluedroid/stack/include/btm_ble_api.h b/components/bt/bluedroid/stack/include/btm_ble_api.h index 927812453f..dda03d1810 100644 --- a/components/bt/bluedroid/stack/include/btm_ble_api.h +++ b/components/bt/bluedroid/stack/include/btm_ble_api.h @@ -1596,7 +1596,7 @@ tBTM_STATUS BTM_BleBroadcast(BOOLEAN start); ** *******************************************************************************/ //extern -BOOLEAN BTM_BleConfigPrivacy(BOOLEAN enable); +BOOLEAN BTM_BleConfigPrivacy(BOOLEAN enable, tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cabck); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/btm_ble_int.h b/components/bt/bluedroid/stack/include/btm_ble_int.h index 055591d774..60684bdb32 100644 --- a/components/bt/bluedroid/stack/include/btm_ble_int.h +++ b/components/bt/bluedroid/stack/include/btm_ble_int.h @@ -185,6 +185,7 @@ typedef struct { tBTM_BLE_ADDR_CBACK *p_generate_cback; void *p; TIMER_LIST_ENT raddr_timer_ent; + tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback; } tBTM_LE_RANDOM_CB; #define BTM_BLE_MAX_BG_CONN_DEV_NUM 10 From 6afadc32860a016ab4beb03634397048bb7be336 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Mon, 31 Jul 2017 14:48:39 +0800 Subject: [PATCH 09/26] component/bt : fix a bug and optimize controller task handler 1. fix controller task printf in ISR may cause exception. 2. optimize task handler process. --- components/bt/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bt/lib b/components/bt/lib index 10942b2ff0..4e3a5769d3 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 10942b2ff00f7db473c1917f76be93e59730e73f +Subproject commit 4e3a5769d3c4eba23381a647dd781a32813e4335 From 87d58b9ddb5efe5467f526d2f522a65c1af28d4d Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Mon, 31 Jul 2017 17:24:07 +0800 Subject: [PATCH 10/26] Component/bt: fix esp_ble_gatts_set_attr_value( ) memory leak --- components/bt/bluedroid/bta/gatt/bta_gatts_act.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 733976c0ca..f2c601253e 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -506,7 +506,7 @@ void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ UINT16 service_id = p_srvc_cb->service_id; tBTA_GATTS cb_data; tBTA_GATT_STATUS gatts_status; - gatts_status = GATTS_SetAttributeValue(p_msg->api_add_char_descr.hdr.layer_specific, + gatts_status = GATTS_SetAttributeValue(p_msg->api_set_val.hdr.layer_specific, p_msg->api_set_val.length, p_msg->api_set_val.value); @@ -515,6 +515,10 @@ void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ cb_data.attr_val.attr_id = p_msg->api_set_val.hdr.layer_specific; cb_data.attr_val.status = gatts_status; + if (p_msg->api_set_val.value != NULL){ + GKI_freebuf(p_msg->api_set_val.value); + } + if (p_rcb->p_cback) { (*p_rcb->p_cback)(BTA_GATTS_SET_ATTR_VAL_EVT, &cb_data); } From 6ff53d1967a5ef1fb747edb8f6b336adfb28e21a Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Mon, 31 Jul 2017 17:52:38 +0800 Subject: [PATCH 11/26] Component/bt: wrong return code type for notify func - fix esp_ble_gattc_register_for_notify() returen wrong code type - fix esp_ble_gattc_unregister_for_notify() returen wrong code type --- components/bt/bluedroid/api/esp_gattc_api.c | 4 ++-- components/bt/bluedroid/api/include/esp_gattc_api.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index b80966cd86..50d998bea6 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -436,7 +436,7 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, +esp_err_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) @@ -459,7 +459,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, +esp_err_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) diff --git a/components/bt/bluedroid/api/include/esp_gattc_api.h b/components/bt/bluedroid/api/include/esp_gattc_api.h index 6e6f9396a5..f28736e3bc 100644 --- a/components/bt/bluedroid/api/include/esp_gattc_api.h +++ b/components/bt/bluedroid/api/include/esp_gattc_api.h @@ -634,7 +634,7 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, +esp_err_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id); @@ -653,7 +653,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, +esp_err_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id); From 9ebb68c2dc2a4213c2ee02e43c8e53796184e12c Mon Sep 17 00:00:00 2001 From: Dmitry Yakovlev Date: Thu, 20 Jul 2017 13:09:39 +0300 Subject: [PATCH 12/26] Added component to change standard size of the flash device. Now the sector size could be less. The configuration supports it. --- components/wear_levelling/Kconfig | 50 ++++++ components/wear_levelling/README.rst | 13 ++ components/wear_levelling/WL_Ext_Perf.cpp | 163 ++++++++++++++++++ components/wear_levelling/WL_Ext_Safe.cpp | 161 +++++++++++++++++ .../private_include/WL_Ext_Cfg.h | 22 +++ .../private_include/WL_Ext_Perf.h | 46 +++++ .../private_include/WL_Ext_Safe.h | 42 +++++ components/wear_levelling/wear_levelling.cpp | 31 +++- 8 files changed, 527 insertions(+), 1 deletion(-) create mode 100755 components/wear_levelling/Kconfig create mode 100755 components/wear_levelling/WL_Ext_Perf.cpp create mode 100755 components/wear_levelling/WL_Ext_Safe.cpp create mode 100755 components/wear_levelling/private_include/WL_Ext_Cfg.h create mode 100755 components/wear_levelling/private_include/WL_Ext_Perf.h create mode 100755 components/wear_levelling/private_include/WL_Ext_Safe.h diff --git a/components/wear_levelling/Kconfig b/components/wear_levelling/Kconfig new file mode 100755 index 0000000000..158c2175df --- /dev/null +++ b/components/wear_levelling/Kconfig @@ -0,0 +1,50 @@ +menu "FAT FS Wear Levelling Settings" + +choice WL_SECTOR_SIZE + bool "FAT FS sector size" + default WL_SECTOR_SIZE_FAT + help + Specify the FAT sector size. + You can set default sector size or size that will + fit to the flash device sector size. + +config WL_SECTOR_SIZE_FAT + bool "512" +config WL_SECTOR_SIZE_FLASH + bool "4096" +endchoice + +config WL_SECTOR_SIZE + int + default 512 if WL_SECTOR_SIZE_FAT + default 4096 if WL_SECTOR_SIZE_FLASH + +choice WL_SECTOR_MODE + bool "Sector store mode" + default WL_SECTOR_MODE_PERF + help + Specify the mode to store data into the flash. + +config WL_SECTOR_MODE_PERF + bool "Perfomance" + help + In Performance mode a data will be stored to the RAM and then + stored back to the flash. Compare to the Safety mode, this operation + faster, but if by the erase sector operation power will be off, the + data from complete flash device sector will be lost. + +config WL_SECTOR_MODE_SAFE + bool "Safety" + help + In Safety mode a data from complete flash device sector will be stored to the flash and then + stored back to the flash. Compare to the Performance mode, this operation + slower, but if by the erase sector operation power will be off, the + data of the full flash device sector will not be lost. +endchoice + +config WL_SECTOR_MODE + int + default 0 if WL_SECTOR_MODE_PERF + default 1 if WL_SECTOR_MODE_SAFE + +endmenu diff --git a/components/wear_levelling/README.rst b/components/wear_levelling/README.rst index ebe298355c..c9fdd849ba 100644 --- a/components/wear_levelling/README.rst +++ b/components/wear_levelling/README.rst @@ -14,6 +14,19 @@ memory mapping data in the external SPI flash through the partition component. I also has higher-level APIs which work with FAT filesystem defined in the :doc:`FAT filesystem `. +The weat levelling componwnt, together with FAT FS component, works with FAT FS sector size 4096 +bytes which is standard size of the flash devices. In this mode the component has best perfomance, +but needs additional memoty in the RAM. To save internal memory the component has two additional modes +to work with sector size 512 bytes: Performance and Safety modes. In Performance mode by erase sector +operation data will be stored to the RAM, sector will be erased and then data will be stored +back to the flash. If by this operation power off situation will occure, the complete 4096 bytes +will be lost. To prevent this the Safety mode was implemented. In dafety mode the data will be first +stored to the flash and after sector will be erased, will be stored back. If power off situation will +occure, after power on, the data will be recovered. +By default defined the sector size 512 bytes and Performance mode. To change these values please use +the configuratoin menu. + + The wear levelling component does not cache data in RAM. Write and erase functions modify flash directly, and flash contents is consistent when the function returns. diff --git a/components/wear_levelling/WL_Ext_Perf.cpp b/components/wear_levelling/WL_Ext_Perf.cpp new file mode 100755 index 0000000000..18869f217c --- /dev/null +++ b/components/wear_levelling/WL_Ext_Perf.cpp @@ -0,0 +1,163 @@ +// Copyright 2015-2017 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 "WL_Ext_Perf.h" +#include +#include "esp_log.h" + +static const char *TAG = "wl_ext_perf"; + +#define WL_EXT_RESULT_CHECK(result) \ + if (result != ESP_OK) { \ + ESP_LOGE(TAG,"%s(%d): result = 0x%08x", __FUNCTION__, __LINE__, result); \ + return (result); \ + } + +WL_Ext_Perf::WL_Ext_Perf(): WL_Flash() +{ + this->sector_buffer = NULL; +} + +WL_Ext_Perf::~WL_Ext_Perf() +{ + free(this->sector_buffer); +} + +esp_err_t WL_Ext_Perf::config(WL_Config_s *cfg, Flash_Access *flash_drv) +{ + wl_ext_cfg_t *config = (wl_ext_cfg_t *)cfg; + + this->fat_sector_size = config->fat_sector_size; + this->flash_sector_size = cfg->sector_size; + + this->sector_buffer = (uint32_t *)malloc(cfg->sector_size); + if (this->sector_buffer == NULL) { + return ESP_ERR_NO_MEM; + } + + this->size_factor = this->flash_sector_size / this->fat_sector_size; + if (this->size_factor < 1) { + return ESP_ERR_INVALID_ARG; + } + + return WL_Flash::config(cfg, flash_drv); +} + +esp_err_t WL_Ext_Perf::init() +{ + return WL_Flash::init(); +} + +size_t WL_Ext_Perf::chip_size() +{ + return WL_Flash::chip_size(); +} +size_t WL_Ext_Perf::sector_size() +{ + return this->fat_sector_size; +} + +esp_err_t WL_Ext_Perf::erase_sector(size_t sector) +{ + return this->erase_sector_fit(sector, 1); +} + +esp_err_t WL_Ext_Perf::erase_sector_fit(uint32_t start_sector, uint32_t count) +{ + // This method works with one flash device sector and able to erase "count" of fatfs sectors from this sector + esp_err_t result = ESP_OK; + + uint32_t pre_check_start = start_sector % this->size_factor; + + + for (int i = 0; i < this->size_factor; i++) { + if ((i < pre_check_start) || (i >= count + pre_check_start)) { + result = this->read(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size); + WL_EXT_RESULT_CHECK(result); + } + } + + result = WL_Flash::erase_sector(start_sector / this->size_factor); // erase comlete flash sector + WL_EXT_RESULT_CHECK(result); + // And write back only data that should not be erased... + for (int i = 0; i < this->size_factor; i++) { + if ((i < pre_check_start) || (i >= count + pre_check_start)) { + result = this->write(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size); + WL_EXT_RESULT_CHECK(result); + } + } + return ESP_OK; +} + +esp_err_t WL_Ext_Perf::erase_range(size_t start_address, size_t size) +{ + esp_err_t result = ESP_OK; + if ((start_address % this->fat_sector_size) != 0) { + result = ESP_ERR_INVALID_ARG; + } + if (((size % this->fat_sector_size) != 0) || (size == 0)) { + result = ESP_ERR_INVALID_ARG; + } + WL_EXT_RESULT_CHECK(result); + + // The range to erase could be allocated in any possible way + // --------------------------------------------------------- + // | | | | | + // |0|0|x|x|x|x|x|x|x|x|x|x|x|x|0|0| + // | pre | rest | rest | post | <- check ranges + // + // Pre check - the data that is not fit to the full sector at the begining of the erased block + // Post check - the data that are not fit to the full sector at the end of the erased block + // rest - data that are fit to the flash device sector at the middle of the erased block + // + // In case of pre and post check situations the data of the non erased area have to be readed first and then + // stored back. + // For the rest area this operation not needed because complete flash device sector will be erased. + + ESP_LOGV(TAG, "%s begin, addr = 0x%08x, size = %i", __func__, start_address, size); + // Calculate pre check values + uint32_t pre_check_start = (start_address / this->fat_sector_size) % this->size_factor; + uint32_t sectors_count = size / this->fat_sector_size; + uint32_t pre_check_count = (this->size_factor - pre_check_start); + if (pre_check_count > sectors_count) { + pre_check_count = sectors_count; + } + + // Calculate post ckeck + uint32_t post_check_count = (sectors_count - pre_check_count) % this->size_factor; + uint32_t post_check_start = ((start_address + size - post_check_count * this->fat_sector_size) / this->fat_sector_size); + + // Calculate rest + uint32_t rest_check_count = sectors_count - pre_check_count - post_check_count; + if ((pre_check_count == this->size_factor) && (0 == pre_check_start)) { + rest_check_count++; + pre_check_count = 0; + } + uint32_t rest_check_start = start_address + pre_check_count * this->fat_sector_size; + + // Here we will clear pre_check_count amount of sectors + if (pre_check_count != 0) { + result = this->erase_sector_fit(start_address / this->fat_sector_size, pre_check_count); + WL_EXT_RESULT_CHECK(result); + } + if (rest_check_count > 0) { + rest_check_count = rest_check_count / this->size_factor; + result = WL_Flash::erase_range(rest_check_start, rest_check_count * this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + } + if (post_check_count != 0) { + result = this->erase_sector_fit(post_check_start, post_check_count); + WL_EXT_RESULT_CHECK(result); + } + return ESP_OK; +} diff --git a/components/wear_levelling/WL_Ext_Safe.cpp b/components/wear_levelling/WL_Ext_Safe.cpp new file mode 100755 index 0000000000..d77bcbefaa --- /dev/null +++ b/components/wear_levelling/WL_Ext_Safe.cpp @@ -0,0 +1,161 @@ +// Copyright 2015-2017 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 "WL_Ext_Safe.h" +#include +#include "esp_log.h" + +static const char *TAG = "wl_ext_safe"; + +#define WL_EXT_RESULT_CHECK(result) \ + if (result != ESP_OK) { \ + ESP_LOGE(TAG,"%s(%d): result = 0x%08x", __FUNCTION__, __LINE__, result); \ + return (result); \ + } + +#ifndef FLASH_ERASE_VALUE +#define FLASH_ERASE_VALUE 0xffffffff +#endif // FLASH_ERASE_VALUE + + +#ifndef WL_EXT_SAFE_OK +#define WL_EXT_SAFE_OK 0x12345678 +#endif // WL_EXT_SAFE_OK + +#ifndef WL_EXT_SAFE_OFFSET +#define WL_EXT_SAFE_OFFSET 16 +#endif // WL_EXT_SAFE_OFFSET + + +struct WL_Ext_Safe_State { +public: + uint32_t erase_begin; + uint32_t local_addr_base; + uint32_t local_addr_shift; + uint32_t count; +}; + +WL_Ext_Safe::WL_Ext_Safe(): WL_Ext_Perf() +{ +} + +WL_Ext_Safe::~WL_Ext_Safe() +{ +} + +esp_err_t WL_Ext_Safe::config(WL_Config_s *cfg, Flash_Access *flash_drv) +{ + esp_err_t result = ESP_OK; + + result = WL_Ext_Perf::config(cfg, flash_drv); + WL_EXT_RESULT_CHECK(result); + this->state_addr = WL_Flash::chip_size() - 2 * WL_Flash::sector_size(); + this->dump_addr = WL_Flash::chip_size() - 1 * WL_Flash::sector_size(); + return ESP_OK; +} + +esp_err_t WL_Ext_Safe::init() +{ + esp_err_t result = ESP_OK; + ESP_LOGV(TAG, "%s", __func__); + + result = WL_Ext_Perf::init(); + WL_EXT_RESULT_CHECK(result); + + result = this->recover(); + return result; +} + +size_t WL_Ext_Safe::chip_size() +{ + ESP_LOGV(TAG, "%s size = %i", __func__, WL_Flash::chip_size() - 2 * this->flash_sector_size); + return WL_Flash::chip_size() - 2 * this->flash_sector_size; +} + +esp_err_t WL_Ext_Safe::recover() +{ + esp_err_t result = ESP_OK; + + WL_Ext_Safe_State state; + result = WL_Flash::read(this->state_addr, &state, sizeof(WL_Ext_Safe_State)); + WL_EXT_RESULT_CHECK(result); + ESP_LOGI(TAG, "%s recover, start_addr = 0x%08x, local_addr_base = 0x%08x, local_addr_shift = %i, count=%i", __func__, state.erase_begin, state.local_addr_base, state.local_addr_shift, state.count); + + // check if we have transaction + if (state.erase_begin == WL_EXT_SAFE_OK) { + + result = this->read(this->dump_addr, this->sector_buffer, this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + + result = WL_Flash::erase_sector(state.local_addr_base); // erase comlete flash sector + WL_EXT_RESULT_CHECK(result); + + // And write back... + for (int i = 0; i < this->size_factor; i++) { + if ((i < state.local_addr_shift) || (i >= state.count + state.local_addr_shift)) { + result = this->write(state.local_addr_base * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size); + WL_EXT_RESULT_CHECK(result); + } + } + // clear transaction + result = WL_Flash::erase_range(this->state_addr, this->flash_sector_size); + } + return result; +} + +esp_err_t WL_Ext_Safe::erase_sector_fit(uint32_t start_sector, uint32_t count) +{ + esp_err_t result = ESP_OK; + + uint32_t local_addr_base = start_sector / this->size_factor; + uint32_t pre_check_start = start_sector % this->size_factor; + ESP_LOGV(TAG, "%s start_sector=0x%08x, count = %i", __func__, start_sector, count); + for (int i = 0; i < this->size_factor; i++) { + if ((i < pre_check_start) || (i >= count + pre_check_start)) { + result = this->read(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size); + WL_EXT_RESULT_CHECK(result); + } + } + + result = WL_Flash::erase_sector(this->dump_addr / this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + result = WL_Flash::write(this->dump_addr, this->sector_buffer, this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + + WL_Ext_Safe_State state; + state.erase_begin = WL_EXT_SAFE_OK; + state.local_addr_base = local_addr_base; + state.local_addr_shift = pre_check_start; + state.count = count; + + result = WL_Flash::erase_sector(this->state_addr / this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + result = WL_Flash::write(this->state_addr + 0, &state, sizeof(WL_Ext_Safe_State)); + WL_EXT_RESULT_CHECK(result); + + // Erase + result = WL_Flash::erase_sector(local_addr_base); // erase comlete flash sector + WL_EXT_RESULT_CHECK(result); + // And write back... + for (int i = 0; i < this->size_factor; i++) { + if ((i < pre_check_start) || (i >= count + pre_check_start)) { + result = this->write(local_addr_base * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size); + WL_EXT_RESULT_CHECK(result); + } + } + + result = WL_Flash::erase_sector(this->state_addr / this->flash_sector_size); + WL_EXT_RESULT_CHECK(result); + + return ESP_OK; +} diff --git a/components/wear_levelling/private_include/WL_Ext_Cfg.h b/components/wear_levelling/private_include/WL_Ext_Cfg.h new file mode 100755 index 0000000000..a82ba5f52e --- /dev/null +++ b/components/wear_levelling/private_include/WL_Ext_Cfg.h @@ -0,0 +1,22 @@ +// Copyright 2015-2017 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 _WL_Ext_Cfg_H_ +#define _WL_Ext_Cfg_H_ +#include "WL_Config.h" + +typedef struct WL_Ext_Cfg_s : public WL_Config_s { + uint32_t fat_sector_size; /*!< virtual sector size*/ +} wl_ext_cfg_t; + +#endif // _WL_Ext_Cfg_H_ \ No newline at end of file diff --git a/components/wear_levelling/private_include/WL_Ext_Perf.h b/components/wear_levelling/private_include/WL_Ext_Perf.h new file mode 100755 index 0000000000..63823d8b38 --- /dev/null +++ b/components/wear_levelling/private_include/WL_Ext_Perf.h @@ -0,0 +1,46 @@ +// Copyright 2015-2017 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 _WL_Ext_Perf_H_ +#define _WL_Ext_Perf_H_ + +#include "WL_Flash.h" +#include "WL_Ext_Cfg.h" + +class WL_Ext_Perf : public WL_Flash +{ +public: + WL_Ext_Perf(); + ~WL_Ext_Perf() override; + + esp_err_t config(WL_Config_s *cfg, Flash_Access *flash_drv) override; + esp_err_t init() override; + + size_t chip_size() override; + size_t sector_size() override; + + + esp_err_t erase_sector(size_t sector) override; + esp_err_t erase_range(size_t start_address, size_t size) override; + +protected: + uint32_t flash_sector_size; + uint32_t fat_sector_size; + uint32_t size_factor; + uint32_t *sector_buffer; + + virtual esp_err_t erase_sector_fit(uint32_t start_sector, uint32_t count); + +}; + +#endif // _WL_Ext_Perf_H_ \ No newline at end of file diff --git a/components/wear_levelling/private_include/WL_Ext_Safe.h b/components/wear_levelling/private_include/WL_Ext_Safe.h new file mode 100755 index 0000000000..5c20371390 --- /dev/null +++ b/components/wear_levelling/private_include/WL_Ext_Safe.h @@ -0,0 +1,42 @@ +// Copyright 2015-2017 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 _WL_Ext_Safe_H_ +#define _WL_Ext_Safe_H_ + +#include "WL_Flash.h" +#include "WL_Ext_Cfg.h" +#include "WL_Ext_Perf.h" + +class WL_Ext_Safe : public WL_Ext_Perf +{ +public: + WL_Ext_Safe(); + ~WL_Ext_Safe() override; + + esp_err_t config(WL_Config_s *cfg, Flash_Access *flash_drv) override; + esp_err_t init() override; + + size_t chip_size() override; + +protected: + esp_err_t erase_sector_fit(uint32_t start_sector, uint32_t count) override; + + // Dump Sector + uint32_t dump_addr; // dump buffer address + uint32_t state_addr;// sectore where state of transaction will be stored + + esp_err_t recover(); +}; + +#endif // _WL_Ext_Safe_H_ \ No newline at end of file diff --git a/components/wear_levelling/wear_levelling.cpp b/components/wear_levelling/wear_levelling.cpp index 4e581196b0..30d36ce209 100644 --- a/components/wear_levelling/wear_levelling.cpp +++ b/components/wear_levelling/wear_levelling.cpp @@ -17,7 +17,10 @@ #include #include "wear_levelling.h" #include "WL_Config.h" +#include "WL_Ext_Cfg.h" #include "WL_Flash.h" +#include "WL_Ext_Perf.h" +#include "WL_Ext_Safe.h" #include "SPI_Flash.h" #include "Partition.h" @@ -79,7 +82,7 @@ esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle) goto out; } - wl_config_t cfg; + wl_ext_cfg_t cfg; cfg.full_mem_size = partition->size; cfg.start_addr = WL_DEFAULT_START_ADDR; cfg.version = WL_CURRENT_VERSION; @@ -88,6 +91,8 @@ esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle) cfg.updaterate = WL_DEFAULT_UPDATERATE; cfg.temp_buff_size = WL_DEFAULT_TEMP_BUFF_SIZE; cfg.wr_size = WL_DEFAULT_WRITE_SIZE; + // FAT sector size by default will be 512 + cfg.fat_sector_size = CONFIG_WL_SECTOR_SIZE; // Allocate memory for a Partition object, and then initialize the object // using placement new operator. This way we can recover from out of @@ -101,13 +106,37 @@ esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle) part = new (part_ptr) Partition(partition); // Same for WL_Flash: allocate memory, use placement new +#if CONFIG_WL_SECTOR_SIZE == 512 +#if CONFIG_WL_SECTOR_MODE == 1 + wl_flash_ptr = malloc(sizeof(WL_Ext_Safe)); + + if (wl_flash_ptr == NULL) { + result = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Safe", __func__); + goto out; + } + wl_flash = new (wl_flash_ptr) WL_Ext_Safe(); +#else + wl_flash_ptr = malloc(sizeof(WL_Ext_Perf)); + + if (wl_flash_ptr == NULL) { + result = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Perf", __func__); + goto out; + } + wl_flash = new (wl_flash_ptr) WL_Ext_Perf(); +#endif // CONFIG_WL_SECTOR_MODE +#endif // CONFIG_WL_SECTOR_SIZE +#if CONFIG_WL_SECTOR_SIZE == 4096 wl_flash_ptr = malloc(sizeof(WL_Flash)); + if (wl_flash_ptr == NULL) { result = ESP_ERR_NO_MEM; ESP_LOGE(TAG, "%s: can't allocate WL_Flash", __func__); goto out; } wl_flash = new (wl_flash_ptr) WL_Flash(); +#endif // CONFIG_WL_SECTOR_SIZE result = wl_flash->config(&cfg, part); if (ESP_OK != result) { From 06805842ea8e3c4f973bc6beeeddbbab4b5d2521 Mon Sep 17 00:00:00 2001 From: Dmitry Yakovlev Date: Mon, 24 Jul 2017 08:47:34 +0300 Subject: [PATCH 13/26] Some typos corrected in readme. --- components/wear_levelling/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/wear_levelling/README.rst b/components/wear_levelling/README.rst index c9fdd849ba..cd5bd438db 100644 --- a/components/wear_levelling/README.rst +++ b/components/wear_levelling/README.rst @@ -14,13 +14,13 @@ memory mapping data in the external SPI flash through the partition component. I also has higher-level APIs which work with FAT filesystem defined in the :doc:`FAT filesystem `. -The weat levelling componwnt, together with FAT FS component, works with FAT FS sector size 4096 +The weat levelling component, together with FAT FS component, works with FAT FS sector size 4096 bytes which is standard size of the flash devices. In this mode the component has best perfomance, but needs additional memoty in the RAM. To save internal memory the component has two additional modes to work with sector size 512 bytes: Performance and Safety modes. In Performance mode by erase sector operation data will be stored to the RAM, sector will be erased and then data will be stored back to the flash. If by this operation power off situation will occure, the complete 4096 bytes -will be lost. To prevent this the Safety mode was implemented. In dafety mode the data will be first +will be lost. To prevent this the Safety mode was implemented. In safety mode the data will be first stored to the flash and after sector will be erased, will be stored back. If power off situation will occure, after power on, the data will be recovered. By default defined the sector size 512 bytes and Performance mode. To change these values please use From 831cc4b42fb9533809d927b3efd090a82ce05a3a Mon Sep 17 00:00:00 2001 From: Dmitry Yakovlev Date: Mon, 31 Jul 2017 07:50:20 +0300 Subject: [PATCH 14/26] Typos correction. --- components/wear_levelling/README.rst | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/components/wear_levelling/README.rst b/components/wear_levelling/README.rst index cd5bd438db..88d54ee749 100644 --- a/components/wear_levelling/README.rst +++ b/components/wear_levelling/README.rst @@ -4,30 +4,30 @@ Wear Levelling APIs Overview -------- Most of the flash devices and specially SPI flash devices that are used in ESP32 -have sector based organization and have limited amount of erase/modification cycles -per memory sector. To avoid situation when one sector reach the limit of erases when +have sector based organization and have limited amount of erase/modification cycles +per memory sector. To avoid situation when one sector reach the limit of erases when other sectors was used not often, we have made a component that avoid this situation. -The wear levelling component share the amount of erases between all sectors in the +The wear levelling component share the amount of erases between all sectors in the memory without user interaction. The wear_levelling component contains APIs related to reading, writing, erasing, -memory mapping data in the external SPI flash through the partition component. It -also has higher-level APIs which work with FAT filesystem defined in +memory mapping data in the external SPI flash through the partition component. It +also has higher-level APIs which work with FAT filesystem defined in the :doc:`FAT filesystem `. -The weat levelling component, together with FAT FS component, works with FAT FS sector size 4096 -bytes which is standard size of the flash devices. In this mode the component has best perfomance, -but needs additional memoty in the RAM. To save internal memory the component has two additional modes -to work with sector size 512 bytes: Performance and Safety modes. In Performance mode by erase sector -operation data will be stored to the RAM, sector will be erased and then data will be stored -back to the flash. If by this operation power off situation will occure, the complete 4096 bytes -will be lost. To prevent this the Safety mode was implemented. In safety mode the data will be first -stored to the flash and after sector will be erased, will be stored back. If power off situation will -occure, after power on, the data will be recovered. -By default defined the sector size 512 bytes and Performance mode. To change these values please use -the configuratoin menu. +The wear levelling component, together with FAT FS component, works with FAT FS sector size 4096 +bytes which is standard size of the flash devices. In this mode the component has best performance, +but needs additional memoty in the RAM. To save internal memory the component has two additional modes +to work with sector size 512 bytes: Performance and Safety modes. In Performance mode by erase sector +operation data will be stored to the RAM, sector will be erased and then data will be stored +back to the flash. If by this operation power off situation will occur, the complete 4096 bytes +will be lost. To prevent this the Safety mode was implemented. In safety mode the data will be first +stored to the flash and after sector will be erased, will be stored back. If power off situation will +occur, after power on, the data will be recovered. +By default defined the sector size 512 bytes and Performance mode. To change these values please use +the configuration menu. -The wear levelling component does not cache data in RAM. Write and erase functions +The wear levelling component does not cache data in RAM. Write and erase functions modify flash directly, and flash contents is consistent when the function returns. @@ -50,6 +50,6 @@ filesystem-specific functions. Memory Size ----------- -The memory size calculated in the wear Levelling module based on parameters of +The memory size calculated in the wear Levelling module based on parameters of partition. The module use few sectors of flash for internal data. From 512898edeef1b5df87d1fc0859ee7149916e7332 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 1 Aug 2017 02:24:25 +0800 Subject: [PATCH 15/26] sdmmc: fix reads/writes to/from unaligned buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDMMC hardware treats all buffers as aligned, and ignores 2 LSBs of addresses written into DMA descriptors. Previously SDMMC host driver assumed that data buffers passed from SDDMC command layer would be aligned. However alignment checks were never implemented in the command layer, as were the checks that the buffer coming from the application would be in DMA capable memory. Most of the time this was indeed true. However in some cases FATFS library can pass buffers offset by 2 bytes from word boundary. “DMA capable” restriction may be broken if pSRAM support is used. This change adds buffer checks to the SDMMC host driver (alignment and DMA capability), so that the host layer will error out for incompatible buffers. In SDMMC command layer, a check is added to read and write functions. If an incompatible buffer is passed from the application, new buffer (512 bytes size) is allocated, and the transfer is performed using {READ,WRITE}_SINGLE_BLOCK commands. --- components/driver/include/driver/sdmmc_host.h | 4 + components/driver/sdmmc_transaction.c | 14 +- components/sdmmc/sdmmc_cmd.c | 67 ++++++++++ components/sdmmc/test/test_sd.c | 124 +++++++++++++----- 4 files changed, 173 insertions(+), 36 deletions(-) diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h index dc3e9ef6bf..f2e2d66d5d 100644 --- a/components/driver/include/driver/sdmmc_host.h +++ b/components/driver/include/driver/sdmmc_host.h @@ -142,6 +142,8 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz); * can call sdmmc_host_do_transaction as long as other sdmmc_host_* * functions are not called. * + * @attention Data buffer passed in cmdinfo->data must be in DMA capable memory + * * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) * @param cmdinfo pointer to structure describing command and data to transfer * @return @@ -149,6 +151,8 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz); * - ESP_ERR_TIMEOUT if response or data transfer has timed out * - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed * - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response + * - ESP_ERR_INVALID_SIZE if the size of data transfer is not valid in SD protocol + * - ESP_ERR_INVALID_ARG if the data buffer is not in DMA capable memory */ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo); diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index 3adc9ce68e..c3a5827544 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -20,6 +20,7 @@ #include "freertos/semphr.h" #include "soc/sdmmc_reg.h" #include "soc/sdmmc_struct.h" +#include "soc/soc_memory_layout.h" #include "driver/sdmmc_types.h" #include "driver/sdmmc_defs.h" #include "driver/sdmmc_host.h" @@ -105,9 +106,16 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) // convert cmdinfo to hardware register value sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo); if (cmdinfo->data) { - // these constraints should be handled by upper layer - assert(cmdinfo->datalen >= 4); - assert(cmdinfo->blklen % 4 == 0); + if (cmdinfo->datalen < 4 || cmdinfo->blklen % 4 != 0) { + ESP_LOGD(TAG, "%s: invalid size: total=%d block=%d", + __func__, cmdinfo->datalen, cmdinfo->blklen); + return ESP_ERR_INVALID_SIZE; + } + if ((intptr_t) cmdinfo->data % 4 != 0 || + !esp_ptr_dma_capable(cmdinfo->data)) { + ESP_LOGD(TAG, "%s: buffer %p can not be used for DMA", __func__, cmdinfo->data); + return ESP_ERR_INVALID_ARG; + } // this clears "owned by IDMAC" bits memset(s_dma_desc, 0, sizeof(s_dma_desc)); // initialize first descriptor diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index a25ac2674f..0784a279a1 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -24,6 +24,7 @@ #include "driver/sdmmc_types.h" #include "sdmmc_cmd.h" #include "sys/param.h" +#include "soc/soc_memory_layout.h" #define SDMMC_GO_IDLE_DELAY_MS 20 @@ -51,6 +52,10 @@ static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_st static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable); static uint32_t get_host_ocr(float voltage); static void response_ntoh(sdmmc_response_t response); +static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src, + size_t start_block, size_t block_count); +static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, + size_t start_block, size_t block_count); static bool host_is_spi(const sdmmc_card_t* card) @@ -618,6 +623,37 @@ static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_st esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, size_t start_block, size_t block_count) +{ + esp_err_t err = ESP_OK; + size_t block_size = card->csd.sector_size; + if (esp_ptr_dma_capable(src) && (intptr_t)src % 4 == 0) { + err = sdmmc_write_sectors_dma(card, src, start_block, block_count); + } else { + // SDMMC peripheral needs DMA-capable buffers. Split the write into + // separate single block writes, if needed, and allocate a temporary + // DMA-capable buffer. + void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA); + if (tmp_buf == NULL) { + return ESP_ERR_NO_MEM; + } + const uint8_t* cur_src = (const uint8_t*) src; + for (size_t i = 0; i < block_count; ++i) { + memcpy(tmp_buf, cur_src, block_size); + cur_src += block_size; + err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d", + __func__, err, start_block, i); + break; + } + } + free(tmp_buf); + } + return err; +} + +static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src, + size_t start_block, size_t block_count) { if (start_block + block_count > card->csd.capacity) { return ESP_ERR_INVALID_SIZE; @@ -661,6 +697,37 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, size_t start_block, size_t block_count) +{ + esp_err_t err = ESP_OK; + size_t block_size = card->csd.sector_size; + if (esp_ptr_dma_capable(dst) && (intptr_t)dst % 4 == 0) { + err = sdmmc_read_sectors_dma(card, dst, start_block, block_count); + } else { + // SDMMC peripheral needs DMA-capable buffers. Split the read into + // separate single block reads, if needed, and allocate a temporary + // DMA-capable buffer. + void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA); + if (tmp_buf == NULL) { + return ESP_ERR_NO_MEM; + } + uint8_t* cur_dst = (uint8_t*) dst; + for (size_t i = 0; i < block_count; ++i) { + err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d", + __func__, err, start_block, i); + break; + } + memcpy(cur_dst, tmp_buf, block_size); + cur_dst += block_size; + } + free(tmp_buf); + } + return err; +} + +static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, + size_t start_block, size_t block_count) { if (start_block + block_count > card->csd.capacity) { return ESP_ERR_INVALID_SIZE; diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 8d371f67e9..489911aa06 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -22,7 +22,7 @@ #include "driver/sdmmc_defs.h" #include "sdmmc_cmd.h" #include "esp_log.h" -#include "esp_heap_alloc_caps.h" +#include "esp_heap_caps.h" #include #include @@ -55,61 +55,85 @@ TEST_CASE("can probe SD (using SPI)", "[sdspi][ignore]") free(card); } +// Fill buffer pointed to by 'dst' with 'count' 32-bit ints generated +// from 'rand' with the starting value of 'seed' +static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count) { + srand(seed); + for (size_t i = 0; i < count; ++i) { + uint32_t val = rand(); + memcpy(dst + i * sizeof(uint32_t), &val, sizeof(val)); + } +} + +// Check if the buffer pointed to by 'dst' contains 'count' 32-bit +// ints generated from 'rand' with the starting value of 'seed' +static void check_buffer(uint32_t seed, const uint8_t* src, size_t count) { + srand(seed); + for (size_t i = 0; i < count; ++i) { + uint32_t val; + memcpy(&val, src + i * sizeof(uint32_t), sizeof(val)); + TEST_ASSERT_EQUAL_HEX32(rand(), val); + } +} + static void do_single_write_read_test(sdmmc_card_t* card, - size_t start_block, size_t block_count) + size_t start_block, size_t block_count, size_t alignment) { size_t block_size = card->csd.sector_size; size_t total_size = block_size * block_count; - printf(" %8d | %3d | %4.1f ", start_block, block_count, total_size / 1024.0f); - uint32_t* buffer = pvPortMallocCaps(total_size, MALLOC_CAP_DMA); - srand(start_block); - for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { - buffer[i] = rand(); - } + printf(" %8d | %3d | %d | %4.1f ", start_block, block_count, alignment, total_size / 1024.0f); + + uint32_t* buffer = heap_caps_malloc(total_size + 4, MALLOC_CAP_DMA); + size_t offset = alignment % 4; + uint8_t* c_buffer = (uint8_t*) buffer + offset; + fill_buffer(start_block, c_buffer, total_size / sizeof(buffer[0])); + struct timeval t_start_wr; gettimeofday(&t_start_wr, NULL); - TEST_ESP_OK(sdmmc_write_sectors(card, buffer, start_block, block_count)); + TEST_ESP_OK(sdmmc_write_sectors(card, c_buffer, start_block, block_count)); struct timeval t_stop_wr; gettimeofday(&t_stop_wr, NULL); float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec); - memset(buffer, 0xbb, total_size); + + memset(buffer, 0xbb, total_size + 4); + struct timeval t_start_rd; gettimeofday(&t_start_rd, NULL); - TEST_ESP_OK(sdmmc_read_sectors(card, buffer, start_block, block_count)); + TEST_ESP_OK(sdmmc_read_sectors(card, c_buffer, start_block, block_count)); struct timeval t_stop_rd; gettimeofday(&t_stop_rd, NULL); float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec); - printf(" | %6.2f | %.2f | %.2f | %.2f\n", + printf(" | %6.2f | %5.2f | %6.2f | %5.2f\n", time_wr, total_size / (time_wr / 1000) / (1024 * 1024), time_rd, total_size / (time_rd / 1000) / (1024 * 1024)); - srand(start_block); - for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { - TEST_ASSERT_EQUAL_HEX32(rand(), buffer[i]); - } + check_buffer(start_block, c_buffer, total_size / sizeof(buffer[0])); free(buffer); } static void read_write_test(sdmmc_card_t* card) { sdmmc_card_print_info(stdout, card); - printf(" sector | count | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n"); - do_single_write_read_test(card, 0, 1); - do_single_write_read_test(card, 0, 4); - do_single_write_read_test(card, 1, 16); - do_single_write_read_test(card, 16, 32); - do_single_write_read_test(card, 48, 64); - do_single_write_read_test(card, 128, 128); - do_single_write_read_test(card, card->csd.capacity - 64, 32); - do_single_write_read_test(card, card->csd.capacity - 64, 64); - do_single_write_read_test(card, card->csd.capacity - 8, 1); - do_single_write_read_test(card, card->csd.capacity/2, 1); - do_single_write_read_test(card, card->csd.capacity/2, 4); - do_single_write_read_test(card, card->csd.capacity/2, 8); - do_single_write_read_test(card, card->csd.capacity/2, 16); - do_single_write_read_test(card, card->csd.capacity/2, 32); - do_single_write_read_test(card, card->csd.capacity/2, 64); - do_single_write_read_test(card, card->csd.capacity/2, 128); + printf(" sector | count | align | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n"); + do_single_write_read_test(card, 0, 1, 4); + do_single_write_read_test(card, 0, 4, 4); + do_single_write_read_test(card, 1, 16, 4); + do_single_write_read_test(card, 16, 32, 4); + do_single_write_read_test(card, 48, 64, 4); + do_single_write_read_test(card, 128, 128, 4); + do_single_write_read_test(card, card->csd.capacity - 64, 32, 4); + do_single_write_read_test(card, card->csd.capacity - 64, 64, 4); + do_single_write_read_test(card, card->csd.capacity - 8, 1, 4); + do_single_write_read_test(card, card->csd.capacity/2, 1, 4); + do_single_write_read_test(card, card->csd.capacity/2, 4, 4); + do_single_write_read_test(card, card->csd.capacity/2, 8, 4); + do_single_write_read_test(card, card->csd.capacity/2, 16, 4); + do_single_write_read_test(card, card->csd.capacity/2, 32, 4); + do_single_write_read_test(card, card->csd.capacity/2, 64, 4); + do_single_write_read_test(card, card->csd.capacity/2, 128, 4); + do_single_write_read_test(card, card->csd.capacity/2, 1, 1); + do_single_write_read_test(card, card->csd.capacity/2, 8, 1); + do_single_write_read_test(card, card->csd.capacity/2, 128, 1); } TEST_CASE("can write and read back blocks", "[sd][ignore]") @@ -142,3 +166,37 @@ TEST_CASE("can write and read back blocks (using SPI)", "[sdspi][ignore]") TEST_ESP_OK(sdspi_host_deinit()); } +TEST_CASE("reads and writes with an unaligned buffer", "[sd]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + TEST_ESP_OK(sdmmc_host_init()); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config)); + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + + const size_t buffer_size = 4096; + const size_t block_count = buffer_size / 512; + const size_t extra = 4; + uint8_t* buffer = heap_caps_malloc(buffer_size + extra, MALLOC_CAP_DMA); + + // Check read behavior: do aligned write, then unaligned read + const uint32_t seed = 0x89abcdef; + fill_buffer(seed, buffer, buffer_size / sizeof(uint32_t)); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer, 0, block_count)); + memset(buffer, 0xcc, buffer_size + extra); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count)); + check_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t)); + + // Check write behavior: do unaligned write, then aligned read + fill_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t)); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 8, block_count)); + memset(buffer, 0xcc, buffer_size + extra); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 8, block_count)); + check_buffer(seed, buffer, buffer_size / sizeof(uint32_t)); + + free(buffer); + free(card); + TEST_ESP_OK(sdmmc_host_deinit()); +} From d2872cbf2ec72091676985791902382ff8862e5b Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Mon, 23 Jan 2017 16:11:27 +0800 Subject: [PATCH 16/26] component/bt : remove fixed queue from btu --- .../bt/bluedroid/bta/sys/bta_sys_main.c | 18 +- components/bt/bluedroid/hci/hci_hal_h4.c | 9 +- components/bt/bluedroid/hci/hci_layer.c | 32 +-- .../bt/bluedroid/hci/include/hci_layer.h | 3 - components/bt/bluedroid/main/bte_main.c | 66 ------- components/bt/bluedroid/osi/include/thread.h | 37 +++- components/bt/bluedroid/stack/btu/btu_hcif.c | 9 +- components/bt/bluedroid/stack/btu/btu_init.c | 73 +------ components/bt/bluedroid/stack/btu/btu_task.c | 187 +++++------------- 9 files changed, 104 insertions(+), 330 deletions(-) diff --git a/components/bt/bluedroid/bta/sys/bta_sys_main.c b/components/bt/bluedroid/bta/sys/bta_sys_main.c index c1d1c0018a..b023ef4635 100644 --- a/components/bt/bluedroid/bta/sys/bta_sys_main.c +++ b/components/bt/bluedroid/bta/sys/bta_sys_main.c @@ -51,7 +51,6 @@ tBTA_SYS_CB bta_sys_cb; #endif -fixed_queue_t *btu_bta_alarm_queue; static hash_map_t *bta_alarm_hash_map; static const size_t BTA_ALARM_HASH_MAP_SIZE = 17; static pthread_mutex_t bta_alarm_lock; @@ -62,8 +61,6 @@ static pthread_mutex_t bta_alarm_lock; UINT8 appl_trace_level = BT_TRACE_LEVEL_WARNING; //APPL_INITIAL_TRACE_LEVEL; UINT8 btif_trace_level = BT_TRACE_LEVEL_WARNING; -// Communication queue between btu_task and bta. -extern fixed_queue_t *btu_bta_msg_queue; void btu_bta_alarm_ready(fixed_queue_t *queue); static const tBTA_SYS_REG bta_sys_hw_reg = { @@ -175,10 +172,6 @@ void bta_sys_init(void) bta_alarm_hash_map = hash_map_new(BTA_ALARM_HASH_MAP_SIZE, hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); - btu_bta_alarm_queue = fixed_queue_new(SIZE_MAX); - - fixed_queue_register_dequeue(btu_bta_alarm_queue, - btu_bta_alarm_ready); appl_trace_level = APPL_INITIAL_TRACE_LEVEL; @@ -196,7 +189,6 @@ void bta_sys_init(void) void bta_sys_free(void) { - fixed_queue_free(btu_bta_alarm_queue, NULL); hash_map_free(bta_alarm_hash_map); pthread_mutex_destroy(&bta_alarm_lock); } @@ -575,10 +567,8 @@ void bta_sys_sendmsg(void *p_msg) // there is a procedure in progress that can schedule a task via this // message queue. This causes |btu_bta_msg_queue| to get cleaned up before // it gets used here; hence we check for NULL before using it. - if (btu_bta_msg_queue) { - fixed_queue_enqueue(btu_bta_msg_queue, p_msg); - //ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, TASK_POST_BLOCKING) != TASK_POST_SUCCESS) { + GKI_freebuf(p_msg); } } @@ -597,9 +587,7 @@ void bta_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - fixed_queue_enqueue(btu_bta_alarm_queue, p_tle); - - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_BTA_ALARM, p_tle, TASK_POST_BLOCKING); } void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms) diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 30e225e951..0cc5db676f 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -163,23 +163,26 @@ static void hci_hal_h4_rx_handler(void *arg) for (;;) { if (pdTRUE == xQueueReceive(xHciH4Queue, &e, (portTickType)portMAX_DELAY)) { - if (e.sig == 0xff) { + if (e.sig == SIG_HCI_HAL_RECV_PACKET) { fixed_queue_process(hci_hal_env.rx_q); } } } } -void hci_hal_h4_task_post(task_post_t timeout) +task_post_status_t hci_hal_h4_task_post(task_post_t timeout) { BtTaskEvt_t evt; - evt.sig = 0xff; + evt.sig = SIG_HCI_HAL_RECV_PACKET; evt.par = 0; if (xQueueSend(xHciH4Queue, &evt, timeout) != pdTRUE) { LOG_ERROR("xHciH4Queue failed\n"); + return TASK_POST_SUCCESS; } + + return TASK_POST_FAIL; } static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet) diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index 4273074217..aa93184e2b 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -54,9 +54,6 @@ typedef struct { fixed_queue_t *command_queue; fixed_queue_t *packet_queue; - // The hand-off point for data going to a higher layer, set by the higher layer - fixed_queue_t *upwards_data_queue; - command_waiting_response_t cmd_waiting_q; /* @@ -135,20 +132,23 @@ void hci_shut_down(void) } -void hci_host_task_post(task_post_t timeout) +task_post_status_t hci_host_task_post(task_post_t timeout) { BtTaskEvt_t evt; if (hci_host_startup_flag == false) { - return; + return TASK_POST_FAIL; } - evt.sig = 0xff; + evt.sig = SIG_HCI_HOST_SEND_AVAILABLE; evt.par = 0; if (xQueueSend(xHciHostQueue, &evt, timeout) != pdTRUE) { LOG_ERROR("xHciHostQueue failed\n"); + return TASK_POST_FAIL; } + + return TASK_POST_SUCCESS; } static int hci_layer_init_env(void) @@ -227,7 +227,7 @@ static void hci_host_thread_handler(void *arg) for (;;) { if (pdTRUE == xQueueReceive(xHciHostQueue, &e, (portTickType)portMAX_DELAY)) { - if (e.sig == 0xff) { + if (e.sig == SIG_HCI_HOST_SEND_AVAILABLE) { if (esp_vhci_host_check_send_available()) { /*Now Target only allowed one packet per TX*/ BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); @@ -247,11 +247,6 @@ static void hci_host_thread_handler(void *arg) } } -static void set_data_queue(fixed_queue_t *queue) -{ - hci_host_env.upwards_data_queue = queue; -} - static void transmit_command( BT_HDR *command, command_complete_cb complete_callback, @@ -311,7 +306,7 @@ static void transmit_downward(uint16_t type, void *data) } else { fixed_queue_enqueue(hci_host_env.packet_queue, data); } - //ke_event_set(KE_EVENT_HCI_HOST_THREAD); + hci_host_task_post(TASK_POST_BLOCKING); } @@ -495,7 +490,6 @@ intercepted: !fixed_queue_is_empty(hci_host_env.command_queue)) { hci_host_task_post(TASK_POST_BLOCKING); } - //ke_event_set(KE_EVENT_HCI_HOST_THREAD); if (wait_entry) { // If it has a callback, it's responsible for freeing the packet @@ -521,13 +515,8 @@ intercepted: static void dispatch_reassembled(BT_HDR *packet) { // Events should already have been dispatched before this point - - if (hci_host_env.upwards_data_queue) { - fixed_queue_enqueue(hci_host_env.upwards_data_queue, packet); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); - //Tell Up-layer received packet. - } else { - LOG_DEBUG("%s had no queue to place upwards data packet in. Dropping it on the floor.", __func__); + //Tell Up-layer received packet. + if (btu_task_post(SIG_BTU_HCI_MSG, packet, TASK_POST_BLOCKING) != TASK_POST_SUCCESS) { buffer_allocator->free(packet); } } @@ -576,7 +565,6 @@ static waiting_command_t *get_waiting_command(command_opcode_t opcode) static void init_layer_interface() { if (!interface_created) { - interface.set_data_queue = set_data_queue; interface.transmit_command = transmit_command; interface.transmit_command_futured = transmit_command_futured; interface.transmit_downward = transmit_downward; diff --git a/components/bt/bluedroid/hci/include/hci_layer.h b/components/bt/bluedroid/hci/include/hci_layer.h index caea29f28d..76f93638ad 100644 --- a/components/bt/bluedroid/hci/include/hci_layer.h +++ b/components/bt/bluedroid/hci/include/hci_layer.h @@ -77,9 +77,6 @@ typedef struct hci_t { // Do the postload sequence (call after the rest of the BT stack initializes). void (*do_postload)(void); - // Set the queue to receive ACL data in - void (*set_data_queue)(fixed_queue_t *queue); - // Send a command through the HCI layer void (*transmit_command)( BT_HDR *command, diff --git a/components/bt/bluedroid/main/bte_main.c b/components/bt/bluedroid/main/bte_main.c index 2705bff9b3..8e77072e92 100644 --- a/components/bt/bluedroid/main/bte_main.c +++ b/components/bt/bluedroid/main/bte_main.c @@ -32,56 +32,16 @@ #include "bt_trace.h" #include "osi.h" #include "alarm.h" -#include "fixed_queue.h" #include "hash_map.h" #include "hash_functions.h" #include "controller.h" #include "hci_layer.h" #include "bta_api.h" -//#include "bluedroid_test.h" -/* -#define LOG_TAG "bt_main" - -#include -#include -#include -#include -#include -#include -#include - -#include "osi/include/alarm.h" -#include "bta_api.h" -#include "bt_hci_bdroid.h" -#include "bte.h" -#include "btif_common.h" -#include "btu.h" -#include "btsnoop.h" -#include "bt_utils.h" -#include "btcore/include/counter.h" -#include "btcore/include/module.h" -#include "osi/include/fixed_queue.h" -#include "osi/include/future.h" -#include "gki.h" -#include "osi/include/hash_functions.h" -#include "osi/include/hash_map.h" -#include "hci_layer.h" -#include "osi/include/osi.h" -#include "osi/include/log.h" -#include "stack_config.h" -#include "osi/include/thread.h" -*/ /******************************************************************************* ** Constants & Macros *******************************************************************************/ -/* Run-time configuration file for BLE*/ -/* -#ifndef BTE_BLE_STACK_CONF_FILE -#define BTE_BLE_STACK_CONF_FILE "/etc/bluetooth/ble_stack.conf" -#endif -*/ /****************************************************************************** ** Variables ******************************************************************************/ @@ -100,8 +60,6 @@ static void bte_main_enable(void); /******************************************************************************* ** Externs *******************************************************************************/ -//extern void bte_load_ble_conf(const char *p_path); -fixed_queue_t *btu_hci_msg_queue; bluedroid_init_done_cb_t bluedroid_init_done_cb; @@ -128,18 +86,8 @@ int bte_main_boot_entry(bluedroid_init_done_cb_t cb) return -2; } - btu_hci_msg_queue = fixed_queue_new(SIZE_MAX); - if (btu_hci_msg_queue == NULL) { - LOG_ERROR("%s unable to allocate hci message queue.\n", __func__); - return -3; - } - bluedroid_init_done_cb = cb; - //Caution: No event dispatcher defined now in hci layer - //data_dispatcher_register_default(hci->event_dispatcher, btu_hci_msg_queue); - hci->set_data_queue(btu_hci_msg_queue); - //Enbale HCI bte_main_enable(); @@ -157,20 +105,6 @@ int bte_main_boot_entry(bluedroid_init_done_cb_t cb) ******************************************************************************/ void bte_main_shutdown(void) { - //data_dispatcher_register_default(hci_layer_get_interface()->event_dispatcher, NULL); - hci->set_data_queue(NULL); - fixed_queue_unregister_dequeue(btu_hci_msg_queue); - fixed_queue_free(btu_hci_msg_queue, NULL); - - btu_hci_msg_queue = NULL; - - /* - module_clean_up(get_module(STACK_CONFIG_MODULE)); - - module_clean_up(get_module(COUNTER_MODULE)); - module_clean_up(get_module(GKI_MODULE)); - */ - #if (BLE_INCLUDED == TRUE) BTA_VendorCleanup(); #endif diff --git a/components/bt/bluedroid/osi/include/thread.h b/components/bt/bluedroid/osi/include/thread.h index 4d2b435962..5f023c657f 100644 --- a/components/bt/bluedroid/osi/include/thread.h +++ b/components/bt/bluedroid/osi/include/thread.h @@ -35,12 +35,27 @@ typedef struct bt_task_evt BtTaskEvt_t; typedef bt_status_t (* BtTaskCb_t)(void *arg); -enum { - SIG_PRF_START_UP = 0xfc, - SIG_PRF_WORK = 0xfd, - SIG_BTU_START_UP = 0xfe, - SIG_BTU_WORK = 0xff, -}; +typedef enum { + SIG_HCI_HAL_RECV_PACKET = 0, + SIG_HCI_HAL_NUM, +} SIG_HCI_HAL_t; + + +typedef enum { + SIG_HCI_HOST_SEND_AVAILABLE = 0, + SIG_HCI_HOST_NUM, +} SIG_HCI_HOST_t; + +typedef enum { + SIG_BTU_START_UP = 0, + SIG_BTU_HCI_MSG, + SIG_BTU_BTA_MSG, + SIG_BTU_BTA_ALARM, + SIG_BTU_GENERAL_ALARM, + SIG_BTU_ONESHOT_ALARM, + SIG_BTU_L2CAP_ALARM, + SIG_BTU_NUM, +} SIG_BTU_t; #define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) #define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3) @@ -67,9 +82,13 @@ enum { #define TASK_POST_BLOCKING (portMAX_DELAY) typedef uint32_t task_post_t; /* Timeout of task post return, unit TICK */ -void btu_task_post(uint32_t sig, task_post_t timeout); -void hci_host_task_post(task_post_t timeout); -void hci_hal_h4_task_post(task_post_t timeout); +typedef enum { + TASK_POST_SUCCESS = 0, + TASK_POST_FAIL, +} task_post_status_t; +task_post_status_t btu_task_post(uint32_t sig, void *param, task_post_t timeout); +task_post_status_t hci_host_task_post(task_post_t timeout); +task_post_status_t hci_hal_h4_task_post(task_post_t timeout); #endif /* __THREAD_H__ */ diff --git a/components/bt/bluedroid/stack/btu/btu_hcif.c b/components/bt/bluedroid/stack/btu/btu_hcif.c index 7d00f0b76f..f3a6dd5c8e 100644 --- a/components/bt/bluedroid/stack/btu/btu_hcif.c +++ b/components/bt/bluedroid/stack/btu/btu_hcif.c @@ -47,7 +47,6 @@ // TODO(zachoverflow): remove this horrible hack #include "btu.h" -extern fixed_queue_t *btu_hci_msg_queue; extern void btm_process_cancel_complete(UINT8 status, UINT8 mode); extern void btm_ble_test_command_complete(UINT8 *p); @@ -1009,9 +1008,7 @@ static void btu_hcif_command_complete_evt(BT_HDR *response, void *context) event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - fixed_queue_enqueue(btu_hci_msg_queue, event); - // ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, TASK_POST_BLOCKING); } @@ -1209,9 +1206,7 @@ static void btu_hcif_command_status_evt(uint8_t status, BT_HDR *command, void *c event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - fixed_queue_enqueue(btu_hci_msg_queue, event); - //ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, TASK_POST_BLOCKING); } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index df387bceb3..5ebb6edfc2 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -22,7 +22,6 @@ #include "bt_trace.h" #include "controller.h" #include "alarm.h" -#include "fixed_queue.h" #include "hash_map.h" #include "hash_functions.h" #include "thread.h" @@ -44,28 +43,14 @@ #endif #endif -// extern fixed_queue_t *btif_msg_queue; - -// Communication queue from bta thread to bt_workqueue. -fixed_queue_t *btu_bta_msg_queue; - -// Communication queue from hci thread to bt_workqueue. -extern fixed_queue_t *btu_hci_msg_queue; - -// General timer queue. -fixed_queue_t *btu_general_alarm_queue; hash_map_t *btu_general_alarm_hash_map; pthread_mutex_t btu_general_alarm_lock; static const size_t BTU_GENERAL_ALARM_HASH_MAP_SIZE = 34; -// Oneshot timer queue. -fixed_queue_t *btu_oneshot_alarm_queue; hash_map_t *btu_oneshot_alarm_hash_map; pthread_mutex_t btu_oneshot_alarm_lock; static const size_t BTU_ONESHOT_ALARM_HASH_MAP_SIZE = 34; -// l2cap timer queue. -fixed_queue_t *btu_l2cap_alarm_queue; hash_map_t *btu_l2cap_alarm_hash_map; pthread_mutex_t btu_l2cap_alarm_lock; static const size_t BTU_L2CAP_ALARM_HASH_MAP_SIZE = 34; @@ -156,11 +141,6 @@ void BTU_StartUp(void) memset (&btu_cb, 0, sizeof (tBTU_CB)); btu_cb.trace_level = HCI_INITIAL_TRACE_LEVEL; - btu_bta_msg_queue = fixed_queue_new(SIZE_MAX); - if (btu_bta_msg_queue == NULL) { - goto error_exit; - } - btu_general_alarm_hash_map = hash_map_new(BTU_GENERAL_ALARM_HASH_MAP_SIZE, hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); if (btu_general_alarm_hash_map == NULL) { @@ -169,11 +149,6 @@ void BTU_StartUp(void) pthread_mutex_init(&btu_general_alarm_lock, NULL); - btu_general_alarm_queue = fixed_queue_new(SIZE_MAX); - if (btu_general_alarm_queue == NULL) { - goto error_exit; - } - btu_oneshot_alarm_hash_map = hash_map_new(BTU_ONESHOT_ALARM_HASH_MAP_SIZE, hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); if (btu_oneshot_alarm_hash_map == NULL) { @@ -182,11 +157,6 @@ void BTU_StartUp(void) pthread_mutex_init(&btu_oneshot_alarm_lock, NULL); - btu_oneshot_alarm_queue = fixed_queue_new(SIZE_MAX); - if (btu_oneshot_alarm_queue == NULL) { - goto error_exit; - } - btu_l2cap_alarm_hash_map = hash_map_new(BTU_L2CAP_ALARM_HASH_MAP_SIZE, hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL); if (btu_l2cap_alarm_hash_map == NULL) { @@ -195,18 +165,11 @@ void BTU_StartUp(void) pthread_mutex_init(&btu_l2cap_alarm_lock, NULL); - btu_l2cap_alarm_queue = fixed_queue_new(SIZE_MAX); - if (btu_l2cap_alarm_queue == NULL) { - goto error_exit; - } - xBtuQueue = xQueueCreate(BTU_QUEUE_NUM, sizeof(BtTaskEvt_t)); xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, 0); - btu_task_post(SIG_BTU_START_UP, TASK_POST_BLOCKING); - /* - // Continue startup on bt workqueue thread. - thread_post(bt_workqueue_thread, btu_task_start_up, NULL); - */ + + btu_task_post(SIG_BTU_START_UP, NULL, TASK_POST_BLOCKING); + return; error_exit:; @@ -218,36 +181,24 @@ void BTU_ShutDown(void) { btu_task_shut_down(); - fixed_queue_free(btu_bta_msg_queue, NULL); - hash_map_free(btu_general_alarm_hash_map); pthread_mutex_destroy(&btu_general_alarm_lock); - fixed_queue_free(btu_general_alarm_queue, NULL); hash_map_free(btu_oneshot_alarm_hash_map); pthread_mutex_destroy(&btu_oneshot_alarm_lock); - fixed_queue_free(btu_oneshot_alarm_queue, NULL); hash_map_free(btu_l2cap_alarm_hash_map); pthread_mutex_destroy(&btu_l2cap_alarm_lock); - fixed_queue_free(btu_l2cap_alarm_queue, NULL); - //thread_free(bt_workqueue_thread); vTaskDelete(xBtuTaskHandle); vQueueDelete(xBtuQueue); - btu_bta_msg_queue = NULL; - btu_general_alarm_hash_map = NULL; - btu_general_alarm_queue = NULL; btu_oneshot_alarm_hash_map = NULL; - btu_oneshot_alarm_queue = NULL; btu_l2cap_alarm_hash_map = NULL; - btu_l2cap_alarm_queue = NULL; -// bt_workqueue_thread = NULL; xBtuTaskHandle = NULL; xBtuQueue = 0; } @@ -269,21 +220,3 @@ UINT16 BTU_BleAclPktSize(void) return 0; #endif } -/******************************************************************************* -** -** Function btu_uipc_rx_cback -** -** Description -** -** -** Returns void -** -*******************************************************************************/ -/* -void btu_uipc_rx_cback(BT_HDR *p_msg) { - assert(p_msg != NULL); - BT_TRACE(TRACE_LAYER_BTM, TRACE_TYPE_DEBUG, "btu_uipc_rx_cback event 0x%x," - " len %d, offset %d", p_msg->event, p_msg->len, p_msg->offset); - fixed_queue_enqueue(btu_hci_msg_queue, p_msg); -} -*/ diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 5d7b1ec60b..0f0b3ec9bd 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -27,7 +27,6 @@ #include "btm_api.h" #include "btm_int.h" #include "btu.h" -#include "fixed_queue.h" #include "gki.h" #include "hash_map.h" #include "hcimsgs.h" @@ -94,34 +93,17 @@ extern void BTE_InitStack(void); tBTU_CB btu_cb; #endif -// Communication queue between btu_task and bta. -extern fixed_queue_t *btu_bta_msg_queue; - -// alarm queue between btu_task and bta -extern fixed_queue_t *btu_bta_alarm_queue; - -// Communication queue between btu_task and hci. -extern fixed_queue_t *btu_hci_msg_queue; - -// General timer queue. -extern fixed_queue_t *btu_general_alarm_queue; extern hash_map_t *btu_general_alarm_hash_map; extern pthread_mutex_t btu_general_alarm_lock; // Oneshot timer queue. -extern fixed_queue_t *btu_oneshot_alarm_queue; extern hash_map_t *btu_oneshot_alarm_hash_map; extern pthread_mutex_t btu_oneshot_alarm_lock; // l2cap timer queue. -extern fixed_queue_t *btu_l2cap_alarm_queue; extern hash_map_t *btu_l2cap_alarm_hash_map; extern pthread_mutex_t btu_l2cap_alarm_lock; -extern fixed_queue_t *event_queue; -//extern fixed_queue_t *btif_msg_queue; - -//extern thread_t *bt_workqueue_thread; extern xTaskHandle xBtuTaskHandle; extern xQueueHandle xBtuQueue; extern bluedroid_init_done_cb_t bluedroid_init_done_cb; @@ -137,87 +119,6 @@ static void btu_hci_msg_process(BT_HDR *p_msg); static void btu_bta_alarm_process(TIMER_LIST_ENT *p_tle); #endif -void btu_hci_msg_ready(fixed_queue_t *queue) -{ - BT_HDR *p_msg; - - while (!fixed_queue_is_empty(queue)) { - p_msg = (BT_HDR *)fixed_queue_dequeue(queue); - btu_hci_msg_process(p_msg); - } -} - -void btu_general_alarm_ready(fixed_queue_t *queue) -{ - TIMER_LIST_ENT *p_tle; - - while (!fixed_queue_is_empty(queue)) { - p_tle = (TIMER_LIST_ENT *)fixed_queue_dequeue(queue); - btu_general_alarm_process(p_tle); - } -} - -void btu_oneshot_alarm_ready(fixed_queue_t *queue) -{ - TIMER_LIST_ENT *p_tle; - - while (!fixed_queue_is_empty(queue)) { - p_tle = (TIMER_LIST_ENT *)fixed_queue_dequeue(queue); - btu_general_alarm_process(p_tle); - - switch (p_tle->event) { -#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) - case BTU_TTYPE_BLE_RANDOM_ADDR: - btm_ble_timeout(p_tle); - break; -#endif - case BTU_TTYPE_USER_FUNC: { - tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; - (*p_uf)(p_tle); - } - break; - - default: - // FAIL - LOG_ERROR("Received unexpected oneshot timer event:0x%x\n", - p_tle->event); - break; - } - } -} - -void btu_l2cap_alarm_ready(fixed_queue_t *queue) -{ - TIMER_LIST_ENT *p_tle; - - while (!fixed_queue_is_empty(queue)) { - p_tle = (TIMER_LIST_ENT *)fixed_queue_dequeue(queue); - btu_l2cap_alarm_process(p_tle); - } -} - -#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) -void btu_bta_msg_ready(fixed_queue_t *queue) -{ - BT_HDR *p_msg; - - while (!fixed_queue_is_empty(queue)) { - p_msg = (BT_HDR *)fixed_queue_dequeue(queue); - bta_sys_event(p_msg); - } -} - -void btu_bta_alarm_ready(fixed_queue_t *queue) -{ - TIMER_LIST_ENT *p_tle; - - while (!fixed_queue_is_empty(queue)) { - p_tle = (TIMER_LIST_ENT *)fixed_queue_dequeue(queue); - btu_bta_alarm_process(p_tle); - } -} -#endif - static void btu_hci_msg_process(BT_HDR *p_msg) { /* Determine the input message type. */ @@ -317,47 +218,74 @@ void btu_task_thread_handler(void *arg) for (;;) { if (pdTRUE == xQueueReceive(xBtuQueue, &e, (portTickType)portMAX_DELAY)) { - if (e.sig == SIG_BTU_WORK) { - fixed_queue_process(btu_hci_msg_queue); -#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) - fixed_queue_process(btu_bta_msg_queue); - fixed_queue_process(btu_bta_alarm_queue); -#endif - fixed_queue_process(btu_general_alarm_queue); - fixed_queue_process(btu_oneshot_alarm_queue); - fixed_queue_process(btu_l2cap_alarm_queue); - } else if (e.sig == SIG_BTU_START_UP) { + switch (e.sig) { + case SIG_BTU_START_UP: btu_task_start_up(); + break; + case SIG_BTU_HCI_MSG: + btu_hci_msg_process((BT_HDR *)e.par); + break; +#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) + case SIG_BTU_BTA_MSG: + bta_sys_event((BT_HDR *)e.par); + break; + case SIG_BTU_BTA_ALARM: + btu_bta_alarm_process((TIMER_LIST_ENT *)e.par); + break; +#endif + case SIG_BTU_GENERAL_ALARM: + btu_general_alarm_process((TIMER_LIST_ENT *)e.par); + break; + case SIG_BTU_ONESHOT_ALARM: { + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)e.par; + btu_general_alarm_process(p_tle); + + switch (p_tle->event) { +#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) + case BTU_TTYPE_BLE_RANDOM_ADDR: + btm_ble_timeout(p_tle); + break; +#endif + case BTU_TTYPE_USER_FUNC: { + tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; + (*p_uf)(p_tle); + break; + } + default: + // FAIL + LOG_ERROR("Received unexpected oneshot timer event:0x%x\n", p_tle->event); + break; + } + break; + } + case SIG_BTU_L2CAP_ALARM: + btu_l2cap_alarm_process((TIMER_LIST_ENT *)e.par); + break; + default: + break; } } } } -void btu_task_post(uint32_t sig, task_post_t timeout) +task_post_status_t btu_task_post(uint32_t sig, void *param, task_post_t timeout) { BtTaskEvt_t evt; evt.sig = sig; - evt.par = 0; + evt.par = param; if (xQueueSend(xBtuQueue, &evt, timeout) != pdTRUE) { LOG_ERROR("xBtuQueue failed\n"); + return TASK_POST_FAIL; } + + return TASK_POST_SUCCESS; } void btu_task_start_up(void) { - -#if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) - fixed_queue_register_dequeue(btu_bta_msg_queue, btu_bta_msg_ready); -#endif - - fixed_queue_register_dequeue(btu_hci_msg_queue, btu_hci_msg_ready); - fixed_queue_register_dequeue(btu_general_alarm_queue, btu_general_alarm_ready); - fixed_queue_register_dequeue(btu_oneshot_alarm_queue, btu_oneshot_alarm_ready); - fixed_queue_register_dequeue(btu_l2cap_alarm_queue, btu_l2cap_alarm_ready); - /* Initialize the mandatory core stack control blocks (BTU, BTM, L2CAP, and SDP) */ @@ -381,12 +309,7 @@ void btu_task_start_up(void) void btu_task_shut_down(void) { - fixed_queue_unregister_dequeue(btu_general_alarm_queue); - fixed_queue_unregister_dequeue(btu_oneshot_alarm_queue); - fixed_queue_unregister_dequeue(btu_l2cap_alarm_queue); - #if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) - fixed_queue_unregister_dequeue(btu_bta_msg_queue); bta_sys_free(); #endif @@ -515,9 +438,7 @@ void btu_general_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - fixed_queue_enqueue(btu_general_alarm_queue, p_tle); - //ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, TASK_POST_BLOCKING); } void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) @@ -606,9 +527,7 @@ static void btu_l2cap_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - fixed_queue_enqueue(btu_l2cap_alarm_queue, p_tle); - //ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, TASK_POST_BLOCKING); } void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ticks) @@ -674,9 +593,7 @@ void btu_oneshot_alarm_cb(void *data) btu_stop_timer_oneshot(p_tle); - fixed_queue_enqueue(btu_oneshot_alarm_queue, p_tle); - //ke_event_set(KE_EVENT_BTU_TASK_THREAD); - btu_task_post(SIG_BTU_WORK, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, TASK_POST_BLOCKING); } /* From ae571ddddfed09f93345d50ed9fdbc0d38743d1f Mon Sep 17 00:00:00 2001 From: shangke Date: Tue, 1 Aug 2017 11:59:57 +0800 Subject: [PATCH 17/26] fix rx buf --- components/ethernet/emac_main.c | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 391ffcdb43..0343c2f82d 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -450,16 +450,17 @@ static void emac_process_rx_unavail(void) while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { emac_config.cnt_rx++; - - //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { ESP_LOGE(TAG, "emac rx unavail buf err !!\n"); } + uint32_t tmp_dirty = emac_config.dirty_rx; emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; - } + + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), + (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + + } emac_enable_rx_intr(); emac_enable_rx_unavail_intr(); xSemaphoreGiveRecursive( emac_rx_xMutex ); @@ -479,16 +480,17 @@ static void emac_process_rx(void) while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { emac_config.cnt_rx++; - - //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - if (emac_config.cnt_rx > DMA_RX_BUF_NUM ) { ESP_LOGE(TAG, "emac rx buf err!!\n"); } + uint32_t tmp_dirty = emac_config.dirty_rx; emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), + (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + cur_rx_desc = emac_read_rx_cur_reg(); } } else { @@ -497,16 +499,17 @@ static void emac_process_rx(void) while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { emac_config.cnt_rx++; - //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { ESP_LOGE(TAG, "emac rx buf err!!!\n"); } - + uint32_t tmp_dirty = emac_config.dirty_rx; emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; - } + + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), + (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + + } } } } @@ -963,6 +966,9 @@ esp_err_t esp_eth_init(eth_config_t *config) ret = ESP_FAIL; goto _exit; #endif + if (emac_config.emac_status != EMAC_RUNTIME_NOT_INIT) { + goto _exit; + } emac_init_default_data(); From d5119dd529dbad866a3fd40e5610d19bd8071065 Mon Sep 17 00:00:00 2001 From: krzychb Date: Sun, 30 Jul 2017 21:40:26 +0200 Subject: [PATCH 18/26] Provided instruction to build OpenOCD from sources using MSYS2 --- .../jtag-debugging/building-openocd-linux.rst | 8 --- .../building-openocd-windows.rst | 60 ++++++++++++++++++- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/docs/api-guides/jtag-debugging/building-openocd-linux.rst b/docs/api-guides/jtag-debugging/building-openocd-linux.rst index c7bae0c9e3..cdc27a5355 100644 --- a/docs/api-guides/jtag-debugging/building-openocd-linux.rst +++ b/docs/api-guides/jtag-debugging/building-openocd-linux.rst @@ -7,14 +7,6 @@ The following instructions are alternative to downloading binary OpenOCD from Es .. highlight:: bash -Install Git -=========== - -To be able to download OpenOCD sources from GitHub, install ``git`` package:: - - sudo apt-get install git - - Download Sources of OpenOCD =========================== diff --git a/docs/api-guides/jtag-debugging/building-openocd-windows.rst b/docs/api-guides/jtag-debugging/building-openocd-windows.rst index 572a13f74f..4f0419135c 100644 --- a/docs/api-guides/jtag-debugging/building-openocd-windows.rst +++ b/docs/api-guides/jtag-debugging/building-openocd-windows.rst @@ -5,11 +5,67 @@ Building OpenOCD from Sources for Windows The following instructions are alternative to downloading binary OpenOCD from Espressif website. To quickly setup the binary OpenOCD, instead of compiling it yourself, backup and proceed to section :doc:`setup-openocd-windows`. +.. highlight:: bash + +Download Sources of OpenOCD +=========================== + +The sources for the ESP32-enabled variant of OpenOCD are available from Espressif GitHub under https://github.com/espressif/openocd-esp32. To download the sources, use the following commands:: + + cd ~/esp + git clone –recursive https://github.com/espressif/openocd-esp32.git + +The clone of sources should be now saved in ``~/esp/openocd-esp32`` directory. + + +Install Dependencies +==================== + +Install packages that are required to compile OpenOCD: + +.. note:: + + Install the following packages one by one, check if installation was successful and then proceed to the next package. Resolve reported problems before moving to the next step. + +:: + + pacman -S libtool + pacman -S autoconf + pacman -S automake + pacman -S texinfo + pacman -S mingw-w64-i686-libusb-compat-git + pacman -S pkg-config + +.. note:: + + Installation of ``pkg-config`` is breaking operation of esp-idf toolchain. After building of OpenOCD it should be uninstalled. It be covered at the end of this instruction. To build OpenOCD again, you will need to run ``pacman -S pkg-config`` once more. This issue does not concern other packages installed in this step (before ``pkg-config``). + + Build OpenOCD ============= -Refer to build process described in ``README.Windows`` file, that is provided with -OpenOCD source code in https://github.com/espressif/openocd-esp32 repository. +Proceed with configuring and building OpenOCD:: + + cd ~/esp/openocd-esp32 + ./bootstrap + ./configure + make + +Optionally you can add ``make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten. + +.. note:: + + * Should an error occur, resolve it and try again until the command ``make`` works. + * If there is a submodule problem from OpenOCD, please ``cd`` to the ``openocd-esp32`` directory and input ``git submodule update --init``. + * If the ``./configure`` is successfully run, information of enabled JTAG will be printed under ``OpenOCD configuration summary``. + * If the information of your device is not shown in the log, use ``./configure`` to enable it as described in ``../openocd-esp32/doc/INSTALL.txt``. + * For details concerning compiling OpenOCD, please refer to ``openocd-esp32/README.Windows``. + +Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/esp/openocd-esp32/src/openocd`` directory. + +Remove ``pkg-config``, as discussed during installation of dependencies:: + + pacman -Rs pkg-config Next Steps From b1fcfca98751d30a5d11630c84b7786b70c2bb63 Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 31 Jul 2017 07:06:25 +0200 Subject: [PATCH 19/26] Updated desription of ESP-WROVER-KIT features in getting started guide, changed link to JTAG Debugging --- .../_static/esp32-wrover-kit-layout-front.jpg | Bin 111275 -> 114428 bytes docs/get-started/get-started-devkitc.rst | 4 ++-- docs/get-started/get-started-wrover-kit.rst | 22 +++++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/_static/esp32-wrover-kit-layout-front.jpg b/docs/_static/esp32-wrover-kit-layout-front.jpg index 96297e70bc3cc8f8c890716e0d784280cdf48a83..b67a8ad3deece63ce90fe02a0c0d01003f35c6e0 100644 GIT binary patch literal 114428 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 AL;wH) literal 111275 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

dwxc=xf&g)^N*9&v+ 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 (PDF) * `ESP-WROOM-32 Datasheet `_ (PDF) -* `JTAG Debugging for ESP32 `_ (PDF) +* :doc:`../api-guides/jtag-debugging/index` From 1e6f904d38dbc58b7b9c8ca844dcca8454be3502 Mon Sep 17 00:00:00 2001 From: shangke Date: Thu, 3 Aug 2017 14:44:22 +0800 Subject: [PATCH 20/26] add comments for rx list --- components/ethernet/emac_main.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 0343c2f82d..f57fc95cca 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -115,6 +115,25 @@ static void emac_set_rx_base_reg(void) REG_WRITE(EMAC_DMARXBASEADDR_REG, (uint32_t)(emac_config.dma_erx)); } +/* +* dirty_rx indicates the hardware has been fed with data packets and is the first node software needs to handle; +* +* cur_rx indicates the completion of software handling and is the last node hardware could use; +* +* cnt_rx is to count the numbers of packets handled by software, passed to protocol stack and not been freed. +* +* (1) Initializing the Linked List. Connect the numerable nodes to a circular linked list, appoint one of the nodes as the head node, mark* the dirty_rx and cur_rx into the node, and mount the node on the hardware base address. Initialize cnt_rx into 0. +* +* (2) When hardware receives packets, nodes of linked lists will be fed with data packets from the base address by turns, marks the node +* of linked lists as “HARDWARE UNUSABLE” and reports interrupts. +* +* (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol +* stack. dirty_rx will deviate backwards by turns and cnt_rx will by turns ++. +* +* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as “HARDWARE USABLE” and cnt_rx will by turns ——. +* +* (5) Cycle from Step 2 to Step 4 without break and build up circular linked list handling. +*/ static void emac_reset_dma_chain(void) { emac_config.cnt_tx = 0; From 05aa09fbb99b95ab98ca7cc2590f6b61e46e488d Mon Sep 17 00:00:00 2001 From: shangke Date: Fri, 4 Aug 2017 14:24:44 +0800 Subject: [PATCH 21/26] beautify the code --- components/ethernet/emac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index f57fc95cca..b6989f0bbe 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -54,8 +54,8 @@ static struct emac_config_data emac_config; -static uint8_t emac_dma_rx_chain_buf[32 * DMA_RX_BUF_NUM]; -static uint8_t emac_dma_tx_chain_buf[32 * DMA_TX_BUF_NUM]; +static uint8_t emac_dma_rx_chain_buf[sizeof(struct dma_extended_desc) * DMA_RX_BUF_NUM]; +static uint8_t emac_dma_tx_chain_buf[sizeof(struct dma_extended_desc) * DMA_TX_BUF_NUM]; static uint8_t emac_dma_rx_buf[DMA_RX_BUF_SIZE * DMA_RX_BUF_NUM]; static uint8_t emac_dma_tx_buf[DMA_TX_BUF_SIZE * DMA_TX_BUF_NUM]; @@ -130,7 +130,7 @@ static void emac_set_rx_base_reg(void) * (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol * stack. dirty_rx will deviate backwards by turns and cnt_rx will by turns ++. * -* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as “HARDWARE USABLE” and cnt_rx will by turns ——. +* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as “HARDWARE USABLE” and cnt_rx will by turns --. * * (5) Cycle from Step 2 to Step 4 without break and build up circular linked list handling. */ From 01a2c31e1ffb1072cd4db63ad28e8360e30e4e46 Mon Sep 17 00:00:00 2001 From: shangke Date: Fri, 4 Aug 2017 14:56:58 +0800 Subject: [PATCH 22/26] change copy right --- components/ethernet/emac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index b6989f0bbe..df614582d4 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2017 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. From 097dced4992057dc4ef20724eac07bc3f4616de8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Aug 2017 03:20:02 +0800 Subject: [PATCH 23/26] wear_levelling: clean up Kconfig --- components/wear_levelling/Kconfig | 50 +++++++++++++++++++------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/components/wear_levelling/Kconfig b/components/wear_levelling/Kconfig index 158c2175df..8e8d2a671d 100755 --- a/components/wear_levelling/Kconfig +++ b/components/wear_levelling/Kconfig @@ -1,45 +1,57 @@ -menu "FAT FS Wear Levelling Settings" +menu "Wear Levelling" choice WL_SECTOR_SIZE - bool "FAT FS sector size" - default WL_SECTOR_SIZE_FAT + bool "Wear Levelling library sector size" + default WL_SECTOR_SIZE help - Specify the FAT sector size. + Sector size used by wear levelling library. You can set default sector size or size that will fit to the flash device sector size. -config WL_SECTOR_SIZE_FAT + With sector size set to 4096 bytes, wear levelling library is more + efficient. However if FAT filesystem is used on top of wear levelling + library, it will need more temporary storage: 4096 bytes for each + mounted filesystem and 4096 bytes for each opened file. + + With sector size set to 512 bytes, wear levelling library will perform + more operations with flash memory, but less RAM will be used by FAT + filesystem library (512 bytes for the filesystem and 512 bytes for each + file opened). + +config WL_SECTOR_SIZE_512 bool "512" -config WL_SECTOR_SIZE_FLASH +config WL_SECTOR_SIZE_4096 bool "4096" endchoice config WL_SECTOR_SIZE int - default 512 if WL_SECTOR_SIZE_FAT - default 4096 if WL_SECTOR_SIZE_FLASH + default 512 if WL_SECTOR_SIZE_512 + default 4096 if WL_SECTOR_SIZE_4096 choice WL_SECTOR_MODE bool "Sector store mode" + depends on WL_SECTOR_SIZE_512 default WL_SECTOR_MODE_PERF help - Specify the mode to store data into the flash. + Specify the mode to store data into flash: + + - In Performance mode a data will be stored to the RAM and then + stored back to the flash. Compared to the Safety mode, this operation is + faster, but if power will be lost when erase sector operation is in + progress, then the data from complete flash device sector will be lost. + + - In Safety mode data from complete flash device sector will be read from + flash, modified, and then stored back to flash. + Compared to the Performance mode, this operation is slower, but if + power is lost during erase sector operation, then the data from full + flash device sector will not be lost. config WL_SECTOR_MODE_PERF bool "Perfomance" - help - In Performance mode a data will be stored to the RAM and then - stored back to the flash. Compare to the Safety mode, this operation - faster, but if by the erase sector operation power will be off, the - data from complete flash device sector will be lost. config WL_SECTOR_MODE_SAFE bool "Safety" - help - In Safety mode a data from complete flash device sector will be stored to the flash and then - stored back to the flash. Compare to the Performance mode, this operation - slower, but if by the erase sector operation power will be off, the - data of the full flash device sector will not be lost. endchoice config WL_SECTOR_MODE From 59a28ebccc7454009ffcfa52a8ef46c72bcd035f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Aug 2017 03:22:42 +0800 Subject: [PATCH 24/26] wear_levelling: temporarily disable 512 byte sectors Unit tests are failing with the new 512 byte sector option. Disabling this option to let the tests pass. --- components/wear_levelling/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/wear_levelling/Kconfig b/components/wear_levelling/Kconfig index 8e8d2a671d..9224c25ad4 100755 --- a/components/wear_levelling/Kconfig +++ b/components/wear_levelling/Kconfig @@ -2,7 +2,7 @@ menu "Wear Levelling" choice WL_SECTOR_SIZE bool "Wear Levelling library sector size" - default WL_SECTOR_SIZE + default WL_SECTOR_SIZE_4096 help Sector size used by wear levelling library. You can set default sector size or size that will @@ -20,6 +20,8 @@ choice WL_SECTOR_SIZE config WL_SECTOR_SIZE_512 bool "512" + # This mode is temporary disabled, until unit test is fixed + depends on false config WL_SECTOR_SIZE_4096 bool "4096" endchoice @@ -32,7 +34,7 @@ config WL_SECTOR_SIZE choice WL_SECTOR_MODE bool "Sector store mode" depends on WL_SECTOR_SIZE_512 - default WL_SECTOR_MODE_PERF + default WL_SECTOR_MODE_SAFE help Specify the mode to store data into flash: From 5539bfb83b1b7b64b588f97e9d58acc7caad7b01 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Aug 2017 03:51:54 +0800 Subject: [PATCH 25/26] sdmmc: mark new unit test as ignored --- components/sdmmc/test/test_sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 489911aa06..86ff501b85 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -166,7 +166,7 @@ TEST_CASE("can write and read back blocks (using SPI)", "[sdspi][ignore]") TEST_ESP_OK(sdspi_host_deinit()); } -TEST_CASE("reads and writes with an unaligned buffer", "[sd]") +TEST_CASE("reads and writes with an unaligned buffer", "[sd][ignore]") { sdmmc_host_t config = SDMMC_HOST_DEFAULT(); TEST_ESP_OK(sdmmc_host_init()); From 57bc38d867f1d4a60a5323b86b4349a57d3ada3e Mon Sep 17 00:00:00 2001 From: Deng Xin Date: Tue, 1 Aug 2017 22:29:16 +0800 Subject: [PATCH 26/26] Add the fast crypto function in supplicant optimize the speed when do wifi connect --- components/esp32/fast_crypto_ops.c | 61 ++++ components/esp32/include/esp_wifi.h | 7 +- .../esp32/include/esp_wifi_crypto_types.h | 301 ++++++++++++++++++ components/esp32/include/esp_wpa2.h | 15 +- components/esp32/include/esp_wps.h | 16 +- components/esp32/lib | 2 +- components/wpa_supplicant/component.mk | 4 +- .../wpa_supplicant/include/crypto/aes_wrap.h | 7 +- .../wpa_supplicant/include/crypto/crypto.h | 138 +++++++- .../wpa_supplicant/include/crypto/sha256.h | 6 + .../wpa_supplicant/src/crypto/dh_groups.c | 37 ++- .../src/fast_crypto/fast_aes-cbc.c | 78 +++++ .../src/fast_crypto/fast_aes-unwrap.c | 81 +++++ .../src/fast_crypto/fast_aes-wrap.c | 80 +++++ .../fast_crypto/fast_crypto_internal-cipher.c | 283 ++++++++++++++++ .../fast_crypto/fast_crypto_internal-modexp.c | 59 ++++ .../src/fast_crypto/fast_crypto_internal.c | 282 ++++++++++++++++ .../src/fast_crypto/fast_sha256-internal.c | 48 +++ .../src/fast_crypto/fast_sha256.c | 165 ++++++++++ docs/Doxyfile | 1 + docs/api-guides/wifi.rst | 10 + examples/wifi/power_save/main/power_save.c | 1 + .../wifi/smart_config/main/smartconfig_main.c | 2 + .../main/wpa2_enterprise_main.c | 4 +- examples/wifi/wps/main/wps.c | 11 +- 25 files changed, 1673 insertions(+), 26 deletions(-) create mode 100644 components/esp32/fast_crypto_ops.c create mode 100644 components/esp32/include/esp_wifi_crypto_types.h create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_aes-cbc.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_aes-unwrap.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_aes-wrap.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-cipher.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-modexp.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_crypto_internal.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_sha256-internal.c create mode 100644 components/wpa_supplicant/src/fast_crypto/fast_sha256.c diff --git a/components/esp32/fast_crypto_ops.c b/components/esp32/fast_crypto_ops.c new file mode 100644 index 0000000000..23c79885be --- /dev/null +++ b/components/esp32/fast_crypto_ops.c @@ -0,0 +1,61 @@ +// Copyright 2015-2017 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 "crypto/common.h" +#include "crypto/aes_wrap.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "esp_wifi_crypto_types.h" + +/* + * The parameters is used to set the cyrpto callback function for station connect when in security mode, + * every callback function can register as fast_xxx or normal one, i.e, fast_aes_wrap or aes_wrap, the + * difference between them is the normal API is calculate by software, the fast one use the hardware + * crypto in it, can be faster than the normal one, so the callback function register in default is which + * we recommend, so as the API in WPS default and WPA2 default. + */ +const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs = { + .aes_wrap = (esp_aes_wrap_t)fast_aes_wrap, + .aes_unwrap = (esp_aes_unwrap_t)fast_aes_unwrap, + .hmac_sha256_vector = (esp_hmac_sha256_vector_t)fast_hmac_sha256_vector, + .sha256_prf = (esp_sha256_prf_t)fast_sha256_prf +}; + +const wps_crypto_funcs_t g_wifi_default_wps_crypto_funcs = { + .aes_128_encrypt = (esp_aes_128_encrypt_t)fast_aes_128_cbc_encrypt, + .aes_128_decrypt = (esp_aes_128_decrypt_t)fast_aes_128_cbc_decrypt, + .crypto_mod_exp = (esp_crypto_mod_exp_t)fast_crypto_mod_exp, + .hmac_sha256 = (esp_hmac_sha256_t)fast_hmac_sha256, + .hmac_sha256_vector = (esp_hmac_sha256_vector_t)fast_hmac_sha256_vector, + .sha256_vector = (esp_sha256_vector_t)fast_sha256_vector +}; + +/* + * What should notice is that the cyrpto hash type function and crypto cipher type function can not register + * as different, i.e, if you use fast_crypto_hash_init, you should use fast_crypto_hash_update and + * fast_crypto_hash_finish for finish hash calculate, rather than call crypto_hash_update and + * crypto_hash_finish, so do crypto_cipher. + */ +const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = { + .crypto_hash_init = (esp_crypto_hash_init_t)fast_crypto_hash_init, + .crypto_hash_update = (esp_crypto_hash_update_t)fast_crypto_hash_update, + .crypto_hash_finish = (esp_crypto_hash_finish_t)fast_crypto_hash_finish, + .crypto_cipher_init = (esp_crypto_cipher_init_t)fast_crypto_cipher_init, + .crypto_cipher_encrypt = (esp_crypto_cipher_encrypt_t)fast_crypto_cipher_encrypt, + .crypto_cipher_decrypt = (esp_crypto_cipher_decrypt_t)fast_crypto_cipher_decrypt, + .crypto_cipher_deinit = (esp_crypto_cipher_deinit_t)fast_crypto_cipher_deinit, + .sha256_vector = (esp_sha256_vector_t)fast_sha256_vector, + .crypto_mod_exp = (esp_crypto_mod_exp_t)crypto_mod_exp +}; + diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 2881eb1fee..b6450c7fae 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -65,6 +65,7 @@ #include "sdkconfig.h" #include "esp_err.h" #include "esp_wifi_types.h" +#include "esp_wifi_crypto_types.h" #include "esp_event.h" #ifdef __cplusplus @@ -96,6 +97,7 @@ extern "C" { */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ + wpa_crypto_funcs_t wpa_crypto_funcs; /**< WiFi station crypto functions when connect */ int static_rx_buf_num; /**< WiFi static RX buffer number */ int dynamic_rx_buf_num; /**< WiFi dynamic RX buffer number */ int tx_buf_type; /**< WiFi TX buffer type */ @@ -138,11 +140,14 @@ typedef struct { #else #define WIFI_NANO_FORMAT_ENABLED 0 #endif - + +extern const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs; + #define WIFI_INIT_CONFIG_MAGIC 0x1F2F3F4F #ifdef CONFIG_WIFI_ENABLED #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send, \ + .wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs, \ .static_rx_buf_num = CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM,\ .dynamic_rx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM,\ .tx_buf_type = CONFIG_ESP32_WIFI_TX_BUFFER_TYPE,\ diff --git a/components/esp32/include/esp_wifi_crypto_types.h b/components/esp32/include/esp_wifi_crypto_types.h new file mode 100644 index 0000000000..10a55a7088 --- /dev/null +++ b/components/esp32/include/esp_wifi_crypto_types.h @@ -0,0 +1,301 @@ +// Hardware crypto support Copyright 2017 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_WIFI_CRYPTO_TYPES_H__ +#define __ESP_WIFI_CRYPTO_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This enumation is about alorigthm will be set when do crypt hash + * operation.When do wpa2 connecting, after invoke crypto_hash_xxx of + * fast_crypto_hash_xxx API, it will do relation crypt operation according + * to the enumation. + */ +typedef enum { + ESP_CRYPTO_HASH_ALG_MD5, ESP_CRYPTO_HASH_ALG_SHA1, + ESP_CRYPTO_HASH_ALG_HMAC_MD5, ESP_CRYPTO_HASH_ALG_HMAC_SHA1, + ESP_CRYPTO_HASH_ALG_SHA256, ESP_CRYPTO_HASH_ALG_HMAC_SHA256 +}esp_crypto_hash_alg_t; + +/* + * This enumation is about alorigthm will be set when do crypt cipher + * operation.When do wpa2 connecting, after invoke crypto_cipher_xxx of + * fast_crypto_cipher_xxx API, it will do relation crypt operation according + * to the enumation. + */ +typedef enum { + ESP_CRYPTO_CIPHER_NULL, ESP_CRYPTO_CIPHER_ALG_AES, ESP_CRYPTO_CIPHER_ALG_3DES, + ESP_CRYPTO_CIPHER_ALG_DES, ESP_CRYPTO_CIPHER_ALG_RC2, ESP_CRYPTO_CIPHER_ALG_RC4 +} esp_crypto_cipher_alg_t; + +/* + * This structure is about the algorithm when do crypto_hash operation, for detail, + * please reference to the structure crypto_hash. + */ +typedef struct crypto_hash esp_crypto_hash_t; + +/* + * This structure is about the algorithm when do crypto_cipher operation, for detail, + * please reference to the structure crypto_cipher. + */ +typedef struct crypto_cipher esp_crypto_cipher_t; + +/** + * @brief The crypto callback function used in wpa enterprise hash operation when connect. + * Initialize a esp_crypto_hash_t structure. + * + * @param alg Hash algorithm. + * @param key Key for keyed hash (e.g., HMAC) or %NULL if not needed. + * @param key_len Length of the key in bytes + * + */ +typedef esp_crypto_hash_t * (*esp_crypto_hash_init_t)(esp_crypto_hash_alg_t alg, const unsigned char *key, int key_len); + +/** + * @brief The crypto callback function used in wpa enterprise hash operation when connect. + * Add data to hash calculation. + * + * @param ctz Context pointer from esp_crypto_hash_init_t function. + * @param data Data buffer to add. + * @param len Length of the buffer. + * + */ +typedef void * (*esp_crypto_hash_update_t)(esp_crypto_hash_t *ctx, const unsigned char *data, int len); + +/** + * @brief The crypto callback function used in wpa enterprise hash operation when connect. + * Complete hash calculation. + * + * @param ctz Context pointer from esp_crypto_hash_init_t function. + * @param hash Buffer for hash value or %NULL if caller is just freeing the hash + * context. + * @param len Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + */ +typedef int * (*esp_crypto_hash_finish_t)(esp_crypto_hash_t *ctx, unsigned char *hash, int *len); + +/** + * @brief The AES callback function when do WPS connect. + * + * @param key Encryption key. + * @param iv Encryption IV for CBC mode (16 bytes). + * @param data Data to encrypt in-place. + * @param data_len Length of data in bytes (must be divisible by 16) + */ +typedef int * (*esp_aes_128_encrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len); + +/** + * @brief The AES callback function when do WPS connect. + * + * @param key Decryption key. + * @param iv Decryption IV for CBC mode (16 bytes). + * @param data Data to decrypt in-place. + * @param data_len Length of data in bytes (must be divisible by 16) + * + */ +typedef int * (*esp_aes_128_decrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len); + +/** + * @brief The AES callback function when do STA connect. + * + * @param kek 16-octet Key encryption key (KEK). + * @param n Length of the plaintext key in 64-bit units; + * @param plain Plaintext key to be wrapped, n * 64 bits + * @param cipher Wrapped key, (n + 1) * 64 bits + * + */ +typedef int * (*esp_aes_wrap_t)(const unsigned char *kek, int n, const unsigned char *plain, unsigned char *cipher); + +/** + * @brief The AES callback function when do STA connect. + * + * @param kek 16-octet Key decryption key (KEK). + * @param n Length of the plaintext key in 64-bit units; + * @param cipher Wrapped key to be unwrapped, (n + 1) * 64 bits + * @param plain Plaintext key, n * 64 bits + * + */ +typedef int * (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned char *cipher, unsigned char *plain); + +/** + * @brief The crypto callback function used in wpa enterprise cipher operation when connect. + * Initialize a esp_crypto_cipher_t structure. + * + * @param alg cipher algorithm. + * @param iv Initialization vector for block ciphers or %NULL for stream ciphers. + * @param key Cipher key + * @param key_len Length of key in bytes + * + */ +typedef esp_crypto_cipher_t * (*esp_crypto_cipher_init_t)(esp_crypto_cipher_alg_t alg, const unsigned char *iv, const unsigned char *key, int key_len); + +/** + * @brief The crypto callback function used in wpa enterprise cipher operation when connect. + * Cipher encrypt. + * + * @param ctx Context pointer from esp_crypto_cipher_init_t callback function. + * @param plain Plaintext to cipher. + * @param crypt Resulting ciphertext. + * @param len Length of the plaintext. + * + */ +typedef int * (*esp_crypto_cipher_encrypt_t)(esp_crypto_cipher_t *ctx, + const unsigned char *plain, unsigned char *crypt, int len); +/** + * @brief The crypto callback function used in wpa enterprise cipher operation when connect. + * Cipher decrypt. + * + * @param ctx Context pointer from esp_crypto_cipher_init_t callback function. + * @param crypt Ciphertext to decrypt. + * @param plain Resulting plaintext. + * @param len Length of the cipher text. + * + */ +typedef int * (*esp_crypto_cipher_decrypt_t)(esp_crypto_cipher_t *ctx, + const unsigned char *crypt, unsigned char *plain, int len); +/** + * @brief The crypto callback function used in wpa enterprise cipher operation when connect. + * Free cipher context. + * + * @param ctx Context pointer from esp_crypto_cipher_init_t callback function. + * + */ +typedef void * (*esp_crypto_cipher_deinit_t)(esp_crypto_cipher_t *ctx); + +/** + * @brief The SHA256 callback function when do WPS connect. + * + * @param key Key for HMAC operations. + * @param key_len Length of the key in bytes. + * @param data Pointers to the data area. + * @param data_len Length of the data area. + * @param mac Buffer for the hash (20 bytes). + * + */ +typedef void * (*esp_hmac_sha256_t)(const unsigned char *key, int key_len, const unsigned char *data, + int data_len, unsigned char *mac); + +/** + * @brief The SHA256 callback function when do WPS connect. + * + * @param key Key for HMAC operations. + * @param key_len Length of the key in bytes. + * @param num_elem Number of elements in the data vector. + * @param addr Pointers to the data areas. + * @param len Lengths of the data blocks. + * @param mac Buffer for the hash (32 bytes). + * + */ +typedef void * (*esp_hmac_sha256_vector_t)(const unsigned char *key, int key_len, int num_elem, + const unsigned char *addr[], const int *len, unsigned char *mac); + +/** + * @brief The AES callback function when do STA connect. + * + * @param key Key for PRF. + * @param key_len Length of the key in bytes. + * @param label A unique label for each purpose of the PRF. + * @param data Extra data to bind into the key. + * @param data_len Length of the data. + * @param buf Buffer for the generated pseudo-random key. + * @param buf_len Number of bytes of key to generate. + * + */ +typedef void * (*esp_sha256_prf_t)(const unsigned char *key, int key_len, const char *label, + const unsigned char *data, int data_len, unsigned char *buf, int buf_len); + +/** + * @brief The SHA256 callback function when do WPS connect. + * + * @param num_elem Number of elements in the data vector. + * @param addr Pointers to the data areas. + * @param len Lengths of the data blocks. + * @param mac Buffer for the hash. + * + */ +typedef int * (*esp_sha256_vector_t)(int num_elem, const unsigned char *addr[], const int *len, + unsigned char *mac); + +/** + * @brief The bignum calculate callback function used when do connect. + * In WPS process, it used to calculate public key and private key. + * + * @param base Base integer (big endian byte array). + * @param base_len Length of base integer in bytes. + * @param power Power integer (big endian byte array). + * @param power_len Length of power integer in bytes. + * @param modulus Modulus integer (big endian byte array). + * @param modulus_len Length of modulus integer in bytes. + * @param result Buffer for the result. + * @param result_len Result length (max buffer size on input, real len on output). + * + */ +typedef int * (*esp_crypto_mod_exp_t)(const unsigned char *base, int base_len, + const unsigned char *power, int power_len, + const unsigned char *modulus, int modulus_len, + unsigned char *result, unsigned int *result_len); +/** + * @brief The crypto callback function structure used when do station security connect. + * The structure can be set as software crypto or the crypto optimized by ESP32 + * hardware. + */ +typedef struct { + esp_aes_wrap_t aes_wrap; /**< station connect function used when send EAPOL frame */ + esp_aes_unwrap_t aes_unwrap; /**< station connect function used when decrypt key data */ + esp_hmac_sha256_vector_t hmac_sha256_vector; /**< station connect function used when check MIC */ + esp_sha256_prf_t sha256_prf; /**< station connect function used when check MIC */ +}wpa_crypto_funcs_t; + +/** + * @brief The crypto callback function structure used when do WPS process. The + * structure can be set as software crypto or the crypto optimized by ESP32 + * hardware. + */ +typedef struct{ + esp_aes_128_encrypt_t aes_128_encrypt; /**< function used to process message when do WPS */ + esp_aes_128_decrypt_t aes_128_decrypt; /**< function used to process message when do WPS */ + esp_crypto_mod_exp_t crypto_mod_exp; /**< function used to calculate public key and private key */ + esp_hmac_sha256_t hmac_sha256; /**< function used to get attribute */ + esp_hmac_sha256_vector_t hmac_sha256_vector; /**< function used to process message when do WPS */ + esp_sha256_vector_t sha256_vector; /**< function used to process message when do WPS */ +}wps_crypto_funcs_t; + +/** + * @brief The crypto callback function structure used when do WPA enterprise connect. + * The structure can be set as software crypto or the crypto optimized by ESP32 + * hardware. + */ +typedef struct { + esp_crypto_hash_init_t crypto_hash_init; /**< function used to initialize a crypto_hash structure when use TLSV1 */ + esp_crypto_hash_update_t crypto_hash_update; /**< function used to calculate hash data when use TLSV1 */ + esp_crypto_hash_finish_t crypto_hash_finish; /**< function used to finish the hash calculate when use TLSV1 */ + esp_crypto_cipher_init_t crypto_cipher_init; /**< function used to initialize a crypt_cipher structure when use TLSV1 */ + esp_crypto_cipher_encrypt_t crypto_cipher_encrypt; /**< function used to encrypt cipher when use TLSV1 */ + esp_crypto_cipher_decrypt_t crypto_cipher_decrypt; /**< function used to decrypt cipher when use TLSV1 */ + esp_crypto_cipher_deinit_t crypto_cipher_deinit; /**< function used to free context when use TLSV1 */ + esp_sha256_vector_t sha256_vector; /**< function used to do X.509v3 certificate parsing and processing */ + esp_crypto_mod_exp_t crypto_mod_exp; /**< function used to do key exchange when use TLSV1 */ +}wpa2_crypto_funcs_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/components/esp32/include/esp_wpa2.h b/components/esp32/include/esp_wpa2.h index e33e0e6dcf..6628bd1c90 100644 --- a/components/esp32/include/esp_wpa2.h +++ b/components/esp32/include/esp_wpa2.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Hardware crypto support Copyright 2017 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. @@ -16,11 +16,22 @@ #define ESP_WPA2_H #include "esp_err.h" +#include "esp_wifi_crypto_types.h" #ifdef __cplusplus extern "C" { #endif +extern const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs; + +typedef struct { + const wpa2_crypto_funcs_t *crypto_funcs; +}esp_wpa2_config_t; + +#define WPA2_CONFIG_INIT_DEFAULT() { \ + .crypto_funcs = &g_wifi_default_wpa2_crypto_funcs \ +} + /** * @brief Enable wpa2 enterprise authentication. * @@ -31,7 +42,7 @@ extern "C" { * - ESP_ERR_WIFI_OK: succeed. * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ -esp_err_t esp_wifi_sta_wpa2_ent_enable(void); +esp_err_t esp_wifi_sta_wpa2_ent_enable(const esp_wpa2_config_t *config); /** * @brief Disable wpa2 enterprise authentication. diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index f95eaa5e2a..bfbb215370 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -15,8 +15,10 @@ #ifndef __ESP_WPS_H__ #define __ESP_WPS_H__ +#include #include #include "esp_err.h" +#include "esp_wifi_crypto_types.h" #ifdef __cplusplus extern "C" { @@ -52,6 +54,18 @@ typedef enum wps_type { WPS_TYPE_MAX, } wps_type_t; +extern const wps_crypto_funcs_t g_wifi_default_wps_crypto_funcs; + +typedef struct { + wps_type_t wps_type; + const wps_crypto_funcs_t *crypto_funcs; +}esp_wps_config_t; + +#define WPS_CONFIG_INIT_DEFAULT(type) { \ + .wps_type = type, \ + .crypto_funcs = &g_wifi_default_wps_crypto_funcs, \ +} + /** * @brief Enable Wi-Fi WPS function. * @@ -65,7 +79,7 @@ typedef enum wps_type { * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -esp_err_t esp_wifi_wps_enable(wps_type_t wps_type); +esp_err_t esp_wifi_wps_enable(const esp_wps_config_t *config); /** * @brief Disable Wi-Fi WPS function and release resource it taken. diff --git a/components/esp32/lib b/components/esp32/lib index 57b3072bd3..e8d8e908fb 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 57b3072bd3fd2f7759225d8407443e9e983520ae +Subproject commit e8d8e908fb37c4d9e8a3f23e63f6b34f5178e24a diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index daac5ca70e..f34cb95f31 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -1,4 +1,4 @@ -COMPONENT_ADD_INCLUDEDIRS := include port/include -COMPONENT_SRCDIRS := src/crypto port +COMPONENT_ADD_INCLUDEDIRS := include port/include ../esp32/include +COMPONENT_SRCDIRS := src/crypto port src/fast_crypto CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing diff --git a/components/wpa_supplicant/include/crypto/aes_wrap.h b/components/wpa_supplicant/include/crypto/aes_wrap.h index 4b1c7b083b..933031e4ba 100644 --- a/components/wpa_supplicant/include/crypto/aes_wrap.h +++ b/components/wpa_supplicant/include/crypto/aes_wrap.h @@ -44,5 +44,10 @@ int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); - +int __must_check fast_aes_wrap(const uint8_t *kek, int n, const uint8_t *plain, uint8_t *cipher); +int __must_check fast_aes_unwrap(const uint8_t *kek, int n, const uint8_t *cipher, uint8_t *plain); +int __must_check fast_aes_128_cbc_encrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, + size_t data_len); +int __must_check fast_aes_128_cbc_decrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, + size_t data_len); #endif /* AES_WRAP_H */ diff --git a/components/wpa_supplicant/include/crypto/crypto.h b/components/wpa_supplicant/include/crypto/crypto.h index f0b9f22430..b8877993df 100644 --- a/components/wpa_supplicant/include/crypto/crypto.h +++ b/components/wpa_supplicant/include/crypto/crypto.h @@ -100,6 +100,17 @@ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +/** + * fast_sha256_vector - fast SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int fast_sha256_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, + uint8_t *mac); + /** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) @@ -159,7 +170,6 @@ enum crypto_hash_alg { CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 }; - struct crypto_hash; /** @@ -177,6 +187,21 @@ struct crypto_hash; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len); +/** + * fast_crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * fast_crypto_hash_init(enum crypto_hash_alg alg, const uint8_t *key, + size_t key_len); + /** * crypto_hash_update - Add data to hash calculation * @ctx: Context pointer from crypto_hash_init() @@ -189,6 +214,18 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, */ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); +/** + * fast_crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void fast_crypto_hash_update(struct crypto_hash *ctx, const uint8_t *data, size_t len); + /** * crypto_hash_finish - Complete hash calculation * @ctx: Context pointer from crypto_hash_init() @@ -208,6 +245,25 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); */ int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); +/** + * fast_crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int fast_crypto_hash_finish(struct crypto_hash *ctx, uint8_t *hash, size_t *len); + enum crypto_cipher_alg { CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, @@ -233,6 +289,22 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len); +/** + * fast_crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * fast_crypto_cipher_init(enum crypto_cipher_alg alg, + const uint8_t *iv, const uint8_t *key, + size_t key_len); /** * crypto_cipher_encrypt - Cipher encrypt * @ctx: Context pointer from crypto_cipher_init() @@ -248,6 +320,21 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len); +/** + * fast_crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check fast_crypto_cipher_encrypt(struct crypto_cipher *ctx, + const uint8_t *plain, uint8_t *crypt, size_t len); + /** * crypto_cipher_decrypt - Cipher decrypt * @ctx: Context pointer from crypto_cipher_init() @@ -263,6 +350,21 @@ int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len); +/** + * fast_crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check fast_crypto_cipher_decrypt(struct crypto_cipher *ctx, + const uint8_t *crypt, uint8_t *plain, size_t len); + /** * crypto_cipher_decrypt - Free cipher context * @ctx: Context pointer from crypto_cipher_init() @@ -273,6 +375,15 @@ int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, */ void crypto_cipher_deinit(struct crypto_cipher *ctx); +/** + * fast_crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void fast_crypto_cipher_deinit(struct crypto_cipher *ctx); struct crypto_public_key; struct crypto_private_key; @@ -452,6 +563,31 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len); +/** + * fast_crypto_mod_exp - Modular exponentiation of large integers + * @base: Base integer (big endian byte array) + * @base_len: Length of base integer in bytes + * @power: Power integer (big endian byte array) + * @power_len: Length of power integer in bytes + * @modulus: Modulus integer (big endian byte array) + * @modulus_len: Length of modulus integer in bytes + * @result: Buffer for the result + * @result_len: Result length (max buffer size on input, real len on output) + * Returns: 0 on success, -1 on failure + * + * This function calculates result = base ^ power mod modulus. modules_len is + * used as the maximum size of modulus buffer. It is set to the used size on + * success. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check fast_crypto_mod_exp(const uint8_t *base, size_t base_len, + const uint8_t *power, size_t power_len, + const uint8_t *modulus, size_t modulus_len, + uint8_t *result, size_t *result_len); + /** * rc4_skip - XOR RC4 stream to given data with skip-stream-start * @key: RC4 key diff --git a/components/wpa_supplicant/include/crypto/sha256.h b/components/wpa_supplicant/include/crypto/sha256.h index dc597f09b5..8025a29de3 100644 --- a/components/wpa_supplicant/include/crypto/sha256.h +++ b/components/wpa_supplicant/include/crypto/sha256.h @@ -24,4 +24,10 @@ void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void fast_hmac_sha256_vector(const uint8_t *key, size_t key_len, size_t num_elem, + const uint8_t *addr[], const size_t *len, uint8_t *mac); +void fast_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, + size_t data_len, uint8_t *mac); +void fast_sha256_prf(const uint8_t *key, size_t key_len, const char *label, + const uint8_t *data, size_t data_len, uint8_t *buf, size_t buf_len); #endif /* SHA256_H */ diff --git a/components/wpa_supplicant/src/crypto/dh_groups.c b/components/wpa_supplicant/src/crypto/dh_groups.c index 6ec9a36a90..c08f8f29df 100644 --- a/components/wpa_supplicant/src/crypto/dh_groups.c +++ b/components/wpa_supplicant/src/crypto/dh_groups.c @@ -20,11 +20,9 @@ #include "crypto/dh_groups.h" #include "wpa/wpabuf.h" #include "wpa/wpa_debug.h" +#include "esp_wifi_crypto_types.h" -extern int crypto_mod_exp(const u8 *base, size_t base_len, - const u8 *power, size_t power_len, - const u8 *modulus, size_t modulus_len, - u8 *result, size_t *result_len); +extern wps_crypto_funcs_t wps_crypto_funcs; #ifdef ALL_DH_GROUPS @@ -589,10 +587,17 @@ dh_init(const struct dh_group *dh, struct wpabuf **priv) pv = wpabuf_alloc(pv_len); if (pv == NULL) return NULL; - if (crypto_mod_exp(dh->generator, dh->generator_len, - wpabuf_head(*priv), wpabuf_len(*priv), - dh->prime, dh->prime_len, wpabuf_mhead(pv), - &pv_len) < 0) { + + if (wps_crypto_funcs.crypto_mod_exp) { + if (wps_crypto_funcs.crypto_mod_exp(dh->generator, dh->generator_len, + wpabuf_head(*priv), wpabuf_len(*priv), + dh->prime, dh->prime_len, wpabuf_mhead(pv), + &pv_len)) { + wpabuf_free(pv); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + } else { wpabuf_free(pv); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; @@ -626,14 +631,22 @@ dh_derive_shared(const struct wpabuf *peer_public, shared = wpabuf_alloc(shared_len); if (shared == NULL) return NULL; - if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), - wpabuf_head(own_private), wpabuf_len(own_private), - dh->prime, dh->prime_len, - wpabuf_mhead(shared), &shared_len) < 0) { + + if (wps_crypto_funcs.crypto_mod_exp) { + if (wps_crypto_funcs.crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), + wpabuf_head(own_private), wpabuf_len(own_private), + dh->prime, dh->prime_len, + wpabuf_mhead(shared), &shared_len)) { + wpabuf_free(shared); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + } else { wpabuf_free(shared); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } + wpabuf_put(shared, shared_len); wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); diff --git a/components/wpa_supplicant/src/fast_crypto/fast_aes-cbc.c b/components/wpa_supplicant/src/fast_crypto/fast_aes-cbc.c new file mode 100644 index 0000000000..5c362fa225 --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_aes-cbc.c @@ -0,0 +1,78 @@ +// Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); + + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" +#include "mbedtls/aes.h" + +/** + * fast_aes_128_cbc_encrypt - AES-128 CBC encryption + * @key: Encryption key + * @iv: Encryption IV for CBC mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int +fast_aes_128_cbc_encrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, size_t data_len) +{ + int ret = 0; + mbedtls_aes_context ctx; + uint8_t cbc[AES_BLOCK_SIZE]; + + mbedtls_aes_init(&ctx); + + ret = mbedtls_aes_setkey_enc(&ctx, key, 128); + + if(ret < 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, data_len, cbc, data, data); + + mbedtls_aes_free(&ctx); + + return ret; +} + + +/** + * fast_aes_128_cbc_decrypt - AES-128 CBC decryption + * @key: Decryption key + * @iv: Decryption IV for CBC mode (16 bytes) + * @data: Data to decrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int +fast_aes_128_cbc_decrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, size_t data_len) +{ + int ret = 0; + mbedtls_aes_context ctx; + uint8_t cbc[AES_BLOCK_SIZE]; + + mbedtls_aes_init(&ctx); + + ret = mbedtls_aes_setkey_dec(&ctx, key, 128); + + if(ret < 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, data_len, cbc, data, data); + + mbedtls_aes_free(&ctx); + + return ret; + +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_aes-unwrap.c b/components/wpa_supplicant/src/fast_crypto/fast_aes-unwrap.c new file mode 100644 index 0000000000..d6bfdfab15 --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_aes-unwrap.c @@ -0,0 +1,81 @@ +// 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 +// +// Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "crypto/includes.h" +#include "crypto/common.h" +#include "mbedtls/aes.h" + +/** + * fast_aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits + * @plain: Plaintext key, n * 64 bits + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int +fast_aes_unwrap(const uint8_t *kek, int n, const uint8_t *cipher, uint8_t *plain) +{ + uint8_t a[8], *r, b[16]; + int32_t i, j; + int32_t ret = 0; + mbedtls_aes_context ctx; + + /* 1) Initialize variables. */ + os_memcpy(a, cipher, 8); + r = plain; + os_memcpy(r, cipher + 8, 8 * n); + + mbedtls_aes_init(&ctx); + ret = mbedtls_aes_setkey_dec(&ctx, kek, 128); + if (ret < 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + os_memcpy(b, a, 8); + b[7] ^= n * j + i; + os_memcpy(b + 8, r, 8); + mbedtls_aes_decrypt(&ctx, b, b); + os_memcpy(a, b, 8); + os_memcpy(r, b + 8, 8); + r -= 8; + } + } + mbedtls_aes_free(&ctx); + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) { + return -1; + } + } + + return 0; +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_aes-wrap.c b/components/wpa_supplicant/src/fast_crypto/fast_aes-wrap.c new file mode 100644 index 0000000000..01a98a3add --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_aes-wrap.c @@ -0,0 +1,80 @@ +// 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 +// +// Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" +#include "mbedtls/aes.h" + +/** + * fast_aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: 16-octet Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @plain: Plaintext key to be wrapped, n * 64 bits + * @cipher: Wrapped key, (n + 1) * 64 bits + * Returns: 0 on success, -1 on failure + */ +int fast_aes_wrap(const uint8_t *kek, int n, const uint8_t *plain, uint8_t *cipher) +{ + uint8_t *a, *r, b[16]; + int32_t i, j; + int32_t ret = 0; + mbedtls_aes_context ctx; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + os_memset(a, 0xa6, 8); + os_memcpy(r, plain, 8 * n); + + mbedtls_aes_init(&ctx); + ret = mbedtls_aes_setkey_enc(&ctx, kek, 128); + if (ret < 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + os_memcpy(b, a, 8); + os_memcpy(b + 8, r, 8); + mbedtls_aes_encrypt(&ctx, b, b); + os_memcpy(a, b, 8); + a[7] ^= n * j + i; + os_memcpy(r, b + 8, 8); + r += 8; + } + } + mbedtls_aes_free(&ctx); + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ + + return 0; +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-cipher.c b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-cipher.c new file mode 100644 index 0000000000..fb00760bdf --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-cipher.c @@ -0,0 +1,283 @@ +// 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 +// +// Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "wpa/includes.h" + +//#include "wpa/common.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/aes.h" +#if defined(CONFIG_DES) || defined(CONFIG_DES3) +#include "crypto/des_i.h" +#endif +#include "mbedtls/aes.h" + +struct fast_crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct { + size_t used_bytes; + uint8_t key[16]; + size_t keylen; + } rc4; + struct { + uint8_t cbc[32]; + mbedtls_aes_context ctx_enc; + mbedtls_aes_context ctx_dec; + } aes; +#ifdef CONFIG_DES3 + struct { + struct des3_key_s key; + uint8_t cbc[8]; + } des3; +#endif +#ifdef CONFIG_DES + struct { + uint32_t ek[32]; + uint32_t dk[32]; + uint32_t cbc[8]; + } des; +#endif + } u; +}; + + +struct crypto_cipher * fast_crypto_cipher_init(enum crypto_cipher_alg alg, + const uint8_t *iv, const uint8_t *key, + size_t key_len) +{ + struct fast_crypto_cipher *ctx; + + ctx = (struct fast_crypto_cipher *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + break; + case CRYPTO_CIPHER_ALG_AES: + mbedtls_aes_init(&(ctx->u.aes.ctx_enc)); + mbedtls_aes_setkey_enc(&(ctx->u.aes.ctx_enc), key, 256); + mbedtls_aes_init(&(ctx->u.aes.ctx_dec)); + mbedtls_aes_setkey_dec(&(ctx->u.aes.ctx_dec), key, 256); + os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != 24) { + os_free(ctx); + return NULL; + } + des3_key_setup(key, &ctx->u.des3.key); + os_memcpy(ctx->u.des3.cbc, iv, 8); + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (key_len != 8) { + os_free(ctx); + return NULL; + } + des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk); + os_memcpy(ctx->u.des.cbc, iv, 8); + break; +#endif + default: + os_free(ctx); + return NULL; + } + + return (struct crypto_cipher *)ctx; +} + + +int fast_crypto_cipher_encrypt(struct crypto_cipher *ctx, const uint8_t *plain, + uint8_t *crypt, size_t len) +{ + size_t i, j, blocks; + struct fast_crypto_cipher *fast_ctx; + + fast_ctx = (struct fast_crypto_cipher *)ctx; + + switch (fast_ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) { + os_memcpy(crypt, plain, len); + } + rc4_skip(fast_ctx->u.rc4.key, fast_ctx->u.rc4.keylen, + fast_ctx->u.rc4.used_bytes, crypt, len); + fast_ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) { + return -1; + } + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < AES_BLOCK_SIZE; j++) + fast_ctx->u.aes.cbc[j] ^= plain[j]; + mbedtls_aes_encrypt(&(fast_ctx->u.aes.ctx_enc), fast_ctx->u.aes.cbc, fast_ctx->u.aes.cbc); + os_memcpy(crypt, fast_ctx->u.aes.cbc, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) { + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + fast_ctx->u.des3.cbc[j] ^= plain[j]; + des3_encrypt(fast_ctx->u.des3.cbc, &fast_ctx->u.des3.key, + fast_ctx->u.des3.cbc); + os_memcpy(crypt, fast_ctx->u.des3.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) { + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + fast_ctx->u.des3.cbc[j] ^= plain[j]; + des_block_encrypt(fast_ctx->u.des.cbc, fast_ctx->u.des.ek, + fast_ctx->u.des.cbc); + os_memcpy(crypt, fast_ctx->u.des.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; + } + + return 0; +} + + +int fast_crypto_cipher_decrypt(struct crypto_cipher *ctx, const uint8_t *crypt, + uint8_t *plain, size_t len) +{ + size_t i, j, blocks; + uint8_t tmp[32]; + struct fast_crypto_cipher *fast_ctx; + + fast_ctx = (struct fast_crypto_cipher *)ctx; + + switch (fast_ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) { + os_memcpy(plain, crypt, len); + } + rc4_skip(fast_ctx->u.rc4.key, fast_ctx->u.rc4.keylen, + fast_ctx->u.rc4.used_bytes, plain, len); + fast_ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) { + return -1; + } + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); + mbedtls_aes_decrypt(&(fast_ctx->u.aes.ctx_dec), crypt, plain); + for (j = 0; j < AES_BLOCK_SIZE; j++) + plain[j] ^= fast_ctx->u.aes.cbc[j]; + os_memcpy(fast_ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) { + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des3_decrypt(crypt, &fast_ctx->u.des3.key, plain); + for (j = 0; j < 8; j++) { + plain[j] ^= fast_ctx->u.des3.cbc[j]; + } + os_memcpy(fast_ctx->u.des3.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) { + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des_block_decrypt(crypt, fast_ctx->u.des.dk, plain); + for (j = 0; j < 8; j++) { + plain[j] ^= fast_ctx->u.des.cbc[j]; + } + os_memcpy(fast_ctx->u.des.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; +} + +return 0; +} + + +void fast_crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + struct fast_crypto_cipher *fast_ctx; + + fast_ctx = (struct fast_crypto_cipher *)ctx; + + switch (fast_ctx->alg) { + case CRYPTO_CIPHER_ALG_AES: + mbedtls_aes_free(&(fast_ctx->u.aes.ctx_enc)); + mbedtls_aes_free(&(fast_ctx->u.aes.ctx_dec)); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + break; +#endif + default: + break; + } + os_free(ctx); +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-modexp.c b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-modexp.c new file mode 100644 index 0000000000..66d4aa2de6 --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal-modexp.c @@ -0,0 +1,59 @@ +// Hardware crypto support Copyright 2017 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 "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "mbedtls/bignum.h" + +int +fast_crypto_mod_exp(const uint8_t *base, size_t base_len, + const uint8_t *power, size_t power_len, + const uint8_t *modulus, size_t modulus_len, + uint8_t *result, size_t *result_len) +{ + mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result, bn_rinv; + int32_t ret = 0; + mbedtls_mpi_init(&bn_base); + mbedtls_mpi_init(&bn_exp); + mbedtls_mpi_init(&bn_modulus); + mbedtls_mpi_init(&bn_result); + mbedtls_mpi_init(&bn_rinv); + + mbedtls_mpi_read_binary(&bn_base, base, base_len); + mbedtls_mpi_read_binary(&bn_exp, power, power_len); + mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len); + + ret = mbedtls_mpi_exp_mod(&bn_result, &bn_base, &bn_exp, &bn_modulus, &bn_rinv); + if (ret < 0) { + mbedtls_mpi_free(&bn_base); + mbedtls_mpi_free(&bn_exp); + mbedtls_mpi_free(&bn_modulus); + mbedtls_mpi_free(&bn_result); + mbedtls_mpi_free(&bn_rinv); + return ret; + } + + ret = mbedtls_mpi_write_binary(&bn_result, result, *result_len); + + + mbedtls_mpi_free(&bn_base); + mbedtls_mpi_free(&bn_exp); + mbedtls_mpi_free(&bn_modulus); + mbedtls_mpi_free(&bn_result); + mbedtls_mpi_free(&bn_rinv); + + return ret; +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal.c b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal.c new file mode 100644 index 0000000000..a0f7da83ae --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_crypto_internal.c @@ -0,0 +1,282 @@ +/* + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2011, Jouni Malinen + * + * Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/sha1_i.h" +#include "crypto/md5_i.h" +#include "mbedtls/sha256.h" + + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +struct fast_crypto_hash { + enum crypto_hash_alg alg; + union { + struct MD5Context md5; + struct SHA1Context sha1; +#ifdef CONFIG_SHA256 + mbedtls_sha256_context sha256; +#endif /* CONFIG_SHA256 */ + } u; + u8 key[64]; + size_t key_len; +}; + +struct crypto_hash * fast_crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct fast_crypto_hash *ctx; + u8 k_pad[64]; + u8 tk[32]; + size_t i; + + ctx = (struct fast_crypto_hash *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + MD5Init(&ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + SHA1Init(&ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + mbedtls_sha256_init(&ctx->u.sha256); + mbedtls_sha256_starts(&ctx->u.sha256, 0); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (key_len > sizeof(k_pad)) { + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, key, key_len); + MD5Final(tk, &ctx->u.md5); + key = tk; + key_len = 16; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (key_len > sizeof(k_pad)) { + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, key, key_len); + SHA1Final(tk, &ctx->u.sha1); + key = tk; + key_len = 20; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + mbedtls_sha256_init(&ctx->u.sha256); + mbedtls_sha256_starts(&ctx->u.sha256, 0); + mbedtls_sha256_update(&ctx->u.sha256, key, key_len); + mbedtls_sha256_finish(&ctx->u.sha256, tk); + mbedtls_sha256_free(&ctx->u.sha256); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + mbedtls_sha256_init(&ctx->u.sha256); + mbedtls_sha256_starts(&ctx->u.sha256, 0); + mbedtls_sha256_update(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return NULL; + } + + return (struct crypto_hash *)ctx; +} + + +void fast_crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + + struct fast_crypto_hash *fast_ctx; + fast_ctx = (struct fast_crypto_hash *)ctx; + + if (fast_ctx == NULL) + return; + + switch (fast_ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + MD5Update(&fast_ctx->u.md5, data, len); + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + SHA1Update(&fast_ctx->u.sha1, data, len); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + mbedtls_sha256_update(&fast_ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; + } +} + + +int fast_crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + u8 k_pad[64]; + size_t i; + struct fast_crypto_hash *fast_ctx; + + if (ctx == NULL) + return -2; + + fast_ctx = (struct fast_crypto_hash *)ctx; + + if (mac == NULL || len == NULL) { + os_free(fast_ctx); + return 0; + } + + switch (fast_ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(fast_ctx); + return -1; + } + *len = 16; + MD5Final(mac, &fast_ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(fast_ctx); + return -1; + } + *len = 20; + SHA1Final(mac, &fast_ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(fast_ctx); + return -1; + } + *len = 32; + mbedtls_sha256_finish(&fast_ctx->u.sha256, mac); + mbedtls_sha256_free(&fast_ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(fast_ctx); + return -1; + } + *len = 16; + + MD5Final(mac, &fast_ctx->u.md5); + + os_memcpy(k_pad, fast_ctx->key, fast_ctx->key_len); + os_memset(k_pad + fast_ctx->key_len, 0, + sizeof(k_pad) - fast_ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + MD5Init(&fast_ctx->u.md5); + MD5Update(&fast_ctx->u.md5, k_pad, sizeof(k_pad)); + MD5Update(&fast_ctx->u.md5, mac, 16); + MD5Final(mac, &fast_ctx->u.md5); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + + SHA1Final(mac, &fast_ctx->u.sha1); + os_memcpy(k_pad, fast_ctx->key, fast_ctx->key_len); + os_memset(k_pad + fast_ctx->key_len, 0, + sizeof(k_pad) - fast_ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + SHA1Init(&fast_ctx->u.sha1); + SHA1Update(&fast_ctx->u.sha1, k_pad, sizeof(k_pad)); + SHA1Update(&fast_ctx->u.sha1, mac, 20); + SHA1Final(mac, &fast_ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(fast_ctx); + return -1; + } + *len = 32; + mbedtls_sha256_finish(&fast_ctx->u.sha256, mac); + mbedtls_sha256_free(&fast_ctx->u.sha256); + + os_memcpy(k_pad, fast_ctx->key, fast_ctx->key_len); + os_memset(k_pad + fast_ctx->key_len, 0, + sizeof(k_pad) - fast_ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + mbedtls_sha256_init(&fast_ctx->u.sha256); + mbedtls_sha256_starts(&fast_ctx->u.sha256, 0); + mbedtls_sha256_update(&fast_ctx->u.sha256, k_pad, sizeof(k_pad)); + mbedtls_sha256_update(&fast_ctx->u.sha256, mac, 32); + mbedtls_sha256_finish(&fast_ctx->u.sha256, mac); + mbedtls_sha256_free(&fast_ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(fast_ctx); + return -1; + } + + os_free(fast_ctx); + + return 0; +} diff --git a/components/wpa_supplicant/src/fast_crypto/fast_sha256-internal.c b/components/wpa_supplicant/src/fast_crypto/fast_sha256-internal.c new file mode 100644 index 0000000000..587b9096fc --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_sha256-internal.c @@ -0,0 +1,48 @@ +// Hardware crypto support Copyright 2017 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 "crypto/includes.h" +#include "crypto/common.h" +#include "mbedtls/sha256.h" + +/** + * fast_sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int +fast_sha256_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, + uint8_t *mac) +{ + mbedtls_sha256_context ctx; + + mbedtls_sha256_init(&ctx); + + mbedtls_sha256_starts(&ctx, 0); + + for(size_t index = 0; index < num_elem; index++) { + mbedtls_sha256_update(&ctx, addr[index], len[index]); + } + + mbedtls_sha256_finish(&ctx, mac); + + mbedtls_sha256_free(&ctx); + + return 0; +} + + diff --git a/components/wpa_supplicant/src/fast_crypto/fast_sha256.c b/components/wpa_supplicant/src/fast_crypto/fast_sha256.c new file mode 100644 index 0000000000..b0fb03e4f8 --- /dev/null +++ b/components/wpa_supplicant/src/fast_crypto/fast_sha256.c @@ -0,0 +1,165 @@ +/* + * SHA-256 hash implementation and interface functions + * Copyright (c) 2003-2007, Jouni Malinen + * + * Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" + + +/** + * fast_hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (32 bytes) + */ +void +fast_hmac_sha256_vector(const uint8_t *key, size_t key_len, size_t num_elem, + const uint8_t *addr[], const size_t *len, uint8_t *mac) +{ + uint8_t k_pad[64]; /* padding - key XORd with ipad/opad */ + uint8_t tk[32]; + const uint8_t *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } + + /* if key is longer than 64 bytes reset it to key = SHA256(key) */ + if (key_len > 64) { + fast_sha256_vector(1, &key, &key_len, tk); + key = tk; + key_len = 32; + } + + /* the HMAC_SHA256 transform looks like: + * + * SHA256(K XOR opad, SHA256(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) { + k_pad[i] ^= 0x36; + } + + /* perform inner SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + fast_sha256_vector(1 + num_elem, _addr, _len, mac); + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) { + k_pad[i] ^= 0x5c; + } + + /* perform outer SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA256_MAC_LEN; + fast_sha256_vector(2, _addr, _len, mac); +} + + +/** + * fast_hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ +void +fast_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, + size_t data_len, uint8_t *mac) +{ + fast_hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +/** + * fast_sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void +fast_sha256_prf(const uint8_t *key, size_t key_len, const char *label, + const uint8_t *data, size_t data_len, uint8_t *buf, size_t buf_len) +{ + uint16_t counter = 1; + size_t pos, plen; + uint8_t hash[SHA256_MAC_LEN]; + const uint8_t *addr[4]; + size_t len[4]; + uint8_t counter_le[2], length_le[2]; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (uint8_t *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len * 8); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + fast_hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + fast_hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} diff --git a/docs/Doxyfile b/docs/Doxyfile index e6af25c901..15feda1a7e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -25,6 +25,7 @@ INPUT = \ ## Wi-Fi - API Reference ## ../components/esp32/include/esp_wifi.h \ + ../components/esp32/include/esp_wifi_crypto_types.h\ ../components/esp32/include/esp_smartconfig.h \ ## Bluetooth - API Reference ## Controller && VHCI diff --git a/docs/api-guides/wifi.rst b/docs/api-guides/wifi.rst index 800f8ae05d..26b032e446 100644 --- a/docs/api-guides/wifi.rst +++ b/docs/api-guides/wifi.rst @@ -19,6 +19,7 @@ ESP32 Wi-Fi Feature List - Supports an Espressif-specific protocol which, in turn, supports up to **1 km** of data traffic - Up to 20 MBit/sec TCP throughput and 30 MBit/sec UDP throughput over the air - Supports Sniffer +- Support set fast_crypto algorithm and normal algorithm switch which used in wifi connect How To Write a Wi-Fi Application ---------------------------------- @@ -1215,6 +1216,15 @@ Currently, ESP32 Wi-Fi supports the Modem-sleep mode which refers to WMM (Wi-Fi Call esp_wifi_set_ps(WIFI_PS_MODEM) to enable Modem-sleep mode after calling esp_wifi_init(). About 10 seconds after the station connects to the AP, Modem-sleep will start. When the station disconnects from the AP, Modem-sleep will stop. +ESP32 Wi-Fi Connect Crypto +----------------------------------- +Now ESP32 have two group crypto functions can be used when do wifi connect, one is the original functions, the other is optimized by ESP hardware: +1. Original functions which is the source code used in the folder components/wpa_supplicant/src/crypto function; +2. The optimized functions is in the folder components/wpa_supplicant/src/fast_crypto, these function used the hardware crypto to make it faster than origin one, the type of function's name add fast_ to distinguish with the original one. For example, the API aes_wrap() is used to encrypt frame information when do 4 way handshake, the fast_aes_wrap() has the same result but can be faster. + +Two groups of crypto function can be used when register in the wpa_crypto_funcs_t, wpa2_crypto_funcs_t and wps_crypto_funcs_t structure, also we have given the recommend functions to register in the +fast_crypto_ops.c, you can register the function as the way you need, however what should make action is that the crypto_hash_xxx function and crypto_cipher_xxx function need to register with the same function to operation. For example, if you register crypto_hash_init() function to initialize the esp_crypto_hash structure, you need use the crypto_hash_update() and crypto_hash_finish() function to finish the operation, rather than fast_crypto_hash_update() or fast_crypto_hash_finish(). + ESP32 Wi-Fi Throughput ----------------------------------- diff --git a/examples/wifi/power_save/main/power_save.c b/examples/wifi/power_save/main/power_save.c index d03881ff80..98bc601edd 100644 --- a/examples/wifi/power_save/main/power_save.c +++ b/examples/wifi/power_save/main/power_save.c @@ -62,6 +62,7 @@ static void wifi_power_save(void) { tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { diff --git a/examples/wifi/smart_config/main/smartconfig_main.c b/examples/wifi/smart_config/main/smartconfig_main.c index 63af25e654..3cbf8737d4 100644 --- a/examples/wifi/smart_config/main/smartconfig_main.c +++ b/examples/wifi/smart_config/main/smartconfig_main.c @@ -57,7 +57,9 @@ static void initialise_wifi(void) tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_init(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_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_start() ); diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c b/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c index d2b6344ecc..5b3f804e17 100644 --- a/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c +++ b/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c @@ -100,6 +100,7 @@ static void initialise_wifi(void) unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; unsigned int client_crt_bytes = client_crt_end - client_crt_start; unsigned int client_key_bytes = client_key_end - client_key_start; + esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); @@ -123,7 +124,8 @@ static void initialise_wifi(void) ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EXAMPLE_EAP_USERNAME, strlen(EXAMPLE_EAP_USERNAME)) ); ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD)) ); } - ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() ); + + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable(&config) ); ESP_ERROR_CHECK( esp_wifi_start() ); } diff --git a/examples/wifi/wps/main/wps.c b/examples/wifi/wps/main/wps.c index dd4a66c195..6fb8ad2468 100644 --- a/examples/wifi/wps/main/wps.c +++ b/examples/wifi/wps/main/wps.c @@ -40,7 +40,7 @@ static const char *TAG = "example_wps"; - +static esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(WPS_TEST_MODE); static esp_err_t event_handler(void *ctx, system_event_t *event) { @@ -68,13 +68,13 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) case SYSTEM_EVENT_STA_WPS_ER_FAILED: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_FAILED"); ESP_ERROR_CHECK(esp_wifi_wps_disable()); - ESP_ERROR_CHECK(esp_wifi_wps_enable(WPS_TEST_MODE)); + ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); ESP_ERROR_CHECK(esp_wifi_wps_start(0)); break; case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_TIMEOUT"); ESP_ERROR_CHECK(esp_wifi_wps_disable()); - ESP_ERROR_CHECK(esp_wifi_wps_enable(WPS_TEST_MODE)); + ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); ESP_ERROR_CHECK(esp_wifi_wps_start(0)); break; case SYSTEM_EVENT_STA_WPS_ER_PIN: @@ -95,13 +95,16 @@ static void start_wps(void) ESP_ERROR_CHECK(esp_event_loop_init(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_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "start wps..."); - ESP_ERROR_CHECK(esp_wifi_wps_enable(WPS_TEST_MODE)); + + + ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); ESP_ERROR_CHECK(esp_wifi_wps_start(0)); }