From 7617a3c43407f757b88091711bc9bc1e52e0ae62 Mon Sep 17 00:00:00 2001 From: Yulong Date: Wed, 11 Oct 2017 07:47:12 -0400 Subject: [PATCH 01/44] component/bt: Fix the bug cann't use esp_ble_gatts_get_attr_value to get the attribute value after long write. --- components/bt/bluedroid/stack/gatt/gatt_sr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 08591334f..972b17e23 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -422,6 +422,8 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); } + //don't forget to increase the attribute value length in the gatts database. + queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; } osi_free(queue_data); } From 4b1968f3156b515e7f118b5b1ff012a4875f18dd Mon Sep 17 00:00:00 2001 From: Yulong Date: Mon, 16 Oct 2017 03:42:49 -0400 Subject: [PATCH 02/44] component/bt: Change the queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len code to the if case. --- components/bt/bluedroid/stack/gatt/gatt_sr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 972b17e23..cbfb3e492 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -421,9 +421,9 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U if (is_prepare_write_valid){ if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); + //don't forget to increase the attribute value length in the gatts database. + queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; } - //don't forget to increase the attribute value length in the gatts database. - queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; } osi_free(queue_data); } From 9ad451dced673b59830a61cab6151154ea5dfc40 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Sat, 21 Oct 2017 15:03:54 +0800 Subject: [PATCH 03/44] Component/bt: fix memory leak in bluefi demo --- .../bluetooth/blufi/main/blufi_example_main.c | 2 +- .../bluetooth/blufi/main/blufi_security.c | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/examples/bluetooth/blufi/main/blufi_example_main.c b/examples/bluetooth/blufi/main/blufi_example_main.c index d349663a6..dfb2da493 100644 --- a/examples/bluetooth/blufi/main/blufi_example_main.c +++ b/examples/bluetooth/blufi/main/blufi_example_main.c @@ -184,11 +184,11 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para server_if=param->connect.server_if; conn_id=param->connect.conn_id; esp_ble_gap_stop_advertising(); - blufi_security_deinit(); blufi_security_init(); break; case ESP_BLUFI_EVENT_BLE_DISCONNECT: BLUFI_INFO("BLUFI ble disconnect\n"); + blufi_security_deinit(); esp_ble_gap_start_advertising(&example_adv_params); break; case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: diff --git a/examples/bluetooth/blufi/main/blufi_security.c b/examples/bluetooth/blufi/main/blufi_security.c index ab07435bb..0a6d6a344 100644 --- a/examples/bluetooth/blufi/main/blufi_security.c +++ b/examples/bluetooth/blufi/main/blufi_security.c @@ -90,22 +90,28 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da blufi_sec->dh_param_len = ((data[1]<<8)|data[2]); if (blufi_sec->dh_param) { free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; } blufi_sec->dh_param = (uint8_t *)malloc(blufi_sec->dh_param_len); if (blufi_sec->dh_param == NULL) { + BLUFI_ERROR("%s, malloc failed\n", __func__); return; } break; - case SEC_TYPE_DH_PARAM_DATA: - + case SEC_TYPE_DH_PARAM_DATA:{ + if (blufi_sec->dh_param == NULL) { + BLUFI_ERROR("%s, blufi_sec->dh_param == NULL\n", __func__); + return; + } + uint8_t *param = blufi_sec->dh_param; memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len); - - ret = mbedtls_dhm_read_params(&blufi_sec->dhm, &blufi_sec->dh_param, &blufi_sec->dh_param[blufi_sec->dh_param_len]); + ret = mbedtls_dhm_read_params(&blufi_sec->dhm, ¶m, ¶m[blufi_sec->dh_param_len]); if (ret) { BLUFI_ERROR("%s read param failed %d\n", __func__, ret); return; } - + free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size( &blufi_sec->dhm.P ), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL); if (ret) { BLUFI_ERROR("%s make public failed %d\n", __func__, ret); @@ -126,6 +132,8 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da *output_data = &blufi_sec->self_public_key[0]; *output_len = blufi_sec->dhm.len; *need_free = false; + + } break; case SEC_TYPE_DH_P: break; @@ -194,6 +202,13 @@ esp_err_t blufi_security_init(void) void blufi_security_deinit(void) { + if (blufi_sec == NULL) { + return; + } + if (blufi_sec->dh_param){ + free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; + } mbedtls_dhm_free(&blufi_sec->dhm); mbedtls_aes_free(&blufi_sec->aes); From da32fbce7a8ba762cafba912c73f0ee1f901e2af Mon Sep 17 00:00:00 2001 From: Yulong Date: Wed, 11 Oct 2017 03:33:20 -0400 Subject: [PATCH 04/44] component/bt: Fix the bug of can't pair if master send pair req but slave don't send sec req. --- .../bt/bluedroid/api/include/esp_gap_ble_api.h | 18 ++++++++++++++---- components/bt/bluedroid/stack/btm/btm_ble.c | 2 +- 2 files changed, 15 insertions(+), 5 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 146bb9ec5..5c55c02a8 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -179,10 +179,20 @@ typedef enum { /* relate to BTA_DM_BLE_SEC_xxx in bta_api.h */ typedef enum { - ESP_BLE_SEC_NONE = 0, /* relate to BTA_DM_BLE_SEC_NONE in bta_api.h */ - ESP_BLE_SEC_ENCRYPT, /* relate to BTA_DM_BLE_SEC_ENCRYPT in bta_api.h */ - ESP_BLE_SEC_ENCRYPT_NO_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_NO_MITM in bta_api.h */ - ESP_BLE_SEC_ENCRYPT_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_MITM in bta_api.h */ + ESP_BLE_SEC_ENCRYPT = 1, /* relate to BTA_DM_BLE_SEC_ENCRYPT in bta_api.h. If the device has already + bonded, the stack will used LTK to encrypt with the remote device directly. + Else if the device hasn't bonded, the stack will used the default authentication request + used the esp_ble_gap_set_security_param function set by the user. */ + ESP_BLE_SEC_ENCRYPT_NO_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_NO_MITM in bta_api.h. If the device has already + bonded, the stack will check the LTK Whether the authentication request has been met, if met, used the LTK + to encrypt with the remote device directly, else Re-pair with the remote device. + Else if the device hasn't bonded, the stack will used NO MITM authentication request in the current link instead of + used the authreq in the esp_ble_gap_set_security_param function set by the user. */ + ESP_BLE_SEC_ENCRYPT_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_MITM in bta_api.h. If the device has already + bonded, the stack will check the LTK Whether the authentication request has been met, if met, used the LTK + to encrypt with the remote device directly, else Re-pair with the remote device. + Else if the device hasn't bonded, the stack will used MITM authentication request in the current link instead of + used the authreq in the esp_ble_gap_set_security_param function set by the user. */ }esp_ble_sec_act_t; typedef enum { diff --git a/components/bt/bluedroid/stack/btm/btm_ble.c b/components/bt/bluedroid/stack/btm/btm_ble.c index fd4cb81ad..4e49b16ef 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/bluedroid/stack/btm/btm_ble.c @@ -1413,7 +1413,7 @@ tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 lin switch (sec_act) { case BTM_BLE_SEC_ENCRYPT: - if (link_role == BTM_ROLE_MASTER) { + if (link_role == BTM_ROLE_MASTER && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) { /* start link layer encryption using the security info stored */ cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL); break; From a9a423a025421f437059e4d77f40ad05995e0692 Mon Sep 17 00:00:00 2001 From: yulong Date: Mon, 23 Oct 2017 15:01:00 +0800 Subject: [PATCH 05/44] component/bt: Added the sec_act != BTM_BLE_SEC_ENCRYPT check in the btm_ble_set_encryption function when the sec_act is BTM_BLE_SEC_ENCRYPT_NO_MITM or BTM_BLE_SEC_ENCRYPT_MITM. --- components/bt/bluedroid/stack/btm/btm_ble.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bt/bluedroid/stack/btm/btm_ble.c b/components/bt/bluedroid/stack/btm/btm_ble.c index 4e49b16ef..72e7023c2 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/bluedroid/stack/btm/btm_ble.c @@ -1422,7 +1422,7 @@ tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 lin sec_request to request the master to encrypt the link */ case BTM_BLE_SEC_ENCRYPT_NO_MITM: case BTM_BLE_SEC_ENCRYPT_MITM: - if (link_role == BTM_ROLE_MASTER) { + if ((link_role == BTM_ROLE_MASTER) && (sec_act != BTM_BLE_SEC_ENCRYPT)) { auth_req = (sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM) ? SMP_AUTH_GEN_BOND : (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT); btm_ble_link_sec_check (bd_addr, auth_req, &sec_req_act); From 84a55f9ee427072942bddad8e931436630dcbb06 Mon Sep 17 00:00:00 2001 From: baohongde Date: Wed, 11 Oct 2017 17:36:46 +0800 Subject: [PATCH 06/44] component/bt: Fix bug of function smp_decide_association_model --- components/bt/bluedroid/stack/smp/smp_act.c | 25 +++++++-------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/components/bt/bluedroid/stack/smp/smp_act.c b/components/bt/bluedroid/stack/smp/smp_act.c index 1ca5bf0d3..99f248acc 100644 --- a/components/bt/bluedroid/stack/smp/smp_act.c +++ b/components/bt/bluedroid/stack/smp/smp_act.c @@ -1221,25 +1221,16 @@ void smp_decide_association_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) switch (p_cb->selected_association_model) { case SMP_MODEL_ENCRYPTION_ONLY: /* TK = 0, go calculate Confirm */ - if (p_cb->role == HCI_ROLE_MASTER && - ((p_cb->peer_auth_req & SMP_AUTH_YN_BIT) != 0) && - ((p_cb->loc_auth_req & SMP_AUTH_YN_BIT) == 0)) { - SMP_TRACE_ERROR ("IO capability does not meet authentication requirement\n"); - failure = SMP_PAIR_AUTH_FAIL; - p = (tSMP_INT_DATA *)&failure; - int_evt = SMP_AUTH_CMPL_EVT; - } else { - p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; - SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) \n", p_cb->sec_level ); + p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; + SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) \n", p_cb->sec_level ); - key.key_type = SMP_KEY_TYPE_TK; - key.p_data = p_cb->tk; - p = (tSMP_INT_DATA *)&key; + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + p = (tSMP_INT_DATA *)&key; - memset(p_cb->tk, 0, BT_OCTET16_LEN); - /* TK, ready */ - int_evt = SMP_KEY_READY_EVT; - } + memset(p_cb->tk, 0, BT_OCTET16_LEN); + /* TK, ready */ + int_evt = SMP_KEY_READY_EVT; break; case SMP_MODEL_PASSKEY: From a1495b0e493712fbf6a9316856c019b3be5f9722 Mon Sep 17 00:00:00 2001 From: Yulong Date: Sun, 22 Oct 2017 23:07:03 -0400 Subject: [PATCH 07/44] Squash the two submissions of previous. component/bt: The application layer does not allocate memory correctly causing the btc layer pointer to cross the border. bt/examples: Change the gattc_multi_connect.c incorrect memory apply method. --- examples/bluetooth/gatt_client/main/gattc_demo.c | 4 ++-- .../main/example_ble_sec_gattc_demo.c | 4 ++-- .../gattc_multi_connect/main/gattc_multi_connect.c | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/bluetooth/gatt_client/main/gattc_demo.c b/examples/bluetooth/gatt_client/main/gattc_demo.c index 24fe67ac0..14290c2d8 100644 --- a/examples/bluetooth/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/gatt_client/main/gattc_demo.c @@ -170,7 +170,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } if (count > 0){ - char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result) * count); + char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); if (!char_elem_result){ ESP_LOGE(GATTC_TAG, "gattc no mem"); }else{ @@ -216,7 +216,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); } if (count > 0){ - descr_elem_result = malloc(sizeof(descr_elem_result) * count); + descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count); if (!descr_elem_result){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); }else{ diff --git a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c index cf9ec505a..6ae360e65 100644 --- a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c +++ b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c @@ -191,7 +191,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__); } if (count > 0){ - char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result) * count); + char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); if (!char_elem_result){ ESP_LOGE(GATTC_TAG, "gattc no mem"); }else{ @@ -245,7 +245,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__); } if (count > 0){ - descr_elem_result = malloc(sizeof(descr_elem_result) * count); + descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count); if (!descr_elem_result){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); }else{ diff --git a/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c b/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c index 61c29edc0..b734d7fa4 100644 --- a/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c +++ b/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c @@ -209,7 +209,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); } if (count > 0) { - char_elem_result_a = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_a) * count); + char_elem_result_a = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); if (!char_elem_result_a){ ESP_LOGE(GATTC_TAG, "gattc no mem"); }else { @@ -255,7 +255,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); } if (count > 0){ - descr_elem_result_a = malloc(sizeof(descr_elem_result_a) * count); + descr_elem_result_a = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count); if (!descr_elem_result_a){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); }else{ @@ -410,7 +410,7 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i } if (count > 0){ - char_elem_result_b = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_b) * count); + char_elem_result_b = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); if (!char_elem_result_b){ ESP_LOGE(GATTC_TAG, "gattc no mem"); }else{ @@ -457,7 +457,7 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); } if (count > 0){ - descr_elem_result_b = malloc(sizeof(descr_elem_result_b) * count); + descr_elem_result_b = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count); if (!descr_elem_result_b){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); }else{ @@ -609,7 +609,7 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i } if (count > 0){ - char_elem_result_c = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_c) * count); + char_elem_result_c = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); if (!char_elem_result_c){ ESP_LOGE(GATTC_TAG, "gattc no mem"); }else{ @@ -655,7 +655,7 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); } if (count > 0){ - descr_elem_result_c = malloc(sizeof(descr_elem_result_c) * count); + descr_elem_result_c = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count); if (!descr_elem_result_c){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); }else{ From 18dcbfa1e290329e65775d4ccb82a032f3b341fc Mon Sep 17 00:00:00 2001 From: zhangyanjiao Date: Tue, 24 Oct 2017 09:43:29 +0800 Subject: [PATCH 08/44] fix tcp crash --- components/lwip/core/tcp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/lwip/core/tcp.c b/components/lwip/core/tcp.c index 1f915128a..7d16fce5b 100755 --- a/components/lwip/core/tcp.c +++ b/components/lwip/core/tcp.c @@ -1353,8 +1353,6 @@ tcp_kill_state(enum tcp_state state) struct tcp_pcb *pcb, *inactive; u32_t inactivity; - LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK)); - inactivity = 0; inactive = NULL; /* Go through the list of active pcbs and get the oldest pcb that is in state From 837678e4f3ad5fb23e608cef0f390c98478fea41 Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Tue, 24 Oct 2017 14:44:53 +0800 Subject: [PATCH 09/44] component/bt: disable the use of ROLE_SWITCH feature for classic BT as workaround --- components/bt/bluedroid/stack/include/hcidefs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/bt/bluedroid/stack/include/hcidefs.h b/components/bt/bluedroid/stack/include/hcidefs.h index faf04fd9f..87dec34a0 100644 --- a/components/bt/bluedroid/stack/include/hcidefs.h +++ b/components/bt/bluedroid/stack/include/hcidefs.h @@ -1469,7 +1469,8 @@ typedef struct { #define HCI_FEATURE_SWITCH_MASK 0x20 #define HCI_FEATURE_SWITCH_OFF 0 -#define HCI_SWITCH_SUPPORTED(x) ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK) +// temporarily disable ROLE_SWITCH since there is an issue to be fixed +#define HCI_SWITCH_SUPPORTED(x) (0 & ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK)) #define HCI_FEATURE_HOLD_MODE_MASK 0x40 #define HCI_FEATURE_HOLD_MODE_OFF 0 From 85eb5d4374cb47fd8a3dcfcc314743471b88b851 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Fri, 20 Oct 2017 17:09:03 +0800 Subject: [PATCH 10/44] component/bt : fix bug of sw interrupt cause to run btdm to different cpu core. 1. add sw interrupt cause osi to controller. 2. modify the kconfig to improve the option view. 3. add option of the cpu core which bluedroid run. 4. add option of the cpu core which bluetooth controller run. --- components/bt/Kconfig | 134 ++++++++++++------ components/bt/bluedroid/btc/core/btc_task.c | 4 +- .../btc/profile/std/a2dp/btc_media_task.c | 10 +- components/bt/bluedroid/hci/hci_hal_h4.c | 4 +- components/bt/bluedroid/hci/hci_layer.c | 4 +- components/bt/bluedroid/osi/include/thread.h | 21 ++- components/bt/bluedroid/stack/btu/btu_init.c | 4 +- components/bt/bt.c | 28 +++- components/bt/lib | 2 +- 9 files changed, 145 insertions(+), 66 deletions(-) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index 174b2deef..8a4f5248f 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1,14 +1,96 @@ -menuconfig BT_ENABLED +menu Bluetooth + + +config BT_ENABLED bool "Bluetooth" help Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices. -menuconfig BLUEDROID_ENABLED - bool "Bluedroid Bluetooth stack enabled" +choice BTDM_CONTROLLER_PINNED_TO_CORE_CHOICE + prompt "The cpu core which bluetooth controller run" + depends on BT_ENABLED && !FREERTOS_UNICORE + help + Specify the cpu core to run bluetooth controller. + Can not specify no-affinity. + +config BTDM_CONTROLLER_PINNED_TO_CORE_0 + bool "Core 0 (PRO CPU)" +config BTDM_CONTROLLER_PINNED_TO_CORE_1 + bool "Core 1 (APP CPU)" + depends on !FREERTOS_UNICORE +endchoice + +config BTDM_CONTROLLER_PINNED_TO_CORE + int + default 0 if BTDM_CONTROLLER_PINNED_TO_CORE_0 + default 1 if BTDM_CONTROLLER_PINNED_TO_CORE_1 + default 0 + +choice BTDM_CONTROLLER_HCI_MODE_CHOICE + prompt "HCI mode" depends on BT_ENABLED - default y - help - This enables the default Bluedroid Bluetooth stack + help + Speicify HCI mode as VHCI or UART(H4) + +config BTDM_CONTROLLER_HCI_MODE_VHCI + bool "VHCI" + help + Normal option. Mostly, choose this VHCI when bluetooth host run on ESP32, too. + +config BTDM_CONTROLLER_HCI_MODE_UART_H4 + bool "UART(H4)" + help + If use external bluetooth host which run on other hardware and use UART as the HCI interface, + choose this option. +endchoice + +menu "HCI UART(H4) Options" + visible if BTDM_CONTROLLER_HCI_MODE_UART_H4 + +config BT_HCI_UART_NO + int "UART Number for HCI" + depends on BTDM_CONTROLLER_HCI_MODE_UART_H4 + range 1 2 + default 1 + help + Uart number for HCI. The available uart is UART1 and UART2. + +config BT_HCI_UART_BAUDRATE + int "UART Baudrate for HCI" + depends on BTDM_CONTROLLER_HCI_MODE_UART_H4 + range 115200 921600 + default 921600 + help + UART Baudrate for HCI. Please use standard baudrate. +endmenu + +menuconfig BLUEDROID_ENABLED + bool "Bluedroid Enable" + depends on BTDM_CONTROLLER_HCI_MODE_VHCI + default y + help + This enables the default Bluedroid Bluetooth stack + +choice BLUEDROID_PINNED_TO_CORE_CHOICE + prompt "The cpu core which Bluedroid run" + depends on BLUEDROID_ENABLED && !FREERTOS_UNICORE + help + Which the cpu core to run Bluedroid. Can choose core0 and core1. + Can not specify no-affinity. + +config BLUEDROID_PINNED_TO_CORE_0 + bool "Core 0 (PRO CPU)" +config BLUEDROID_PINNED_TO_CORE_1 + bool "Core 1 (APP CPU)" + depends on !FREERTOS_UNICORE +endchoice + +config BLUEDROID_PINNED_TO_CORE + int + depends on BLUEDROID_ENABLED + default 0 if BLUEDROID_PINNED_TO_CORE_0 + default 1 if BLUEDROID_PINNED_TO_CORE_1 + default 0 config BTC_TASK_STACK_SIZE int "Bluetooth event (callback to application) task stack size" @@ -67,44 +149,6 @@ config BT_ACL_CONNECTIONS help Maximum BT/BLE connection count -#disable now for app cpu due to a known issue -config BTDM_CONTROLLER_RUN_APP_CPU - bool "Run controller on APP CPU" - depends on BT_ENABLED && !FREERTOS_UNICORE && 0 - default n - help - Run controller on APP CPU. - -config BTDM_CONTROLLER_RUN_CPU - int - depends on BT_ENABLED - default 1 if BTDM_CONTROLLER_RUN_APP_CPU - default 0 - -menuconfig BT_HCI_UART - bool "HCI use UART as IO" - depends on BT_ENABLED && !BLUEDROID_ENABLED - default n - help - Default HCI use VHCI, if this option choose, HCI will use UART(0/1/2) as IO. - Besides, it can set uart number and uart baudrate. - -config BT_HCI_UART_NO - int "UART Number for HCI" - depends on BT_HCI_UART - range 1 2 - default 1 - help - Uart number for HCI. - -config BT_HCI_UART_BAUDRATE - int "UART Baudrate for HCI" - depends on BT_HCI_UART - range 115200 921600 - default 921600 - help - UART Baudrate for HCI. Please use standard baudrate. - config SMP_ENABLE bool depends on BLUEDROID_ENABLED @@ -115,3 +159,5 @@ config BT_RESERVE_DRAM hex default 0x10000 if BT_ENABLED default 0 + +endmenu diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 52cbf6dcf..bed68bbb7 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -139,8 +139,8 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg int btc_init(void) { - xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_NUM, sizeof(btc_msg_t)); - xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, 0); + xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_LEN, sizeof(btc_msg_t)); + xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, BTC_TASK_PINNED_TO_CORE); btc_gap_callback_init(); /* TODO: initial the profile_tab */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c index 6444392a4..1b6db63c4 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c @@ -112,10 +112,6 @@ enum { /* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */ #define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5) -#define MEDIA_DATA_Q_LEN (1) -#define MEDIA_CTRL_Q_LEN (5) -#define COMBINED_MEDIA_Q_LEN (MEDIA_DATA_Q_LEN + MEDIA_CTRL_Q_LEN) - typedef struct { UINT16 num_frames_to_be_processed; UINT16 len; @@ -276,13 +272,13 @@ bool btc_a2dp_start_media_task(void) APPL_TRACE_EVENT("## A2DP START MEDIA THREAD ##"); - xBtcMediaQueueSet = xQueueCreateSet(COMBINED_MEDIA_Q_LEN); + xBtcMediaQueueSet = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN); configASSERT(xBtcMediaQueueSet); - xBtcMediaDataQueue = xQueueCreate(MEDIA_DATA_Q_LEN, sizeof(void *)); + xBtcMediaDataQueue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *)); configASSERT(xBtcMediaDataQueue); xQueueAddToSet(xBtcMediaDataQueue, xBtcMediaQueueSet); - xBtcMediaCtrlQueue = xQueueCreate(MEDIA_CTRL_Q_LEN, sizeof(void *)); + xBtcMediaCtrlQueue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *)); configASSERT(xBtcMediaCtrlQueue); xQueueAddToSet(xBtcMediaCtrlQueue, xBtcMediaQueueSet); diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 0cc5db676..6d823a58f 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -101,8 +101,8 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX); - xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, 0); + xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_LEN, sizeof(BtTaskEvt_t)); + xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, HCI_H4_TASK_PINNED_TO_CORE); //register vhci host cb esp_vhci_host_register_callback(&vhci_host_cb); diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index 6651fb6f2..cbf180f7e 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -107,8 +107,8 @@ int hci_start_up(void) goto error; } - xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, 0); + xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_LEN, sizeof(BtTaskEvt_t)); + xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, HCI_HOST_TASK_PINNED_TO_CORE); packet_fragmenter->init(&packet_fragmenter_callbacks); hal->open(&hal_callbacks); diff --git a/components/bt/bluedroid/osi/include/thread.h b/components/bt/bluedroid/osi/include/thread.h index 5f023c657..9ac5da6bd 100644 --- a/components/bt/bluedroid/osi/include/thread.h +++ b/components/bt/bluedroid/osi/include/thread.h @@ -57,26 +57,39 @@ typedef enum { SIG_BTU_NUM, } SIG_BTU_t; +#define TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) + +#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) #define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3) #define HCI_HOST_TASK_NAME "hciHostT" -#define HCI_HOST_QUEUE_NUM 40 +#define HCI_HOST_QUEUE_LEN 40 +#define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) #define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 4) #define HCI_H4_TASK_NAME "hciH4T" -#define HCI_H4_QUEUE_NUM 60 +#define HCI_H4_QUEUE_LEN 60 +#define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define BTU_TASK_STACK_SIZE (4096 + BT_TASK_EXTRA_STACK_SIZE) #define BTU_TASK_PRIO (configMAX_PRIORITIES - 5) #define BTU_TASK_NAME "btuT" -#define BTU_QUEUE_NUM 50 +#define BTU_QUEUE_LEN 50 +#define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define BTC_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig #define BTC_TASK_NAME "btcT" #define BTC_TASK_PRIO (configMAX_PRIORITIES - 6) -#define BTC_TASK_QUEUE_NUM 60 +#define BTC_TASK_QUEUE_LEN 60 +#define BTC_MEDIA_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTC_MEDIA_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) +#define BTC_MEDIA_TASK_NAME "BtcMediaT" +#define BTC_MEDIA_TASK_PRIO (configMAX_PRIORITIES - 3) +#define BTC_MEDIA_DATA_QUEUE_LEN (1) +#define BTC_MEDIA_CTRL_QUEUE_LEN (5) +#define BTC_MEDIA_TASK_QUEUE_SET_LEN (BTC_MEDIA_DATA_QUEUE_LEN + BTC_MEDIA_CTRL_QUEUE_LEN) #define TASK_POST_NON_BLOCKING (0) #define TASK_POST_BLOCKING (portMAX_DELAY) diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 9776139ec..a3ffd0e70 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -168,8 +168,8 @@ void BTU_StartUp(void) osi_mutex_new(&btu_l2cap_alarm_lock); - 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); + xBtuQueue = xQueueCreate(BTU_QUEUE_LEN, sizeof(BtTaskEvt_t)); + xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, BTU_TASK_PINNED_TO_CORE); btu_task_post(SIG_BTU_START_UP, NULL, TASK_POST_BLOCKING); diff --git a/components/bt/bt.c b/components/bt/bt.c index a0539a19a..6f351cad1 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -24,6 +24,7 @@ #include "freertos/semphr.h" #include "freertos/xtensa_api.h" #include "freertos/portmacro.h" +#include "xtensa/core-macros.h" #include "esp_types.h" #include "esp_system.h" #include "esp_task.h" @@ -34,6 +35,7 @@ #include "esp_err.h" #include "esp_log.h" #include "esp_pm.h" +#include "esp_ipc.h" #if CONFIG_BT_ENABLED @@ -135,6 +137,7 @@ struct osi_funcs_t { int32_t (* _task_create)(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id); void (* _task_delete)(void *task_handle); bool (* _is_in_isr)(void); + int (* _cause_sw_intr_to_core)(int core_id, int intr_no); void *(* _malloc)(uint32_t size); void (* _free)(void *p); int32_t (* _read_efuse_mac)(uint8_t mac[6]); @@ -274,6 +277,26 @@ static bool IRAM_ATTR is_in_isr_wrapper(void) return (bool)xPortInIsrContext(); } +static void IRAM_ATTR cause_sw_intr(void *arg) +{ + /* just convert void * to int, because the width is the same */ + uint32_t intr_no = (uint32_t)arg; + XTHAL_SET_INTSET((1< Date: Wed, 25 Oct 2017 12:02:41 +0800 Subject: [PATCH 11/44] component/bt: Fix bug of set MAX_L2CAP_CHANNELS error --- components/bt/bluedroid/include/bt_target.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/bt/bluedroid/include/bt_target.h b/components/bt/bluedroid/include/bt_target.h index b5883416d..fe39f8b9b 100644 --- a/components/bt/bluedroid/include/bt_target.h +++ b/components/bt/bluedroid/include/bt_target.h @@ -681,11 +681,15 @@ /* The maximum number of simultaneous channels that L2CAP can support. Up to 16*/ #ifndef MAX_L2CAP_CHANNELS #if (CLASSIC_BT_INCLUDED == TRUE) -#define MAX_L2CAP_CHANNELS 8 +#define MAX_L2CAP_CHANNELS 16 #else +#if (SMP_INCLUDED == FALSE) #define MAX_L2CAP_CHANNELS MAX_ACL_CONNECTIONS //This is used in the BLE client when start connected with the peer device +#else +#define MAX_L2CAP_CHANNELS (MAX_ACL_CONNECTIONS * 2) //This is used in the BLE client when start connected with the peer device and in SMP +#endif ///SMP_INCLUDED == FALSE #endif ///CLASSIC_BT_INCLUDED == TRUE -#endif +#endif ///MAX_L2CAP_CHANNELS /* The maximum number of simultaneous applications that can register with L2CAP. */ #ifndef MAX_L2CAP_CLIENTS From 05a0fbd49b227fad956c5eec90fac5ef397143b1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 18:33:13 +0800 Subject: [PATCH 12/44] soc/rtc: add a function to wait for slow clock cycle Some RTC features are synchronized to RTC_SLOW_CLK, so sometimes software needs to wait for the next slow clock cycle. This function implements waiting using Timer Group clock calibration feature. --- components/soc/esp32/include/soc/rtc.h | 9 +++++++++ components/soc/esp32/rtc_time.c | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index f13c113b4..e2fa4791f 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -400,6 +400,15 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period); */ uint64_t rtc_time_get(); +/** + * @brief Busy loop until next RTC_SLOW_CLK cycle + * + * This function returns not earlier than the next RTC_SLOW_CLK clock cycle. + * In some cases (e.g. when RTC_SLOW_CLK cycle is very close), it may return + * one RTC_SLOW_CLK cycle later. + */ +void rtc_clk_wait_for_slow_cycle(); + /** * @brief sleep configuration for rtc_sleep_init function */ diff --git a/components/soc/esp32/rtc_time.c b/components/soc/esp32/rtc_time.c index 07a9337a3..6f354f884 100644 --- a/components/soc/esp32/rtc_time.c +++ b/components/soc/esp32/rtc_time.c @@ -135,3 +135,20 @@ uint64_t rtc_time_get() t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32; return t; } + +void rtc_clk_wait_for_slow_cycle() +{ + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START); + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY); + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX); + /* Request to run calibration for 0 slow clock cycles. + * RDY bit will be set on the nearest slow clock cycle. + */ + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0); + REG_SET_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + ets_delay_us(1); /* RDY needs some time to go low */ + while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) { + ets_delay_us(1); + } +} + From 6d4ed4ff6c4ca6e3b3bd4af3c7aeabe33570b540 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 18:46:27 +0800 Subject: [PATCH 13/44] soc/rtc: wait for SLOW_CLK cycle when switching CPU clock Previous implementation waited for 20us after setting RTC_CNTL_SOC_CLK_SEL_XTL register, using ets_delay_us, assuming that the CPU was running at XTAL frequency. In reality, clock switch happened on the next RTC_SLOW_CLK cycle, and CPU could be running at the previous frequency (for example, 240 MHz) until then. ets_delay_us would wait for 20 us * 40 cycles per us = 800 CPU cycles (assuming 40 MHz XTAL; even less with a 26 MHz XTAL). But if CPU was running at 240 MHz, 800 cycles would pass in just 3.3us, while SLOW_CLK cycle could happen as much as 1/150kHz = 6.7us after RTC_CNTL_SOC_CLK_SEL_XTL was set. So the software would not actually wait long enough for the clock switch to happen, and would disable the PLL while CPU was still clocked from PLL, leading to a halt. This implementation uses rtc_clk_wait_for_slow_cycle() function to wait until the clock switch, removing the need to wait for a fixed number of CPU cycles. --- components/soc/esp32/rtc_clk.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index 6e2b0909d..049b47306 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -72,9 +72,6 @@ static const char* TAG = "rtc_clk"; * All values are in microseconds. * TODO: some of these are excessive, and should be reduced. */ -#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 20 -#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160 -#define DELAY_CPU_FREQ_SWITCH_TO_PLL 20 #define DELAY_PLL_DBIAS_RAISE 3 #define DELAY_PLL_ENABLE_WITH_150K 80 #define DELAY_PLL_ENABLE_WITH_32K 160 @@ -397,9 +394,12 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); ets_update_cpu_frequency(xtal_freq); - uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? - DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K; - ets_delay_us(delay_xtal_switch); + + /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch + * is complete before disabling the PLL. + */ + rtc_clk_wait_for_slow_cycle(); + DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | @@ -443,7 +443,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) s_pll_freq = 480; } REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); - ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL); + rtc_clk_wait_for_slow_cycle(); rtc_clk_apb_freq_update(80 * MHZ); } s_cur_freq = cpu_freq; From 9317cb343482b22202d27317a4164d3c151c3fc8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 18:47:31 +0800 Subject: [PATCH 14/44] soc/rtc: add tests for CPU frequency switching These tests switch between PLL and XTAL frequencies for 10 seconds. --- components/soc/esp32/test/test_rtc_clk.c | 36 +++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 48905acd0..00fb0040f 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -1,15 +1,17 @@ #include #include "unity.h" #include "rom/ets_sys.h" +#include "rom/uart.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/rtc_io_reg.h" #include "soc/sens_reg.h" #include "soc/io_mux_reg.h" #include "driver/rtc_io.h" - +#include "test_utils.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk) @@ -89,3 +91,35 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]") SET_PERI_REG_MASK(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_12M_NO_GATING); pull_out_clk(RTC_IO_DEBUG_SEL0_8M); } + +static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) +{ + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + + const int test_duration_sec = 10; + ref_clock_init(); + uint64_t t_start = ref_clock_get(); + + rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); + int count = 0; + while (ref_clock_get() - t_start < test_duration_sec * 1000000) { + switch_func(RTC_CPU_FREQ_XTAL); + switch_func(cur_freq); + ++count; + } + uint64_t t_end = ref_clock_get(); + printf("Switch count: %d. Average time to switch PLL -> XTAL -> PLL: %d us\n", count, (int) ((t_end - t_start) / count)); + ref_clock_deinit(); +} + +TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") +{ + test_clock_switching(rtc_clk_cpu_freq_set); +} + +TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]") +{ + test_clock_switching(rtc_clk_cpu_freq_set_fast); +} + + From f11ad0c904018cd4fa0527a2a6600e67c12be60b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 18:52:00 +0800 Subject: [PATCH 15/44] soc/rtc: fix spurious warnings about XTAL frequency on startup 1. Make sure that 8MD256 clock used to estimate XTAL frequency is enabled before trying to use rtc_clk_cal_ratio. This fixes "Bogus XTAL frequency: 0 MHz" warnings after software reset. 2. Don't call rtc_clk_xtal_freq_estimate if XTAL frequency is already known. This reduces startup time after deep sleep or software reset. 3. Compare known XTAL frequency and estimated one before printing a warning. This fixes "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (40MHz). Detected 40 MHz." warnings. --- components/soc/esp32/rtc_clk.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index 049b47306..612476cf8 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -558,6 +558,13 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq) static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() { + /* Enable 8M/256 clock if needed */ + const bool clk_8m_enabled = rtc_clk_8m_enabled(); + const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); + if (!clk_8md256_enabled) { + rtc_clk_8m_enable(true, true); + } + uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); /* cal_val contains period of 8M/256 clock in XTAL clock cycles * (shifted by RTC_CLK_CAL_FRACT bits). @@ -581,6 +588,8 @@ static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); return RTC_XTAL_FREQ_AUTO; } + /* Restore 8M and 8md256 clocks to original state */ + rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); } void rtc_clk_apb_freq_update(uint32_t apb_freq) @@ -634,7 +643,6 @@ void rtc_clk_init(rtc_clk_config_t cfg) CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); /* Estimate XTAL frequency */ - rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; if (xtal_freq == RTC_XTAL_FREQ_AUTO) { if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { @@ -642,7 +650,7 @@ void rtc_clk_init(rtc_clk_config_t cfg) xtal_freq = rtc_clk_xtal_freq_get(); } else { /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ - xtal_freq = est_xtal_freq; + xtal_freq = rtc_clk_xtal_freq_estimate(); if (xtal_freq == RTC_XTAL_FREQ_AUTO) { SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); xtal_freq = RTC_XTAL_FREQ_26M; @@ -653,8 +661,11 @@ void rtc_clk_init(rtc_clk_config_t cfg) * frequency is different. If autodetection failed, worst case we get a * bit of garbage output. */ - SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", - xtal_freq, est_xtal_freq); + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); + if (est_xtal_freq != xtal_freq) { + SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", + xtal_freq, est_xtal_freq); + } } uart_tx_wait_idle(0); rtc_clk_xtal_freq_update(xtal_freq); From eb5752c635b1d42a322093dc3717c38b4581d664 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 19:11:47 +0800 Subject: [PATCH 16/44] esp_restart: fix possible race while stalling other CPU, enable WDT early Previously esp_restart would stall the other CPU before enabling RTC_WDT. If the other CPU was executing an s32c1i instruction, the lock signal from CPU to the arbiter would still be held after CPU was stalled. If the CPU running esp_restart would then try to access the same locked memory pool, it would be stuck, because lock signal would never be released. With this change, esp_restart resets the other CPU before stalling it. Ideally, we would want to reset the CPU and keep it in reset, but the hardware doesn't have such feature for PRO_CPU (it is possible to hold APP_CPU in reset using DPORT register). Given that ROM code will not use s32c1i in the first few hundred cycles, doing reset and then stall seems to be safe. In addition to than, RTC_WDT initialization is moved to the beginning of the function, to prevent possible lock-up if CPU stalling still has any issue. --- components/esp32/system_api.c | 42 ++++++++++++++------------ components/soc/esp32/cpu_util.c | 6 ++++ components/soc/esp32/include/soc/cpu.h | 7 +++++ 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 37958db40..851d4a450 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -266,15 +266,10 @@ void IRAM_ATTR esp_restart(void) */ void IRAM_ATTR esp_restart_noos() { - const uint32_t core_id = xPortGetCoreID(); - const uint32_t other_core_id = core_id == 0 ? 1 : 0; - esp_cpu_stall(other_core_id); + // Disable interrupts + xt_ints_off(0xFFFFFFFF); - // other core is now stalled, can access DPORT registers directly - esp_dport_access_int_pause(); - - // We need to disable TG0/TG1 watchdogs - // First enable RTC watchdog for 1 second + // Enable RTC watchdog for 1 second REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | @@ -284,6 +279,18 @@ void IRAM_ATTR esp_restart_noos() (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1); + // Reset and stall the other CPU. + // CPU must be reset before stalling, in case it was running a s32c1i + // instruction. This would cause memory pool to be locked by arbiter + // to the stalled CPU, preventing current CPU from accessing this pool. + const uint32_t core_id = xPortGetCoreID(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); + + // Other core is now stalled, can access DPORT registers directly + esp_dport_access_int_abort(); + // Disable TG0/TG1 watchdogs TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; @@ -292,8 +299,10 @@ void IRAM_ATTR esp_restart_noos() TIMERG1.wdt_config0.en = 0; TIMERG1.wdt_wprotect=0; - // Disable all interrupts - xt_ints_off(0xFFFFFFFF); + // Flush any data left in UART FIFOs + uart_tx_wait_idle(0); + uart_tx_wait_idle(1); + uart_tx_wait_idle(2); // Disable cache Cache_Read_Disable(0); @@ -310,11 +319,6 @@ void IRAM_ATTR esp_restart_noos() WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30); #endif - // Flush any data left in UART FIFOs - uart_tx_wait_idle(0); - uart_tx_wait_idle(1); - uart_tx_wait_idle(2); - // Reset wifi/bluetooth/ethernet/sdio (bb/mac) DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | @@ -337,14 +341,14 @@ void IRAM_ATTR esp_restart_noos() // Reset CPUs if (core_id == 0) { // Running on PRO CPU: APP CPU is stalled. Can reset both CPUs. - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); + esp_cpu_reset(0); } else { // Running on APP CPU: need to reset PRO CPU and unstall it, // then reset APP CPU - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M); + esp_cpu_reset(0); esp_cpu_unstall(0); - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); } while(true) { ; diff --git a/components/soc/esp32/cpu_util.c b/components/soc/esp32/cpu_util.c index ecfcab4ba..bc052af98 100644 --- a/components/soc/esp32/cpu_util.c +++ b/components/soc/esp32/cpu_util.c @@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) } } +void IRAM_ATTR esp_cpu_reset(int cpu_id) +{ + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M); +} + bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() { #if CONFIG_ESP32_DEBUG_OCDAWARE diff --git a/components/soc/esp32/include/soc/cpu.h b/components/soc/esp32/include/soc/cpu.h index b56fb3dc8..05ec91776 100644 --- a/components/soc/esp32/include/soc/cpu.h +++ b/components/soc/esp32/include/soc/cpu.h @@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Reset CPU using RTC controller + * @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP) + */ +void esp_cpu_reset(int cpu_id); + + /** * @brief Returns true if a JTAG debugger is attached to CPU * OCD (on chip debug) port. From 15c75974de75135c7d9c5be983043fff4db3652e Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Fri, 27 Oct 2017 14:07:47 +0800 Subject: [PATCH 17/44] component/bt: fix crash when set device name NULL --- components/bt/bluedroid/api/esp_bt_device.c | 4 +++- examples/bluetooth/gatt_server/main/gatts_demo.c | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/bt/bluedroid/api/esp_bt_device.c b/components/bt/bluedroid/api/esp_bt_device.c index b4885cc0e..c50ada980 100644 --- a/components/bt/bluedroid/api/esp_bt_device.c +++ b/components/bt/bluedroid/api/esp_bt_device.c @@ -36,7 +36,9 @@ esp_err_t esp_bt_dev_set_device_name(const char *name) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + if (!name){ + return ESP_ERR_INVALID_ARG; + } if (strlen(name) > ESP_DEV_DEVICE_NAME_MAX) { return ESP_ERR_INVALID_ARG; } diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index 8c30fd326..a60d567c8 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -313,7 +313,10 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A; - esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + if (set_dev_name_ret){ + ESP_LOGE(GATTS_TAG, "set device name failed, error code = %x", set_dev_name_ret); + } #ifdef CONFIG_SET_RAW_ADV_DATA esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data)); if (raw_adv_ret){ From 102eb96c8bf830295782983fcbce0415cf4b8dcd Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 26 Oct 2017 17:18:08 +0800 Subject: [PATCH 18/44] bugfix(uart): Don't disable console UART peripheral --- components/driver/uart.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 9ea92158c..4a2a1640e 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -1140,12 +1140,14 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) free(p_uart_obj[uart_num]); p_uart_obj[uart_num] = NULL; - if(uart_num == UART_NUM_0) { - periph_module_disable(PERIPH_UART0_MODULE); - } else if(uart_num == UART_NUM_1) { - periph_module_disable(PERIPH_UART1_MODULE); - } else if(uart_num == UART_NUM_2) { - periph_module_disable(PERIPH_UART2_MODULE); + if (uart_num != CONFIG_CONSOLE_UART_NUM ) { + if(uart_num == UART_NUM_0) { + periph_module_disable(PERIPH_UART0_MODULE); + } else if(uart_num == UART_NUM_1) { + periph_module_disable(PERIPH_UART1_MODULE); + } else if(uart_num == UART_NUM_2) { + periph_module_disable(PERIPH_UART2_MODULE); + } } return ESP_OK; } From 1195ced75c234a7faf59d1e9dff4eb6a48005644 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Sat, 28 Oct 2017 12:15:40 +0800 Subject: [PATCH 19/44] esp32: reduce default wifi static tx buffer Modify the the default WiFi static tx buffer from 32 to 16 --- components/esp32/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 1b219cf2e..e5b65fba9 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -842,7 +842,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER_NUM int "Max number of WiFi static TX buffers" depends on ESP32_WIFI_STATIC_TX_BUFFER range 6 64 - default 32 + default 16 help Set the number of WiFi static TX buffers. Each buffer takes approximately 1.6KB of RAM. The static RX buffers are allocated when esp_wifi_init() is called, they are not released @@ -1009,4 +1009,4 @@ config PM_TRACE applications. -endmenu # "Power Management" \ No newline at end of file +endmenu # "Power Management" From b908b3cd58b3f4ec206984e59c8b05f431cabce5 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 30 Oct 2017 19:42:16 +0800 Subject: [PATCH 20/44] unit_tests/Update unit test timer divider This commit updates various test cases throughout esp-idf such that the values used for timer divider pass the assertions in the timer component. Timer divider values must be between 2 to 65536 --- components/app_trace/test/test_trace.c | 6 +++--- components/freertos/test/test_suspend_scheduler.c | 2 +- components/freertos/test/test_task_suspend_resume.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/app_trace/test/test_trace.c b/components/app_trace/test/test_trace.c index 6846ea50a..6d95a87c5 100644 --- a/components/app_trace/test/test_trace.c +++ b/components/app_trace/test/test_trace.c @@ -67,7 +67,7 @@ static void esp_apptrace_test_timer_init(int timer_group, int timer_idx, uint32_ config.alarm_en = 1; config.auto_reload = 1; config.counter_dir = TIMER_COUNT_UP; - config.divider = 1; + config.divider = 2; //Range is 2 to 65536 config.intr_type = TIMER_INTR_LEVEL; config.counter_en = TIMER_PAUSE; /*Configure timer*/ @@ -403,7 +403,7 @@ static void esp_apptrace_test_ts_init(int timer_group, int timer_idx) config.alarm_en = 0; config.auto_reload = 0; config.counter_dir = TIMER_COUNT_UP; - config.divider = 1; + config.divider = 2; //Range is 2 to 65536 config.counter_en = 0; /*Configure timer*/ timer_init(timer_group, timer_idx, &config); @@ -420,7 +420,7 @@ static void esp_apptrace_test_ts_cleanup() config.alarm_en = 0; config.auto_reload = 0; config.counter_dir = TIMER_COUNT_UP; - config.divider = 1; + config.divider = 2; //Range is 2 to 65536 config.counter_en = 0; /*Configure timer*/ timer_init(s_ts_timer_group, s_ts_timer_idx, &config); diff --git a/components/freertos/test/test_suspend_scheduler.c b/components/freertos/test/test_suspend_scheduler.c index c613dc647..3d8932d86 100644 --- a/components/freertos/test/test_suspend_scheduler.c +++ b/components/freertos/test/test_suspend_scheduler.c @@ -69,7 +69,7 @@ TEST_CASE("Scheduler disabled can handle a pending context switch on resume", "[ .alarm_en = 1, .auto_reload = 1, .counter_dir = TIMER_COUNT_UP, - .divider = 1, + .divider = 2, //Range is 2 to 65536 .intr_type = TIMER_INTR_LEVEL, .counter_en = TIMER_PAUSE, }; diff --git a/components/freertos/test/test_task_suspend_resume.c b/components/freertos/test/test_task_suspend_resume.c index 5908d2135..d66e4af52 100644 --- a/components/freertos/test/test_task_suspend_resume.c +++ b/components/freertos/test/test_task_suspend_resume.c @@ -140,7 +140,7 @@ static void test_resume_task_from_isr(int target_core) .alarm_en = 1, .auto_reload = 0, .counter_dir = TIMER_COUNT_UP, - .divider = 1, + .divider = 2, //Range is 2 to 65536 .intr_type = TIMER_INTR_LEVEL, .counter_en = TIMER_PAUSE, }; From ccfbecd25f7defac8f37cdcbc0f68a3e0c8143c0 Mon Sep 17 00:00:00 2001 From: Deng Xin Date: Mon, 30 Oct 2017 21:29:32 +0800 Subject: [PATCH 21/44] Wifi: bugfix of get wrong ap information in all channel scan fix the issue get wrong ap information in all channel scan --- components/esp32/include/esp_wifi.h | 5 ++++- components/esp32/lib | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 0ae0644ea..e5ba38d7e 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -91,6 +91,8 @@ extern "C" { #define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 11) /*!< Password is invalid */ #define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 12) /*!< Timeout error */ #define ESP_ERR_WIFI_WAKE_FAIL (ESP_ERR_WIFI_BASE + 13) /*!< WiFi is in sleep state(RF closed) and wakeup fail */ +#define ESP_ERR_WIFI_WOULD_BLOCK (ESP_ERR_WIFI_BASE + 14) /*!< The caller would block */ +#define ESP_ERR_WIFI_NOT_CONNECT (ESP_ERR_WIFI_BASE + 15) /*!< Station still in disconnect status */ /** * @brief WiFi stack configuration parameters passed to esp_wifi_init call. @@ -392,7 +394,8 @@ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_re * * @return * - ESP_OK: succeed - * - others: fail + * - ESP_ERR_WIFI_CONN: The station interface don't initialized + * - ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status */ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); diff --git a/components/esp32/lib b/components/esp32/lib index a0d77be61..b0ee6e697 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a0d77be618bc933415eb0ffd7c2a0610ef3336d6 +Subproject commit b0ee6e697658a0f3756e44e375fbcd646604c6d5 From d63795255565c1fff1420ebc20eeb577b75909a2 Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 30 Oct 2017 06:27:40 +0100 Subject: [PATCH 22/44] Small updates of documentation --- .../jtag-debugging/configure-wrover.rst | 2 +- docs/api-reference/peripherals/uart.rst | 4 +- docs/get-started/get-started-devkitc.rst | 18 +- docs/get-started/get-started-pico-kit.rst | 18 +- docs/get-started/get-started-wrover-kit.rst | 203 ++++++++++++------ docs/hw-reference/index.rst | 1 + docs/hw-reference/modules-and-boards.rst | 2 +- 7 files changed, 173 insertions(+), 75 deletions(-) diff --git a/docs/api-guides/jtag-debugging/configure-wrover.rst b/docs/api-guides/jtag-debugging/configure-wrover.rst index 1857e7f59..6b1d40cf5 100644 --- a/docs/api-guides/jtag-debugging/configure-wrover.rst +++ b/docs/api-guides/jtag-debugging/configure-wrover.rst @@ -7,7 +7,7 @@ All versions of ESP32 WROVER KIT boards have JTAG functionality build in. Puttin Configure Hardware ^^^^^^^^^^^^^^^^^^ -1. Enable on-board JTAG functionality by setting JP8 according to :doc:`../../get-started/get-started-wrover-kit`, section :ref:`esp-wrover-kit-setup-options`. +1. Enable on-board JTAG functionality by setting JP8 according to :doc:`../../get-started/get-started-wrover-kit`, section :ref:`get-started-esp-wrover-kit-setup-options`. 2. Verify if ESP32 pins used for JTAG communication are not connected to some other h/w that may disturb JTAG operation: diff --git a/docs/api-reference/peripherals/uart.rst b/docs/api-reference/peripherals/uart.rst index 4f5cdc786..da75b0843 100644 --- a/docs/api-reference/peripherals/uart.rst +++ b/docs/api-reference/peripherals/uart.rst @@ -138,7 +138,9 @@ Application Examples Configure UART settings and install UART driver to read/write using UART1 interface: :example:`peripherals/uart_echo`. -Demonstration how report report various communication events and how to use patern detection interrupts: :example:`peripherals/uart_events`. +Demonstration of how to report various communication events and how to use patern detection interrupts: :example:`peripherals/uart_events`. + +Transmitting and receiveing with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart_async_rxtxtasks`. API Reference diff --git a/docs/get-started/get-started-devkitc.rst b/docs/get-started/get-started-devkitc.rst index 94c79c2ea..b5f14ab10 100644 --- a/docs/get-started/get-started-devkitc.rst +++ b/docs/get-started/get-started-devkitc.rst @@ -7,7 +7,7 @@ This user guide shows how to get started with ESP32-DevKitC development board. What You Need ------------- -* 1 × ESP32-DevKitC board +* 1 × :ref:`ESP32-DevKitC board ` * 1 × USB A / micro USB B cable * 1 × PC loaded with Windows, Linux or Mac OS @@ -34,6 +34,8 @@ USB I/O Most of the pins on the ESP-WROOM-32 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM,ADC, DAC, I2C, I2S, SPI, etc. +.. _get-started-esp32-devkitc-board-front: + .. figure:: ../_static/esp32-devkitc-functional-overview.png :align: center :alt: ESP32-DevKitC board layout @@ -42,6 +44,20 @@ I/O ESP32-DevKitC board layout +Power Supply Options +-------------------- + +There following options are available to provide power supply to the ESP32-PICO-KIT V4: + +1. Micro USB port, this is default power supply connection +2. 5V / GND header pins +3. 3V3 / GND header pins + +.. warning:: + + Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source. + + Start Application Development ------------------------------ diff --git a/docs/get-started/get-started-pico-kit.rst b/docs/get-started/get-started-pico-kit.rst index 1ae2bd2fb..44134e72b 100644 --- a/docs/get-started/get-started-pico-kit.rst +++ b/docs/get-started/get-started-pico-kit.rst @@ -7,7 +7,7 @@ This user guide shows how to get started with the ESP32-PICO-KIT V4 mini develop What You Need ------------- -* 1 × ESP32-PICO-KIT V4 mini development board +* 1 × :ref:`ESP32-PICO-KIT V4 mini development board ` * 1 × USB A / Micro USB B cable * 1 × PC loaded with Windows, Linux or Mac OS @@ -40,6 +40,8 @@ EN Button BOOT Button Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port. +.. _get-started-pico-kit-v4-board-front: + .. figure:: ../_static/esp32-pico-kit-v4-layout.jpg :align: center :alt: ESP32-PICO-KIT V4 board layout @@ -48,6 +50,20 @@ BOOT Button ESP32-PICO-KIT V4 board layout +Power Supply Options +-------------------- + +There following options are available to provide power supply to the ESP32-PICO-KIT V4: + +1. Micro USB port, this is default power supply connection +2. 5V / GND header pins +3. 3V3 / GND header pins + +.. warning:: + + Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source. + + Start Application Development ------------------------------ diff --git a/docs/get-started/get-started-wrover-kit.rst b/docs/get-started/get-started-wrover-kit.rst index 89effa95d..25948fc27 100644 --- a/docs/get-started/get-started-wrover-kit.rst +++ b/docs/get-started/get-started-wrover-kit.rst @@ -3,13 +3,13 @@ ESP-WROVER-KIT V3 Getting Started Guide This user guide shows how to get started with ESP-WROVER-KIT V3 development board including description of its functionality and configuration options. For description of other versions of the ESP-WROVER-KIT check :doc:`../hw-reference/index`. -If you like to start using this board right now, go directly to section :ref:`esp-wrover-kit-start-development`. +If you like to start using this board right now, go directly to section :ref:`get-started-esp-wrover-kit-start-development`. What You Need ------------- -* 1 × ESP-WROVER-KIT V3 board +* 1 × :ref:`ESP-WROVER-KIT V3 board ` * 1 x Micro USB 2.0 Cable, Type A to Micro B * 1 × PC loaded with Windows, Linux or Mac OS @@ -70,7 +70,7 @@ Boot USB USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32 module. Power Select - Power supply selection interface: the ESP-WROVER-KIT can be powered through the USB interface or the 5V Input interface. The user can select the power supply with a jumper. More details can be found in section :ref:`esp-wrover-kit-setup-options`, jumper header JP7. + Power supply selection interface: the ESP-WROVER-KIT can be powered through the USB interface or the 5V Input interface. The user can select the power supply with a jumper. More details can be found in section :ref:`get-started-esp-wrover-kit-setup-options`, jumper header JP7. Power Key Power on/off button: toggling to the right powers the board on; toggling to the left powers the board off. 5V Input @@ -82,11 +82,13 @@ Camera RGB Red, green and blue (RGB) light emitting diodes (LEDs), which may be controlled by pulse width modulation (PWM). I/O - All the pins on the ESP32 module are led out to the pin headers on the ESPWROVER-KIT. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc. + All the pins on the ESP32 module are led out to the pin headers on the ESP-WROVER-KIT. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc. Micro SD Card Micro SD card slot for data storage. LCD - ESP-WROVER-KIT supports mounting and interfacing a 3.2” SPI (standard 4-wire Serial Peripheral Interface) LCD, as shown on figure :ref:`esp-wrover-kit-board-back`. + ESP-WROVER-KIT supports mounting and interfacing a 3.2” SPI (standard 4-wire Serial Peripheral Interface) LCD, as shown on figure :ref:`get-started-esp-wrover-kit-board-back`. + +.. _get-started-esp-wrover-kit-board-front: .. figure:: ../_static/esp32-wrover-kit-layout-front.jpg :align: center @@ -95,7 +97,7 @@ LCD ESP-WROVER-KIT board layout - front -.. _esp-wrover-kit-board-back: +.. _get-started-esp-wrover-kit-board-back: .. figure:: ../_static/esp32-wrover-kit-layout-back.jpg :align: center @@ -105,7 +107,7 @@ LCD ESP-WROVER-KIT board layout - back -.. _esp-wrover-kit-setup-options: +.. _get-started-esp-wrover-kit-setup-options: Setup Options ^^^^^^^^^^^^^ @@ -116,7 +118,7 @@ There are five jumper headers available to set up the board functionality. Typic | Header | Jumper Setting | Description of Functionality | +--------+------------------+--------------------------------------------------+ | JP7 | |jp7-ext_5v| | Power ESP-WROVER-KIT board from an external | -| | | power supply | +| | | | power supply | +--------+------------------+--------------------------------------------------+ | JP7 | |jp7-usb_5v| | Power ESP-WROVER-KIT board from an USB port | +--------+------------------+--------------------------------------------------+ @@ -135,60 +137,116 @@ Allocation of ESP32 Pins Several pins / terminals of ESP32 module are allocated to the on board hardware. Some of them, like GPIO0 or GPIO2, have multiple functions. If certain hardware is not installed, e.g. nothing is plugged in to the Camera / JP4 header, then selected GPIOs may be used for other purposes. +Main I/O Connector / JP1 +"""""""""""""""""""""""" + +The JP1 connector is shown in two columns in the middle under "I/O" headers. The two columns "Shared With" outside, describe where else on the board certain GPIO is used. + ++----------------------+------+------+----------------------+ +| Shared With | I/O | I/O | Shared With | ++======================+======+======+======================+ +| | 3.3V | GND | | ++----------------------+------+------+----------------------+ +| NC/XTAL | IO32 | IO33 | NC/XTAL | ++----------------------+------+------+----------------------+ +| JTAG, MicroSD | IO12 | IO13 | JTAG, MicroSD | ++----------------------+------+------+----------------------+ +| JTAG, MicroSD | IO14 | IO27 | Camera | ++----------------------+------+------+----------------------+ +| Camera | IO26 | IO25 | Camera, LCD | ++----------------------+------+------+----------------------+ +| Camera | IO35 | IO34 | Camera | ++----------------------+------+------+----------------------+ +| Camera | IO39 | IO36 | Camera | ++----------------------+------+------+----------------------+ +| JTAG | EN | IO23 | Camera, LCD | ++----------------------+------+------+----------------------+ +| Camera, LCD | IO22 | IO21 | Camera, LCD, MicroSD | ++----------------------+------+------+----------------------+ +| Camera, LCD | IO19 | IO18 | Camera, LCD | ++----------------------+------+------+----------------------+ +| Camera, LCD | IO5 | IO17 | PSRAM | ++----------------------+------+------+----------------------+ +| PSRAM | IO16 | IO4 | LED, Camera, MicroSD | ++----------------------+------+------+----------------------+ +| LED, Boot | IO0 | IO2 | LED, Camera, MicroSD | ++----------------------+------+------+----------------------+ +| JTAG, MicroSD | IO15 | 5V | | ++----------------------+------+------+----------------------+ + +Legend: + +* NC/XTAL - :ref:`32.768 kHz Oscillator ` +* JTAG - :ref:`JTAG / JP8 ` +* Boot - Boot button / SW2 +* Camera - :ref:`Camera / JP4 ` +* LED - :ref:`RGB LED ` +* MicroSD - :ref:`MicroSD Card / J4 ` +* LCD - :ref:`LCD / U5 ` +* PSRAM - ESP32-WROVER's PSRAM, if ESP32-WROVER is installed + + +.. _get-started-esp-wrover-kit-xtal: + 32.768 kHz Oscillator """"""""""""""""""""" -+---+---------------+ -| | ESP32 Pin | -+===+===============+ -| 1 | GPIO32 | -+---+---------------+ -| 2 | GPIO33 | -+---+---------------+ ++---+-----------+ +| | ESP32 Pin | ++===+===========+ +| 1 | GPIO32 | ++---+-----------+ +| 2 | GPIO33 | ++---+-----------+ .. note:: As GPIO32 and GPIO33 are connected to the oscillator, to maintain signal integrity, they are not connected to JP1 I/O expansion connector. This allocation may be changed from oscillator to JP1 by desoldering 0R resistors from positions R11 / R23 and installing them in positions R12 / R24. +.. _get-started-esp-wrover-spi-flash-header: + SPI Flash / JP13 """""""""""""""" -+---+---------------+ -| | ESP32 Pin | -+===+===============+ -| 1 | CLK / GPIO6 | -+---+---------------+ -| 2 | SD0 / GPIO7 | -+---+---------------+ -| 3 | SD1 / GPIO8 | -+---+---------------+ -| 4 | SD2 / GPIO9 | -+---+---------------+ -| 5 | SD3 / GPIO10 | -+---+---------------+ -| 6 | CMD / GPIO11 | -+---+---------------+ ++---+--------------+ +| | ESP32 Pin | ++===+==============+ +| 1 | CLK / GPIO6 | ++---+--------------+ +| 2 | SD0 / GPIO7 | ++---+--------------+ +| 3 | SD1 / GPIO8 | ++---+--------------+ +| 4 | SD2 / GPIO9 | ++---+--------------+ +| 5 | SD3 / GPIO10 | ++---+--------------+ +| 6 | CMD / GPIO11 | ++---+--------------+ +.. _get-started-esp-wrover-jtag-header: JTAG / JP8 """""""""" -+---+---------------+----------------+ -| | ESP32 Pin | JTAG Signal | -+===+===============+================+ -| 1 | CHIP_PU | TRST_N | -+---+---------------+----------------+ -| 2 | MTDO / GPIO15 | TDO | -+---+---------------+----------------+ -| 3 | MTDI / GPIO12 | TDI | -+---+---------------+----------------+ -| 4 | MTCK / GPIO13 | TCK | -+---+---------------+----------------+ -| 5 | MTMS / GPIO14 | TMS | -+---+---------------+----------------+ ++---+---------------+-------------+ +| | ESP32 Pin | JTAG Signal | ++===+===============+=============+ +| 1 | EN | TRST_N | ++---+---------------+-------------+ +| 2 | MTDO / GPIO15 | TDO | ++---+---------------+-------------+ +| 3 | MTDI / GPIO12 | TDI | ++---+---------------+-------------+ +| 4 | MTCK / GPIO13 | TCK | ++---+---------------+-------------+ +| 5 | MTMS / GPIO14 | TMS | ++---+---------------+-------------+ +.. _get-started-esp-wrover-camera-header: + Camera / JP4 """""""""""" @@ -227,20 +285,24 @@ Camera / JP4 +----+--------------+----------------------+ +.. _get-started-esp-wrover-rgb-led-connections: + RGB LED """"""" -+---+---------------+----------------+ -| | ESP32 Pin | RGB LED | -+===+===============+================+ -| 1 | GPIO0 | Red | -+---+---------------+----------------+ -| 2 | GPIO2 | Blue | -+---+---------------+----------------+ -| 3 | GPIO4 | Green | -+---+---------------+----------------+ ++---+-----------+---------+ +| | ESP32 Pin | RGB LED | ++===+===========+=========+ +| 1 | GPIO0 | Red | ++---+-----------+---------+ +| 2 | GPIO2 | Blue | ++---+-----------+---------+ +| 3 | GPIO4 | Green | ++---+-----------+---------+ +.. _get-started-esp-wrover-microsd-card-slot: + MicroSD Card / J4 """"""""""""""""" @@ -263,30 +325,31 @@ MicroSD Card / J4 +---+---------------+----------------+ +.. _get-started-esp-wrover-lcd-connector: LCD / U5 """""""" -+---+---------------+----------------+ -| | ESP32 Pin | LCD Signal | -+===+===============+================+ -| 1 | GPIO18 | RESET | -+---+---------------+----------------+ -| 2 | GPIO19 | SCL | -+---+---------------+----------------+ -| 3 | GPIO21 | D/C | -+---+---------------+----------------+ -| 4 | GPIO22 | CS | -+---+---------------+----------------+ -| 5 | GPIO23 | SDA | -+---+---------------+----------------+ -| 6 | GPIO25 | SDO | -+---+---------------+----------------+ -| 7 | GPIO5 | Backlight | -+---+---------------+----------------+ ++---+-----------+------------+ +| | ESP32 Pin | LCD Signal | ++===+===========+============+ +| 1 | GPIO18 | RESET | ++---+-----------+------------+ +| 2 | GPIO19 | SCL | ++---+-----------+------------+ +| 3 | GPIO21 | D/C | ++---+-----------+------------+ +| 4 | GPIO22 | CS | ++---+-----------+------------+ +| 5 | GPIO23 | SDA | ++---+-----------+------------+ +| 6 | GPIO25 | SDO | ++---+-----------+------------+ +| 7 | GPIO5 | Backlight | ++---+-----------+------------+ -.. _esp-wrover-kit-start-development: +.. _get-started-esp-wrover-kit-start-development: Start Application Development ----------------------------- diff --git a/docs/hw-reference/index.rst b/docs/hw-reference/index.rst index dad6fefc3..4fcb91e24 100644 --- a/docs/hw-reference/index.rst +++ b/docs/hw-reference/index.rst @@ -11,3 +11,4 @@ ESP32 Hardware Reference Silicon Errata (PDF) Modules and Boards Previous Versions of Modules and Boards + Espressif Products Ordering Information (PDF) diff --git a/docs/hw-reference/modules-and-boards.rst b/docs/hw-reference/modules-and-boards.rst index 4143e6865..2bd85e89a 100644 --- a/docs/hw-reference/modules-and-boards.rst +++ b/docs/hw-reference/modules-and-boards.rst @@ -89,7 +89,7 @@ ESP32 Core Board V2 / ESP32 DevKitC Small and convenient development board with ESP-WROOM-32 module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has press buttons to reset the board and put it in upload mode. -.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.jpg +.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png :align: center :alt: ESP32 Core Board V2 / ESP32 DevKitC board :width: 50% From 87d3986b87281788338b4a47cc4fec38fe16df39 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Tue, 31 Oct 2017 11:52:05 +0800 Subject: [PATCH 23/44] Fix the return value of esp_now_send() --- components/esp32/include/esp_now.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/esp32/include/esp_now.h b/components/esp32/include/esp_now.h index 497d73776..4a0a4be7d 100644 --- a/components/esp32/include/esp_now.h +++ b/components/esp32/include/esp_now.h @@ -48,6 +48,7 @@ extern "C" { #define ESP_ERR_ESPNOW_NOT_FOUND (ESP_ERR_ESPNOW_BASE + 4) /*!< ESPNOW peer is not found */ #define ESP_ERR_ESPNOW_INTERNAL (ESP_ERR_ESPNOW_BASE + 5) /*!< Internal error */ #define ESP_ERR_ESPNOW_EXIST (ESP_ERR_ESPNOW_BASE + 6) /*!< ESPNOW peer has existed */ +#define ESP_ERR_ESPNOW_IF (ESP_ERR_ESPNOW_BASE + 7) /*!< Interface error */ #define ESP_NOW_ETH_ALEN 6 /*!< Length of ESPNOW peer MAC address */ #define ESP_NOW_KEY_LEN 16 /*!< Length of ESPNOW peer local master key */ @@ -191,6 +192,7 @@ esp_err_t esp_now_unregister_send_cb(void); * - ESP_ERR_ESPNOW_INTERNAL : internal error * - ESP_ERR_ESPNOW_NO_MEM : out of memory * - ESP_ERR_ESPNOW_NOT_FOUND : peer is not found + * - ESP_ERR_ESPNOW_IF : current WiFi interface doesn't match that of peer */ esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len); From 50b6912bf80c2958e1a2e0fc67738f1374f03db7 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Tue, 31 Oct 2017 14:45:25 +0800 Subject: [PATCH 24/44] Increase maximum number of WiFi dynamic transmitting buffer --- components/esp32/Kconfig | 2 +- components/esp32/lib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index e5b65fba9..dd5ac7041 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -856,7 +856,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER_NUM config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM int "Max number of WiFi dynamic TX buffers" depends on ESP32_WIFI_DYNAMIC_TX_BUFFER - range 16 64 + range 16 128 default 32 help Set the number of WiFi dynamic TX buffers. The size of each dynamic TX buffer is not fixed, diff --git a/components/esp32/lib b/components/esp32/lib index b0ee6e697..4d59fe962 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit b0ee6e697658a0f3756e44e375fbcd646604c6d5 +Subproject commit 4d59fe9623f5a7cab7ef4b0b4cda1772d4795631 From 47a9a4a6146290ebacce303da1856ee40a835fda Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Tue, 10 Oct 2017 10:44:55 +0800 Subject: [PATCH 25/44] test: add test fw for example test --- tools/tiny-test-fw/App.py | 93 ++++ tools/tiny-test-fw/CIAssignExampleTest.py | 179 +++++++ tools/tiny-test-fw/DUT.py | 550 ++++++++++++++++++++++ tools/tiny-test-fw/Env.py | 156 ++++++ tools/tiny-test-fw/EnvConfig.py | 74 +++ tools/tiny-test-fw/EnvConfigTemplate.yml | 6 + tools/tiny-test-fw/IDF/IDFApp.py | 164 +++++++ tools/tiny-test-fw/IDF/IDFDUT.py | 126 +++++ tools/tiny-test-fw/IDF/__init__.py | 36 ++ tools/tiny-test-fw/Runner.py | 80 ++++ tools/tiny-test-fw/TestCase.py | 53 +++ tools/tiny-test-fw/TinyFW.py | 220 +++++++++ tools/tiny-test-fw/Utility/CaseConfig.py | 199 ++++++++ tools/tiny-test-fw/Utility/GitlabCIJob.py | 73 +++ tools/tiny-test-fw/Utility/SearchCases.py | 112 +++++ tools/tiny-test-fw/Utility/__init__.py | 0 tools/tiny-test-fw/docs/Makefile | 26 + tools/tiny-test-fw/docs/_static/.keep | 0 tools/tiny-test-fw/docs/conf.py | 159 +++++++ tools/tiny-test-fw/docs/index.rst | 139 ++++++ tools/tiny-test-fw/example.py | 51 ++ 21 files changed, 2496 insertions(+) create mode 100644 tools/tiny-test-fw/App.py create mode 100644 tools/tiny-test-fw/CIAssignExampleTest.py create mode 100644 tools/tiny-test-fw/DUT.py create mode 100644 tools/tiny-test-fw/Env.py create mode 100644 tools/tiny-test-fw/EnvConfig.py create mode 100644 tools/tiny-test-fw/EnvConfigTemplate.yml create mode 100644 tools/tiny-test-fw/IDF/IDFApp.py create mode 100644 tools/tiny-test-fw/IDF/IDFDUT.py create mode 100644 tools/tiny-test-fw/IDF/__init__.py create mode 100644 tools/tiny-test-fw/Runner.py create mode 100644 tools/tiny-test-fw/TestCase.py create mode 100644 tools/tiny-test-fw/TinyFW.py create mode 100644 tools/tiny-test-fw/Utility/CaseConfig.py create mode 100644 tools/tiny-test-fw/Utility/GitlabCIJob.py create mode 100644 tools/tiny-test-fw/Utility/SearchCases.py create mode 100644 tools/tiny-test-fw/Utility/__init__.py create mode 100644 tools/tiny-test-fw/docs/Makefile create mode 100644 tools/tiny-test-fw/docs/_static/.keep create mode 100644 tools/tiny-test-fw/docs/conf.py create mode 100644 tools/tiny-test-fw/docs/index.rst create mode 100644 tools/tiny-test-fw/example.py diff --git a/tools/tiny-test-fw/App.py b/tools/tiny-test-fw/App.py new file mode 100644 index 000000000..84e8716a1 --- /dev/null +++ b/tools/tiny-test-fw/App.py @@ -0,0 +1,93 @@ +# 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. + +""" +class for handling Test Apps. Currently it provides the following features: + +1. get SDK path +2. get SDK tools +3. parse application info from its path. for example: + * provide download info + * provide partition table info + +Test Apps should inherent from BaseApp class and overwrite the methods. +""" +import os +import sys +import time + +# timestamp used for calculate log folder name +LOG_FOLDER_TIMESTAMP = time.time() + + +class BaseApp(object): + """ + Base Class for App. + Defines the mandatory methods that App need to implement. + Also implements some common methods. + + :param app_path: the path for app. + """ + + def __init__(self, app_path): + pass + + @classmethod + def get_sdk_path(cls): + """ + get sdk path. + + subclass must overwrite this method. + + :return: abs sdk path + """ + pass + + @classmethod + def get_tools(cls): + """ + get SDK related tools for applications + + subclass must overwrite this method. + + :return: tuple, abs path of each tool + """ + pass + + @classmethod + def get_log_folder(cls, test_suite_name): + """ + By default log folder is ``${SDK_PATH}/TEST_LOGS/${test_suite_name}_${timestamp}``. + + The log folder name is consist once start running, ensure all logs of will be put into the same folder. + + :param test_suite_name: the test suite name, by default it's the base file name for main module + :return: the log folder path + """ + if not test_suite_name: + test_suite_name = os.path.splitext(os.path.basename(sys.modules['__main__'].__file__))[0] + sdk_path = cls.get_sdk_path() + return os.path.join(sdk_path, "TEST_LOGS", + test_suite_name + + time.strftime("_%m%d_%H_%M_%S", time.localtime(LOG_FOLDER_TIMESTAMP))) + + def process_app_info(self): + """ + parse built app info for DUTTool + + subclass must overwrite this method. + + :return: required info for specific DUTTool + """ + pass diff --git a/tools/tiny-test-fw/CIAssignExampleTest.py b/tools/tiny-test-fw/CIAssignExampleTest.py new file mode 100644 index 000000000..1cd361313 --- /dev/null +++ b/tools/tiny-test-fw/CIAssignExampleTest.py @@ -0,0 +1,179 @@ +# 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. + +""" +Command line tool to assign example tests to CI test jobs. +""" + +# TODO: Need to handle running examples on different chips +import os +import sys +import re +import argparse + +import yaml + +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path: + sys.path.insert(0, test_fw_path) + +from Utility import CaseConfig, SearchCases, GitlabCIJob + + +class Group(object): + + MAX_EXECUTION_TIME = 30 + MAX_CASE = 15 + SORT_KEYS = ["env_tag"] + + def __init__(self, case): + self.execution_time = 0 + self.case_list = [case] + self.filters = dict(zip(self.SORT_KEYS, [case.case_info[x] for x in self.SORT_KEYS])) + + def accept_new_case(self): + """ + check if allowed to add any case to this group + + :return: True or False + """ + max_time = (sum([x.case_info["execution_time"] for x in self.case_list]) < self.MAX_EXECUTION_TIME) + max_case = (len(self.case_list) < self.MAX_CASE) + return max_time and max_case + + def add_case(self, case): + """ + add case to current group + + :param case: test case + :return: True if add succeed, else False + """ + added = False + if self.accept_new_case(): + for key in self.filters: + if case.case_info[key] != self.filters[key]: + break + else: + self.case_list.append(case) + added = True + return added + + def output(self): + """ + output data for job configs + + :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group} + """ + output_data = { + "Filter": self.filters, + "CaseConfig": [{"name": x.case_info["name"]} for x in self.case_list], + } + return output_data + + +class AssignTest(object): + """ + Auto assign tests to CI jobs. + + :param test_case: path of test case file(s) + :param ci_config_file: path of ``.gitlab-ci.yml`` + """ + + CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+") + + def __init__(self, test_case, ci_config_file): + self.test_cases = self._search_cases(test_case) + self.jobs = self._parse_gitlab_ci_config(ci_config_file) + + def _parse_gitlab_ci_config(self, ci_config_file): + + with open(ci_config_file, "r") as f: + ci_config = yaml.load(f) + + job_list = list() + for job_name in ci_config: + if self.CI_TEST_JOB_PATTERN.search(job_name) is not None: + job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name)) + return job_list + + @staticmethod + def _search_cases(test_case, case_filter=None): + """ + :param test_case: path contains test case folder + :param case_filter: filter for test cases + :return: filtered test case list + """ + test_methods = SearchCases.Search.search_test_cases(test_case) + return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict()) + + def _group_cases(self): + """ + separate all cases into groups according group rules. each group will be executed by one CI job. + + :return: test case groups. + """ + groups = [] + for case in self.test_cases: + for group in groups: + # add to current group + if group.add_case(case): + break + else: + # create new group + groups.append(Group(case)) + return groups + + def assign_cases(self): + """ + separate test cases to groups and assign test cases to CI jobs. + + :raise AssertError: if failed to assign any case to CI job. + :return: None + """ + failed_to_assign = [] + test_groups = self._group_cases() + for group in test_groups: + for job in self.jobs: + if job.match_group(group): + job.assign_group(group) + break + else: + failed_to_assign.append(group) + assert not failed_to_assign + + def output_configs(self, output_path): + """ + + :param output_path: path to output config files for each CI job + :return: None + """ + if not os.path.exists(output_path): + os.makedirs(output_path) + for job in self.jobs: + job.output_config(output_path) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("test_case", + help="test case folder or file") + parser.add_argument("ci_config_file", + help="gitlab ci config file") + parser.add_argument("output_path", + help="output path of config files") + args = parser.parse_args() + + assign_test = AssignTest(args.test_case, args.ci_config_file) + assign_test.assign_cases() + assign_test.output_configs(args.output_path) diff --git a/tools/tiny-test-fw/DUT.py b/tools/tiny-test-fw/DUT.py new file mode 100644 index 000000000..abb0ce800 --- /dev/null +++ b/tools/tiny-test-fw/DUT.py @@ -0,0 +1,550 @@ +# 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. + +""" +DUT provides 3 major groups of features: + +* DUT port feature, provide basic open/close/read/write features +* DUT tools, provide extra methods to control the device, like download and start app +* DUT expect method, provide features for users to check DUT outputs + +The current design of DUT have 3 classes for one DUT: BaseDUT, DUTPort, DUTTool. + +* BaseDUT class: + * defines methods DUT port and DUT tool need to overwrite + * provide the expect methods and some other methods based on DUTPort +* DUTPort class: + * inherent from BaseDUT class + * implements the port features by overwriting port methods defined in BaseDUT +* DUTTool class: + * inherent from one of the DUTPort class + * implements the tools features by overwriting tool methods defined in BaseDUT + * could add some new methods provided by the tool + +This module implements the BaseDUT class and one of the port class SerialDUT. +User should implement their DUTTool classes. +If they using different port then need to implement their DUTPort class as well. +""" + +import time +import re +import threading +import copy +import sys +import functools + +import serial +from serial.tools import list_ports + +if sys.version_info[0] == 2: + import Queue as _queue +else: + import queue as _queue + + +class ExpectTimeout(ValueError): + """ timeout for expect method """ + pass + + +class UnsupportedExpectItem(ValueError): + """ expect item not supported by the expect method """ + pass + + +def _expect_lock(func): + @functools.wraps(func) + def handler(self, *args, **kwargs): + with self.expect_lock: + ret = func(self, *args, **kwargs) + return ret + return handler + + +class _DataCache(_queue.Queue): + """ + Data cache based on Queue. Allow users to process data cache based on bytes instead of Queue." + """ + + def __init__(self, maxsize=0): + _queue.Queue.__init__(self, maxsize=maxsize) + self.data_cache = str() + + def get_data(self, timeout=0): + """ + get a copy of data from cache. + + :param timeout: timeout for waiting new queue item + :return: copy of data cache + """ + # make sure timeout is non-negative + if timeout < 0: + timeout = 0 + + try: + data = self.get(timeout=timeout) + if isinstance(data, bytes): + # convert bytes to string + try: + data = data.decode("utf-8", "ignore") + except UnicodeDecodeError: + data = data.decode("iso8859-1",) + self.data_cache += data + except _queue.Empty: + # don't do anything when on update for cache + pass + return copy.deepcopy(self.data_cache) + + def flush(self, index=0xFFFFFFFF): + """ + flush data from cache. + + :param index: if < 0 then don't do flush, otherwise flush data before index + :return: None + """ + # first add data in queue to cache + self.get_data() + + if index > 0: + self.data_cache = self.data_cache[index:] + + +class _RecvThread(threading.Thread): + + def __init__(self, read, data_cache): + super(_RecvThread, self).__init__() + self.exit_event = threading.Event() + self.setDaemon(True) + self.read = read + self.data_cache = data_cache + + def run(self): + while not self.exit_event.isSet(): + data = self.read(1000) + if data: + self.data_cache.put(data) + + def exit(self): + self.exit_event.set() + self.join() + + +class BaseDUT(object): + """ + :param name: application defined name for port + :param port: comport name, used to create DUT port + :param log_file: log file name + :param app: test app instance + :param kwargs: extra args for DUT to create ports + """ + + DEFAULT_EXPECT_TIMEOUT = 5 + + def __init__(self, name, port, log_file, app, **kwargs): + + self.expect_lock = threading.Lock() + self.name = name + self.port = port + self.log_file = log_file + self.app = app + self.data_cache = _DataCache() + self.receive_thread = None + # open and start during init + self.open() + + def __str__(self): + return "DUT({}: {})".format(self.name, str(self.port)) + + # define for methods need to be overwritten by Port + @classmethod + def list_available_ports(cls): + """ + list all available ports. + + subclass (port) must overwrite this method. + + :return: list of available comports + """ + pass + + def _port_open(self): + """ + open the port. + + subclass (port) must overwrite this method. + + :return: None + """ + pass + + def _port_read(self, size=1): + """ + read form port. This method should not blocking for long time, otherwise receive thread can not exit. + + subclass (port) must overwrite this method. + + :param size: max size to read. + :return: read data. + """ + pass + + def _port_write(self, data): + """ + write to port. + + subclass (port) must overwrite this method. + + :param data: data to write + :return: None + """ + pass + + def _port_close(self): + """ + close port. + + subclass (port) must overwrite this method. + + :return: None + """ + pass + + # methods that need to be overwritten by Tool + @classmethod + def confirm_dut(cls, port, app, **kwargs): + """ + confirm if it's a DUT, usually used by auto detecting DUT in by Env config. + + subclass (tool) must overwrite this method. + + :param port: comport + :param app: app instance + :return: True or False + """ + pass + + def start_app(self): + """ + usually after we got DUT, we need to do some extra works to let App start. + For example, we need to reset->download->reset to let IDF application start on DUT. + + subclass (tool) must overwrite this method. + + :return: None + """ + pass + + # methods that features raw port methods + def open(self): + """ + open port and create thread to receive data. + + :return: None + """ + self._port_open() + self.receive_thread = _RecvThread(self._port_read, self.data_cache) + self.receive_thread.start() + + def close(self): + """ + close receive thread and then close port. + + :return: None + """ + if self.receive_thread: + self.receive_thread.exit() + self._port_close() + + def write(self, data, eol="\r\n", flush=True): + """ + :param data: data + :param eol: end of line pattern. + :param flush: if need to flush received data cache before write data. + usually we need to flush data before write, + make sure processing outputs generated by wrote. + :return: None + """ + # do flush before write + if flush: + self.data_cache.flush() + # do write if cache + if data: + self._port_write(data + eol if eol else data) + + @_expect_lock + def read(self, size=0xFFFFFFFF): + """ + read(size=0xFFFFFFFF) + read raw data. NOT suggested to use this method. + Only use it if expect method doesn't meet your requirement. + + :param size: read size. default read all data + :return: read data + """ + data = self.data_cache.get_data(0)[:size] + self.data_cache.flush(size) + return data + + # expect related methods + + @staticmethod + def _expect_str(data, pattern): + """ + protected method. check if string is matched in data cache. + + :param data: data to process + :param pattern: string + :return: pattern if match succeed otherwise None + """ + index = data.find(pattern) + if index != -1: + ret = pattern + index += len(pattern) + else: + ret = None + return ret, index + + @staticmethod + def _expect_re(data, pattern): + """ + protected method. check if re pattern is matched in data cache + + :param data: data to process + :param pattern: compiled RegEx pattern + :return: match groups if match succeed otherwise None + """ + ret = None + match = pattern.search(data) + if match: + ret = match.groups() + index = match.end() + else: + index = -1 + return ret, index + + EXPECT_METHOD = [ + [type(re.compile("")), "_expect_re"], + [str, "_expect_str"], + ] + + def _get_expect_method(self, pattern): + """ + protected method. get expect method according to pattern type. + + :param pattern: expect pattern, string or compiled RegEx + :return: ``_expect_str`` or ``_expect_re`` + """ + for expect_method in self.EXPECT_METHOD: + if isinstance(pattern, expect_method[0]): + method = expect_method[1] + break + else: + raise UnsupportedExpectItem() + return self.__getattribute__(method) + + @_expect_lock + def expect(self, pattern, timeout=DEFAULT_EXPECT_TIMEOUT): + """ + expect(pattern, timeout=DEFAULT_EXPECT_TIMEOUT) + expect received data on DUT match the pattern. will raise exception when expect timeout. + + :raise ExpectTimeout: failed to find the pattern before timeout + :raise UnsupportedExpectItem: pattern is not string or compiled RegEx + + :param pattern: string or compiled RegEx(string pattern) + :param timeout: timeout for expect + :return: string if pattern is string; matched groups if pattern is RegEx + """ + method = self._get_expect_method(pattern) + + # non-blocking get data for first time + data = self.data_cache.get_data(0) + start_time = time.time() + while True: + ret, index = method(data, pattern) + if ret is not None or time.time() - start_time > timeout: + self.data_cache.flush(index) + break + # wait for new data from cache + data = self.data_cache.get_data(time.time() + timeout - start_time) + + if ret is None: + raise ExpectTimeout(self.name + ": " + str(pattern)) + return ret + + def _expect_multi(self, expect_all, expect_item_list, timeout): + """ + protected method. internal logical for expect multi. + + :param expect_all: True or False, expect all items in the list or any in the list + :param expect_item_list: expect item list + :param timeout: timeout + :return: None + """ + def process_expected_item(item_raw): + # convert item raw data to standard dict + item = { + "pattern": item_raw[0] if isinstance(item_raw, tuple) else item_raw, + "method": self._get_expect_method(item_raw[0] if isinstance(item_raw, tuple) + else item_raw), + "callback": item_raw[1] if isinstance(item_raw, tuple) else None, + "index": -1, + "ret": None, + } + return item + + expect_items = [process_expected_item(x) for x in expect_item_list] + + # non-blocking get data for first time + data = self.data_cache.get_data(0) + + start_time = time.time() + matched_expect_items = list() + while True: + for expect_item in expect_items: + if expect_item not in matched_expect_items: + # exclude those already matched + expect_item["ret"], expect_item["index"] = \ + expect_item["method"](data, expect_item["pattern"]) + if expect_item["ret"] is not None: + # match succeed for one item + matched_expect_items.append(expect_item) + break + + # if expect all, then all items need to be matched, + # else only one item need to matched + if expect_all: + match_succeed = (matched_expect_items == expect_items) + else: + match_succeed = True if matched_expect_items else False + + if time.time() - start_time > timeout or match_succeed: + break + else: + data = self.data_cache.get_data(time.time() + timeout - start_time) + + if match_succeed: + # do callback and flush matched data cache + slice_index = -1 + for expect_item in matched_expect_items: + # trigger callback + if expect_item["callback"]: + expect_item["callback"](expect_item["ret"]) + slice_index = max(slice_index, expect_item["index"]) + # flush already matched data + self.data_cache.flush(slice_index) + else: + raise ExpectTimeout(self.name + ": " + str(expect_items)) + + @_expect_lock + def expect_any(self, *expect_items, **timeout): + """ + expect_any(*expect_items, timeout=DEFAULT_TIMEOUT) + expect any of the patterns. + will call callback (if provided) if pattern match succeed and then return. + will pass match result to the callback. + + :raise ExpectTimeout: failed to match any one of the expect items before timeout + :raise UnsupportedExpectItem: pattern in expect_item is not string or compiled RegEx + + :arg expect_items: one or more expect items. + string, compiled RegEx pattern or (string or RegEx(string pattern), callback) + :keyword timeout: timeout for expect + :return: None + """ + # to be compatible with python2 + # in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT) + if "timeout" not in timeout: + timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT + return self._expect_multi(False, expect_items, **timeout) + + @_expect_lock + def expect_all(self, *expect_items, **timeout): + """ + expect_all(*expect_items, timeout=DEFAULT_TIMEOUT) + expect all of the patterns. + will call callback (if provided) if all pattern match succeed and then return. + will pass match result to the callback. + + :raise ExpectTimeout: failed to match all of the expect items before timeout + :raise UnsupportedExpectItem: pattern in expect_item is not string or compiled RegEx + + :arg expect_items: one or more expect items. + string, compiled RegEx pattern or (string or RegEx(string pattern), callback) + :keyword timeout: timeout for expect + :return: None + """ + # to be compatible with python2 + # in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT) + if "timeout" not in timeout: + timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT + return self._expect_multi(True, expect_items, **timeout) + + +class SerialDUT(BaseDUT): + """ serial with logging received data feature """ + + DEFAULT_UART_CONFIG = { + "baudrate": 115200, + "bytesize": serial.EIGHTBITS, + "parity": serial.PARITY_NONE, + "stopbits": serial.STOPBITS_ONE, + "timeout": 0.05, + "xonxoff": False, + "rtscts": False, + } + + def __init__(self, name, port, log_file, app, **kwargs): + self.port_inst = None + self.serial_configs = self.DEFAULT_UART_CONFIG.copy() + self.serial_configs.update(kwargs) + super(SerialDUT, self).__init__(name, port, log_file, app, **kwargs) + + @staticmethod + def _format_data(data): + """ + format data for logging. do decode and add timestamp. + + :param data: raw data from read + :return: formatted data (str) + """ + timestamp = time.time() + timestamp = "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)), + str(timestamp % 1)[2:5]) + try: + formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, data.decode("utf-8", "ignore")) + except UnicodeDecodeError: + # if utf-8 fail, use iso-8859-1 (single char codec with range 0-255) + formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, data.decode("iso8859-1",)) + return formatted_data + + def _port_open(self): + self.port_inst = serial.Serial(self.port, **self.serial_configs) + + def _port_close(self): + self.port_inst.close() + + def _port_read(self, size=1): + data = self.port_inst.read(size) + if data: + with open(self.log_file, "a+") as _log_file: + _log_file.write(self._format_data(data)) + return data + + def _port_write(self, data): + self.port_inst.write(data) + + @classmethod + def list_available_ports(cls): + return [x.device for x in list_ports.comports()] diff --git a/tools/tiny-test-fw/Env.py b/tools/tiny-test-fw/Env.py new file mode 100644 index 000000000..da5c0b598 --- /dev/null +++ b/tools/tiny-test-fw/Env.py @@ -0,0 +1,156 @@ +# 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. + +""" Test Env, manages DUT, App and EnvConfig, interface for test cases to access these components """ +import os +import threading +import functools + +import EnvConfig + + +def _synced(func): + @functools.wraps(func) + def decorator(self, *args, **kwargs): + with self.lock: + ret = func(self, *args, **kwargs) + return ret + + decorator.__doc__ = func.__doc__ + return decorator + + +class Env(object): + """ + test env, manages DUTs and env configs. + + :keyword app: class for default application + :keyword dut: class for default DUT + :keyword env_tag: test env tag, used to select configs from env config file + :keyword env_config_file: test env config file path + :keyword test_name: test suite name, used when generate log folder name + """ + + def __init__(self, + app=None, + dut=None, + env_tag=None, + env_config_file=None, + test_name=None, + **kwargs): + self.app_cls = app + self.default_dut_cls = dut + self.config = EnvConfig.Config(env_config_file, env_tag) + self.log_path = self.app_cls.get_log_folder(test_name) + if not os.path.exists(self.log_path): + os.makedirs(self.log_path) + + self.allocated_duts = dict() + self.lock = threading.RLock() + + @_synced + def get_dut(self, dut_name, app_path, dut_class=None, app_class=None): + """ + get_dut(dut_name, app_path, dut_class=None, app_class=None) + + :param dut_name: user defined name for DUT + :param app_path: application path, app instance will use this path to process application info + :param dut_class: dut class, if not specified will use default dut class of env + :param app_class: app class, if not specified will use default app of env + :return: dut instance + """ + if dut_name in self.allocated_duts: + dut = self.allocated_duts[dut_name]["dut"] + else: + if dut_class is None: + dut_class = self.default_dut_cls + if app_class is None: + app_class = self.app_cls + app_inst = app_class(app_path) + try: + port = self.config.get_variable(dut_name) + except ValueError: + # try to auto detect ports + allocated_ports = [self.allocated_duts[x]["port"] for x in self.allocated_duts] + available_ports = dut_class.list_available_ports() + for port in available_ports: + if port not in allocated_ports: + if dut_class.confirm_dut(port, app_inst): + break + else: + port = None + if port: + try: + dut_config = self.get_variable(dut_name + "_port_config") + except ValueError: + dut_config = dict() + dut = self.default_dut_cls(dut_name, port, + os.path.join(self.log_path, dut_name + ".log"), + app_inst, + **dut_config) + self.allocated_duts[dut_name] = {"port": port, "dut": dut} + else: + raise ValueError("Failed to get DUT") + return dut + + @_synced + def close_dut(self, dut_name): + """ + close_dut(dut_name) + close one DUT by name if DUT name is valid (the name used by ``get_dut``). otherwise will do nothing. + + :param dut_name: user defined name for DUT + :return: None + """ + try: + dut = self.allocated_duts.pop(dut_name)["dut"] + dut.close() + except KeyError: + pass + + @_synced + def get_variable(self, variable_name): + """ + get_variable(variable_name) + get variable from config file. If failed then try to auto-detected it. + + :param variable_name: name of the variable + :return: value of variable if successfully found. otherwise None. + """ + return self.config.get_variable(variable_name) + + @_synced + def get_pc_nic_info(self, nic_name="pc_nic"): + """ + get_pc_nic_info(nic_name="pc_nic") + try to get nic info (ip address, ipv6 address, mac address) + + :param nic_name: pc nic name. allows passing variable name, nic name value or omitted (to get default nic info). + :return: a dict of address ("ipv4", "ipv6", "mac") if successfully found. otherwise None. + """ + # TODO: need to implement auto get nic info method + return self.config.get_variable("nic_info/" + nic_name) + + @_synced + def close(self): + """ + close() + close all DUTs of the Env. + + :return: None + """ + for dut_name in self.allocated_duts: + dut = self.allocated_duts[dut_name]["dut"] + dut.close() + self.allocated_duts = dict() diff --git a/tools/tiny-test-fw/EnvConfig.py b/tools/tiny-test-fw/EnvConfig.py new file mode 100644 index 000000000..2ce28d811 --- /dev/null +++ b/tools/tiny-test-fw/EnvConfig.py @@ -0,0 +1,74 @@ +# 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. + +""" +The test env could change when we running test from different computers. +Test env config provide ``get_variable`` method to allow user get test environment related variables. +It will first try to get variable from config file. +If failed, then it will try to auto detect (Not supported yet). + +Config file format is yaml. it's a set of key-value pair. The following is an example of config file:: + + Example_WIFI: + ap_ssid: "myssid" + ap_password: "mypassword" + Example_ShieldBox: + attenuator_port: "/dev/ttyUSB2" + ap_ssid: "myssid" + ap_password: "mypassword" + +It will first define the env tag for each environment, then add its key-value pairs. +This will prevent test cases from getting configs from other env when there're configs for multiple env in one file. +""" + +import yaml + + +class Config(object): + """ Test Env Config """ + + def __init__(self, config_file, env_tag): + self.configs = self.load_config_file(config_file, env_tag) + + @staticmethod + def load_config_file(config_file, env_name): + """ + load configs from config file. + + :param config_file: config file path + :param env_name: env tag name + :return: configs for the test env + """ + try: + with open(config_file) as f: + configs = yaml.load(f)[env_name] + except (OSError, TypeError): + configs = dict() + return configs + + def get_variable(self, variable_name): + """ + first try to get from config file. if not found, try to auto detect the variable. + + :param variable_name: name of variable + :return: value or None + """ + try: + value = self.configs[variable_name] + except KeyError: + #TODO: to support auto get variable here + value = None + if value is None: + raise ValueError("Failed to get variable") + return value diff --git a/tools/tiny-test-fw/EnvConfigTemplate.yml b/tools/tiny-test-fw/EnvConfigTemplate.yml new file mode 100644 index 000000000..ef65a378f --- /dev/null +++ b/tools/tiny-test-fw/EnvConfigTemplate.yml @@ -0,0 +1,6 @@ +.external_ap: &external_ap + ap_ssid: "myssid" + ap_password: "mypassword" + +Examples_WIFI: + <<: external_ap diff --git a/tools/tiny-test-fw/IDF/IDFApp.py b/tools/tiny-test-fw/IDF/IDFApp.py new file mode 100644 index 000000000..3828277ed --- /dev/null +++ b/tools/tiny-test-fw/IDF/IDFApp.py @@ -0,0 +1,164 @@ +# 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. + +""" IDF Test Applications """ +import subprocess + +import os +import App + + +class IDFApp(App.BaseApp): + """ + Implements common esp-idf application behavior. + idf applications should inherent from this class and overwrite method get_binary_path. + """ + + IDF_DOWNLOAD_CONFIG_FILE = "download.config" + + def __init__(self, app_path): + super(IDFApp, self).__init__(app_path) + self.idf_path = self.get_sdk_path() + self.binary_path = self.get_binary_path(app_path) + assert os.path.exists(self.binary_path) + assert self.IDF_DOWNLOAD_CONFIG_FILE in os.listdir(self.binary_path) + self.esptool, self.partition_tool = self.get_tools() + + @classmethod + def get_sdk_path(cls): + idf_path = os.getenv("IDF_PATH") + assert idf_path + assert os.path.exists(idf_path) + return idf_path + + @classmethod + def get_tools(cls): + idf_path = cls.get_sdk_path() + # get esptool and partition tool for esp-idf + esptool = os.path.join(idf_path, "components", + "esptool_py", "esptool", "esptool.py") + partition_tool = os.path.join(idf_path, "components", + "partition_table", "gen_esp32part.py") + assert os.path.exists(esptool) and os.path.exists(partition_tool) + return esptool, partition_tool + + def get_binary_path(self, app_path): + """ + get binary path according to input app_path. + + subclass must overwrite this method. + + :param app_path: path of application + :return: abs app binary path + """ + pass + + def process_arg(self, arg): + """ + process args in download.config. convert to abs path for .bin args. strip spaces and CRLFs. + """ + if ".bin" in arg: + ret = os.path.join(self.binary_path, arg) + else: + ret = arg + return ret.strip("\r\n ") + + def process_app_info(self): + """ + get app download config and partition info from a specific app path + + :return: download config, partition info + """ + with open(os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE), "r") as f: + configs = f.read().split(" ") + + download_configs = ["--chip", "auto", "--before", "default_reset", + "--after", "hard_reset", "write_flash", "-z"] + download_configs += [self.process_arg(x) for x in configs] + + # handle partition table + for partition_file in download_configs: + if "partition" in partition_file: + partition_file = os.path.join(self.binary_path, partition_file) + break + else: + raise ValueError("No partition table found for IDF binary path: {}".format(self.binary_path)) + + process = subprocess.Popen(["python", self.partition_tool, partition_file], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data = process.stdout.read() + if isinstance(raw_data, bytes): + raw_data = raw_data.decode() + partition_table = dict() + + for line in raw_data.splitlines(): + if line[0] != "#": + try: + _name, _type, _subtype, _offset, _size, _flags = line.split(",") + if _size[-1] == "K": + _size = int(_size[:-1]) * 1024 + elif _size[-1] == "M": + _size = int(_size[:-1]) * 1024 * 1024 + else: + _size = int(_size) + except ValueError: + continue + partition_table[_name] = { + "type": _type, + "subtype": _subtype, + "offset": _offset, + "size": _size, + "flags": _flags + } + return download_configs, partition_table + + +class Example(IDFApp): + def get_binary_path(self, app_path): + # build folder of example path + path = os.path.join(self.idf_path, app_path, "build") + if not os.path.exists(path): + # search for CI build folders + app = os.path.basename(app_path) + example_path = os.path.join(self.idf_path, "build_examples", "example_builds") + for dirpath, dirnames, files in os.walk(example_path): + if dirnames: + if dirnames[0] == app: + path = os.path.join(example_path, dirpath, dirnames[0], "build") + break + else: + raise OSError("Failed to find example binary") + return path + + +class UT(IDFApp): + def get_binary_path(self, app_path): + if app_path: + # specified path, join it and the idf path + path = os.path.join(self.idf_path, app_path) + else: + path = os.path.join(self.idf_path, "tools", "unit-test-app", "build") + return path + + +class SSC(IDFApp): + def get_binary_path(self, app_path): + # TODO: to implement SSC get binary path + return app_path + + +class AT(IDFApp): + def get_binary_path(self, app_path): + # TODO: to implement AT get binary path + return app_path diff --git a/tools/tiny-test-fw/IDF/IDFDUT.py b/tools/tiny-test-fw/IDF/IDFDUT.py new file mode 100644 index 000000000..4c722e06e --- /dev/null +++ b/tools/tiny-test-fw/IDF/IDFDUT.py @@ -0,0 +1,126 @@ +# 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. + +""" DUT for IDF applications """ +import os +import re +import subprocess +import functools + +import DUT + + +class IDFToolError(OSError): + pass + + +def _tool_method(func): + """ close port, execute tool method and then reopen port """ + @functools.wraps(func) + def handler(self, *args, **kwargs): + self.close() + ret = func(self, *args, **kwargs) + self.open() + return ret + return handler + + +class IDFDUT(DUT.SerialDUT): + """ IDF DUT, extends serial with ESPTool methods """ + + CHIP_TYPE_PATTERN = re.compile(r"Detecting chip type[.:\s]+(.+)") + + def __init__(self, name, port, log_file, app, **kwargs): + self.download_config, self.partition_table = app.process_app_info() + super(IDFDUT, self).__init__(name, port, log_file, app, **kwargs) + + @classmethod + def get_chip(cls, app, port): + """ + get chip id via esptool + + :param app: application instance (to get tool) + :param port: comport + :return: chip ID or None + """ + try: + output = subprocess.check_output(["python", app.esptool, "--port", port, "chip_id"]) + except subprocess.CalledProcessError: + output = bytes() + if isinstance(output, bytes): + output = output.decode() + chip_type = cls.CHIP_TYPE_PATTERN.search(output) + return chip_type.group(1) if chip_type else None + + @classmethod + def confirm_dut(cls, port, app, **kwargs): + return cls.get_chip(app, port) is not None + + @_tool_method + def start_app(self): + """ + download and start app. + + :return: None + """ + retry_baud_rates = ["921600", "115200"] + error = IDFToolError() + for baud_rate in retry_baud_rates: + try: + subprocess.check_output(["python", self.app.esptool, + "--port", self.port, "--baud", baud_rate] + + self.download_config) + break + except subprocess.CalledProcessError as error: + continue + else: + raise error + + @_tool_method + def reset(self): + """ + reset DUT with esptool + + :return: None + """ + subprocess.check_output(["python", self.app.esptool, "--port", self.port, "run"]) + + @_tool_method + def dump_flush(self, output_file, **kwargs): + """ + dump flush + + :param output_file: output file name, if relative path, will use sdk path as base path. + :keyword partition: partition name, dump the partition. + ``partition`` is preferred than using ``address`` and ``size``. + :keyword address: dump from address (need to be used with size) + :keyword size: dump size (need to be used with address) + :return: None + """ + if os.path.isabs(output_file) is False: + output_file = os.path.relpath(output_file, self.app.get_log_folder()) + if "partition" in kwargs: + partition = self.partition_table[kwargs["partition"]] + _address = partition["offset"] + _size = partition["size"] + elif "address" in kwargs and "size" in kwargs: + _address = kwargs["address"] + _size = kwargs["size"] + else: + raise IDFToolError("You must specify 'partition' or ('address' and 'size') to dump flash") + subprocess.check_output( + ["python", self.app.esptool, "--port", self.port, "--baud", "921600", + "--before", "default_reset", "--after", "hard_reset", "read_flash", + _address, _size, output_file] + ) diff --git a/tools/tiny-test-fw/IDF/__init__.py b/tools/tiny-test-fw/IDF/__init__.py new file mode 100644 index 000000000..8975a5299 --- /dev/null +++ b/tools/tiny-test-fw/IDF/__init__.py @@ -0,0 +1,36 @@ +# 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. + +import TinyFW +from IDF.IDFApp import Example, UT +from IDF.IDFDUT import IDFDUT + + +def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", + module="examples", execution_time=1, + **kwargs): + """ + decorator for testing idf examples (with default values for some keyword args). + + :param app: test application class + :param dut: dut class + :param chip: chip supported, string or tuple + :param module: module, string + :param execution_time: execution time in minutes, int + :param kwargs: other keyword args + :return: test method + """ + # not use partial function as define as function support auto generating document + return TinyFW.test_method(app=app, dut=dut, chip=chip, module=module, + execution_time=execution_time, **kwargs) diff --git a/tools/tiny-test-fw/Runner.py b/tools/tiny-test-fw/Runner.py new file mode 100644 index 000000000..0adf441fe --- /dev/null +++ b/tools/tiny-test-fw/Runner.py @@ -0,0 +1,80 @@ +# 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. + +""" +Command line interface to run test cases from a given path. + +* search and run test cases of a given path +* config file which support to filter test cases and passing data to test case + +Use ``python Runner.py test_case_path -c config_file -e env_config_file`` to run test cases. + +""" +import os +import sys +import argparse +import threading + +import TinyFW +from Utility import SearchCases, CaseConfig + + +class Runner(threading.Thread): + """ + :param test_case: test case file or folder + :param case_config: case config file, allow to filter test cases and pass data to test case + :param env_config_file: env config file + """ + + def __init__(self, test_case, case_config, env_config_file=None): + super(Runner, self).__init__() + self.setDaemon(True) + test_methods = SearchCases.Search.search_test_cases(test_case) + self.test_cases = CaseConfig.Parser.apply_config(test_methods, case_config) + self.test_result = True + if case_config: + test_suite_name = os.path.splitext(os.path.basename(case_config))[0] + else: + test_suite_name = "TestRunner" + TinyFW.set_default_config(env_config_file=env_config_file, test_suite_name=test_suite_name) + + def run(self): + for case in self.test_cases: + self.test_result = self.test_result and case.run() + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument("test_case", + help="test case folder or file") + parser.add_argument("--case_config", "-c", default=None, + help="case filter/config file") + parser.add_argument("--env_config_file", "-e", default=None, + help="test env config file") + args = parser.parse_args() + + runner = Runner(args.test_case, args.case_config, args.env_config_file) + runner.start() + + while True: + try: + runner.join(1) + if not runner.isAlive(): + break + except KeyboardInterrupt: + print("exit by Ctrl-C") + break + if not runner.test_result: + sys.exit(1) diff --git a/tools/tiny-test-fw/TestCase.py b/tools/tiny-test-fw/TestCase.py new file mode 100644 index 000000000..80b548530 --- /dev/null +++ b/tools/tiny-test-fw/TestCase.py @@ -0,0 +1,53 @@ +# 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. + +import yaml + + +class TestCase(object): + """ + Test Case Object, mainly used with runner. + runner can parse all test cases from a given path, set data and config for test case in prepare stage. + TestCase instance will record these data, provide run method to let runner execute test case. + + :param test_method: test function + :param extra_data: data passed to test function + :param overwrite_args: kwargs that overwrite original test case configs + """ + DEFAULT_CASE_DOC = dict() + + def __init__(self, test_method, extra_data, **overwrite_args): + self.test_method = test_method + self.extra_data = extra_data + self.overwrite_args = overwrite_args + + def run(self): + """ execute the test case """ + return self.test_method(self.extra_data, **self.overwrite_args) + + def document(self): + """ + generate test case document. + parse the case doc with yaml parser and update to original case attributes. + + :return: case document, dict of case attributes and values + """ + doc_string = self.test_method.__doc__ + try: + doc = yaml.load(doc_string) + except (AttributeError, OSError, UnicodeDecodeError): + doc = self.DEFAULT_CASE_DOC + doc.update(self.test_method.env_args) + doc.update(self.test_method.accepted_filter) + return doc diff --git a/tools/tiny-test-fw/TinyFW.py b/tools/tiny-test-fw/TinyFW.py new file mode 100644 index 000000000..5eace4e4a --- /dev/null +++ b/tools/tiny-test-fw/TinyFW.py @@ -0,0 +1,220 @@ +# 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. + +""" Interface for test cases. """ +import sys +import os +import time +import traceback +import inspect +import functools + +import xunitgen + +import Env +import DUT +import App + + +XUNIT_FILE_NAME = "XUNIT_RESULT.xml" +XUNIT_RECEIVER = xunitgen.EventReceiver() +XUNIT_DEFAULT_TEST_SUITE = "test-suite" + + +_COLOR_CODES = { + "white": '\033[0m', + "red": '\033[31m', + "green": '\033[32m', + "orange": '\033[33m', + "blue": '\033[34m', + "purple": '\033[35m', + "W": '\033[0m', + "R": '\033[31m', + "G": '\033[32m', + "O": '\033[33m', + "B": '\033[34m', + "P": '\033[35m' +} + + +def console_log(data, color="white"): + """ + log data to console. + (if not flush console log, Gitlab-CI won't update logs during job execution) + + :param data: data content + :param color: color + """ + if color not in _COLOR_CODES: + color = "white" + color_codes = _COLOR_CODES[color] + print(color_codes + data) + if color not in ["white", "W"]: + # reset color to white for later logs + print(_COLOR_CODES["white"] + "\r") + sys.stdout.flush() + + +class DefaultEnvConfig(object): + """ + default test configs. There're 3 places to set configs, priority is (high -> low): + + 1. overwrite set by caller of test method + 2. values set by test_method decorator + 3. default env config get from this class + """ + DEFAULT_CONFIG = { + "app": App.BaseApp, + "dut": DUT.BaseDUT, + "env_tag": "default", + "env_config_file": None, + "test_suite_name": None, + } + + @classmethod + def set_default_config(cls, **kwargs): + """ + :param kwargs: configs need to be updated + :return: None + """ + cls.DEFAULT_CONFIG.update(kwargs) + + @classmethod + def get_default_config(cls): + """ + :return: current default config + """ + return cls.DEFAULT_CONFIG.copy() + + +set_default_config = DefaultEnvConfig.set_default_config +get_default_config = DefaultEnvConfig.get_default_config + + +class TestResult(object): + TEST_RESULT = { + "pass": [], + "fail": [], + } + + @classmethod + def get_failed_cases(cls): + """ + :return: failed test cases + """ + return cls.TEST_RESULT["fail"] + + @classmethod + def get_passed_cases(cls): + """ + :return: passed test cases + """ + return cls.TEST_RESULT["pass"] + + @classmethod + def set_result(cls, result, case_name): + """ + :param result: True or False + :param case_name: test case name + :return: None + """ + cls.TEST_RESULT["pass" if result else "fail"].append(case_name) + + +get_failed_cases = TestResult.get_failed_cases +get_passed_cases = TestResult.get_passed_cases + + +MANDATORY_INFO = { + "execution_time": 1, + "env_tag": "default", +} + + +def test_method(**kwargs): + """ + decorator for test case function. + The following keyword arguments are pre-defined. + Any other keyword arguments will be regarded as filter for the test case, + able to access them by ``case_info`` attribute of test method. + + :keyword app: class for test app. see :doc:`App ` for details + :keyword dut: class for current dut. see :doc:`DUT ` for details + :keyword env_tag: name for test environment, used to select configs from config file + :keyword env_config_file: test env config file. usually will not set this keyword when define case + :keyword test_suite_name: test suite name, used for generating log folder name and adding xunit format test result. + usually will not set this keyword when define case + """ + def test(test_func): + # get test function file name + frame = inspect.stack() + test_func_file_name = frame[1][1] + + case_info = MANDATORY_INFO.copy() + case_info["name"] = test_func.__name__ + case_info.update(kwargs) + + # create env instance + env_config = DefaultEnvConfig.get_default_config() + for key in kwargs: + if key in env_config: + env_config[key] = kwargs[key] + + @functools.wraps(test_func) + def handle_test(extra_data=None, **overwrite): + """ + create env, run test and record test results + + :param extra_data: extra data that runner or main passed to test case + :param overwrite: args that runner or main want to overwrite + :return: None + """ + env_config.update(overwrite) + env_inst = Env.Env(**env_config) + # prepare for xunit test results + xunit_file = os.path.join(env_inst.app_cls.get_log_folder(env_config["test_suite_name"]), + XUNIT_FILE_NAME) + XUNIT_RECEIVER.begin_case(test_func.__name__, time.time(), test_func_file_name) + try: + console_log("starting running test: " + test_func.__name__, color="green") + # execute test function + test_func(env_inst, extra_data) + # if finish without exception, test result is True + result = True + except Exception as e: + # handle all the exceptions here + traceback.print_exc() + result = False + # log failure + XUNIT_RECEIVER.failure(str(e), test_func_file_name) + finally: + # do close all DUTs + env_inst.close() + # end case and output result + XUNIT_RECEIVER.end_case(test_func.__name__, time.time()) + with open(xunit_file, "ab+") as f: + f.write(xunitgen.toxml(XUNIT_RECEIVER.results(), + XUNIT_DEFAULT_TEST_SUITE)) + + if result: + console_log("Test Succeed: " + test_func.__name__, color="green") + else: + console_log(("Test Fail: " + test_func.__name__), color="red") + TestResult.set_result(result, test_func.__name__) + return result + + handle_test.case_info = case_info + handle_test.test_method = True + return handle_test + return test diff --git a/tools/tiny-test-fw/Utility/CaseConfig.py b/tools/tiny-test-fw/Utility/CaseConfig.py new file mode 100644 index 000000000..1fe5df42b --- /dev/null +++ b/tools/tiny-test-fw/Utility/CaseConfig.py @@ -0,0 +1,199 @@ +# 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. + +""" +Processing case config files. +This is mainly designed for CI, we need to auto create and assign test jobs. + +Template Config File:: + + TestConfig: + app: + path: Users/Test/TinyTestFW/IDF/IDFApp.py + class: Example + dut: + path: + class: + config_file: /somewhere/config_file_for_runner + test_name: CI_test_job_1 + + Filter: + chip: ESP32 + env_tag: default + + CaseConfig: + - name: test_examples_protocol_https_request + # optional + extra_data: some extra data passed to case with kwarg extra_data + overwrite: # overwrite test configs + app: + path: Users/Test/TinyTestFW/IDF/IDFApp.py + class: Example + - name: xxx +""" + +# TODO: add a function to use suitable import lib for python2 and python3 +import imp + +import yaml + +import TestCase + + +def _filter_one_case(test_method, case_filter): + """ Apply filter for one case (the filter logic is the same as described in ``filter_test_cases``) """ + filter_result = True + for key in case_filter: + if key in test_method.case_info: + # the filter key is both in case and filter + # we need to check if they match + filter_item, accepted_item = case_filter[key], test_method.case_info[key] + + if isinstance(filter_item, (tuple, list)) \ + and isinstance(accepted_item, (tuple, list)): + # both list/tuple, check if they have common item + filter_result = True if set(filter_item) & set(accepted_item) else False + elif isinstance(filter_item, (tuple, list)): + # filter item list/tuple, check if case accepted value in filter item list/tuple + filter_result = True if accepted_item in filter_item else False + elif isinstance(accepted_item, (tuple, list)): + # accepted item list/tuple, check if case filter value is in accept item list/tuple + filter_result = True if filter_item in accepted_item else False + else: + # both string/int, just do string compare + filter_result = (filter_item == accepted_item) + else: + # key in filter only, which means the case supports all values for this filter key, match succeed + pass + if not filter_result: + # match failed + break + return filter_result + + +def filter_test_cases(test_methods, case_filter): + """ + filter test case. filter logic: + + 1. if filter key both in case attribute and filter: + * if both value is string/int, then directly compare + * if one is list/tuple, the other one is string/int, then check if string/int is in list/tuple + * if both are list/tuple, then check if they have common item + 2. if only case attribute or filter have the key, filter succeed + + for example, the following are match succeed scenarios + (the rule is symmetric, result is same if exchange values for user filter and case attribute): + + * user case filter is ``chip: ["esp32", "esp32c"]``, case doesn't have ``chip`` attribute + * user case filter is ``chip: ["esp32", "esp32c"]``, case attribute is ``chip: "esp32"`` + * user case filter is ``chip: "esp32"``, case attribute is ``chip: "esp32"`` + + + :param test_methods: a list of test methods functions + :param case_filter: case filter + :return: filtered test methods + """ + filtered_test_methods = [] + for test_method in test_methods: + if _filter_one_case(test_method, case_filter): + filtered_test_methods.append(test_method) + return filtered_test_methods + + +class Parser(object): + DEFAULT_CONFIG = { + "TestConfig": dict(), + "Filter": dict(), + "CaseConfig": [{"extra_data": None}], + } + + @classmethod + def parse_config_file(cls, config_file): + """ + parse from config file and then update to default config. + + :param config_file: config file path + :return: configs + """ + configs = cls.DEFAULT_CONFIG.copy() + if config_file: + with open(config_file, "r") as f: + configs.update(yaml.load(f)) + return configs + + @classmethod + def handle_overwrite_args(cls, overwrite): + """ + handle overwrite configs. import module from path and then get the required class. + + :param overwrite: overwrite args + :return: dict of (original key: class) + """ + output = dict() + for key in overwrite: + _path = overwrite[key]["path"] + # TODO: add a function to use suitable import lib for python2 and python3 + _module = imp.load_source(str(hash(_path)), overwrite[key]["path"]) + output[key] = _module.__getattribute__(overwrite[key]["class"]) + return output + + @classmethod + def apply_config(cls, test_methods, config_file): + """ + apply config for test methods + + :param test_methods: a list of test methods functions + :param config_file: case filter file + :return: filtered cases + """ + configs = cls.parse_config_file(config_file) + test_case_list = [] + for _config in configs["CaseConfig"]: + _filter = configs["Filter"].copy() + _filter.update(_config) + _overwrite = cls.handle_overwrite_args(_filter.pop("overwrite", dict())) + _extra_data = _filter.pop("extra_data", None) + for test_method in test_methods: + if _filter_one_case(test_method, _filter): + test_case_list.append(TestCase.TestCase(test_method, _extra_data, **_overwrite)) + return test_case_list + + +class Generator(object): + """ Case config file generator """ + + def __init__(self): + self.default_config = { + "TestConfig": dict(), + "Filter": dict(), + } + + def set_default_configs(self, test_config, case_filter): + """ + :param test_config: "TestConfig" value + :param case_filter: "Filter" value + :return: None + """ + self.default_config = {"TestConfig": test_config, "Filter": case_filter} + + def generate_config(self, case_configs, output_file): + """ + :param case_configs: "CaseConfig" value + :param output_file: output file path + :return: None + """ + config = self.default_config.copy() + config.update({"CaseConfig": case_configs}) + with open(output_file, "w") as f: + yaml.dump(config, f) diff --git a/tools/tiny-test-fw/Utility/GitlabCIJob.py b/tools/tiny-test-fw/Utility/GitlabCIJob.py new file mode 100644 index 000000000..05f6393c6 --- /dev/null +++ b/tools/tiny-test-fw/Utility/GitlabCIJob.py @@ -0,0 +1,73 @@ +# 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. + +import os + +import yaml + + +class Job(dict): + """ + Gitlab CI job + + :param job: job data loaded from .gitlab-ci.yml + :param job_name: job name + """ + def __init__(self, job, job_name): + super(Job, self).__init__(job) + self["name"] = job_name + + def match_group(self, group): + """ + Match group by tags of job. + All filters values of group should be included in tags. + + :param group: case group to match + :return: True or False + """ + match_result = False + for _ in range(1): + if "case group" in self: + # this job is already assigned + break + for value in group.filters.values(): + if value not in self["tags"]: + break + else: + continue + break + else: + match_result = True + return match_result + + def assign_group(self, group): + """ + assign a case group to a test job. + + :param group: the case group to assign + """ + self["case group"] = group + + def output_config(self, file_path): + """ + output test config to the given path. + file name will be job_name.yml + + :param file_path: output file path + :return: None + """ + file_name = os.path.join(file_path, self["name"] + ".yml") + if "case group" in self: + with open(file_name, "w") as f: + yaml.dump(self["case group"].output(), f) diff --git a/tools/tiny-test-fw/Utility/SearchCases.py b/tools/tiny-test-fw/Utility/SearchCases.py new file mode 100644 index 000000000..c4c681b0d --- /dev/null +++ b/tools/tiny-test-fw/Utility/SearchCases.py @@ -0,0 +1,112 @@ +# 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. + +""" search test cases from a given file or path """ +import os +import fnmatch +import types +import copy +# TODO: add a function to use suitable import lib for python2 and python3 +import imp + + +class Search(object): + TEST_CASE_FILE_PATTERN = "*_test.py" + + @classmethod + def _search_cases_from_file(cls, file_name): + """ get test cases from test case .py file """ + + print("Try to get cases from: " + file_name) + test_functions = [] + try: + # TODO: add a function to use suitable import lib for python2 and python3 + mod = imp.load_source(str(hash(file_name)), file_name) + for func in [mod.__getattribute__(x) for x in dir(mod) + if isinstance(mod.__getattribute__(x), types.FunctionType)]: + try: + # test method decorator will add test_method attribute to test function + if func.test_method: + test_functions.append(func) + except AttributeError: + continue + except ImportError as e: + print("ImportError: \r\n\tFile:" + file_name + "\r\n\tError:" + str(e)) + for i, test_function in enumerate(test_functions): + print("\t{}. ".format(i+1) + test_function.case_info["name"]) + return test_functions + + @classmethod + def _search_test_case_files(cls, test_case, file_pattern): + """ search all test case files recursively of a path """ + + if not os.path.exists(test_case): + raise OSError("test case path not exist") + if os.path.isdir(test_case): + test_case_files = [] + for root, _, file_names in os.walk(test_case): + for filename in fnmatch.filter(file_names, file_pattern): + test_case_files.append(os.path.join(root, filename)) + else: + test_case_files = [test_case] + return test_case_files + + @classmethod + def replicate_case(cls, case): + """ + Replicate cases according to its filter values. + If one case has specified filter chip=(ESP32, ESP32C), + it will create 2 cases, one for ESP32 and on for ESP32C. + Once the cases are replicated, it's easy to filter those we want to execute. + + :param case: the original case + :return: a list of replicated cases + """ + replicate_config = [] + for key in case.case_info: + if isinstance(case.case_info[key], (list, tuple)): + replicate_config.append(key) + + def _replicate_for_key(case_list, replicate_key, replicate_list): + case_out = [] + for _case in case_list: + for value in replicate_list: + new_case = copy.deepcopy(_case) + new_case.case_info[replicate_key] = value + case_out.append(new_case) + return case_out + + replicated_cases = [case] + for key in replicate_config: + replicated_cases = _replicate_for_key(replicated_cases, key, case.case_info[key]) + + return replicated_cases + + @classmethod + def search_test_cases(cls, test_case): + """ + search all test cases from a folder or file, and then do case replicate. + + :param test_case: test case file(s) path + :return: a list of replicated test methods + """ + test_case_files = cls._search_test_case_files(test_case, cls.TEST_CASE_FILE_PATTERN) + test_cases = [] + for test_case_file in test_case_files: + test_cases += cls._search_cases_from_file(test_case_file) + # handle replicate cases + test_case_out = [] + for case in test_cases: + test_case_out += cls.replicate_case(case) + return test_case_out diff --git a/tools/tiny-test-fw/Utility/__init__.py b/tools/tiny-test-fw/Utility/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/tiny-test-fw/docs/Makefile b/tools/tiny-test-fw/docs/Makefile new file mode 100644 index 000000000..dab95f0ad --- /dev/null +++ b/tools/tiny-test-fw/docs/Makefile @@ -0,0 +1,26 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXAPI = sphinx-apidoc +SPHINXAPISRC = .. +SPHINXBUILD = python -msphinx +SPHINXPROJ = TinyTestFW +SOURCEDIR = . +BUILDDIR = _build + +# define the files to be excluded here +EXCLUEDLIST = "$(SPHINXAPISRC)/example.py" + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXAPI) -o $(SOURCEDIR) $(SPHINXAPISRC) $(EXCLUEDLIST) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/tools/tiny-test-fw/docs/_static/.keep b/tools/tiny-test-fw/docs/_static/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/tools/tiny-test-fw/docs/conf.py b/tools/tiny-test-fw/docs/conf.py new file mode 100644 index 000000000..254f841f7 --- /dev/null +++ b/tools/tiny-test-fw/docs/conf.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# +# TinyTestFW documentation build configuration file, created by +# sphinx-quickstart on Thu Sep 21 20:19:12 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +# import sphinx_rtd_theme + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'TinyTestFW' +copyright = u'2017, Espressif' +author = u'Espressif' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.1' +# The full version, including alpha/beta/rc tags. +release = u'0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'TinyTestFWdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'TinyTestFW.tex', u'TinyTestFW Documentation', + u'He Yinling', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'tinytestfw', u'TinyTestFW Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'TinyTestFW', u'TinyTestFW Documentation', + author, 'TinyTestFW', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/tools/tiny-test-fw/docs/index.rst b/tools/tiny-test-fw/docs/index.rst new file mode 100644 index 000000000..a70e6bf32 --- /dev/null +++ b/tools/tiny-test-fw/docs/index.rst @@ -0,0 +1,139 @@ +.. TinyTestFW documentation master file, created by + sphinx-quickstart on Thu Sep 21 20:19:12 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to TinyTestFW's documentation! +====================================== + +We have a lot of test which depends on interact with DUT via communication port. +Usually we send command to the port and then check response to see if the test succeed. +TinyTestFW is designed for such scenarios. +It supports ESP-IDF applications and is able for other applications by writing new bundles. + +Test FW features +---------------- + +1. Test Environment: + 1. DUT: DUT provides methods to interact with DUT + * read/write through port + * expect method which supports expect one or multiple string or RegEx + * tool methods provided by the tool bundle, like ``start_app``, ``reset`` + 2. App: + * provide some specific features to the test application of DUT, for example: + * SDK path + * SDK tools + * application information like partition table, download configs + 3. Environment Configs: + * support get env configs from config file or auto-detect from current PC + * provide ``get_variable`` method to get variables +2. allow to customize components (DUT, App) to support different devices +3. Integrate to CI: + * provide interfaces for Gitlab-CI + * provide ``search case`` and ``runner`` interfaces, able to integrate with other CI + +Example +------- + +Let's first check a simple simple:: + + import re + import os + import sys + test_fw_path = os.getenv("TEST_FW_PATH") + if test_fw_path: + sys.path.insert(0, test_fw_path) + + import TinyFW + from IDF import IDFApp, IDFDUT + + + @TinyFW.test_method(app=IDFApp.Example, dut=IDFDUT.IDFDUT, env_tag="Example_WIFI", + chip="ESP32", module="examples", execution_time=1) + def test_examples_protocol_https_request(env, extra_data): + """ + steps: | + 1. join AP + 2. connect to www.howsmyssl.com:443 + 3. send http request + """ + dut1 = env.get_dut("https_request", "examples/protocols/https_request") + dut1.start_app() + dut1.expect("Connecting to www.howsmyssl.com:443", timeout=30) + dut1.expect("Performing the SSL/TLS handshake") + dut1.expect("Certificate verified.", timeout=15) + dut1.expect_all(re.compile(r"Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"), + "Reading HTTP response", + timeout=20) + dut1.expect(re.compile(r"Completed (\d) requests")) + + + if __name__ == '__main__': + TinyFW.set_default_config(config_file="EnvConfigTemplate.yml") + test_examples_protocol_https_request() + + +SOP for adding test cases +------------------------- + +1. import test framework: +^^^^^^^^^^^^^^^^^^^^^^^^^ + +* we assume ``TEST_FW_PATH`` is pre-defined before running the tests +* Then we can import python packages and files from ``TEST_FW_PATH`` + +2. define test case: +^^^^^^^^^^^^^^^^^^^^ + +1. define test case ``test_xxx(env, extra_data)`` + * env: instance of test env, see :doc:`Test Env ` for details + * extra_data: extra data passed from test case caller +2. add decorator for test case + * add decorator ``TinyFW.test_method`` to test method + * define default case configs and filters in decorator, see :doc:`TinyFW.test_method ` + +3. execute test cases: +^^^^^^^^^^^^^^^^^^^^^^ + +* define in ``main`` section and execute from this file + 1. set preset configs(optional). If the config is not define in case decorator, it will use the preset configs. + 2. call test case method: + * if you don't pass any arguments, it will use default values + * you can pass ``extra_data`` to test case by adding ``extra_data=some_data`` as kwarg of test case method. + default value for extra_data is None. + * you can overwrite test case config by adding them as kwarg of test case method. + It will overwrite preset configs and case default configs. + + Examples:: + + test_examples_protocol_https_request(extra_data=["data1", "data2"], dut=SomeOtherDUT, env_tag="OtherEnv") + +* or, use ``runner`` to execute. see :doc:`runner ` for details + + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + modules + +Dependency +========== + +Support for both Python2 and Python3 (tested on python 2.7.13 and 3.6.2). + +The following 3rd party lib is required: + + * pyserial + * pyyaml + * xunitgen + +To build document, we need to install ``Sphinx`` and ``sphinx-rtd-theme`` (you may replace this with your own theme). + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/tools/tiny-test-fw/example.py b/tools/tiny-test-fw/example.py new file mode 100644 index 000000000..df1b25576 --- /dev/null +++ b/tools/tiny-test-fw/example.py @@ -0,0 +1,51 @@ +# 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. + +""" example of writing test with TinyTestFW """ +import re +import os +import sys + +# if we want to run test case outside `tiny-test-fw` folder, +# we need to insert tiny-test-fw path into sys path +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_https_request(env, extra_data): + """ + steps: | + 1. join AP + 2. connect to www.howsmyssl.com:443 + 3. send http request + """ + dut1 = env.get_dut("https_request", "examples/protocols/https_request") + dut1.start_app() + dut1.expect(re.compile(r"Connecting to www.howsmyssl.com:443"), timeout=30) + dut1.expect("Performing the SSL/TLS handshake") + dut1.expect("Certificate verified.", timeout=15) + dut1.expect_all(re.compile(r"Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"), + "Reading HTTP response", + timeout=20) + dut1.expect(re.compile(r"Completed (\d) requests")) + + +if __name__ == '__main__': + TinyFW.set_default_config(config_file="EnvConfigTemplate.yml", dut=IDF.IDFDUT) + test_examples_protocol_https_request() From 4cb52e0a3dd79ce8cea262aa441a6ca1c3a8b37f Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Tue, 10 Oct 2017 10:48:57 +0800 Subject: [PATCH 26/44] test: add test for https_request --- .../protocols/https_request/example_test.py | 35 +++++++++++++++++++ .../https_request/main/Kconfig.projbuild | 4 +-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 examples/protocols/https_request/example_test.py diff --git a/examples/protocols/https_request/example_test.py b/examples/protocols/https_request/example_test.py new file mode 100644 index 000000000..b58bdae85 --- /dev/null +++ b/examples/protocols/https_request/example_test.py @@ -0,0 +1,35 @@ +import re +import os +import sys + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_https_request(env, extra_data): + """ + steps: | + 1. join AP + 2. connect to www.howsmyssl.com:443 + 3. send http request + """ + dut1 = env.get_dut("https_request", "examples/protocols/https_request") + dut1.start_app() + dut1.expect("Connecting to www.howsmyssl.com:443", timeout=30) + dut1.expect("Performing the SSL/TLS handshake") + dut1.expect("Certificate verified.", timeout=15) + dut1.expect("Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", timeout=20) + dut1.expect(re.compile(r"Completed (\d) requests")) + + +if __name__ == '__main__': + test_examples_protocol_https_request() diff --git a/examples/protocols/https_request/main/Kconfig.projbuild b/examples/protocols/https_request/main/Kconfig.projbuild index c5d5523a9..1c7241da3 100644 --- a/examples/protocols/https_request/main/Kconfig.projbuild +++ b/examples/protocols/https_request/main/Kconfig.projbuild @@ -8,10 +8,10 @@ config WIFI_SSID config WIFI_PASSWORD string "WiFi Password" - default "myssid" + default "mypassword" help WiFi password (WPA or WPA2) for the example to use. Can be left blank if the network has no security set. -endmenu \ No newline at end of file +endmenu From 8ed14791d02270c96a72d350c0ad8fe59cb8bcb7 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Tue, 10 Oct 2017 10:51:08 +0800 Subject: [PATCH 27/44] make: add make command `print_flash_cmd` --- docs/api-guides/build-system.rst | 9 +-------- make/project.mk | 5 +++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/api-guides/build-system.rst b/docs/api-guides/build-system.rst index 54660741e..ab9ed6492 100644 --- a/docs/api-guides/build-system.rst +++ b/docs/api-guides/build-system.rst @@ -592,14 +592,7 @@ To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment va Save flash arguments -------------------- -There're some scenarios that we want to flash the target board without IDF. For this case we want to save the built binaries, esptool.py and esptool write_flash arguments. It's simple to write a script to save binaries and esptool.py. For flash arguments, we can add the following code to application project makefile:: - - print_flash_cmd: - echo $(ESPTOOL_WRITE_FLASH_OPTIONS) $(ESPTOOL_ALL_FLASH_ARGS) | sed -e 's:'$(PWD)/build/'::g' - -the original ESPTOOL_ALL_FLASH_ARGS are absolute file name. Usually we want to save relative file name so we can move the bin folder to somewhere else. For this case we can use ``sed`` to convert to relative file name, like what we did in the example above. - -When running ``make print_flash_cmd``, it will print the flash arguments:: +There're some scenarios that we want to flash the target board without IDF. For this case we want to save the built binaries, esptool.py and esptool write_flash arguments. It's simple to write a script to save binaries and esptool.py. We can use command ``make print_flash_cmd``, it will print the flash arguments:: --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin diff --git a/make/project.mk b/make/project.mk index deaba74a3..19e4b4250 100644 --- a/make/project.mk +++ b/make/project.mk @@ -38,6 +38,7 @@ help: @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" @echo "make app-clean - Clean just the app" + @echo "make print_flash_cmd - Print the arguments for esptool when flash" @echo "" @echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', " @echo "'make partition_table', etc, etc." @@ -496,6 +497,10 @@ list-components: $(info COMPONENT_PATHS (paths to all components):) $(foreach cp,$(COMPONENT_PATHS),$(info $(cp))) +# print flash command, so users can dump this to config files and download somewhere without idf +print_flash_cmd: + echo $(ESPTOOL_WRITE_FLASH_OPTIONS) $(ESPTOOL_ALL_FLASH_ARGS) | sed -e 's:'$(PWD)/build/'::g' + # Check toolchain version using the output of xtensa-esp32-elf-gcc --version command. # The output normally looks as follows # xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-59-ga194053) 4.8.5 From cd1223a25ed97316ee3ae76f24865809fd5e7777 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Tue, 10 Oct 2017 10:55:25 +0800 Subject: [PATCH 28/44] CI: integrate example test to CI --- .gitlab-ci.yml | 45 +++++++++++++++++++++++++++++++++++--- tools/ci/build_examples.sh | 3 ++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ea5c5981b..982a97bdf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -133,6 +133,7 @@ build_esp_idf_tests: - build_examples/*/*/*/build/*.bin - build_examples/*/*/*/build/*.elf - build_examples/*/*/*/build/*.map + - build_examples/*/*/*/build/download.config - build_examples/*/*/*/build/bootloader/*.bin expire_in: 1 week variables: @@ -411,17 +412,19 @@ check_submodule_sync: assign_test: <<: *build_template stage: assign_test - dependencies: - - build_esp_idf_tests - - build_ssc + # gitlab ci do not support match job with RegEx or wildcard now in dependencies. + # we have a lot build example jobs. now we don't use dependencies, just download all artificats of build stage. variables: UT_BIN_PATH: "tools/unit-test-app/output" OUTPUT_BIN_PATH: "test_bins/ESP32_IDF" + TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw" + EXAMPLE_CONFIG_OUTPUT_PATH: "$CI_PROJECT_DIR/examples/test_configs" artifacts: paths: - test_bins - components/idf_test/*/CIConfigs - components/idf_test/*/TC.sqlite + - $EXAMPLE_CONFIG_OUTPUT_PATH expire_in: 1 mos before_script: *add_gitlab_key_before script: @@ -430,6 +433,8 @@ assign_test: # copy and rename folder name to "UT_config" - for CONFIG in $(ls $UT_BIN_PATH); do cp -r "$UT_BIN_PATH/$CONFIG" "$OUTPUT_BIN_PATH/UT_$CONFIG"; done - cp -r SSC/ssc_bin/* $OUTPUT_BIN_PATH + # assign example tests + - python $TEST_FW_PATH/CIAssignExampleTest.py $IDF_PATH/examples $IDF_PATH/.gitlab-ci.yml $EXAMPLE_CONFIG_OUTPUT_PATH # clone test script to assign tests - git clone $TEST_SCRIPT_REPOSITORY - cd auto_test_script @@ -439,6 +444,34 @@ assign_test: # assgin integration test cases - python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/integration_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins +.example_test_template: &example_test_template + stage: test + when: on_success + only: + - master + - /^release\/v/ + - /^v\d+\.\d+(\.\d+)?($|-)/ + - triggers + # gitlab ci do not support match job with RegEx or wildcard now in dependencies. + # we have a lot build example jobs and the binaries them exceed the limitation of artifacts. + # we can't artifact them in one job. For example test jobs, download all artifacts from previous stages. + artifacts: + when: always + paths: + - $LOG_PATH + expire_in: 6 mos + variables: + TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw" + TEST_CASE_PATH: "$CI_PROJECT_DIR/examples" + CONFIG_FILE: "$CI_PROJECT_DIR/examples/test_configs/$CI_JOB_NAME.yml" + LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS" + script: + # first test if config file exists, if not exist, exit 0 + - test -e $CONFIG_FILE || exit 0 + - cd $TEST_FW_PATH + # run test + - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE + .test_template: &test_template stage: test when: on_success @@ -509,6 +542,12 @@ nvs_compatible_test: # run test - python CIRunner.py -l "$LOG_PATH/$CI_JOB_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH -m $MODULE_UPDATE_FILE +example_test_001_01: + <<: *example_test_template + tags: + - ESP32 + - Example_WIFI + UT_001_01: <<: *unit_test_template tags: diff --git a/tools/ci/build_examples.sh b/tools/ci/build_examples.sh index c22aaf4bd..5da652096 100755 --- a/tools/ci/build_examples.sh +++ b/tools/ci/build_examples.sh @@ -118,7 +118,8 @@ build_example () { ( make MAKEFLAGS= clean && make MAKEFLAGS= defconfig && - make all + make all && + make print_flash_cmd | tail -n 1 > build/download.config ) &> >(tee -a "${BUILDLOG}") || { RESULT=$?; FAILED_EXAMPLES+=" ${EXAMPLE_NAME}" make MAKEFLAGS= V=1 clean defconfig && make V=1 # verbose output for errors From 7cc6b3c5ec523d3ffb4fdb5d8a59223c50e99e33 Mon Sep 17 00:00:00 2001 From: krzychb Date: Tue, 31 Oct 2017 22:52:55 +0100 Subject: [PATCH 29/44] Upgraded Sphinx to latest release 1.6.5 that contains a fix to https://github.com/sphinx-doc/sphinx/issues/4041. Upgraded Breathe to latest release 4.7.3 as well. --- docs/requirements.txt | 4 ++-- docs/sphinx-known-warnings.txt | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 3c54fef29..642f0147f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ # This is a list of python packages used to generate documentation. This file is used with pip: # pip install -r requirements.txt # -sphinx==1.6.3 +sphinx==1.6.5 sphinx-rtd-theme -breathe==4.7.1 +breathe==4.7.3 diff --git a/docs/sphinx-known-warnings.txt b/docs/sphinx-known-warnings.txt index c5d999d1b..2dcc2368a 100644 --- a/docs/sphinx-known-warnings.txt +++ b/docs/sphinx-known-warnings.txt @@ -8,7 +8,7 @@ # # -# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/2683\ +# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/2683 # _build/inc/esp_a2dp_api.inc:line: WARNING: Invalid definition: Expected identifier in nested name. [error at 21] union esp_a2d_mcc_t::@1 esp_a2d_mcc_t::cie @@ -16,10 +16,3 @@ _build/inc/esp_a2dp_api.inc:line: WARNING: Invalid definition: Expected identifi _build/inc/esp_bt_defs.inc:line: WARNING: Invalid definition: Expected identifier in nested name. [error at 21] union esp_bt_uuid_t::@0 esp_bt_uuid_t::uuid ---------------------^ - -# -# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/4041 -# -${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot). -${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot). -${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot). From 481e4365872f65f83494e92dbc1deb0510bfb9f2 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Mon, 16 Oct 2017 16:27:38 +0800 Subject: [PATCH 30/44] Component/bt: modify close event and disconnection event params - modify close event params - modify disconnection event params - modify connect event params --- components/bt/bluedroid/api/include/esp_gattc_api.h | 3 +-- components/bt/bluedroid/api/include/esp_gatts_api.h | 3 +-- components/bt/bluedroid/bta/gatt/bta_gattc_act.c | 5 ++--- components/bt/bluedroid/bta/gatt/bta_gattc_utils.c | 8 +++----- components/bt/bluedroid/bta/include/bta_gatt_api.h | 3 +-- components/bt/bluedroid/bta/include/bta_gattc_int.h | 5 ++--- .../bt/bluedroid/btc/profile/std/gatt/btc_gattc.c | 3 +-- .../bt/bluedroid/btc/profile/std/gatt/btc_gatts.c | 3 +-- .../bluetooth/ble_spp_client/main/spp_client_demo.c | 8 ++------ examples/bluetooth/gatt_client/main/gattc_demo.c | 5 ++--- .../main/example_ble_sec_gattc_demo.c | 2 +- examples/bluetooth/gatt_server/main/gatts_demo.c | 10 ++++------ 12 files changed, 21 insertions(+), 37 deletions(-) diff --git a/components/bt/bluedroid/api/include/esp_gattc_api.h b/components/bt/bluedroid/api/include/esp_gattc_api.h index b72466b82..6a6aa579c 100644 --- a/components/bt/bluedroid/api/include/esp_gattc_api.h +++ b/components/bt/bluedroid/api/include/esp_gattc_api.h @@ -201,7 +201,6 @@ typedef union { * @brief ESP_GATTC_CONNECT_EVT */ struct gattc_connect_evt_param { - esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ } connect; /*!< Gatt client callback param of ESP_GATTC_CONNECT_EVT */ @@ -210,7 +209,7 @@ typedef union { * @brief ESP_GATTC_DISCONNECT_EVT */ struct gattc_disconnect_evt_param { - esp_gatt_status_t status; /*!< Operation status */ + esp_gatt_conn_reason_t reason; /*!< disconnection reason */ uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ } disconnect; /*!< Gatt client callback param of ESP_GATTC_DISCONNECT_EVT */ diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index d964ac9b9..87869bb6f 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -193,7 +193,6 @@ typedef union { struct gatts_connect_evt_param { uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ - bool is_connected; /*!< Indicate it is connected or not */ } connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */ /** @@ -202,7 +201,7 @@ typedef union { struct gatts_disconnect_evt_param { uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ - bool is_connected; /*!< Indicate it is connected or not */ + esp_gatt_conn_reason_t reason; /*!< Indicate the reason of disconnection */ } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ /** diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c index 25814059b..1a9306001 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c @@ -711,7 +711,6 @@ void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) { if (p_rcb) { bta_gattc_send_connect_cback(p_rcb, - BTA_GATT_OK, p_data->int_conn.remote_bda, p_data->int_conn.hdr.layer_specific); @@ -730,7 +729,7 @@ void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) { if (p_rcb) { bta_gattc_send_disconnect_cback(p_rcb, - BTA_GATT_OK, + p_data->int_conn.reason, p_data->int_conn.remote_bda, p_data->int_conn.hdr.layer_specific); @@ -793,7 +792,7 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) { cb_data.close.status = GATT_Disconnect(p_data->hdr.layer_specific); } else if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) { - cb_data.close.status = p_data->int_conn.reason; + cb_data.close.status = BTA_GATT_OK; cb_data.close.reason = p_data->int_conn.reason; } diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c index dfa685f67..864a85fe8 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c @@ -653,15 +653,13 @@ void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status ** Returns ** *******************************************************************************/ -void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, - BD_ADDR remote_bda, UINT16 conn_id) +void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id) { tBTA_GATTC cb_data; if (p_clreg->p_cback) { memset(&cb_data, 0, sizeof(tBTA_GATTC)); - cb_data.connect.status = status; cb_data.connect.client_if = p_clreg->client_if; cb_data.connect.conn_id = conn_id; bdcpy(cb_data.connect.remote_bda, remote_bda); @@ -679,7 +677,7 @@ void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS sta ** Returns ** *******************************************************************************/ -void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, +void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason, BD_ADDR remote_bda, UINT16 conn_id) { tBTA_GATTC cb_data; @@ -687,7 +685,7 @@ void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS if (p_clreg->p_cback) { memset(&cb_data, 0, sizeof(tBTA_GATTC)); - cb_data.disconnect.status = status; + cb_data.disconnect.reason = reason; cb_data.disconnect.client_if = p_clreg->client_if; cb_data.disconnect.conn_id = conn_id; bdcpy(cb_data.disconnect.remote_bda, remote_bda); diff --git a/components/bt/bluedroid/bta/include/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta_gatt_api.h index 35fb42d71..6c17368f5 100644 --- a/components/bt/bluedroid/bta/include/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta_gatt_api.h @@ -367,14 +367,13 @@ typedef struct { } tBTA_GATTC_ENC_CMPL_CB; typedef struct { - tBTA_GATT_STATUS status; UINT16 conn_id; tBTA_GATTC_IF client_if; BD_ADDR remote_bda; } tBTA_GATTC_CONNECT; typedef struct { - tBTA_GATT_STATUS status; + tGATT_DISCONN_REASON reason; UINT16 conn_id; tBTA_GATTC_IF client_if; BD_ADDR remote_bda; diff --git a/components/bt/bluedroid/bta/include/bta_gattc_int.h b/components/bt/bluedroid/bta/include/bta_gattc_int.h index 3a1e71915..f54c0fbee 100644 --- a/components/bt/bluedroid/bta/include/bta_gattc_int.h +++ b/components/bt/bluedroid/bta/include/bta_gattc_int.h @@ -438,9 +438,8 @@ extern void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB * extern void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data); extern void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, BD_ADDR remote_bda, UINT16 conn_id, tBTA_TRANSPORT transport, UINT16 mtu); -extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, - BD_ADDR remote_bda, UINT16 conn_id); -extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, +extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id); +extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason, BD_ADDR remote_bda, UINT16 conn_id); extern void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); extern void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data); diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c index d3149bf85..5b33f0f77 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -858,7 +858,6 @@ void btc_gattc_cb_handler(btc_msg_t *msg) tBTA_GATTC_CONNECT *connect = &arg->connect; gattc_if = connect->client_if; - param.connect.status = connect->status; param.connect.conn_id = BTC_GATT_GET_CONN_ID(connect->conn_id); memcpy(param.connect.remote_bda, connect->remote_bda, sizeof(esp_bd_addr_t)); btc_gattc_cb_to_app(ESP_GATTC_CONNECT_EVT, gattc_if, ¶m); @@ -879,7 +878,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) tBTA_GATTC_DISCONNECT *disconnect = &arg->disconnect; gattc_if = disconnect->client_if; - param.disconnect.status = disconnect->status; + param.disconnect.reason = disconnect->reason; param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(disconnect->conn_id); memcpy(param.disconnect.remote_bda, disconnect->remote_bda, sizeof(esp_bd_addr_t)); btc_gattc_cb_to_app(ESP_GATTC_DISCONNECT_EVT, gattc_if, ¶m); diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index df2d74212..75c53b893 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -849,7 +849,6 @@ void btc_gatts_cb_handler(btc_msg_t *msg) case BTA_GATTS_CONNECT_EVT: gatts_if = p_data->conn.server_if; param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); - param.connect.is_connected = true; memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); @@ -857,7 +856,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) case BTA_GATTS_DISCONNECT_EVT: gatts_if = p_data->conn.server_if; param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); - param.disconnect.is_connected = false; + param.disconnect.reason = p_data->conn.reason; memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); diff --git a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c index 7b570eaf4..9eadc13b7 100644 --- a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c +++ b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c @@ -698,21 +698,17 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_ble_gap_set_scan_params(&ble_scan_params); break; case ESP_GATTC_CONNECT_EVT: - if(p_data->connect.status != ESP_GATT_OK){ - ESP_LOGI(GATTC_TAG, "connect fail, status = %d", p_data->connect.status); - break; - } spp_gattc_if = gattc_if; is_connect = true; spp_conn_id = p_data->connect.conn_id; memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); - ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d, status =%d", spp_conn_id, gattc_if, p_data->connect.status); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d", spp_conn_id, gattc_if); ESP_LOGI(GATTC_TAG, "REMOTE BDA:"); esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t)); esp_ble_gattc_search_service(gattc_if, spp_conn_id, NULL); break; case ESP_GATTC_DISCONNECT_EVT: - ESP_LOGI(GATTC_TAG, "disconnect , status = %d", p_data->disconnect.status); + ESP_LOGI(GATTC_TAG, "disconnect"); free_gattc_srv_db(); esp_ble_gap_start_scanning(0xffff); break; diff --git a/examples/bluetooth/gatt_client/main/gattc_demo.c b/examples/bluetooth/gatt_client/main/gattc_demo.c index 14290c2d8..a2be74da5 100644 --- a/examples/bluetooth/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/gatt_client/main/gattc_demo.c @@ -112,8 +112,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } break; case ESP_GATTC_CONNECT_EVT:{ - //p_data->connect.status always be ESP_GATT_OK - ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d, status %d", p_data->connect.conn_id, gattc_if, p_data->connect.status); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d", p_data->connect.conn_id, gattc_if); gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id; memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); ESP_LOGI(GATTC_TAG, "REMOTE BDA:"); @@ -296,7 +295,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_DISCONNECT_EVT: connect = false; get_server = false; - ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, status = %d", p_data->disconnect.status); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason); break; default: break; diff --git a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c index 6ae360e65..7838dd89d 100644 --- a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c +++ b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c @@ -306,7 +306,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGI(GATTC_TAG, "Write char success "); break; case ESP_GATTC_DISCONNECT_EVT: - ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, status = %d", p_data->disconnect.status); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason); connect = false; get_service = false; break; diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index a60d567c8..885702e9f 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -478,11 +478,10 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms conn_params.timeout = 400; // timeout = 400*10ms = 4000ms - ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d", + ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:", param->connect.conn_id, param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], - param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5], - param->connect.is_connected); + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]); gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; //start sent the update connection parameters to the peer device. esp_ble_gap_update_conn_params(&conn_params); @@ -624,11 +623,10 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_STOP_EVT: break; case ESP_GATTS_CONNECT_EVT: - ESP_LOGI(GATTS_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d", + ESP_LOGI(GATTS_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:", param->connect.conn_id, param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], - param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5], - param->connect.is_connected); + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]); gl_profile_tab[PROFILE_B_APP_ID].conn_id = param->connect.conn_id; break; case ESP_GATTS_DISCONNECT_EVT: From e7020460c622b502a6a74a79cbdf421903e3d8df Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Thu, 26 Oct 2017 14:39:49 +0800 Subject: [PATCH 31/44] Component/bt: add value callback when send notification or indicate failed --- .../bt/bluedroid/api/include/esp_gatts_api.h | 2 ++ components/bt/bluedroid/bta/gatt/bta_gatts_act.c | 9 +++++++++ components/bt/bluedroid/bta/include/bta_gatt_api.h | 2 ++ .../bt/bluedroid/btc/profile/std/gatt/btc_gatts.c | 11 +++++++++++ components/bt/bluedroid/stack/gatt/gatt_api.c | 5 ++++- examples/bluetooth/gatt_client/main/gattc_demo.c | 6 +++++- examples/bluetooth/gatt_server/main/gatts_demo.c | 14 ++++++++++++-- 7 files changed, 45 insertions(+), 4 deletions(-) diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index d964ac9b9..07869ba05 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -119,6 +119,8 @@ typedef union { struct gatts_conf_evt_param { esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ + uint16_t len; /*!< The indication or notification value length, len is valid when send notification or indication failed */ + uint8_t *value; /*!< The indication or notification value , value is valid when send notification or indication failed */ } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ /** diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index c617fef82..4c1380a3c 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -692,6 +692,15 @@ void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) cb_data.req_data.status = status; cb_data.req_data.conn_id = p_msg->api_indicate.hdr.layer_specific; + cb_data.req_data.value =(uint8_t *)osi_malloc(p_msg->api_indicate.len); + if (cb_data.req_data.value != NULL){ + memset(cb_data.req_data.value, 0, p_msg->api_indicate.len); + cb_data.req_data.data_len = p_msg->api_indicate.len; + memcpy(cb_data.req_data.value, p_msg->api_indicate.value, p_msg->api_indicate.len); + }else{ + cb_data.req_data.data_len = 0; + APPL_TRACE_ERROR("%s, malloc failed", __func__); + } (*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data); } } else { diff --git a/components/bt/bluedroid/bta/include/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta_gatt_api.h index 35fb42d71..9342e55c4 100644 --- a/components/bt/bluedroid/bta/include/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta_gatt_api.h @@ -523,6 +523,8 @@ typedef struct { UINT32 trans_id; UINT16 conn_id; tBTA_GATTS_REQ_DATA *p_data; + UINT16 data_len; + UINT8 *value; } tBTA_GATTS_REQ; typedef struct { diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index df2d74212..635e4a740 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -527,6 +527,11 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg, tBTA_GATTS *p_data) osi_free(p_data->req_data.p_data); } break; + case BTA_GATTS_CONF_EVT: + if (p_data && p_data->req_data.value){ + osi_free(p_data->req_data.value); + } + break; default: break; } @@ -787,6 +792,12 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.conf.status = p_data->req_data.status; + if (p_data->req_data.status != ESP_GATT_OK && p_data->req_data.value){ + param.conf.len = p_data->req_data.data_len; + param.conf.value = p_data->req_data.value; + }else{ + param.conf.len = 0; + } btc_gatts_cb_to_app(ESP_GATTS_CONF_EVT, gatts_if, ¶m); break; case BTA_GATTS_CREATE_EVT: diff --git a/components/bt/bluedroid/stack/gatt/gatt_api.c b/components/bt/bluedroid/stack/gatt/gatt_api.c index 6737183fd..44b8527f9 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/bluedroid/stack/gatt/gatt_api.c @@ -567,7 +567,6 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U tGATT_VALUE indication; BT_HDR *p_msg; - tGATT_VALUE *p_buf; tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); tGATT_REG *p_reg = gatt_get_regcb(gatt_if); @@ -591,12 +590,16 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U indication.auth_req = GATT_AUTH_REQ_NONE; if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { + /* TODO: need to further check whether deleting pending queue here cause reducing transport performance */ + /* GATT_TRACE_DEBUG ("Add a pending indication"); if ((p_buf = gatt_add_pending_ind(p_tcb, &indication)) != NULL) { cmd_status = GATT_SUCCESS; } else { cmd_status = GATT_NO_RESOURCES; } + */ + return GATT_BUSY; } else { if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) { diff --git a/examples/bluetooth/gatt_client/main/gattc_demo.c b/examples/bluetooth/gatt_client/main/gattc_demo.c index 14290c2d8..1215b5d70 100644 --- a/examples/bluetooth/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/gatt_client/main/gattc_demo.c @@ -257,7 +257,11 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_NOTIFY_EVT: - ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:"); + if (p_data->notify.is_notify){ + ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:"); + }else{ + ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value:"); + } esp_log_buffer_hex(GATTC_TAG, p_data->notify.value, p_data->notify.value_len); break; case ESP_GATTC_WRITE_DESCR_EVT: diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index a60d567c8..5902ee7bf 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -411,7 +411,6 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_MTU_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu); break; - case ESP_GATTS_CONF_EVT: case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: @@ -492,6 +491,12 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT"); esp_ble_gap_start_advertising(&adv_params); break; + case ESP_GATTS_CONF_EVT: + ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT, status %d", param->conf.status); + if (param->conf.status != ESP_GATT_OK){ + esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len); + } + break; case ESP_GATTS_OPEN_EVT: case ESP_GATTS_CANCEL_OPEN_EVT: case ESP_GATTS_CLOSE_EVT: @@ -578,7 +583,6 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_MTU_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu); break; - case ESP_GATTS_CONF_EVT: case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: @@ -631,6 +635,12 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i param->connect.is_connected); gl_profile_tab[PROFILE_B_APP_ID].conn_id = param->connect.conn_id; break; + case ESP_GATTS_CONF_EVT: + ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT status %d", param->conf.status); + if (param->conf.status != ESP_GATT_OK){ + esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len); + } + break; case ESP_GATTS_DISCONNECT_EVT: case ESP_GATTS_OPEN_EVT: case ESP_GATTS_CANCEL_OPEN_EVT: From 9bfb45aa1d6c90ee1260bb1019d46e5da98ee742 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 2 Nov 2017 13:57:26 +0800 Subject: [PATCH 32/44] soc/rtc: fix increased current consumption in light sleep This fixes a configuration issue of RTC, which caused light sleep current to be 1.35mA instead of 0.85mA. --- components/soc/esp32/rtc_sleep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/soc/esp32/rtc_sleep.c b/components/soc/esp32/rtc_sleep.c index 4294fa59b..0b20692fa 100644 --- a/components/soc/esp32/rtc_sleep.c +++ b/components/soc/esp32/rtc_sleep.c @@ -195,7 +195,7 @@ void rtc_sleep_init(rtc_sleep_config_t cfg) RTC_CNTL_RFRX_PBUS_PU | RTC_CNTL_TXRF_I2C_PU); } else { CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_PD_EN); - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_FORCE_NOSLEEP); + REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0); } if (cfg.vddsdio_pd_en) { From bd6394db92d047cddb12aa3ee7e429b294e9a65f Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Wed, 1 Nov 2017 17:05:38 +0800 Subject: [PATCH 33/44] component/bt: clean up WIFI_CLK_EN_REG settings for Bluetooth 1. move settings of WIFI_CLK_EN_REG for bluetooth into controller init/deinit APIs 2. modify the bit mask used in phy_rf init/deinit to use WIFI-BT shared bits --- components/bt/bt.c | 5 +++++ components/bt/lib | 2 +- components/driver/include/driver/periph_ctrl.h | 2 ++ components/driver/periph_ctrl.c | 9 +++++++++ components/esp32/phy_init.c | 5 +++-- components/soc/esp32/include/soc/dport_reg.h | 3 +++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/components/bt/bt.c b/components/bt/bt.c index 6f351cad1..d34491472 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -36,6 +36,7 @@ #include "esp_log.h" #include "esp_pm.h" #include "esp_ipc.h" +#include "driver/periph_ctrl.h" #if CONFIG_BT_ENABLED @@ -482,6 +483,8 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) btdm_controller_mem_init(); + periph_module_enable(PERIPH_BT_MODULE); + btdm_cfg_mask = btdm_config_mask_load(); ret = btdm_controller_init(btdm_cfg_mask, cfg); @@ -507,6 +510,8 @@ esp_err_t esp_bt_controller_deinit(void) return ESP_ERR_NO_MEM; } + periph_module_disable(PERIPH_BT_MODULE); + btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE; #ifdef CONFIG_PM_ENABLE diff --git a/components/bt/lib b/components/bt/lib index 9a8e4ee15..8e62573de 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 9a8e4ee159e59bb7308f3b5f6fcb89049da48777 +Subproject commit 8e62573de3dd9565b2209a3e8fe19a9900320b77 diff --git a/components/driver/include/driver/periph_ctrl.h b/components/driver/include/driver/periph_ctrl.h index e523598fa..38770a060 100644 --- a/components/driver/include/driver/periph_ctrl.h +++ b/components/driver/include/driver/periph_ctrl.h @@ -49,6 +49,8 @@ typedef enum { PERIPH_SDIO_SLAVE_MODULE, PERIPH_CAN_MODULE, PERIPH_EMAC_MODULE, + PERIPH_BT_MODULE, + PERIPH_WIFI_BT_COMMON_MODULE, } periph_module_t; /** diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index c61e2a4cc..d63206df3 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -106,6 +106,10 @@ static uint32_t get_clk_en_mask(periph_module_t periph) return DPORT_CAN_CLK_EN; case PERIPH_EMAC_MODULE: return DPORT_WIFI_CLK_EMAC_EN; + case PERIPH_BT_MODULE: + return DPORT_WIFI_CLK_BT_EN_M; + case PERIPH_WIFI_BT_COMMON_MODULE: + return DPORT_WIFI_CLK_WIFI_BT_COMMON_M; default: return 0; } @@ -166,6 +170,9 @@ static uint32_t get_rst_en_mask(periph_module_t periph) return DPORT_CAN_RST; case PERIPH_EMAC_MODULE: return DPORT_WIFI_CLK_EMAC_EN; + case PERIPH_BT_MODULE: + case PERIPH_WIFI_BT_COMMON_MODULE: + return 0; default: return 0; } @@ -179,6 +186,8 @@ static bool is_wifi_clk_peripheral(periph_module_t periph) case PERIPH_SDMMC_MODULE: case PERIPH_SDIO_SLAVE_MODULE: case PERIPH_EMAC_MODULE: + case PERIPH_BT_MODULE: + case PERIPH_WIFI_BT_COMMON_MODULE: return true; default: return false; diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index f92f58abe..ac114e950 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -35,6 +35,7 @@ #include "phy.h" #include "phy_init_data.h" #include "esp_coexist.h" +#include "driver/periph_ctrl.h" static const char* TAG = "phy_init"; @@ -51,7 +52,7 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, _lock_acquire(&s_phy_rf_init_lock); if (s_phy_rf_init_count == 0) { // Enable WiFi peripheral clock - DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN | DPORT_WIFI_CLK_RNG_EN); + periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE); ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, calibration_data, mode); phy_set_wifi_mode_only(0); @@ -76,7 +77,7 @@ esp_err_t esp_phy_rf_deinit(void) // Disable PHY and RF. phy_close_rf(); // Disable WiFi peripheral clock. Do not disable clock for hardware RNG - DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN); + periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE); } else { #if CONFIG_SW_COEXIST_ENABLE coex_deinit(); diff --git a/components/soc/esp32/include/soc/dport_reg.h b/components/soc/esp32/include/soc/dport_reg.h index 98e603652..3c2ae160d 100644 --- a/components/soc/esp32/include/soc/dport_reg.h +++ b/components/soc/esp32/include/soc/dport_reg.h @@ -1053,6 +1053,9 @@ #define DPORT_WIFI_CLK_BT_EN_M ((DPORT_WIFI_CLK_BT_EN_V)<<(DPORT_WIFI_CLK_BT_EN_S)) #define DPORT_WIFI_CLK_BT_EN_V 0x61 #define DPORT_WIFI_CLK_BT_EN_S 11 +/* Mask for clock bits used by both WIFI and Bluetooth, bit 0, 3, 6, 7, 8, 9 */ +#define DPORT_WIFI_CLK_WIFI_BT_COMMON_M 0x000003c9 + /* Remaining single bit clock masks */ #define DPORT_WIFI_CLK_SDIOSLAVE_EN BIT(4) #define DPORT_WIFI_CLK_UNUSED_BIT5 BIT(5) From 42cefc173fdd0ac510726330081b16361e3cf0fd Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Sat, 28 Oct 2017 10:19:49 +0800 Subject: [PATCH 34/44] refractor WiFi clock setting Do not set WiFi clock in PHY initializing function, move it to WiFi start/stop function. --- components/driver/include/driver/periph_ctrl.h | 2 ++ components/driver/periph_ctrl.c | 13 ++++++++++--- components/esp32/clk.c | 4 ++++ components/esp32/lib | 2 +- components/esp32/phy_init.c | 4 ++-- components/soc/esp32/include/soc/dport_reg.h | 6 +++--- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/components/driver/include/driver/periph_ctrl.h b/components/driver/include/driver/periph_ctrl.h index 38770a060..b41810090 100644 --- a/components/driver/include/driver/periph_ctrl.h +++ b/components/driver/include/driver/periph_ctrl.h @@ -49,6 +49,8 @@ typedef enum { PERIPH_SDIO_SLAVE_MODULE, PERIPH_CAN_MODULE, PERIPH_EMAC_MODULE, + PERIPH_RNG_MODULE, + PERIPH_WIFI_MODULE, PERIPH_BT_MODULE, PERIPH_WIFI_BT_COMMON_MODULE, } periph_module_t; diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index d63206df3..67431a427 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -106,6 +106,10 @@ static uint32_t get_clk_en_mask(periph_module_t periph) return DPORT_CAN_CLK_EN; case PERIPH_EMAC_MODULE: return DPORT_WIFI_CLK_EMAC_EN; + case PERIPH_RNG_MODULE: + return DPORT_WIFI_CLK_RNG_EN; + case PERIPH_WIFI_MODULE: + return DPORT_WIFI_CLK_WIFI_EN_M; case PERIPH_BT_MODULE: return DPORT_WIFI_CLK_BT_EN_M; case PERIPH_WIFI_BT_COMMON_MODULE: @@ -163,13 +167,14 @@ static uint32_t get_rst_en_mask(periph_module_t periph) case PERIPH_SPI_DMA_MODULE: return DPORT_SPI_DMA_RST; case PERIPH_SDMMC_MODULE: - return DPORT_WIFI_CLK_SDIO_HOST_EN; + return DPORT_SDIO_HOST_RST; case PERIPH_SDIO_SLAVE_MODULE: - return DPORT_WIFI_CLK_SDIOSLAVE_EN; + return DPORT_SDIO_RST; case PERIPH_CAN_MODULE: return DPORT_CAN_RST; case PERIPH_EMAC_MODULE: - return DPORT_WIFI_CLK_EMAC_EN; + return DPORT_EMAC_RST; + case PERIPH_WIFI_MODULE: case PERIPH_BT_MODULE: case PERIPH_WIFI_BT_COMMON_MODULE: return 0; @@ -186,6 +191,8 @@ static bool is_wifi_clk_peripheral(periph_module_t periph) case PERIPH_SDMMC_MODULE: case PERIPH_SDIO_SLAVE_MODULE: case PERIPH_EMAC_MODULE: + case PERIPH_RNG_MODULE: + case PERIPH_WIFI_MODULE: case PERIPH_BT_MODULE: case PERIPH_WIFI_BT_COMMON_MODULE: return true; diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 6e01cc2ea..34b36b9bb 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -29,6 +29,7 @@ #include "soc/rtc_cntl_reg.h" #include "soc/dport_reg.h" #include "soc/i2s_reg.h" +#include "driver/periph_ctrl.h" #include "xtensa/core-macros.h" /* Number of cycles to wait from the 32k XTAL oscillator to consider it running. @@ -236,4 +237,7 @@ void esp_perip_clk_init(void) /* Disable WiFi/BT/SDIO clocks. */ DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, wifi_bt_sdio_clk); + + /* Enable RNG clock. */ + periph_module_enable(PERIPH_RNG_MODULE); } diff --git a/components/esp32/lib b/components/esp32/lib index 4d59fe962..f5733f50c 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 4d59fe9623f5a7cab7ef4b0b4cda1772d4795631 +Subproject commit f5733f50ce43bf54d18328499f3cf8cf61c7087d diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index ac114e950..94a89afd6 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -51,7 +51,7 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, _lock_acquire(&s_phy_rf_init_lock); if (s_phy_rf_init_count == 0) { - // Enable WiFi peripheral clock + // Enable WiFi/BT common peripheral clock periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE); ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, calibration_data, mode); @@ -76,7 +76,7 @@ esp_err_t esp_phy_rf_deinit(void) if (s_phy_rf_init_count == 1) { // Disable PHY and RF. phy_close_rf(); - // Disable WiFi peripheral clock. Do not disable clock for hardware RNG + // Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE); } else { #if CONFIG_SW_COEXIST_ENABLE diff --git a/components/soc/esp32/include/soc/dport_reg.h b/components/soc/esp32/include/soc/dport_reg.h index 3c2ae160d..b7c9bdb90 100644 --- a/components/soc/esp32/include/soc/dport_reg.h +++ b/components/soc/esp32/include/soc/dport_reg.h @@ -1043,10 +1043,10 @@ #define DPORT_WIFI_CLK_EN_V 0xFFFFFFFF #define DPORT_WIFI_CLK_EN_S 0 -/* Mask for all Wifi clock bits - 0, 1, 2, 3, 6, 7, 8, 9, 10, 15 */ -#define DPORT_WIFI_CLK_WIFI_EN 0x000007cf +/* Mask for all Wifi clock bits - 1, 2, 10 */ +#define DPORT_WIFI_CLK_WIFI_EN 0x00000406 #define DPORT_WIFI_CLK_WIFI_EN_M ((DPORT_WIFI_CLK_WIFI_EN_V)<<(DPORT_WIFI_CLK_WIFI_EN_S)) -#define DPORT_WIFI_CLK_WIFI_EN_V 0x1FF +#define DPORT_WIFI_CLK_WIFI_EN_V 0x406 #define DPORT_WIFI_CLK_WIFI_EN_S 0 /* Mask for all Bluetooth clock bits - 11, 16, 17 */ #define DPORT_WIFI_CLK_BT_EN 0x61 From 27b52dfd18dbc489b012848fef549916e4f58272 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Thu, 2 Nov 2017 10:37:59 +0800 Subject: [PATCH 35/44] fix some phy bugs 1. V366, fix a problem which initialize current can reach 800mA. 2. V365, fix a problem for pll_cap tracking in Coexist (BT & WIFI) mode. The problem will make Coexist (BT & WIFI) WIFI AP mode TX Fail in high temperature(>70). 3. V364, fix a bug of BT and Wifi coexist (hung in function of force_wifi_mode()) --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index f5733f50c..8d2b43535 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit f5733f50ce43bf54d18328499f3cf8cf61c7087d +Subproject commit 8d2b43535ea5af93e1b0fc0d04eb1913b5440b8b From 849ad9b37b71ca3b4d1f2df042a4d39b7637eb37 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 3 Nov 2017 14:49:51 +0800 Subject: [PATCH 36/44] tools/unit-test-app: fix build warning --- tools/unit-test-app/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/unit-test-app/Makefile b/tools/unit-test-app/Makefile index e4f05ec99..13873900e 100644 --- a/tools/unit-test-app/Makefile +++ b/tools/unit-test-app/Makefile @@ -7,9 +7,6 @@ PROJECT_NAME := unit-test-app include $(IDF_PATH)/make/project.mk -print_flash_cmd: - echo $(ESPTOOL_WRITE_FLASH_OPTIONS) $(ESPTOOL_ALL_FLASH_ARGS) | sed -e 's:'$(PWD)/build/'::g' - # List of unit-test-app configurations. # Each file in configs/ directory defines a configuration. The format is the # same as sdkconfig file. Configuration is applied on top of sdkconfig.defaults From 5a294c9acd15ba1376dce97a4ec5c7e34f756071 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 1 Nov 2017 16:26:59 +0800 Subject: [PATCH 37/44] soc/gpio: fix description of GPIO_STRAP_REG --- components/soc/esp32/include/soc/gpio_reg.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/soc/esp32/include/soc/gpio_reg.h b/components/soc/esp32/include/soc/gpio_reg.h index 1f4b08d43..8168f4ba1 100644 --- a/components/soc/esp32/include/soc/gpio_reg.h +++ b/components/soc/esp32/include/soc/gpio_reg.h @@ -129,8 +129,7 @@ #define GPIO_STRAP_REG (DR_REG_GPIO_BASE + 0x0038) /* GPIO_STRAPPING : RO ;bitpos:[15:0] ;default: ; */ -/*description: GPIO strapping results: {2'd0 boot_sel_dig[7:1] vsdio_boot_sel - boot_sel_chip[5:0]}. Boot_sel_dig[7:1]: {U0RXD SD_CLK SD_CMD SD_DATA0 SD_DATA1 SD_DATA2 SD_DATA3}. vsdio_boot_sel: MTDI. boot_sel_chip[5:0]: {GPIO0 U0TXD GPIO2 GPIO4 MTDO GPIO5}*/ +/*description: {10'b0, MTDI, GPIO0, GPIO2, GPIO4, MTDO, GPIO5} */ #define GPIO_STRAPPING 0x0000FFFF #define GPIO_STRAPPING_M ((GPIO_STRAPPING_V)<<(GPIO_STRAPPING_S)) #define GPIO_STRAPPING_V 0xFFFF From fb9c106bcbbcc4e0f30bd7949eebf32824cb7a4d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 1 Nov 2017 15:16:32 +0800 Subject: [PATCH 38/44] soc/rtc: add function to get/set VDDSDIO configuration Also consider case of VDDSDIO force powered on in rtc_sleep. --- components/esp32/sleep_modes.c | 16 +++++++-- components/soc/esp32/include/soc/rtc.h | 30 ++++++++++++++++ components/soc/esp32/rtc_init.c | 50 ++++++++++++++++++++++++++ components/soc/esp32/rtc_sleep.c | 8 ++--- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/components/esp32/sleep_modes.c b/components/esp32/sleep_modes.c index 6927716c6..240363830 100644 --- a/components/esp32/sleep_modes.c +++ b/components/esp32/sleep_modes.c @@ -198,14 +198,22 @@ static void rtc_wdt_disable() * Placed into IRAM as flash may need some time to be powered on. */ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, - rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us) IRAM_ATTR __attribute__((noinline)); + rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us, + rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline)); static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, - rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us) + rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us, + rtc_vddsdio_config_t vddsdio_config) { // Enter sleep esp_err_t err = esp_sleep_start(pd_flags); + // If VDDSDIO regulator was controlled by RTC registers before sleep, + // restore the configuration. + if (vddsdio_config.force) { + rtc_vddsdio_set_config(vddsdio_config); + } + // Restore CPU frequency rtc_clk_cpu_freq_set(cpu_freq); @@ -244,6 +252,7 @@ esp_err_t esp_light_sleep_start() s_config.sleep_duration -= flash_enable_time_us; } #endif //CONFIG_SPIRAM_SUPPORT + rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); // Safety net: enable WDT in case exit from light sleep fails rtc_wdt_enable(1000); @@ -252,7 +261,8 @@ esp_err_t esp_light_sleep_start() rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get(); // Enter sleep, then wait for flash to be ready on wakeup - esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq, flash_enable_time_us); + esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq, + flash_enable_time_us, vddsdio_config); // At this point, if FRC1 is used for timekeeping, time will be lagging behind. // This will update the microsecond count based on RTC timer. diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index f13c113b4..e600e39e3 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -554,6 +554,36 @@ typedef struct { */ void rtc_init(rtc_config_t cfg); +/** + * Structure describing vddsdio configuration + */ +typedef struct { + uint32_t force : 1; //!< If 1, use configuration from RTC registers; if 0, use EFUSE/bootstrapping pins. + uint32_t enable : 1; //!< Enable VDDSDIO regulator + uint32_t tieh : 1; //!< Select VDDSDIO voltage: 1 — 1.8V, 0 — 3.3V + uint32_t drefh : 2; //!< Tuning parameter for VDDSDIO regulator + uint32_t drefm : 2; //!< Tuning parameter for VDDSDIO regulator + uint32_t drefl : 2; //!< Tuning parameter for VDDSDIO regulator +} rtc_vddsdio_config_t; + +/** + * Get current VDDSDIO configuration + * If VDDSDIO configuration is overridden by RTC, get values from RTC + * Otherwise, if VDDSDIO is configured by EFUSE, get values from EFUSE + * Otherwise, use default values and the level of MTDI bootstrapping pin. + * @return currently used VDDSDIO configuration + */ +rtc_vddsdio_config_t rtc_vddsdio_get_config(); + +/** + * Set new VDDSDIO configuration using RTC registers. + * If config.force == 1, this overrides configuration done using bootstrapping + * pins and EFUSE. + * + * @param config new VDDSDIO configuration + */ +void rtc_vddsdio_set_config(rtc_vddsdio_config_t config); + #ifdef __cplusplus } diff --git a/components/soc/esp32/rtc_init.c b/components/soc/esp32/rtc_init.c index ff7b6b73c..44786fbcc 100644 --- a/components/soc/esp32/rtc_init.c +++ b/components/soc/esp32/rtc_init.c @@ -18,6 +18,8 @@ #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/dport_reg.h" +#include "soc/efuse_reg.h" +#include "soc/gpio_reg.h" void rtc_init(rtc_config_t cfg) @@ -94,3 +96,51 @@ void rtc_init(rtc_config_t cfg) CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_NOISO); } } + +rtc_vddsdio_config_t rtc_vddsdio_get_config() +{ + rtc_vddsdio_config_t result; + uint32_t sdio_conf_reg = REG_READ(RTC_CNTL_SDIO_CONF_REG); + result.drefh = (sdio_conf_reg & RTC_CNTL_DREFH_SDIO_M) >> RTC_CNTL_DREFH_SDIO_S; + result.drefm = (sdio_conf_reg & RTC_CNTL_DREFM_SDIO_M) >> RTC_CNTL_DREFM_SDIO_S; + result.drefl = (sdio_conf_reg & RTC_CNTL_DREFL_SDIO_M) >> RTC_CNTL_DREFL_SDIO_S; + if (sdio_conf_reg & RTC_CNTL_SDIO_FORCE) { + // Get configuration from RTC + result.force = 1; + result.enable = (sdio_conf_reg & RTC_CNTL_XPD_SDIO_REG_M) >> RTC_CNTL_XPD_SDIO_REG_S; + result.tieh = (sdio_conf_reg & RTC_CNTL_SDIO_TIEH_M) >> RTC_CNTL_SDIO_TIEH_S; + return result; + } + uint32_t efuse_reg = REG_READ(EFUSE_BLK0_RDATA4_REG); + if (efuse_reg & EFUSE_RD_SDIO_FORCE) { + // Get configuration from EFUSE + result.force = 0; + result.enable = (efuse_reg & EFUSE_RD_XPD_SDIO_REG_M) >> EFUSE_RD_XPD_SDIO_REG_S; + result.tieh = (efuse_reg & EFUSE_RD_SDIO_TIEH_M) >> EFUSE_RD_SDIO_TIEH_S; + // in this case, DREFH/M/L are also set from EFUSE + result.drefh = (efuse_reg & EFUSE_RD_SDIO_DREFH_M) >> EFUSE_RD_SDIO_DREFH_S; + result.drefm = (efuse_reg & EFUSE_RD_SDIO_DREFM_M) >> EFUSE_RD_SDIO_DREFM_S; + result.drefl = (efuse_reg & EFUSE_RD_SDIO_DREFL_M) >> EFUSE_RD_SDIO_DREFL_S; + return result; + } + + // Otherwise, VDD_SDIO is controlled by bootstrapping pin + uint32_t strap_reg = REG_READ(GPIO_STRAP_REG); + result.force = 0; + result.tieh = (strap_reg & BIT(5)) ? 0 : 1; + result.enable = result.tieh == 0; // only power on the regulator if VDD=1.8 + return result; +} + +void rtc_vddsdio_set_config(rtc_vddsdio_config_t config) +{ + uint32_t val = 0; + val |= (config.force << RTC_CNTL_SDIO_FORCE_S); + val |= (config.enable << RTC_CNTL_XPD_SDIO_REG_S); + val |= (config.drefh << RTC_CNTL_DREFH_SDIO_S); + val |= (config.drefm << RTC_CNTL_DREFM_SDIO_S); + val |= (config.drefl << RTC_CNTL_DREFL_SDIO_S); + val |= (config.tieh << RTC_CNTL_SDIO_TIEH_S); + val |= RTC_CNTL_SDIO_PD_EN; + REG_WRITE(RTC_CNTL_SDIO_CONF_REG, val); +} diff --git a/components/soc/esp32/rtc_sleep.c b/components/soc/esp32/rtc_sleep.c index 0b20692fa..82a04d8ca 100644 --- a/components/soc/esp32/rtc_sleep.c +++ b/components/soc/esp32/rtc_sleep.c @@ -198,11 +198,9 @@ void rtc_sleep_init(rtc_sleep_config_t cfg) REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0); } - if (cfg.vddsdio_pd_en) { - SET_PERI_REG_MASK(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN); - } else { - CLEAR_PERI_REG_MASK(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN); - } + /* enable VDDSDIO control by state machine */ + REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE); + REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, cfg.rtc_dbias_slp); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, cfg.rtc_dbias_wak); From a02b30ccdafc197b78bb647249974bcd306b5ece Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 3 Nov 2017 14:54:02 +0800 Subject: [PATCH 39/44] efuse: add package definitions for PICOD2/D4 --- components/esp32/system_api.c | 6 ++++-- components/soc/esp32/include/soc/efuse_reg.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 37958db40..0a5d51f66 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -397,8 +397,10 @@ static void get_chip_info_esp32(esp_chip_info_t* out_info) if ((reg & EFUSE_RD_CHIP_VER_DIS_BT_M) == 0) { out_info->features |= CHIP_FEATURE_BT | CHIP_FEATURE_BLE; } - if (((reg & EFUSE_RD_CHIP_VER_PKG_M) >> EFUSE_RD_CHIP_VER_PKG_S) == - EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { + int package = (reg & EFUSE_RD_CHIP_VER_PKG_M) >> EFUSE_RD_CHIP_VER_PKG_S; + if (package == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || + package == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 || + package == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { out_info->features |= CHIP_FEATURE_EMB_FLASH; } } diff --git a/components/soc/esp32/include/soc/efuse_reg.h b/components/soc/esp32/include/soc/efuse_reg.h index b9ad20ee6..affcfa878 100644 --- a/components/soc/esp32/include/soc/efuse_reg.h +++ b/components/soc/esp32/include/soc/efuse_reg.h @@ -100,6 +100,8 @@ #define EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 0 #define EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 1 #define EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 2 +#define EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 4 +#define EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 5 /* EFUSE_RD_SPI_PAD_CONFIG_HD : RO ;bitpos:[8:4] ;default: 5'b0 ; */ /*description: read for SPI_pad_config_hd*/ #define EFUSE_RD_SPI_PAD_CONFIG_HD 0x0000001F From d034bc9ca04d86e1d3264090763489a5e60d52fb Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 3 Nov 2017 15:09:19 +0800 Subject: [PATCH 40/44] bootloader: add configuration of flash pins and VDDIO boost --- components/bootloader/Kconfig.projbuild | 10 ++ .../subproject/main/bootloader_start.c | 103 ++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 5cb315b5f..c61808591 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -43,6 +43,16 @@ config BOOTLOADER_SPI_WP_PIN The default value (GPIO 7) is correct for WP pin on ESP32-D2WD integrated flash. +config BOOTLOADER_VDDSDIO_BOOST + bool "Increase VDDSDIO LDO voltage to 1.9V" + default y + help + If this option is enabled, and VDDSDIO LDO is set to 1.8V (using EFUSE + or MTDI bootstrapping pin), bootloader will change LDO settings to + output 1.9V instead. This helps prevent flash chip from browning out + during flash programming operations. + For 3.3V flash, this option has no effect. + endmenu # Bootloader diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index 012348ddf..5b68b1979 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -73,6 +73,8 @@ static void set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_size, uint32_t entry_addr); static void update_flash_config(const esp_image_header_t* pfhdr); +static void vddsdio_configure(); +static void flash_gpio_configure(); static void clock_configure(void); static void uart_console_configure(void); static void wdt_reset_check(void); @@ -443,6 +445,8 @@ static bool load_boot_image(const bootloader_state_t *bs, int start_index, esp_i void bootloader_main() { + vddsdio_configure(); + flash_gpio_configure(); clock_configure(); uart_console_configure(); wdt_reset_check(); @@ -737,6 +741,105 @@ static void print_flash_info(const esp_image_header_t* phdr) } +static void vddsdio_configure() +{ +#if CONFIG_BOOTLOADER_VDDSDIO_BOOST + rtc_vddsdio_config_t cfg = rtc_vddsdio_get_config(); + if (cfg.tieh == 0) { // 1.8V is used + cfg.drefh = 3; + cfg.drefm = 3; + cfg.drefl = 3; + cfg.force = 1; + cfg.enable = 1; + rtc_vddsdio_set_config(cfg); + ets_delay_us(10); // wait for regulator to become stable + } +#endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST +} + + +#define FLASH_CLK_IO 6 +#define FLASH_CS_IO 11 +#define FLASH_SPIQ_IO 7 +#define FLASH_SPID_IO 8 +#define FLASH_SPIWP_IO 10 +#define FLASH_SPIHD_IO 9 +#define FLASH_IO_MATRIX_DUMMY_40M 1 +#define FLASH_IO_MATRIX_DUMMY_80M 2 +static void IRAM_ATTR flash_gpio_configure() +{ + int spi_cache_dummy = 0; + int drv = 2; +#if CONFIG_FLASHMODE_QIO + spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; //qio 3 +#elif CONFIG_FLASHMODE_QOUT + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; //qout 7 +#elif CONFIG_FLASHMODE_DIO + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; //dio 3 +#elif CONFIG_FLASHMODE_DOUT + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; //dout 7 +#endif + /* dummy_len_plus values defined in ROM for SPI flash configuration */ + extern uint8_t g_rom_spiflash_dummy_len_plus[]; +#if CONFIG_ESPTOOLPY_FLASHFREQ_40M + g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_40M; + g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY +#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M + g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_80M; + g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_80M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + drv = 3; +#endif + + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { + // For ESP32D2WD the SPI pins are already configured + ESP_LOGI(TAG, "Detected ESP32D2WD"); + //flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + // For ESP32PICOD2 the SPI pins are already configured + ESP_LOGI(TAG, "Detected ESP32PICOD2"); + //flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { + // For ESP32PICOD4 the SPI pins are already configured + ESP_LOGI(TAG, "Detected ESP32PICOD4"); + //flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else { + ESP_LOGI(TAG, "Detected ESP32"); + const uint32_t spiconfig = ets_efuse_get_spiconfig(); + if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) { + gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); + gpio_matrix_out(FLASH_SPIQ_IO, SPIQ_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIQ_IO, SPIQ_IN_IDX, 0); + gpio_matrix_out(FLASH_SPID_IO, SPID_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPID_IO, SPID_IN_IDX, 0); + gpio_matrix_out(FLASH_SPIWP_IO, SPIWP_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIWP_IO, SPIWP_IN_IDX, 0); + gpio_matrix_out(FLASH_SPIHD_IO, SPIHD_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIHD_IO, SPIHD_IN_IDX, 0); + //select pin function gpio + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO); + // flash clock signal should come from IO MUX. + // set drive ability for clock + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } + } +} + static void clock_configure(void) { /* Set CPU to 80MHz. Keep other clocks unmodified. */ From cea7dfbe90469938b95cf162350a0e7458f616c5 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 3 Nov 2017 15:10:47 +0800 Subject: [PATCH 41/44] psram: improve clock signal generation, increase drive strength Also check the chip type when initializing. --- components/esp32/spiram_psram.c | 56 ++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/components/esp32/spiram_psram.c b/components/esp32/spiram_psram.c index 6be905d14..5d87a0a2b 100644 --- a/components/esp32/spiram_psram.c +++ b/components/esp32/spiram_psram.c @@ -22,6 +22,7 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_types.h" +#include "esp_log.h" #include "spiram_psram.h" #include "rom/ets_sys.h" #include "rom/spi_flash.h" @@ -30,6 +31,7 @@ #include "soc/io_mux_reg.h" #include "soc/dport_reg.h" #include "soc/gpio_sig_map.h" +#include "soc/efuse_reg.h" #include "driver/gpio.h" #include "driver/spi_common.h" @@ -71,6 +73,8 @@ #define PSRAM_SPIWP_IO 10 #define PSRAM_SPIHD_IO 9 +#define PSRAM_INTERNAL_IO_28 28 +#define PSRAM_INTERNAL_IO_29 29 #define PSRAM_IO_MATRIX_DUMMY_40M 1 #define PSRAM_IO_MATRIX_DUMMY_80M 2 @@ -84,7 +88,7 @@ #define SPI_CACHE_DUMMY SPI0_R_FAST_DUMMY_CYCLELEN //dout 7 #endif - +static const char* TAG = "psram"; typedef enum { PSRAM_SPI_1 = 0x1, PSRAM_SPI_2, @@ -418,7 +422,6 @@ void IRAM_ATTR psram_spi_init(psram_spi_num_t spi_num, psram_cache_mode_t mode) static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) { - gpio_matrix_out(FLASH_CLK_IO, SPICLK_OUT_IDX, 0, 0); gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); gpio_matrix_out(PSRAM_SPIQ_IO, SPIQ_OUT_IDX, 0, 0); gpio_matrix_in(PSRAM_SPIQ_IO, SPIQ_IN_IDX, 0); @@ -434,16 +437,25 @@ static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M; SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S); break; case PSRAM_CACHE_F80M_S80M: extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M; g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_80M; SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 3, FUN_DRV_S); break; case PSRAM_CACHE_F40M_S40M: extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M; SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 2, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S); break; default: break; @@ -451,17 +463,31 @@ static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) SET_PERI_REG_MASK(SPI_USER_REG(0), SPI_USR_DUMMY); // dummy en //select pin function gpio - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 2); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 2); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, 2); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, 2); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 2); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO); + //flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); } //psram gpio init , different working frequency we have different solutions esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) //psram init { + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { + ESP_EARLY_LOGE(TAG, "ESP32D2WD do not support psram yet"); + return ESP_FAIL; + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + ESP_EARLY_LOGE(TAG, "ESP32PICOD2 do not support psram yet"); + return ESP_FAIL; + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { + ESP_EARLY_LOGE(TAG, "ESP32PICOD4 do not support psram yet"); + return ESP_FAIL; + } + /* note: If the third mode(80Mhz+80Mhz) is enabled, VSPI port will be occupied by the system, Application code should never touch VSPI hardware in this case. We try to stop applications from doing this using the drivers by claiming the port for ourselves*/ @@ -515,20 +541,20 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad We do this by routing it signal to signal 224/225, which are used as a loopback; the extra run through the GPIO matrix causes the delay. We use GPIO20 (which is not in any package but has pad logic in silicon) as a temporary pad for this. So the signal path is: - GPIO6(SPI CLK) --> signal224(in then out) --> internal GPIO20 --> signal225(in then out) --> GPIO17(PSRAM CLK) + SPI CLK --> GPIO28 --> signal224(in then out) --> internal GPIO29 --> signal225(in then out) --> GPIO17(PSRAM CLK) */ - gpio_matrix_in(FLASH_CLK_IO, SIG_IN_FUNC224_IDX, 0); - gpio_matrix_out(20, SIG_IN_FUNC224_IDX, 0, 0); - gpio_matrix_in(20, SIG_IN_FUNC225_IDX, 0); + gpio_matrix_out(PSRAM_INTERNAL_IO_28, SPICLK_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC224_IDX, 0); + gpio_matrix_out(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC224_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC225_IDX, 0); gpio_matrix_out(PSRAM_CLK_IO, SIG_IN_FUNC225_IDX, 0, 0); break; } CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_SETUP_M); - psram_gpio_config(mode); WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, BIT(PSRAM_CS_IO)| BIT(PSRAM_CLK_IO)); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], 2); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], 2); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], PIN_FUNC_GPIO); uint32_t id; psram_read_id(&id); if (((id >> PSRAM_MFG_ID_S) & PSRAM_MFG_ID_M) != PSRAM_MFG_ID_V) { From 13621852dd94003d9257ec727c8b246efb6b949e Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 3 Nov 2017 23:02:35 +0800 Subject: [PATCH 42/44] esp32: add wifi lib which is compiled with psram gcc Add psram wifi lib because it doesn't impact the WiFi throughput once the psram is not enabled in IDF menuconfig --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 8d2b43535..bd07fc21e 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 8d2b43535ea5af93e1b0fc0d04eb1913b5440b8b +Subproject commit bd07fc21efcf7a91a088cb01e56d24a7ba6bc3a6 From 670733df9fccc35be9a8874e049bec1dbc60f2ea Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 25 Oct 2017 15:22:30 +0800 Subject: [PATCH 43/44] spi_flash: Abort on writes to dangerous regions (bootloader, partition table, app) Can be disabled or made into a failure result in kconfig if needed. --- components/spi_flash/Kconfig | 25 +++++++++++ components/spi_flash/flash_ops.c | 45 +++++++++++++++++++ .../spi_flash/test/test_out_of_bounds_write.c | 33 ++++++++++++++ tools/unit-test-app/sdkconfig.defaults | 1 + 4 files changed, 104 insertions(+) create mode 100644 components/spi_flash/test/test_out_of_bounds_write.c diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 8b9c71482..4028cf389 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -21,6 +21,31 @@ config SPI_FLASH_ROM_DRIVER_PATCH This option is needed to write to flash on ESP32-D2WD, and any configuration where external SPI flash is connected to non-default pins. +choice SPI_FLASH_WRITING_DANGEROUS_REGIONS + bool "Writing to dangerous flash regions" + default SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS + help + SPI flash APIs can optionally abort or return a failure code + if erasing or writing addresses that fall at the beginning + of flash (covering the bootloader and partition table) or that + overlap the app partition that contains the running app. + + It is not recommended to ever write to these regions from an IDF app, + and this check prevents logic errors or corrupted firmware memory from + damaging these regions. + + Note that this feature *does not* check calls to the esp_rom_xxx SPI flash + ROM functions. These functions should not be called directly from IDF + applications. + +config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS + bool "Aborts" +config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS + bool "Fails" +config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED + bool "Allowed" +endchoice + endmenu diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index bbc65e4ca..4b811d115 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -31,6 +31,8 @@ #include "esp_spi_flash.h" #include "esp_log.h" #include "esp_clk.h" +#include "esp_flash_partitions.h" +#include "esp_ota_ops.h" #include "cache_utils.h" /* bytes erased by SPIEraseBlock() ROM function */ @@ -83,6 +85,45 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = { static const spi_flash_guard_funcs_t *s_flash_guard_ops; +#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS +#define UNSAFE_WRITE_ADDRESS abort() +#else +#define UNSAFE_WRITE_ADDRESS return false +#endif + + +/* CHECK_WRITE_ADDRESS macro to fail writes which land in the + bootloader, partition table, or running application region. +*/ +#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#define CHECK_WRITE_ADDRESS(ADDR, SIZE) +#else /* FAILS or ABORTS */ +#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \ + if (!is_safe_write_address(ADDR, SIZE)) { \ + return ESP_ERR_INVALID_ARG; \ + } \ + } while(0) +#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED + +static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size) +{ + bool result = true; + if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) { + UNSAFE_WRITE_ADDRESS; + } + + const esp_partition_t *p = esp_ota_get_running_partition(); + if (addr >= p->address && addr < p->address + p->size) { + UNSAFE_WRITE_ADDRESS; + } + if (addr < p->address && addr + size > p->address) { + UNSAFE_WRITE_ADDRESS; + } + + return result; +} + + void spi_flash_init() { spi_flash_init_lock(); @@ -146,11 +187,13 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock() esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { + CHECK_WRITE_ADDRESS(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); } esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) { + CHECK_WRITE_ADDRESS(start_addr, size); if (start_addr % SPI_FLASH_SEC_SIZE != 0) { return ESP_ERR_INVALID_ARG; } @@ -187,6 +230,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) { + CHECK_WRITE_ADDRESS(dst, size); // Out of bound writes are checked in ROM code, but we can give better // error code here if (dst + size > g_rom_flashchip.chip_size) { @@ -281,6 +325,7 @@ out: esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size) { + CHECK_WRITE_ADDRESS(dest_addr, size); const uint8_t *ssrc = (const uint8_t *)src; if ((dest_addr % 16) != 0) { return ESP_ERR_INVALID_ARG; diff --git a/components/spi_flash/test/test_out_of_bounds_write.c b/components/spi_flash/test/test_out_of_bounds_write.c new file mode 100644 index 000000000..3c9b01747 --- /dev/null +++ b/components/spi_flash/test/test_out_of_bounds_write.c @@ -0,0 +1,33 @@ +#include + +#include "unity.h" +#include "esp_spi_flash.h" +#include "esp_ota_ops.h" + +#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS || CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS + +static const char *data = "blah blah blah"; + +#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS +#define TEST_TAGS "[spi_flash]" +#else // ABORTS +#define TEST_TAGS "[spi_flash][ignore]" +#endif + +TEST_CASE("can't overwrite bootloader", TEST_TAGS) +{ + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1000, data, strlen(data))); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x0FF8, data, strlen(data))); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1400, data, strlen(data))); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x8000, 0x2000)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x7000, 0x2000)); +} + +TEST_CASE("can't overwrite current running app", TEST_TAGS) +{ + const esp_partition_t *p = esp_ota_get_running_partition(); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(p->address + 1024, data, strlen(data))); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(p->address + 4096, 8192)); +} + +#endif // FAILS || ABORTS diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index 5875356e6..5bad91322 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -19,3 +19,4 @@ CONFIG_MBEDTLS_HARDWARE_SHA=y CONFIG_SPI_FLASH_ENABLE_COUNTERS=y CONFIG_ULP_COPROC_ENABLED=y CONFIG_TASK_WDT=n +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y From 0bc3b3205d421e303f602c805c294410a737364f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 5 Nov 2017 21:50:43 +0800 Subject: [PATCH 44/44] ci: add one more job for single core unit tests --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 982a97bdf..877d2c0ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -709,6 +709,13 @@ UT_003_07: - UT_T1_1 - UT_single_core +UT_003_08: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + - UT_single_core + IT_001_01: <<: *test_template tags: